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.annotations.VisibleForTesting;
023 import com.google.common.collect.LinkedHashMultimap;
024 import com.google.common.collect.Lists;
025 import com.google.common.collect.SetMultimap;
026 import org.apache.ibatis.session.SqlSession;
027 import org.slf4j.LoggerFactory;
028 import org.sonar.api.database.model.MeasureMapper;
029 import org.sonar.api.database.model.MeasureModel;
030 import org.sonar.api.database.model.Snapshot;
031 import org.sonar.api.measures.Measure;
032 import org.sonar.api.measures.RuleMeasure;
033 import org.sonar.api.resources.Resource;
034 import org.sonar.api.resources.ResourceUtils;
035 import org.sonar.api.rules.Rule;
036 import org.sonar.api.rules.RuleFinder;
037 import org.sonar.api.utils.SonarException;
038 import org.sonar.core.persistence.MyBatis;
039
040 import java.util.Collection;
041 import java.util.List;
042 import java.util.Map;
043
044 public final class MeasurePersister {
045 private final MyBatis mybatis;
046 private final ResourcePersister resourcePersister;
047 private final RuleFinder ruleFinder;
048 private final MemoryOptimizer memoryOptimizer;
049 private final SetMultimap<Resource, Measure> unsavedMeasuresByResource = LinkedHashMultimap.create();
050 private boolean delayedMode = false;
051
052 public MeasurePersister(MyBatis mybatis, ResourcePersister resourcePersister, RuleFinder ruleFinder, MemoryOptimizer memoryOptimizer) {
053 this.mybatis = mybatis;
054 this.resourcePersister = resourcePersister;
055 this.ruleFinder = ruleFinder;
056 this.memoryOptimizer = memoryOptimizer;
057 }
058
059 public void setDelayedMode(boolean delayedMode) {
060 this.delayedMode = delayedMode;
061 }
062
063 public Measure reloadMeasure(Measure measure) {
064 return memoryOptimizer.reloadMeasure(measure);
065 }
066
067 public void dump() {
068 LoggerFactory.getLogger(getClass()).debug("{} measures to dump", unsavedMeasuresByResource.size());
069
070 insert(getMeasuresToSave());
071 }
072
073 public void saveMeasure(Resource resource, Measure measure) {
074 if (shouldSaveLater(measure)) {
075 unsavedMeasuresByResource.put(resource, measure);
076 return;
077 }
078
079 MeasureModel model = insertOrUpdate(resource, measure);
080 if (model != null) {
081 memoryOptimizer.evictDataMeasure(measure, model);
082 }
083 }
084
085 private MeasureModel insertOrUpdate(Resource resource, Measure measure) {
086 Snapshot snapshot = resourcePersister.getSnapshotOrFail(resource);
087 if (measure.getId() != null) {
088 return update(measure, snapshot);
089 }
090 if (shouldPersistMeasure(resource, measure)) {
091 MeasureModel insert = insert(measure, snapshot);
092 measure.setId(insert.getId());
093 return insert;
094 }
095 return null;
096 }
097
098 private boolean shouldSaveLater(Measure measure) {
099 return delayedMode && measure.getPersistenceMode().useMemory();
100 }
101
102 @VisibleForTesting
103 static boolean shouldPersistMeasure(Resource resource, Measure measure) {
104 return measure.getPersistenceMode().useDatabase() &&
105 !(ResourceUtils.isEntity(resource) && measure.isBestValue());
106 }
107
108 private List<MeasureModel> getMeasuresToSave() {
109 List<MeasureModel> measures = Lists.newArrayList();
110
111 Map<Resource, Collection<Measure>> map = unsavedMeasuresByResource.asMap();
112 for (Map.Entry<Resource, Collection<Measure>> entry : map.entrySet()) {
113 Resource resource = entry.getKey();
114 Snapshot snapshot = resourcePersister.getSnapshot(entry.getKey());
115 for (Measure measure : entry.getValue()) {
116 if (shouldPersistMeasure(resource, measure)) {
117 measures.add(model(measure).setSnapshotId(snapshot.getId()));
118 }
119 }
120 }
121
122 unsavedMeasuresByResource.clear();
123 return measures;
124 }
125
126 private MeasureModel model(Measure measure) {
127 MeasureModel model = new MeasureModel();
128 model.setMetricId(measure.getMetric().getId()); // we assume that the index has updated the metric
129 model.setDescription(measure.getDescription());
130 model.setData(measure.getData());
131 model.setAlertStatus(measure.getAlertStatus());
132 model.setAlertText(measure.getAlertText());
133 model.setTendency(measure.getTendency());
134 model.setVariationValue1(measure.getVariation1());
135 model.setVariationValue2(measure.getVariation2());
136 model.setVariationValue3(measure.getVariation3());
137 model.setVariationValue4(measure.getVariation4());
138 model.setVariationValue5(measure.getVariation5());
139 model.setUrl(measure.getUrl());
140 model.setCharacteristic(measure.getCharacteristic());
141 model.setPersonId(measure.getPersonId());
142 if (measure.getValue() != null) {
143 model.setValue(measure.getValue().doubleValue());
144 } else {
145 model.setValue(null);
146 }
147 if (measure instanceof RuleMeasure) {
148 RuleMeasure ruleMeasure = (RuleMeasure) measure;
149 model.setRulePriority(ruleMeasure.getSeverity());
150 if (ruleMeasure.getRule() != null) {
151 Rule ruleWithId = ruleFinder.findByKey(ruleMeasure.getRule().getRepositoryKey(), ruleMeasure.getRule().getKey());
152 if (ruleWithId == null) {
153 throw new SonarException("Can not save a measure with unknown rule " + ruleMeasure);
154 }
155 model.setRuleId(ruleWithId.getId());
156 }
157 }
158 return model;
159 }
160
161 private void insert(Iterable<MeasureModel> values) {
162 SqlSession session = mybatis.openSession();
163 try {
164 MeasureMapper mapper = session.getMapper(MeasureMapper.class);
165
166 for (MeasureModel value : values) {
167 mapper.insert(value);
168 if (value.getMeasureData() != null) {
169 mapper.insertData(value.getMeasureData());
170 }
171 }
172
173 session.commit();
174 } finally {
175 MyBatis.closeQuietly(session);
176 }
177 }
178
179 private MeasureModel insert(Measure measure, Snapshot snapshot) {
180 MeasureModel value = model(measure);
181 value.setSnapshotId(snapshot.getId());
182
183 SqlSession session = mybatis.openSession();
184 try {
185 MeasureMapper mapper = session.getMapper(MeasureMapper.class);
186
187 mapper.insert(value);
188 if (value.getMeasureData() != null) {
189 mapper.insertData(value.getMeasureData());
190 }
191
192 session.commit();
193 } finally {
194 MyBatis.closeQuietly(session);
195 }
196
197 return value;
198 }
199
200 private MeasureModel update(Measure measure, Snapshot snapshot) {
201 MeasureModel value = model(measure);
202 value.setId(measure.getId());
203 value.setSnapshotId(snapshot.getId());
204
205 SqlSession session = mybatis.openSession();
206 try {
207 MeasureMapper mapper = session.getMapper(MeasureMapper.class);
208
209 mapper.update(value);
210 mapper.deleteData(value);
211 if (value.getMeasureData() != null) {
212 mapper.insertData(value.getMeasureData());
213 }
214
215 session.commit();
216 } finally {
217 MyBatis.closeQuietly(session);
218 }
219
220 return value;
221 }
222 }