mirror of
https://github.com/lightbend/config.git
synced 2025-03-29 21:51:10 +08:00
Add Support for Optional Bean Properties.
This commit is contained in:
parent
596d6c9392
commit
ad3f4804f4
config
build.sbt
src
main/java/com/typesafe/config
test
java/beanconfig
resources/beanconfig
scala/com/typesafe/config/impl
@ -20,6 +20,8 @@ fork in run in Test := true
|
||||
autoScalaLibrary := false
|
||||
crossPaths := false
|
||||
|
||||
libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.3.2"
|
||||
libraryDependencies += "javax.annotation" % "javax.annotation-api" % "1.2"
|
||||
libraryDependencies += "net.liftweb" %% "lift-json" % "2.5" % "test"
|
||||
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
|
||||
|
||||
|
11
config/src/main/java/com/typesafe/config/Nullable.java
Normal file
11
config/src/main/java/com/typesafe/config/Nullable.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.typesafe.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Nullable {
|
||||
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
|
||||
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 +24,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.Nullable;
|
||||
|
||||
/**
|
||||
* Internal implementation detail, not ABI stable, do not touch.
|
||||
@ -90,7 +94,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,8 +111,18 @@ 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()));
|
||||
setter.invoke(bean, unwrapped);
|
||||
String configPropName = originalNames.get(beanProp.getName());
|
||||
if (configPropName == null) {
|
||||
configPropName = beanProp.getName();
|
||||
}
|
||||
try {
|
||||
Object unwrapped = getValue(clazz, parameterType, parameterClass, config, configPropName);
|
||||
setter.invoke(bean, unwrapped);
|
||||
} catch (ConfigException.Missing e) {
|
||||
if (!isOptionalProperty(clazz, beanProp)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bean;
|
||||
} catch (InstantiationException e) {
|
||||
@ -118,6 +134,11 @@ public class ConfigBeanImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOptionalProperty(Class clazz, PropertyDescriptor beanProp) {
|
||||
Field field = FieldUtils.getField(clazz, beanProp.getName(), true);
|
||||
return (field.getAnnotationsByType(Nullable.class).length > 0);
|
||||
}
|
||||
|
||||
// we could magically make this work in many cases by doing
|
||||
// getAnyRef() (or getValue().unwrapped()), but anytime we
|
||||
// rely on that, we aren't doing the type conversions Config
|
||||
|
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.Nullable;
|
||||
|
||||
public class ObjectsConfig {
|
||||
public static class ValueObject {
|
||||
@Nullable
|
||||
private String nullableValue;
|
||||
private String nonNullableValue;
|
||||
|
||||
public String getNonNullableValue() {
|
||||
return nonNullableValue;
|
||||
}
|
||||
|
||||
public void setNonNullableValue(String nonNullableValue) {
|
||||
this.nonNullableValue = nonNullableValue;
|
||||
}
|
||||
|
||||
public String getNullableValue() {
|
||||
return nullableValue;
|
||||
}
|
||||
|
||||
public void setNullableValue(String nullableValue) {
|
||||
this.nullableValue = nullableValue;
|
||||
}
|
||||
}
|
||||
|
||||
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": {
|
||||
"nonNullableValue": "nonNullValue"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +162,15 @@ 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.getNullableValue)
|
||||
assertEquals("nonNullValue", beanConfig.getValueObject.getNonNullableValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
def testNotABeanField() {
|
||||
val e = intercept[ConfigException.BadBean] {
|
||||
|
Loading…
Reference in New Issue
Block a user