diff --git a/owner/src/main/java/org/aeonbits/owner/Config.java b/owner/src/main/java/org/aeonbits/owner/Config.java index 8409cbf2..d27b3455 100644 --- a/owner/src/main/java/org/aeonbits/owner/Config.java +++ b/owner/src/main/java/org/aeonbits/owner/Config.java @@ -80,6 +80,16 @@ public interface Config extends Serializable { String value(); } + /** + * Indicates a mandatory property. If no property is found a {@link MissingMandatoryPropertyException} + * exception is thrown. + */ + @Retention(RUNTIME) + @Target(METHOD) + @Documented + @interface Mandatory { + } + /** * The key used for lookup for the property. If not present, the key will be generated based on the unqualified * method name. diff --git a/owner/src/main/java/org/aeonbits/owner/MissingMandatoryPropertyException.java b/owner/src/main/java/org/aeonbits/owner/MissingMandatoryPropertyException.java new file mode 100644 index 00000000..8cd6a274 --- /dev/null +++ b/owner/src/main/java/org/aeonbits/owner/MissingMandatoryPropertyException.java @@ -0,0 +1,10 @@ +package org.aeonbits.owner; + +public class MissingMandatoryPropertyException extends RuntimeException { + private static final long serialVersionUID = 1L; + + MissingMandatoryPropertyException(String message) { + super(message); + } + +} diff --git a/owner/src/main/java/org/aeonbits/owner/PropertiesInvocationHandler.java b/owner/src/main/java/org/aeonbits/owner/PropertiesInvocationHandler.java index ab17caa2..15b1d762 100644 --- a/owner/src/main/java/org/aeonbits/owner/PropertiesInvocationHandler.java +++ b/owner/src/main/java/org/aeonbits/owner/PropertiesInvocationHandler.java @@ -15,6 +15,8 @@ import java.util.LinkedList; import java.util.List; +import org.aeonbits.owner.Config.Mandatory; + import static org.aeonbits.owner.Config.DisableableFeature.PARAMETER_FORMATTING; import static org.aeonbits.owner.Config.DisableableFeature.VARIABLE_EXPANSION; import static org.aeonbits.owner.Converters.SpecialValue.NULL; @@ -81,8 +83,10 @@ private Object resolveProperty(Method method, Object... args) { String unexpandedKey = key(method); value = propertiesManager.getProperty(unexpandedKey); } - if (value == null) + if (value == null) { + throwIfMandatoryProperty(method, key); return null; + } // Before processing the value, we decrypt it if necessary. // It is a security hole store the decrypted value, so every time we need it it should be decrypted. value = this.propertiesManager.decryptIfNecessary( method, value ); @@ -92,6 +96,14 @@ private Object resolveProperty(Method method, Object... args) { return result; } + private void throwIfMandatoryProperty(Method method, String key) { + Mandatory mandatory = method.getAnnotation(Mandatory.class); + if (mandatory != null) { + String message = String.format("Missing mandatory property: '%s'", key); + throw new MissingMandatoryPropertyException(message); + } + } + private String preProcess(Method method, String value) { List preprocessors = resolvePreprocessors(method); String result = value; diff --git a/owner/src/test/java/org/aeonbits/owner/ConfigTest.java b/owner/src/test/java/org/aeonbits/owner/ConfigTest.java index ab51422e..8ee9db09 100644 --- a/owner/src/test/java/org/aeonbits/owner/ConfigTest.java +++ b/owner/src/test/java/org/aeonbits/owner/ConfigTest.java @@ -8,7 +8,9 @@ package org.aeonbits.owner; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -26,7 +28,10 @@ @RunWith(MockitoJUnitRunner.class) public class ConfigTest { - @Mock + @Rule + public ExpectedException expected = ExpectedException.none(); + + @Mock private ScheduledExecutorService scheduler; public interface SampleConfig extends Config { @@ -85,6 +90,30 @@ public void testDefaultStringValue() { assertEquals("Hello Mr. Luigi!", config.helloMr("Luigi")); } + static interface WithMandatoryPropertyConfig extends Config { + @Mandatory + String missingMandatoryProperty(); + + @Mandatory + @DefaultValue("I'm here") + String mandatory(); + } + + @Test + public void shouldThrowWhenMissingMandatoryProperty() { + expected.expect(MissingMandatoryPropertyException.class); + expected.expectMessage("Missing mandatory property: 'missingMandatoryProperty'"); + + WithMandatoryPropertyConfig config = ConfigFactory.create(WithMandatoryPropertyConfig.class); + config.missingMandatoryProperty(); + } + + @Test + public void testMandatoryStringValue() { + WithMandatoryPropertyConfig config = ConfigFactory.create(WithMandatoryPropertyConfig.class); + assertEquals("I'm here", config.mandatory()); + } + @Test public void testDefaultPropertyOverridden() { SampleConfig config = ConfigFactory.create(SampleConfig.class);