]> source.dussan.org Git - sonarqube.git/blob
23d2e79e95f10674068f1a43942f2c5c6e4f39e9
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2014 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * SonarQube is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * SonarQube is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.db.migrations.v501;
21
22 import com.google.common.base.Predicate;
23 import com.google.common.collect.Iterables;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.sonar.api.utils.System2;
27 import org.sonar.core.persistence.Database;
28 import org.sonar.server.db.migrations.BaseDataChange;
29 import org.sonar.server.db.migrations.Select;
30
31 import javax.annotation.CheckForNull;
32 import javax.annotation.Nullable;
33
34 import java.sql.SQLException;
35 import java.util.Date;
36 import java.util.List;
37
38 /**
39  * See http://jira.codehaus.org/browse/SONAR-6187
40  *
41  * Add a new Characteristic 'Usability' with 2 sub-characteristics 'Accessibility' and 'Ease of Use'
42  * and add a new sub-characteristic 'Compliance' for all characteristics.
43  *
44  * Nothing will be done if there's no characteristics in db, as they're all gonna be created by {@link org.sonar.server.startup.RegisterDebtModel}
45  *
46  * Before 4.3 the characteristics table contains requirements, then when selecting characteristics we should not forget to exclude them (with a filter on rule_id IS NULL)
47  *
48  */
49 public class AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration extends BaseDataChange {
50
51   private static final Logger LOGGER = LoggerFactory.getLogger(AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration.class);
52
53   private static final String COMPLIANCE_NAME = "Compliance";
54   private static final String COMPLIANCE_KEY_SUFFIX = "_COMPLIANCE";
55
56   private final System2 system;
57
58   public AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration(Database db, System2 system) {
59     super(db);
60     this.system = system;
61   }
62
63   @Override
64   public void execute(Context context) throws SQLException {
65     CharacteristicsContext characteristicsContext = new CharacteristicsContext(context, system);
66
67     // On an empty DB, there are no characteristics, they're all gonna be created after in RegisterDebtModel
68     if (!characteristicsContext.characteristics().isEmpty()) {
69       int usabilityOder = moveCharacteristicsDownToBeAbleToInsertUsability(characteristicsContext);
70       createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(characteristicsContext, usabilityOder);
71
72       createSubCharacteristic(characteristicsContext, "REUSABILITY" + COMPLIANCE_KEY_SUFFIX, "Reusability " + COMPLIANCE_NAME, "REUSABILITY");
73       createSubCharacteristic(characteristicsContext, "PORTABILITY" + COMPLIANCE_KEY_SUFFIX, "Portability " + COMPLIANCE_NAME, "PORTABILITY");
74       createSubCharacteristic(characteristicsContext, "MAINTAINABILITY" + COMPLIANCE_KEY_SUFFIX, "Maintainability " + COMPLIANCE_NAME, "MAINTAINABILITY");
75       createSubCharacteristic(characteristicsContext, "SECURITY" + COMPLIANCE_KEY_SUFFIX, "Security " + COMPLIANCE_NAME, "SECURITY");
76       createSubCharacteristic(characteristicsContext, "EFFICIENCY" + COMPLIANCE_KEY_SUFFIX, "Efficiency " + COMPLIANCE_NAME, "EFFICIENCY");
77       createSubCharacteristic(characteristicsContext, "CHANGEABILITY" + COMPLIANCE_KEY_SUFFIX, "Changeability " + COMPLIANCE_NAME, "CHANGEABILITY");
78       createSubCharacteristic(characteristicsContext, "RELIABILITY" + COMPLIANCE_KEY_SUFFIX, "Reliability " + COMPLIANCE_NAME, "RELIABILITY");
79       createSubCharacteristic(characteristicsContext, "TESTABILITY" + COMPLIANCE_KEY_SUFFIX, "Testability " + COMPLIANCE_NAME, "TESTABILITY");
80     }
81   }
82
83   /**
84    * If the characteristic 'Security' exists, the new characteristic 'Usability' should be inserted just below it,
85    * so every existing characteristics below Security should move down.
86    *
87    * If the characteristic 'Security' does not exists, the new characteristic 'Usability' should be the first one,
88    * so every existing characteristics should move down.
89    *
90    * If the characteristic 'Usability' is already at the right place, nothing will be done.
91    */
92   private int moveCharacteristicsDownToBeAbleToInsertUsability(CharacteristicsContext characteristicsContext) throws SQLException {
93     Characteristic security = characteristicsContext.findCharacteristicByKey("SECURITY");
94     Characteristic usability = characteristicsContext.findCharacteristicByKey("USABILITY");
95
96     int usabilityOder = 1;
97     int indexToStart = 0;
98     if (security != null) {
99       indexToStart = characteristicsContext.characteristics().indexOf(security) + 1;
100       usabilityOder = security.getOrder() + 1;
101     }
102
103     if (usability == null || usability.getOrder() != usabilityOder) {
104       // Move root characteristics one step lower
105       for (int i = indexToStart; i < characteristicsContext.characteristics().size(); i++) {
106         Characteristic characteristic = characteristicsContext.characteristics().get(i);
107         if (characteristic.getParentId() == null) {
108           characteristicsContext.updateCharacteristicOrder(characteristic.getKey(), characteristic.getOrder() + 1);
109         }
110       }
111     }
112     return usabilityOder;
113   }
114
115   private void createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(CharacteristicsContext characteristicsContext, int newUsabilityOrder)
116     throws SQLException {
117     String usabilityKey = "USABILITY";
118     Characteristic usability = characteristicsContext.findCharacteristicByKey(usabilityKey);
119     if (usability != null) {
120       if (usability.getOrder() != newUsabilityOrder) {
121         usability.setOrder(newUsabilityOrder);
122         characteristicsContext.updateCharacteristicOrder(usability.getKey(), usability.getOrder());
123       }
124     } else {
125       usability = new Characteristic().setKey(usabilityKey).setName("Usability").setOrder(newUsabilityOrder);
126       characteristicsContext.insertCharacteristic(usability);
127     }
128
129     createSubCharacteristic(characteristicsContext, "USABILITY_ACCESSIBILITY", "Accessibility", usabilityKey);
130     createSubCharacteristic(characteristicsContext, "USABILITY_EASE_OF_USE", "Ease of Use", usabilityKey);
131     createSubCharacteristic(characteristicsContext, "USABILITY" + COMPLIANCE_KEY_SUFFIX, "Usability " + COMPLIANCE_NAME, usabilityKey);
132   }
133
134   private void createSubCharacteristic(CharacteristicsContext characteristicsContext,
135     String subCharacteristicKey, String subCharacteristicName, String parentKey) throws SQLException {
136     Characteristic parent = characteristicsContext.findCharacteristicByKey(parentKey);
137     if (parent != null) {
138       Characteristic subCharacteristic = characteristicsContext.findSubCharacteristicByKey(subCharacteristicKey, parent);
139       if (subCharacteristic == null) {
140         characteristicsContext.insertCharacteristic(new Characteristic().setKey(subCharacteristicKey).setName(subCharacteristicName).setParentId(parent.getId()));
141       }
142     }
143     // If the characteristic parent does not exits, the sub-characteristic is not added
144   }
145
146   private static class Characteristic {
147     private Integer id;
148     private String key;
149     private String name;
150     private Integer order;
151     private Integer parentId;
152
153     public Integer getId() {
154       return id;
155     }
156
157     public Characteristic setId(Integer id) {
158       this.id = id;
159       return this;
160     }
161
162     public String getKey() {
163       return key;
164     }
165
166     public Characteristic setKey(String key) {
167       this.key = key;
168       return this;
169     }
170
171     public String getName() {
172       return name;
173     }
174
175     public Characteristic setName(String name) {
176       this.name = name;
177       return this;
178     }
179
180     @CheckForNull
181     public Integer getOrder() {
182       return order;
183     }
184
185     public Characteristic setOrder(@Nullable Integer order) {
186       this.order = order;
187       return this;
188     }
189
190     @CheckForNull
191     public Integer getParentId() {
192       return parentId;
193     }
194
195     public Characteristic setParentId(@Nullable Integer parentId) {
196       this.parentId = parentId;
197       return this;
198     }
199   }
200
201   private static class CharacteristicsContext {
202     private final System2 system;
203     Context context;
204     Date now;
205     List<Characteristic> characteristics;
206
207     public CharacteristicsContext(Context context, System2 system) throws SQLException {
208       this.context = context;
209       this.system = system;
210       init();
211     }
212
213     private void init() throws SQLException {
214       now = new Date(system.now());
215       characteristics = selectEnabledCharacteristics();
216     }
217
218     public List<Characteristic> characteristics() {
219       return characteristics;
220     }
221
222     @CheckForNull
223     public Characteristic findCharacteristicByKey(final String key) {
224       Characteristic characteristic = Iterables.find(characteristics, new Predicate<Characteristic>() {
225         @Override
226         public boolean apply(@Nullable Characteristic input) {
227           return input != null && input.key.equals(key);
228         }
229       }, null);
230       if (characteristic != null) {
231         if (characteristic.getParentId() != null) {
232           throw new IllegalStateException(String.format("'%s' must be a characteristic", characteristic.getName()));
233         }
234       }
235       return characteristic;
236     }
237
238     @CheckForNull
239     public Characteristic findSubCharacteristicByKey(final String key, Characteristic parent) {
240       Characteristic characteristic = Iterables.find(characteristics, new Predicate<Characteristic>() {
241         @Override
242         public boolean apply(@Nullable Characteristic input) {
243           return input != null && input.key.equals(key);
244         }
245       }, null);
246       if (characteristic != null) {
247         Integer parentId = characteristic.getParentId();
248         if (parentId == null) {
249           throw new IllegalStateException(String.format("'%s' must be a sub-characteristic", characteristic.getName()));
250         } else if (!characteristic.getParentId().equals(parent.getId())) {
251           throw new IllegalStateException(String.format("'%s' must be defined under '%s'", characteristic.getName(), parent.getName()));
252         }
253       }
254       return characteristic;
255     }
256
257     private List<Characteristic> selectEnabledCharacteristics() throws SQLException {
258       return context.prepareSelect(
259         // Exclude requirements (to not fail when coming from a version older than 4.3)
260         "SELECT c.id, c.kee, c.name, c.characteristic_order, c.parent_id FROM characteristics c WHERE c.enabled=? AND c.rule_id IS NULL ORDER BY c.characteristic_order")
261         .setBoolean(1, true)
262         .list(new CharacteristicReader());
263     }
264
265     private int selectCharacteristicId(String key) throws SQLException {
266       return context.prepareSelect(
267         "SELECT c.id FROM characteristics c WHERE c.kee = ? AND c.enabled=?")
268         .setString(1, key)
269         .setBoolean(2, true)
270         .get(Select.LONG_READER).intValue();
271     }
272
273     public void insertCharacteristic(Characteristic characteristic) throws SQLException {
274       if (characteristic.getParentId() == null) {
275         LOGGER.info("Insert new characteristic '{}'", characteristic.getKey());
276       } else {
277         LOGGER.info("Insert new sub characteristic '{}'", characteristic.getKey());
278       }
279
280       context.prepareUpsert("INSERT INTO characteristics (kee, name, parent_id, characteristic_order, enabled, created_at) VALUES (?, ?, ?, ?, ?, ?)")
281         .setString(1, characteristic.getKey())
282         .setString(2, characteristic.getName())
283         .setInt(3, characteristic.getParentId())
284         .setInt(4, characteristic.getOrder())
285         .setBoolean(5, true)
286         .setDate(6, now)
287         .execute()
288         .commit();
289       characteristic.setId(selectCharacteristicId(characteristic.getKey()));
290
291       characteristics.add(characteristic);
292     }
293
294     public void updateCharacteristicOrder(String key, Integer order) throws SQLException {
295       LOGGER.info("Update characteristic '{}' order to {}", key, order);
296
297       context.prepareUpsert("UPDATE characteristics SET characteristic_order=?, updated_at=? WHERE kee=?")
298         .setInt(1, order)
299         .setDate(2, now)
300         .setString(3, key)
301         .execute()
302         .commit();
303     }
304
305     private static class CharacteristicReader implements Select.RowReader<Characteristic> {
306       @Override
307       public Characteristic read(Select.Row row) throws SQLException {
308         return new Characteristic()
309           .setId(row.getInt(1))
310           .setKey(row.getString(2))
311           .setName(row.getString(3))
312           .setOrder(row.getInt(4))
313           .setParentId(row.getInt(5));
314       }
315     }
316   }
317 }