@Test public void testEnum() throws Exception { final ConfigurationOption<TestEnum> option = ConfigurationOption.enumOption(TestEnum.class) .key("test.enum") .dynamic(true) .buildWithDefault(TestEnum.FOO); final ConfigurationRegistry configuration = createConfiguration(Collections.singletonList(option), new SimpleSource()); assertThat(option.getValidOptions()).containsExactlyInAnyOrder("FOO", "BAR"); assertThat(option.getValidOptionsLabelMap()).containsEntry("FOO", "foo").containsEntry("BAR", "bar"); assertThat(option.getValue()).isEqualTo(TestEnum.FOO); configuration.save("test.enum", "BAR", SimpleSource.NAME); assertThat(option.getValue()).isEqualTo(TestEnum.BAR); assertThatThrownBy(() -> configuration.save("test.enum", "BAZ", SimpleSource.NAME)) .isInstanceOf(IllegalArgumentException.class); }
/** * Adds a configuration option for intended classes loaded by {@link ServiceLoader}s * * <p>Restricts the {@link #validOptions} to the class names of the {@link ServiceLoader} implementations of the * provided service loader interface.</p> * * <p> Note that the implementations have to be registered in {@code META-INF/services/{serviceLoaderInterface.getName()}}</p> */ public static <T> ConfigurationOptionBuilder<T> serviceLoaderStrategyOption(Class<T> serviceLoaderInterface) { final ConfigurationOptionBuilder<T> optionBuilder = new ConfigurationOptionBuilder<T>(ClassInstanceValueConverter.of(serviceLoaderInterface), serviceLoaderInterface); for (T impl : ServiceLoader.load(serviceLoaderInterface, ConfigurationOption.class.getClassLoader())) { optionBuilder.addValidOption(impl); } optionBuilder.sealValidOptions(); return optionBuilder; }
/** * Builds the option with a default value so that {@link ConfigurationOption#getValue()} will never return * <code>null</code> * * @param defaultValue The default value which has to be non-<code>null</code> * @throws IllegalArgumentException When <code>null</code> was provided */ public ConfigurationOption<T> buildWithDefault(T defaultValue) { if (defaultValue == null) { throw new IllegalArgumentException("Default value must not be null"); } this.required = true; this.defaultValue = defaultValue; return build(); }
@Test public void testValidateConfigurationOption() throws Exception { final ConfigurationOption<Boolean> configurationOption = ConfigurationOption.booleanOption() .key("foo") .addValidator((value) -> { if (!value) { throw new IllegalArgumentException("Validation failed"); } }) .buildWithDefault(true); final ConfigurationOptionProvider optionProvider = TestConfigurationOptionProvider.of(configurationOption); final SimpleSource configurationSource = new SimpleSource("test"); final ConfigurationRegistry config = ConfigurationRegistry.builder() .addOptionProvider(optionProvider) .addConfigSource(configurationSource) .build(); try { config.save("foo", "false", "test"); fail(); } catch (IllegalArgumentException e) { assertEquals("Validation failed", e.getMessage()); } }
@Test public void testOnConfigurationChanged() throws Exception { AtomicBoolean changeListenerFired = new AtomicBoolean(false); final ConfigurationOption<String> configurationOption = ConfigurationOption.stringOption() .key("foo") .dynamic(true) .addChangeListener((opt, oldValue, newValue) -> { assertEquals("foo", opt.getKey()); assertEquals("old", oldValue); assertEquals("new", newValue); changeListenerFired.set(true); throw new RuntimeException("This is an expected test exception. " + "It is thrown to test whether Configuration can cope with change listeners that throw an exception."); }).buildWithDefault("old"); final ConfigurationOptionProvider optionProvider = TestConfigurationOptionProvider.of(configurationOption); final SimpleSource configurationSource = new SimpleSource("test"); final ConfigurationRegistry config = ConfigurationRegistry.builder() .addOptionProvider(optionProvider) .addConfigSource(configurationSource) .build(); config.save("foo", "new", "test"); assertTrue(changeListenerFired.get()); }
@Test public void testValidateDefaultConfigurationOption() throws Exception { try { ConfigurationOption.booleanOption() .key("foo") .addValidator((value) -> { if (!value) { throw new IllegalArgumentException("Validation failed"); } }) .buildWithDefault(false); fail(); } catch (IllegalArgumentException e) { assertEquals("Validation failed", e.getMessage()); } }
@Test public void testServiceLoaderStrategyOption() throws Exception { final ConfigurationOption<Strategy> option = ConfigurationOption.serviceLoaderStrategyOption(Strategy.class) .key("test.strategy") .dynamic(true) .buildWithDefault(new DefaultStrategyImpl()); final ConfigurationRegistry configuration = createConfiguration(Collections.singletonList(option), new SimpleSource()); assertThat(option.getValidOptions()).containsExactlyInAnyOrder(DefaultStrategyImpl.class.getName(), SpecialStrategyImpl.class.getName()); assertThat(option.getValue()).isInstanceOf(DefaultStrategyImpl.class); configuration.save("test.strategy", SpecialStrategyImpl.class.getName(), SimpleSource.NAME); assertThat(option.getValue()).isInstanceOf(SpecialStrategyImpl.class); assertThatThrownBy(() -> configuration.save("test.strategy", NoMetaInfServicesStrategyImpl.class.getName(), SimpleSource.NAME)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Invalid option"); }
public static <T> ConfigurationOptionBuilder<T> jsonOption(TypeReference<T> typeReference, Class<? super T> clazz) { return new ConfigurationOptionBuilder<T>(new JsonValueConverter<T>(typeReference), clazz); }
public static <T> ConfigurationOptionBuilder<T> builder(ValueConverter<T> valueConverter, Class<? super T> valueType) { return new ConfigurationOptionBuilder<T>(valueConverter, valueType); }
/** * Builds the option and marks it as required. * * <p> Use this method if you don't want to provide a default value but setting a value is still required. You * will have to make sure to provide a value is present on startup. </p> * * <p> When a required option does not have a value the behavior depends on {@link * ConfigurationRegistry#failOnMissingRequiredValues}. Either an {@link IllegalStateException} is raised, which * can potentially prevent the application form starting or a warning gets logged. </p> */ public ConfigurationOption<T> buildRequired() { this.required = true; return build(); }
@Test public void testExclude() throws Exception { testFilter = Mockito.spy(new TestExclusionFilter( ConfigurationOption.stringsOption().buildWithDefault(Arrays.asList("/exclude1", "/exclude2", "/exclude3/")), ConfigurationOption.stringsOption().buildWithDefault(Collections.singletonList("/foo/**")) )); assertExcludes("/exclude1"); assertExcludes("/exclude2/bla/blubb"); assertExcludes("/exclude3/"); assertExcludes("/exclude2bla"); assertIncludes("/exclude3"); assertIncludes("/included"); assertIncludes("/included/exclude1"); }
@Test(expected = IllegalArgumentException.class) public void testDefaultValueNull() { ConfigurationOption.stringOption().key("foo").buildWithDefault(null); }
/** * Constructs a {@link ConfigurationOptionBuilder} whose value is an {@link Enum} * * @return a {@link ConfigurationOptionBuilder} whose value is an {@link Enum} */ public static <T extends Enum<T>> ConfigurationOptionBuilder<T> enumOption(Class<T> clazz) { final ConfigurationOptionBuilder<T> optionBuilder = new ConfigurationOptionBuilder<T>(new EnumValueConverter<T>(clazz), clazz); for (T enumConstant : clazz.getEnumConstants()) { optionBuilder.addValidOption(enumConstant); } optionBuilder.sealValidOptions(); return optionBuilder; }
/** * Constructs a {@link ConfigurationOptionBuilder} whose value is of type {@link Map} * * @return a {@link ConfigurationOptionBuilder} whose value is of type {@link Map} */ public static <K, V> ConfigurationOptionBuilder<Map<K, V>> mapOption(ValueConverter<K> keyConverter, ValueConverter<V> valueConverter) { return new ConfigurationOptionBuilder<Map<K, V>>(new MapValueConverter<K, V>(keyConverter, valueConverter), Map.class) .defaultValue(Collections.<K, V>emptyMap()); }
/** * Constructs a {@link ConfigurationOptionBuilder} whose value is of type {@link List}<{@link Pattern}> * * @return a {@link ConfigurationOptionBuilder} whose value is of type {@link List}<{@link Pattern}> */ public static ConfigurationOptionBuilder<Collection<Pattern>> regexListOption() { return new ConfigurationOptionBuilder<Collection<Pattern>>(new SetValueConverter<Pattern>(RegexValueConverter.INSTANCE), Collection.class) .defaultValue(Collections.<Pattern>emptySet()); }
/** * Constructs a {@link ConfigurationOptionBuilder} whose value is of type {@link List}<{@link String}> and all * Strings are converted to lower case. * * @return a {@link ConfigurationOptionBuilder} whose value is of type {@link List}<{@link String}> */ public static ConfigurationOptionBuilder<Collection<String>> lowerStringsOption() { return new ConfigurationOptionBuilder<Collection<String>>(SetValueConverter.LOWER_STRINGS_VALUE_CONVERTER, Collection.class) .defaultValue(Collections.<String>emptySet()); }
@Test(expected = IllegalArgumentException.class) public void testDuplicateAlternateKeys() { createConfiguration(Arrays.asList( ConfigurationOption.stringOption().key("primaryKey1").aliasKeys("alternateKey1").build(), ConfigurationOption.stringOption().key("primaryKey2").aliasKeys("alternateKey1").build() ), new SimpleSource()); }
@Test public void testToJsonDoesNotThrowException() throws Exception { final ConfigurationOptionProvider optionProvider = TestConfigurationOptionProvider.of( ConfigurationOption.stringOption().key("foo").description("Foo").configurationCategory("Foos").build()); final ConfigurationRegistry configuration = ConfigurationRegistry.builder().addOptionProvider(optionProvider).build(); new ObjectMapper().writeValueAsString(configuration.getConfigurationOptionsByCategory()); }
public ConfigurationOptionBuilder<T> addValidOption(T option) { if (option instanceof Collection) { throw new UnsupportedOperationException("Adding valid options to a collection option is not supported. " + "If you need this feature please raise an issue describing your use case."); } else { final String validOptionAsString = valueConverter.toString(option); addValidOptionAsString(validOptionAsString, getLabel(option, validOptionAsString)); } return this; }
@Test public void testValidOptions_list() throws Exception { assertThatThrownBy(() -> ConfigurationOption.integersOption() .key("test.list") .dynamic(true) .addValidOption(Arrays.asList(1, 2)) .buildWithDefault(Collections.singleton(1))) .isInstanceOf(UnsupportedOperationException.class); }