private final Encryption encryption;
public Props(Properties props) {
- this.properties = props;
+ this.properties = new Properties();
+ props.forEach((k, v) -> this.properties.put(k.toString().trim(), v == null ? null : v.toString().trim()));
this.encryption = new Encryption(props.getProperty(AesCipher.ENCRYPTION_SECRET_KEY_PATH));
}
@CheckForNull
public String value(String key) {
- String value = valueImpl(key);
+ String value = properties.getProperty(key);
if (value != null && encryption.isEncrypted(value)) {
value = encryption.decrypt(value);
}
}
public void setDefault(String key, String value) {
- String s = valueImpl(key);
+ String s = properties.getProperty(key);
if (StringUtils.isBlank(s)) {
properties.setProperty(key, value);
}
}
- @CheckForNull
- private String valueImpl(String key) {
- String value = properties.getProperty(key);
- if (value == null) {
- return null;
- }
- return value.trim();
- }
}
import java.io.File;
import java.io.IOException;
import java.util.Properties;
+import org.apache.commons.lang.RandomStringUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@Rule
public ExpectedException expectedException = ExpectedException.none();
+ @Test
+ @UseDataProvider("beforeAndAfterBlanks")
+ public void constructor_trims_key_and_values_from_Properties_argument(String blankBefore, String blankAfter) {
+ Properties properties = new Properties();
+ String key = RandomStringUtils.randomAlphanumeric(3);
+ String value = RandomStringUtils.randomAlphanumeric(3);
+ properties.put(blankBefore + key + blankAfter, blankBefore + value + blankAfter);
+
+ Props underTest = new Props(properties);
+
+ if (!blankBefore.isEmpty() || !blankAfter.isEmpty()) {
+ assertThat(underTest.contains(blankBefore + key + blankAfter)).isFalse();
+ }
+ assertThat(underTest.value(key)).isEqualTo(value);
+ }
+
@Test
@UseDataProvider("beforeAndAfterBlanks")
public void value(String blankBefore, String blankAfter) {
Properties p = new Properties();
- p.setProperty("foo", blankBefore + "bar" + blankAfter);
+ p.setProperty(blankBefore + "foo" + blankAfter, blankBefore + "bar" + blankAfter);
p.setProperty("blank", blankBefore + blankAfter);
Props props = new Props(p);
private final String randomPrefix = "-" + randomAlphabetic(5).toLowerCase(Locale.ENGLISH);
private final String randomValue = randomAlphanumeric(4).toLowerCase(Locale.ENGLISH);
private final Properties properties = new Properties();
- private final Props props = new Props(properties);
private final JvmOptions underTest = new JvmOptions();
@Test
public void addFromMandatoryProperty_fails_with_IAE_if_property_does_not_exist() {
expectMissingPropertyIAE(this.randomPropertyName);
- underTest.addFromMandatoryProperty(props, this.randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), this.randomPropertyName);
}
@Test
public void addFromMandatoryProperty_fails_with_IAE_if_property_contains_an_empty_value() {
expectMissingPropertyIAE(this.randomPropertyName);
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
}
@Test
public void addFromMandatoryProperty_adds_single_option_of_property_with_trimming(String emptyString) {
properties.put(randomPropertyName, emptyString + "-foo" + emptyString);
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
assertThat(underTest.getAll()).containsOnly("-foo");
}
expectJvmOptionNotEmptyAndStartByDashMessageException(randomPropertyName, "foo");
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
}
@Test
public void addFromMandatoryProperty_adds_options_of_property_with_trimming(String emptyString) {
properties.put(randomPropertyName, emptyString + "-foo" + emptyString + " -bar" + emptyString + " -duck" + emptyString);
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
assertThat(underTest.getAll()).containsOnly("-foo", "-bar", "-duck");
}
public void addFromMandatoryProperty_supports_spaces_inside_options() {
properties.put(randomPropertyName, "-foo bar -duck");
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
assertThat(underTest.getAll()).containsOnly("-foo bar", "-duck");
}
for (String optionOverride : optionOverrides) {
try {
properties.put(randomPropertyName, optionOverride);
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
fail("an MessageException should have been thrown");
} catch (MessageException e) {
assertThat(e.getMessage())
for (String optionOverride : optionOverrides) {
properties.setProperty(randomPropertyName, optionOverride.toUpperCase(Locale.ENGLISH));
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
}
}
"The following JVM options defined by property '" + randomPropertyName + "' are invalid: " +
overriding1 + " overwrites " + randomPrefix + randomValue + ", " + overriding2 + " overwrites " + randomPrefix + randomValue);
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
}
@Test
JvmOptions underTest = new JvmOptions(ImmutableMap.of(randomPrefix, randomValue));
properties.put(randomPropertyName, randomPrefix + randomValue);
- underTest.addFromMandatoryProperty(props, randomPropertyName);
+ underTest.addFromMandatoryProperty(new Props(properties), randomPropertyName);
assertThat(underTest.getAll()).containsOnly(randomPrefix + randomValue);
}
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.config.Settings;
+import static java.util.Objects.requireNonNull;
+
@ComputeEngineSide
public class ProjectSettings extends Settings {
@Override
protected void set(String key, String value) {
- projectProps.put(key, value);
+ projectProps.put(
+ requireNonNull(key, "key can't be null"),
+ requireNonNull(value, "value can't be null").trim());
}
@Override
import org.sonar.api.server.ServerSide;
import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
/**
* Merge of {@link SystemSettings} and the global properties stored in the db table "properties". These
ThreadLocalSettings(PropertyDefinitions definitions, Properties props, SettingLoader settingLoader) {
super(definitions, new Encryption(null));
this.settingLoader = settingLoader;
- this.systemProps = props;
+ this.systemProps = new Properties();
+ props.forEach((k, v) -> systemProps.put(k, v == null ? null : v.toString().trim()));
// TODO something wrong about lifecycle here. It could be improved
getEncryption().setPathToSecretKey(props.getProperty(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
@Override
protected void set(String key, String value) {
+ requireNonNull(key, "key can't be null");
+ requireNonNull(value, "value can't be null");
Map<String, String> dbProps = CACHE.get();
if (dbProps != null) {
- dbProps.put(key, value);
+ dbProps.put(key, value.trim());
}
}
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.AnnotationUtils;
+import static java.util.Objects.requireNonNull;
+
/**
* Metadata of all the properties declared by plugins
*
}
public String validKey(String key) {
- return StringUtils.defaultString(deprecatedKeys.get(key), key);
+ requireNonNull(key, "key can't be null");
+ String trimmedKey = key.trim();
+ return StringUtils.defaultString(deprecatedKeys.get(trimmedKey), trimmedKey);
}
/**
protected abstract Optional<String> get(String key);
+ /**
+ * Add the settings with the specified key and value, both are trimmed and neither can be null.
+ *
+ * @throws NullPointerException if {@code key} and/or {@code value} is {@code null}.
+ */
protected abstract void set(String key, String value);
protected abstract void remove(String key);
* </ul>
*/
public String[] getStringArray(String key) {
- Optional<PropertyDefinition> def = getDefinition(key);
+ String effectiveKey = definitions.validKey(key);
+ Optional<PropertyDefinition> def = getDefinition(effectiveKey);
if ((def.isPresent()) && (def.get().multiValues())) {
String value = getString(key);
if (value == null) {
}
public Settings setProperty(String key, @Nullable String[] values) {
- Optional<PropertyDefinition> def = getDefinition(key);
+ requireNonNull(key, "key can't be null");
+ String effectiveKey = key.trim();
+ Optional<PropertyDefinition> def = getDefinition(effectiveKey);
if (!def.isPresent() || (!def.get().multiValues())) {
throw new IllegalStateException("Fail to set multiple values on a single value property " + key);
}
String validKey = definitions.validKey(key);
if (value == null) {
removeProperty(validKey);
-
} else {
set(validKey, trim(value));
}
import org.sonar.api.config.Settings;
import static java.util.Collections.unmodifiableMap;
+import static java.util.Objects.requireNonNull;
/**
* In-memory map-based implementation of {@link Settings}. It must be used
@Override
protected void set(String key, String value) {
- props.put(key, value);
+ props.put(
+ requireNonNull(key, "key can't be null"),
+ requireNonNull(value, "value can't be null").trim());
}
@Override
private final Map<String, String> keyValues = new HashMap<>();
public Configuration put(String key, String value) {
- keyValues.put(key, value);
+ keyValues.put(key, value.trim());
return this;
}
import java.util.Arrays;
import java.util.List;
+import java.util.Random;
+import java.util.stream.IntStream;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.resources.Qualifiers;
+import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
public class PropertyDefinitionsTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
@Test
public void should_build_with_predefined_list_of_definitions() {
List<PropertyDefinition> list = Arrays.asList(
PropertyDefinition.builder("foo").name("Foo").build(),
PropertyDefinition.builder("one").name("One").build(),
- PropertyDefinition.builder("two").name("Two").defaultValue("2").build()
- );
+ PropertyDefinition.builder("two").name("Two").defaultValue("2").build());
PropertyDefinitions def = new PropertyDefinitions(list);
assertProperties(def);
PropertyDefinitions def = new PropertyDefinitions(
PropertyDefinition.builder("foo").name("Foo").build(),
PropertyDefinition.builder("one").name("One").build(),
- PropertyDefinition.builder("two").name("Two").defaultValue("2").build()
- );
+ PropertyDefinition.builder("two").name("Two").defaultValue("2").build());
assertProperties(def);
}
public void test_categories() {
PropertyDefinitions def = new PropertyDefinitions(
PropertyDefinition.builder("inCateg").name("In Categ").category("categ").build(),
- PropertyDefinition.builder("noCateg").name("No categ").build()
- );
+ PropertyDefinition.builder("noCateg").name("No categ").build());
assertThat(def.getCategory("inCateg")).isEqualTo("categ");
assertThat(def.getCategory("noCateg")).isEmpty();
PropertyDefinition.builder("project").name("Project").category("catProject").onlyOnQualifiers(Qualifiers.PROJECT).build(),
PropertyDefinition.builder("module").name("Module").category("catModule").onlyOnQualifiers(Qualifiers.MODULE).build(),
PropertyDefinition.builder("view").name("View").category("catView").onlyOnQualifiers(Qualifiers.VIEW).build(),
- PropertyDefinition.builder("app").name("Application").category("catApp").onlyOnQualifiers(Qualifiers.APP).build()
- );
+ PropertyDefinition.builder("app").name("Application").category("catApp").onlyOnQualifiers(Qualifiers.APP).build());
assertThat(def.propertiesByCategory(null).keySet()).contains(new Category("catGlobal1"), new Category("catGlobal2"));
assertThat(def.propertiesByCategory(Qualifiers.PROJECT).keySet()).containsOnly(new Category("catProject"));
PropertyDefinition.builder("global1").name("Global1").category("catGlobal1").subCategory("sub1").build(),
PropertyDefinition.builder("global2").name("Global2").category("catGlobal1").subCategory("sub2").build(),
PropertyDefinition.builder("global3").name("Global3").category("catGlobal1").build(),
- PropertyDefinition.builder("global4").name("Global4").category("catGlobal2").build()
- );
+ PropertyDefinition.builder("global4").name("Global4").category("catGlobal2").build());
assertThat(def.propertiesByCategory(null).get(new Category("catGlobal1")).keySet()).containsOnly(new SubCategory("catGlobal1"), new SubCategory("sub1"),
new SubCategory("sub2"));
assertThat(definitions.getAll().size()).isEqualTo(3);
}
+ @Test
+ public void validKey_throws_NPE_if_key_is_null() {
+ PropertyDefinitions underTest = new PropertyDefinitions();
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+
+ underTest.validKey(null);
+ }
+
+ @Test
+ public void get_throws_NPE_if_key_is_null() {
+ PropertyDefinitions underTest = new PropertyDefinitions();
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+
+ underTest.get(null);
+ }
+
+ @Test
+ public void get_trims_key_before_looking_for_replacement() {
+ Random random = new Random();
+ String key = RandomStringUtils.randomAlphanumeric(4);
+ String deprecatedKey = RandomStringUtils.randomAlphanumeric(4);
+ PropertyDefinitions underTest = new PropertyDefinitions(singletonList(
+ PropertyDefinition.builder(key)
+ .deprecatedKey(deprecatedKey)
+ .build()));
+
+ String untrimmedKey = blank(random) + deprecatedKey + blank(random);
+ assertThat(underTest.get(untrimmedKey).key())
+ .describedAs("expecting key %s being returned for get(%s)", key, untrimmedKey)
+ .isEqualTo(key);
+ }
+
+ private static String blank(Random random) {
+ StringBuilder b = new StringBuilder();
+ IntStream.range(0, random.nextInt(3)).mapToObj(s -> " ").forEach(b::append);
+ return b.toString();
+ }
+
@Property(key = "foo", name = "Foo")
static final class PluginWithProperty {
}
*/
package org.sonar.api.config.internal;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.Arrays;
import java.util.Date;
+import java.util.List;
+import java.util.Random;
+import java.util.function.BiConsumer;
+import java.util.stream.IntStream;
import org.assertj.core.data.Offset;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
-import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.utils.DateUtils;
+import static java.util.Collections.singletonList;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
+@RunWith(DataProviderRunner.class)
public class MapSettingsTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
private PropertyDefinitions definitions;
definitions.addComponent(Init.class);
}
+ @Test
+ public void set_throws_NPE_if_key_is_null() {
+ MapSettings underTest = new MapSettings();
+
+ expectKeyNullNPE();
+
+ underTest.set(null, randomAlphanumeric(3));
+ }
+
+ @Test
+ public void set_throws_NPE_if_value_is_null() {
+ MapSettings underTest = new MapSettings();
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("value can't be null");
+
+ underTest.set(randomAlphanumeric(3), null);
+ }
+
+ @Test
+ public void set_accepts_empty_value_and_trims_it() {
+ MapSettings underTest = new MapSettings();
+ Random random = new Random();
+ String key = randomAlphanumeric(3);
+
+ underTest.set(key, blank(random));
+
+ assertThat(underTest.getString(key)).isEmpty();
+ }
+
@Test
public void default_values_should_be_loaded_from_definitions() {
Settings settings = new MapSettings(definitions);
assertThat(settings.getDefaultValue("hello")).isEqualTo("world");
}
+ @Test
+ @UseDataProvider("setPropertyCalls")
+ public void all_setProperty_methods_throws_NPE_if_key_is_null(BiConsumer<Settings, String> setPropertyCaller) {
+ Settings settings = new MapSettings();
+
+ expectKeyNullNPE();
+
+ setPropertyCaller.accept(settings, null);
+ }
+
+ @Test
+ public void set_property_string_throws_NPE_if_key_is_null() {
+ String key = randomAlphanumeric(3);
+
+ Settings underTest = new MapSettings(new PropertyDefinitions(singletonList(PropertyDefinition.builder(key).multiValues(true).build())));
+
+ expectKeyNullNPE();
+
+ underTest.setProperty(null, new String[] {"1", "2"});
+ }
+
+ private void expectKeyNullNPE() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+ }
+
+ @Test
+ @UseDataProvider("setPropertyCalls")
+ public void all_set_property_methods_trims_key(BiConsumer<Settings, String> setPropertyCaller) {
+ Settings underTest = new MapSettings();
+
+ Random random = new Random();
+ String blankBefore = blank(random);
+ String blankAfter = blank(random);
+ String key = randomAlphanumeric(3);
+
+ setPropertyCaller.accept(underTest, blankBefore + key + blankAfter);
+
+ assertThat(underTest.hasKey(key)).isTrue();
+ }
+
+ @Test
+ public void set_property_string_array_trims_key() {
+ String key = randomAlphanumeric(3);
+
+ Settings underTest = new MapSettings(new PropertyDefinitions(singletonList(PropertyDefinition.builder(key).multiValues(true).build())));
+
+ Random random = new Random();
+ String blankBefore = blank(random);
+ String blankAfter = blank(random);
+
+ underTest.setProperty(blankBefore + key + blankAfter, new String[] {"1", "2"});
+
+ assertThat(underTest.hasKey(key)).isTrue();
+ }
+
+ private static String blank(Random random) {
+ StringBuilder b = new StringBuilder();
+ IntStream.range(0, random.nextInt(3)).mapToObj(s -> " ").forEach(b::append);
+ return b.toString();
+ }
+
+ @DataProvider
+ public static Object[][] setPropertyCalls() {
+ List<BiConsumer<Settings, String>> callers = Arrays.asList(
+ (settings, key) -> settings.setProperty(key, 123),
+ (settings, key) -> settings.setProperty(key, 123L),
+ (settings, key) -> settings.setProperty(key, 123.2F),
+ (settings, key) -> settings.setProperty(key, 123.2D),
+ (settings, key) -> settings.setProperty(key, false),
+ (settings, key) -> settings.setProperty(key, new Date()),
+ (settings, key) -> settings.setProperty(key, new Date(), true));
+
+ return callers.stream().map(t -> new Object[] {t}).toArray(Object[][]::new);
+ }
+
+ @Test
+ public void setProperty_methods_trims_value() {
+ Settings underTest = new MapSettings();
+
+ Random random = new Random();
+ String blankBefore = blank(random);
+ String blankAfter = blank(random);
+ String key = randomAlphanumeric(3);
+ String value = randomAlphanumeric(3);
+
+ underTest.setProperty(key, blankBefore + value + blankAfter);
+
+ assertThat(underTest.getString(key)).isEqualTo(value);
+ }
+
@Test
public void set_property_int() {
Settings settings = new MapSettings();
package org.sonar.scanner.bootstrap;
import com.google.common.collect.ImmutableMap;
-import java.util.Collections;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
import org.slf4j.Logger;
public GlobalConfiguration(PropertyDefinitions propertyDefinitions, Encryption encryption, GlobalAnalysisMode mode,
Map<String, String> settings, Map<String, String> serverSideSettings) {
super(propertyDefinitions, encryption, mode, settings);
- this.serverSideSettings = serverSideSettings;
+ this.serverSideSettings = unmodifiableMapWithTrimmedValues(propertyDefinitions, serverSideSettings);
get(CoreProperties.PERMANENT_SERVER_ID).ifPresent(v -> LOG.info("Server id: {}", v));
new DroppedPropertyChecker(getProperties(), DROPPED_PROPERTIES).checkDroppedProperties();
}
public Map<String, String> getServerSideSettings() {
- return Collections.unmodifiableMap(serverSideSettings);
+ return serverSideSettings;
}
}
import org.sonar.api.config.Settings;
import org.sonar.api.utils.MessageException;
+import static java.util.Objects.requireNonNull;
+
/**
* @deprecated since 6.5 {@link GlobalConfiguration} used to be mutable, so keep a mutable copy for backward compatibility.
*/
@Override
protected void set(String key, String value) {
- mutableProperties.put(key, value);
+ mutableProperties.put(
+ requireNonNull(key, "key can't be null"),
+ requireNonNull(value, "value can't be null").trim());
}
@Override
private final PropertyDefinitions definitions;
private final Encryption encryption;
private final GlobalAnalysisMode mode;
- private final Map<String, String> properties = new HashMap<>();
+ private final Map<String, String> properties;
public DefaultConfiguration(PropertyDefinitions propertyDefinitions, Encryption encryption, GlobalAnalysisMode mode, Map<String, String> props) {
this.definitions = requireNonNull(propertyDefinitions);
this.encryption = encryption;
this.mode = mode;
+ this.properties = unmodifiableMapWithTrimmedValues(definitions, props);
+ }
+
+ protected static Map<String, String> unmodifiableMapWithTrimmedValues(PropertyDefinitions definitions, Map<String, String> props) {
+ Map<String, String> map = new HashMap<>(props.size());
props.forEach((k, v) -> {
String validKey = definitions.validKey(k);
- properties.put(validKey, trim(v));
+ map.put(validKey, trim(v));
});
+ return Collections.unmodifiableMap(map);
}
public GlobalAnalysisMode getMode() {
}
public Map<String, String> getProperties() {
- return Collections.unmodifiableMap(properties);
+ return properties;
}
@Override
import org.sonar.scanner.bootstrap.MutableGlobalSettings;
import org.sonar.scanner.repository.ProjectRepositories;
+import static java.util.Objects.requireNonNull;
+
/**
* @deprecated since 6.5 {@link ModuleSettings} used to be mutable, so keep a mutable copy for backward compatibility.
*/
@Override
protected void set(String key, String value) {
- properties.put(key, value);
+ properties.put(
+ requireNonNull(key, "key can't be null"),
+ requireNonNull(value, "value can't be null").trim());
}
@Override
import org.sonar.scanner.bootstrap.MutableGlobalSettings;
import org.sonar.scanner.repository.ProjectRepositories;
+import static java.util.Objects.requireNonNull;
+
/**
* @deprecated since 6.5 {@link ProjectSettings} used to be mutable, so keep a mutable copy for backward compatibility.
*/
@Override
protected void set(String key, String value) {
- properties.put(key, value);
+ properties.put(
+ requireNonNull(key, "key can't be null"),
+ requireNonNull(value, "value can't be null").trim());
}
@Override