mirror of
https://github.com/lightbend/config.git
synced 2025-04-15 05:30:43 +08:00
Merge pull request #400 from karthicks/issue-399
Add Support for Optional Bean Properties.
This commit is contained in:
commit
23c5306038
config/src
main/java/com/typesafe/config
test
java/beanconfig
resources/beanconfig
scala/com/typesafe/config/impl
14
config/src/main/java/com/typesafe/config/Optional.java
Normal file
14
config/src/main/java/com/typesafe/config/Optional.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Allows an config property to be {@code null}.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Optional {
|
||||
|
||||
}
|
@ -4,6 +4,7 @@ import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@ -21,6 +22,7 @@ import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigMemorySize;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
import com.typesafe.config.ConfigValueType;
|
||||
import com.typesafe.config.Optional;
|
||||
|
||||
/**
|
||||
* Internal implementation detail, not ABI stable, do not touch.
|
||||
@ -90,7 +92,9 @@ public class ConfigBeanImpl {
|
||||
if (configValue != null) {
|
||||
SimpleConfig.checkValid(path, expectedType, configValue, problems);
|
||||
} else {
|
||||
SimpleConfig.addMissing(problems, expectedType, path, config.origin());
|
||||
if (!isOptionalProperty(clazz, beanProp)) {
|
||||
SimpleConfig.addMissing(problems, expectedType, path, config.origin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,7 +109,17 @@ public class ConfigBeanImpl {
|
||||
Method setter = beanProp.getWriteMethod();
|
||||
Type parameterType = setter.getGenericParameterTypes()[0];
|
||||
Class<?> parameterClass = setter.getParameterTypes()[0];
|
||||
Object unwrapped = getValue(clazz, parameterType, parameterClass, config, originalNames.get(beanProp.getName()));
|
||||
String configPropName = originalNames.get(beanProp.getName());
|
||||
// Is the property key missing in the config?
|
||||
if (configPropName == null) {
|
||||
// If so, continue if the field is marked as @{link Optional}
|
||||
if (isOptionalProperty(clazz, beanProp)) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise, raise a {@link Missing} exception right here
|
||||
throw new ConfigException.Missing(beanProp.getName());
|
||||
}
|
||||
Object unwrapped = getValue(clazz, parameterType, parameterClass, config, configPropName);
|
||||
setter.invoke(bean, unwrapped);
|
||||
}
|
||||
return bean;
|
||||
@ -252,4 +266,24 @@ public class ConfigBeanImpl {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isOptionalProperty(Class beanClass, PropertyDescriptor beanProp) {
|
||||
Field field = getField(beanClass, beanProp.getName());
|
||||
return (field.getAnnotationsByType(Optional.class).length > 0);
|
||||
}
|
||||
|
||||
private static Field getField(Class beanClass, String fieldName) {
|
||||
try {
|
||||
Field field = beanClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
// Don't give up yet. Try to look for field in super class, if any.
|
||||
}
|
||||
beanClass = beanClass.getSuperclass();
|
||||
if (beanClass == null) {
|
||||
return null;
|
||||
}
|
||||
return getField(beanClass, fieldName);
|
||||
}
|
||||
}
|
||||
|
68
config/src/test/java/beanconfig/ObjectsConfig.java
Normal file
68
config/src/test/java/beanconfig/ObjectsConfig.java
Normal file
@ -0,0 +1,68 @@
|
||||
package beanconfig;
|
||||
|
||||
|
||||
import com.typesafe.config.Optional;
|
||||
|
||||
public class ObjectsConfig {
|
||||
public static class ValueObject {
|
||||
@Optional
|
||||
private String optionalValue;
|
||||
private String mandatoryValue;
|
||||
|
||||
public String getMandatoryValue() {
|
||||
return mandatoryValue;
|
||||
}
|
||||
|
||||
public void setMandatoryValue(String mandatoryValue) {
|
||||
this.mandatoryValue = mandatoryValue;
|
||||
}
|
||||
|
||||
public String getOptionalValue() {
|
||||
return optionalValue;
|
||||
}
|
||||
|
||||
public void setOptionalValue(String optionalValue) {
|
||||
this.optionalValue = optionalValue;
|
||||
}
|
||||
}
|
||||
|
||||
private ValueObject valueObject;
|
||||
|
||||
public ValueObject getValueObject() {
|
||||
|
||||
return valueObject;
|
||||
}
|
||||
|
||||
public void setValueObject(ValueObject valueObject) {
|
||||
this.valueObject = valueObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ObjectsConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectsConfig that = (ObjectsConfig) o;
|
||||
|
||||
return !(getValueObject() != null ? !getValueObject().equals(that.getValueObject()) : that.getValueObject() != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getValueObject() != null ? getValueObject().hashCode() : 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("ObjectsConfig{");
|
||||
sb.append("innerType=").append(valueObject);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -92,5 +92,10 @@
|
||||
"configValue" : "hello world",
|
||||
"list" : [1,2,3],
|
||||
"unwrappedMap" : ${validation}
|
||||
},
|
||||
"objects" : {
|
||||
"valueObject": {
|
||||
"mandatoryValue": "notNull"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +162,25 @@ class ConfigBeanFactoryTest extends TestUtils {
|
||||
assertEquals(42, beanConfig.getUnwrappedMap.get("should-be-boolean"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def testOptionalProperties() {
|
||||
val beanConfig: ObjectsConfig = ConfigBeanFactory.create(loadConfig().getConfig("objects"), classOf[ObjectsConfig])
|
||||
assertNotNull(beanConfig)
|
||||
assertNotNull(beanConfig.getValueObject)
|
||||
assertNull(beanConfig.getValueObject.getOptionalValue)
|
||||
assertEquals("notNull", beanConfig.getValueObject.getMandatoryValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
def testNotAnOptionalProperty(): Unit = {
|
||||
val e = intercept[ConfigException.ValidationFailed] {
|
||||
ConfigBeanFactory.create(parseConfig("{valueObject: {}}"), classOf[ObjectsConfig])
|
||||
}
|
||||
assertTrue("missing value error", e.getMessage.contains("No setting"))
|
||||
assertTrue("error about the right property", e.getMessage.contains("mandatoryValue"))
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
def testNotABeanField() {
|
||||
val e = intercept[ConfigException.BadBean] {
|
||||
|
Loading…
Reference in New Issue
Block a user