001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * SonarQube is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.batch.index;
021    
022    import com.google.common.annotations.VisibleForTesting;
023    import org.sonar.api.database.model.MeasureMapper;
024    import org.sonar.api.database.model.MeasureModel;
025    import org.sonar.api.database.model.Snapshot;
026    import org.sonar.api.measures.Measure;
027    import org.sonar.api.measures.RuleMeasure;
028    import org.sonar.api.resources.Resource;
029    import org.sonar.api.resources.ResourceUtils;
030    import org.sonar.api.rule.RuleKey;
031    import org.sonar.api.rules.Rule;
032    import org.sonar.api.rules.RuleFinder;
033    import org.sonar.api.technicaldebt.batch.Characteristic;
034    import org.sonar.batch.index.Cache.Entry;
035    import org.sonar.batch.scan.measure.MeasureCache;
036    import org.sonar.core.persistence.DbSession;
037    import org.sonar.core.persistence.MyBatis;
038    
039    import javax.annotation.Nullable;
040    
041    public final class MeasurePersister implements ScanPersister {
042      private final MyBatis mybatis;
043      private final RuleFinder ruleFinder;
044      private final MeasureCache measureCache;
045      private final SnapshotCache snapshotCache;
046      private final ResourceCache resourceCache;
047    
048      public MeasurePersister(MyBatis mybatis, RuleFinder ruleFinder,
049        MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache) {
050        this.mybatis = mybatis;
051        this.ruleFinder = ruleFinder;
052        this.measureCache = measureCache;
053        this.snapshotCache = snapshotCache;
054        this.resourceCache = resourceCache;
055      }
056    
057      @Override
058      public void persist() {
059        DbSession session = mybatis.openSession(true);
060        try {
061          MeasureMapper mapper = session.getMapper(MeasureMapper.class);
062    
063          for (Entry<Measure> entry : measureCache.entries()) {
064            String effectiveKey = entry.key()[0].toString();
065            Measure measure = entry.value();
066            Resource resource = resourceCache.get(effectiveKey);
067    
068            if (shouldPersistMeasure(resource, measure)) {
069              Snapshot snapshot = snapshotCache.get(effectiveKey);
070              MeasureModel measureModel = model(measure, ruleFinder).setSnapshotId(snapshot.getId());
071              mapper.insert(measureModel);
072            }
073          }
074    
075          session.commit();
076        } catch (Exception e) {
077          throw new IllegalStateException("Unable to save some measures", e);
078        } finally {
079          MyBatis.closeQuietly(session);
080        }
081      }
082    
083      @VisibleForTesting
084      static boolean shouldPersistMeasure(@Nullable Resource resource, @Nullable Measure measure) {
085        if (resource == null || measure == null) {
086          return false;
087        }
088        return measure.getPersistenceMode().useDatabase() &&
089          !(ResourceUtils.isEntity(resource) && measure.isBestValue()) && isMeasureNotEmpty(measure);
090      }
091    
092      private static boolean isMeasureNotEmpty(Measure measure) {
093        boolean isNotEmpty = false;
094        for (int i = 1; i <= 5; i++) {
095          isNotEmpty = isNotEmpty || measure.getVariation(i) != null;
096        }
097        return measure.getValue() != null || measure.getData() != null
098          || isNotEmpty;
099      }
100    
101      static MeasureModel model(Measure measure, RuleFinder ruleFinder) {
102        MeasureModel model = new MeasureModel();
103        // we assume that the index has updated the metric
104        model.setMetricId(measure.getMetric().getId());
105        model.setDescription(measure.getDescription());
106        model.setData(measure.getData());
107        model.setAlertStatus(measure.getAlertStatus());
108        model.setAlertText(measure.getAlertText());
109        model.setTendency(measure.getTendency());
110        model.setVariationValue1(measure.getVariation1());
111        model.setVariationValue2(measure.getVariation2());
112        model.setVariationValue3(measure.getVariation3());
113        model.setVariationValue4(measure.getVariation4());
114        model.setVariationValue5(measure.getVariation5());
115        model.setUrl(measure.getUrl());
116        Characteristic characteristic = measure.getCharacteristic();
117        if (characteristic != null) {
118          model.setCharacteristicId(characteristic.id());
119        }
120        model.setPersonId(measure.getPersonId());
121        model.setValue(measure.getValue());
122        if (measure instanceof RuleMeasure) {
123          RuleMeasure ruleMeasure = (RuleMeasure) measure;
124          model.setRulePriority(ruleMeasure.getSeverity());
125          RuleKey ruleKey = ruleMeasure.ruleKey();
126          if (ruleKey != null) {
127            Rule ruleWithId = ruleFinder.findByKey(ruleKey);
128            if (ruleWithId == null) {
129              throw new IllegalStateException("Can not save a measure with unknown rule " + ruleMeasure);
130            }
131            model.setRuleId(ruleWithId.getId());
132          }
133        }
134        return model;
135      }
136    }