001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar 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 * Sonar 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
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.batch.index;
021
022 import com.google.common.collect.LinkedHashMultimap;
023 import com.google.common.collect.SetMultimap;
024 import org.apache.commons.lang.math.NumberUtils;
025 import org.slf4j.LoggerFactory;
026 import org.sonar.api.database.DatabaseSession;
027 import org.sonar.api.database.model.MeasureModel;
028 import org.sonar.api.database.model.Snapshot;
029 import org.sonar.api.measures.Measure;
030 import org.sonar.api.measures.Metric;
031 import org.sonar.api.measures.RuleMeasure;
032 import org.sonar.api.resources.Resource;
033 import org.sonar.api.resources.ResourceUtils;
034 import org.sonar.api.rules.Rule;
035 import org.sonar.api.rules.RuleFinder;
036 import org.sonar.api.utils.SonarException;
037
038 import java.util.Collection;
039 import java.util.Map;
040
041 public final class MeasurePersister {
042
043 private boolean delayedMode = false;
044 private SetMultimap<Resource, Measure> unsavedMeasuresByResource = LinkedHashMultimap.create();
045 private DatabaseSession session;
046 private ResourcePersister resourcePersister;
047 private RuleFinder ruleFinder;
048 private MemoryOptimizer memoryOptimizer;
049
050
051 public MeasurePersister(DatabaseSession session, ResourcePersister resourcePersister, RuleFinder ruleFinder, MemoryOptimizer memoryOptimizer) {
052 this.session = session;
053 this.resourcePersister = resourcePersister;
054 this.ruleFinder = ruleFinder;
055 this.memoryOptimizer = memoryOptimizer;
056 }
057
058 public void setDelayedMode(boolean delayedMode) {
059 this.delayedMode = delayedMode;
060 }
061
062 public void saveMeasure(Resource resource, Measure measure) {
063 boolean saveLater = (measure.getPersistenceMode().useMemory() && delayedMode);
064 if (saveLater) {
065 unsavedMeasuresByResource.put(resource, measure);
066
067 } else {
068 Snapshot snapshot = resourcePersister.getSnapshotOrFail(resource);
069 MeasureModel model = null;
070 if (measure.getId() != null) {
071 // update
072 model = session.reattach(MeasureModel.class, measure.getId());
073 model = mergeModel(measure, model);
074 model.save(session);
075
076 } else if (shouldPersistMeasure(resource, measure)) {
077 // insert
078 model = createModel(measure);
079 model.setSnapshotId(snapshot.getId());
080 model.save(session);
081 measure.setId(model.getId()); // could be removed
082 }
083 if (model != null) {
084 memoryOptimizer.evictDataMeasure(measure, model);
085 }
086 }
087 }
088
089 public Measure reloadMeasure(Measure measure) {
090 return memoryOptimizer.reloadMeasure(measure);
091 }
092
093 static boolean shouldPersistMeasure(Resource resource, Measure measure) {
094 Metric metric = measure.getMetric();
095 return measure.getPersistenceMode().useDatabase() &&
096 !(ResourceUtils.isEntity(resource) && isBestValueMeasure(measure, metric));
097 }
098
099 static boolean isBestValueMeasure(Measure measure, Metric metric) {
100 return measure.getId() == null &&
101 metric.isOptimizedBestValue() == Boolean.TRUE &&
102 metric.getBestValue() != null &&
103 (measure.getValue() == null || NumberUtils.compare(metric.getBestValue(), measure.getValue()) == 0) &&
104 measure.getAlertStatus() == null &&
105 measure.getDescription() == null &&
106 measure.getTendency() == null &&
107 measure.getUrl() == null &&
108 !measure.hasData() &&
109 (measure.getVariation1() == null || NumberUtils.compare(measure.getVariation1().doubleValue(), 0.0) == 0) &&
110 (measure.getVariation2() == null || NumberUtils.compare(measure.getVariation2().doubleValue(), 0.0) == 0) &&
111 (measure.getVariation3() == null || NumberUtils.compare(measure.getVariation3().doubleValue(), 0.0) == 0) &&
112 (measure.getVariation4() == null || NumberUtils.compare(measure.getVariation4().doubleValue(), 0.0) == 0) &&
113 (measure.getVariation5() == null || NumberUtils.compare(measure.getVariation5().doubleValue(), 0.0) == 0);
114 }
115
116 public void dump() {
117 LoggerFactory.getLogger(getClass()).debug("{} measures to dump", unsavedMeasuresByResource.size());
118 Map<Resource, Collection<Measure>> map = unsavedMeasuresByResource.asMap();
119 for (Map.Entry<Resource, Collection<Measure>> entry : map.entrySet()) {
120 Resource resource = entry.getKey();
121 Snapshot snapshot = resourcePersister.getSnapshot(entry.getKey());
122 for (Measure measure : entry.getValue()) {
123 if (shouldPersistMeasure(resource, measure)) {
124 MeasureModel model = createModel(measure);
125 model.setSnapshotId(snapshot.getId());
126 model.save(session);
127 }
128 }
129 }
130
131 session.commit();
132 unsavedMeasuresByResource.clear();
133 }
134
135 MeasureModel createModel(Measure measure) {
136 return mergeModel(measure, new MeasureModel());
137 }
138
139 MeasureModel mergeModel(Measure measure, MeasureModel merge) {
140 merge.setMetricId(measure.getMetric().getId());// we assume that the index has updated the metric
141 merge.setDescription(measure.getDescription());
142 merge.setData(measure.getData());
143 merge.setAlertStatus(measure.getAlertStatus());
144 merge.setAlertText(measure.getAlertText());
145 merge.setTendency(measure.getTendency());
146 merge.setVariationValue1(measure.getVariation1());
147 merge.setVariationValue2(measure.getVariation2());
148 merge.setVariationValue3(measure.getVariation3());
149 merge.setVariationValue4(measure.getVariation4());
150 merge.setVariationValue5(measure.getVariation5());
151 merge.setUrl(measure.getUrl());
152 merge.setCharacteristic(measure.getCharacteristic());
153 merge.setPersonId(measure.getPersonId());
154 if (measure.getValue() != null) {
155 merge.setValue(measure.getValue().doubleValue());
156 } else {
157 merge.setValue(null);
158 }
159 if (measure instanceof RuleMeasure) {
160 RuleMeasure ruleMeasure = (RuleMeasure) measure;
161 merge.setRulePriority(ruleMeasure.getSeverity());
162 if (ruleMeasure.getRule() != null) {
163 Rule ruleWithId = ruleFinder.findByKey(ruleMeasure.getRule().getRepositoryKey(), ruleMeasure.getRule().getKey());
164 if (ruleWithId != null) {
165 merge.setRuleId(ruleWithId.getId());
166 } else {
167 throw new SonarException("Can not save a measure with unknown rule " + ruleMeasure);
168 }
169 }
170 }
171 return merge;
172 }
173 }