* @since 3.3
*/
PropertyField[] fields() default {};
+
+ /**
+ * Relocation of key.
+ * @since 3.4
+ */
+ String deprecatedKey() default "";
}
private final boolean isGlobal;
private final boolean multiValues;
private final String propertySetKey;
+ private final String deprecatedKey;
private final List<PropertyFieldDefinition> fields;
private PropertyDefinition(Property annotation) {
this.multiValues = annotation.multiValues();
this.propertySetKey = annotation.propertySetKey();
this.fields = ImmutableList.copyOf(PropertyFieldDefinition.create(annotation.fields()));
+ this.deprecatedKey = annotation.deprecatedKey();
}
private PropertyDefinition(String key, PropertyType type, String[] options) {
this.multiValues = false;
this.propertySetKey = null;
this.fields = null;
+ this.deprecatedKey = null;
}
private static PropertyType fixType(String key, PropertyType type) {
public List<PropertyFieldDefinition> getFields() {
return fields;
}
+
+ /**
+ * @since 3.4
+ */
+ public String getDeprecatedKey() {
+ return deprecatedKey;
+ }
}
*/
package org.sonar.api.config;
+import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
private final Map<String, PropertyDefinition> definitions = Maps.newHashMap();
private final Map<String, String> categories = Maps.newHashMap();
+ // deprecated key -> new key
+ private final Map<String, String> deprecatedKeys = Maps.newHashMap();
+
public PropertyDefinitions(Object... components) {
if (components != null) {
addComponents(Arrays.asList(components));
if (!definitions.containsKey(definition.getKey())) {
definitions.put(definition.getKey(), definition);
categories.put(definition.getKey(), StringUtils.defaultIfBlank(definition.getCategory(), defaultCategory));
+ if (!Strings.isNullOrEmpty(definition.getDeprecatedKey()) && !definition.getDeprecatedKey().equals(definition.getKey())) {
+ deprecatedKeys.put(definition.getDeprecatedKey(), definition.getKey());
+ definitions.put(definition.getDeprecatedKey(), definition);
+ }
}
return this;
}
public String getCategory(Property prop) {
return getCategory(prop.key());
}
+
+ public String getNewKey(String deprecatedKey) {
+ return deprecatedKeys.get(deprecatedKey);
+ }
+
+ public String getDeprecatedKey(String key) {
+ PropertyDefinition def = get(key);
+ if (def == null) {
+ return null;
+ }
+ return StringUtils.defaultIfEmpty(def.getDeprecatedKey(), null);
+ }
}
}
public final String getString(String key) {
- String value = properties.get(key);
- if (value == null) {
- value = getDefaultValue(key);
- } else if (encryption.isEncrypted(value)) {
+ String value = getClearString(key);
+ if (value != null && encryption.isEncrypted(value)) {
try {
value = encryption.decrypt(value);
} catch (Exception e) {
throw new IllegalStateException("Fail to set multiple values on a single value property " + key);
}
- if (values == null) {
- properties.remove(key);
- doOnRemoveProperty(key);
- } else {
+ String text = null;
+ if (values != null) {
List<String> escaped = Lists.newArrayList();
for (String value : values) {
if (null != value) {
}
String escapedValue = Joiner.on(',').join(escaped);
- properties.put(key, StringUtils.trim(escapedValue));
- doOnSetProperty(key, escapedValue);
+ text = StringUtils.trim(escapedValue);
}
- return this;
+ return setProperty(key, text);
}
public final Settings setProperty(String key, @Nullable String value) {
+ return setProperty(key, value, true);
+ }
+
+ private Settings setProperty(String key, @Nullable String value, boolean recursive) {
if (value == null) {
properties.remove(key);
doOnRemoveProperty(key);
properties.put(key, StringUtils.trim(value));
doOnSetProperty(key, value);
}
+ if (recursive) {
+ String newKey = definitions.getNewKey(key);
+ if (newKey != null) {
+ setProperty(newKey, value, false);
+ } else {
+ String deprecatedKey = definitions.getDeprecatedKey(key);
+ if (deprecatedKey != null) {
+ setProperty(deprecatedKey, value, false);
+ }
+ }
+ }
return this;
}
}
public final Settings setProperties(Settings s) {
- if (s.properties==null) {
+ if (s.properties == null) {
return clear();
}
return setProperties(Maps.newHashMap(s.properties));
@Property(key = "integer", name = "Integer", defaultValue = "12345"),
@Property(key = "array", name = "Array", defaultValue = "one,two,three"),
@Property(key = "multi_values", name = "Array", defaultValue = "1,2,3", multiValues = true),
- @Property(key = "sonar.jira", name = "Jira Server", type = PropertyType.PROPERTY_SET, propertySetKey = "jira")
+ @Property(key = "sonar.jira", name = "Jira Server", type = PropertyType.PROPERTY_SET, propertySetKey = "jira"),
+ @Property(key = "newKey", name = "New key", deprecatedKey = "oldKey"),
+ @Property(key = "newKeyWithDefaultValue", name = "New key with default value", deprecatedKey = "oldKeyWithDefaultValue", defaultValue = "default_value"),
+ @Property(key = "new_multi_values", name = "New multi values", defaultValue = "1,2,3", multiValues = true, deprecatedKey = "old_multi_values")
})
static class Init {
}
assertThat(settings.getKeysStartingWith("sonar.jdbc")).containsOnly("sonar.jdbc.url", "sonar.jdbc.username");
assertThat(settings.getKeysStartingWith("other")).hasSize(0);
}
+
+ @Test
+ public void should_fallback_deprecated_key_to_default_value_of_new_key() {
+ Settings settings = new Settings(definitions);
+
+ assertThat(settings.getString("newKeyWithDefaultValue")).isEqualTo("default_value");
+ assertThat(settings.getString("oldKeyWithDefaultValue")).isEqualTo("default_value");
+ }
+
+ @Test
+ public void should_fallback_deprecated_key_to_new_key() {
+ Settings settings = new Settings(definitions);
+ settings.setProperty("newKey", "value of newKey");
+
+ assertThat(settings.getString("newKey")).isEqualTo("value of newKey");
+ assertThat(settings.getString("oldKey")).isEqualTo("value of newKey");
+ }
+
+ @Test
+ public void should_load_value_set_on_deprecated_key() {
+ // it's used for example when deprecated settings are set through command-line
+ Settings settings = new Settings(definitions);
+ settings.setProperty("oldKey", "value of oldKey");
+
+ assertThat(settings.getString("newKey")).isEqualTo("value of oldKey");
+ assertThat(settings.getString("oldKey")).isEqualTo("value of oldKey");
+ }
+
+ @Test
+ public void should_support_deprecated_props_with_multi_values() {
+ Settings settings = new Settings(definitions);
+ settings.setProperty("new_multi_values", new String[]{" A ", " B "});
+ assertThat(settings.getStringArray("new_multi_values")).isEqualTo(new String[]{"A", "B"});
+ assertThat(settings.getStringArray("old_multi_values")).isEqualTo(new String[]{"A", "B"});
+ }
}
import org.sonar.server.startup.RegisterNewProfiles;
import org.sonar.server.startup.RegisterQualityModels;
import org.sonar.server.startup.RegisterRules;
+import org.sonar.server.startup.RenameDeprecatedPropertyKeys;
import org.sonar.server.startup.ServerMetadataPersister;
import org.sonar.server.ui.CodeColorizers;
import org.sonar.server.ui.JRubyI18n;
startupContainer.addSingleton(GeneratePluginIndex.class);
startupContainer.addSingleton(RegisterNewFilters.class);
startupContainer.addSingleton(RegisterNewDashboards.class);
+ startupContainer.addSingleton(RenameDeprecatedPropertyKeys.class);
startupContainer.startComponents();
startupContainer.getComponentByType(ServerLifecycleNotifier.class).notifyStart();
package org.sonar.server.startup;
import com.google.common.base.Strings;
+import org.slf4j.LoggerFactory;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.core.properties.PropertiesDao;
}
public void start() {
+ LoggerFactory.getLogger(RenameDeprecatedPropertyKeys.class).info("Rename deprecated property keys");
for (PropertyDefinition definition : definitions.getAll()) {
if (!Strings.isNullOrEmpty(definition.getDeprecatedKey())) {
dao.renamePropertyKey(definition.getDeprecatedKey(), definition.getKey());
--- /dev/null
+#
+# Sonar, open source software quality management tool.
+# Copyright (C) 2008-2012 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+#
+class MeasuresController < ApplicationController
+
+ def index
+
+ end
+
+
+end