Skip to content

Commit

Permalink
Merge d995c13 into 6c3c989
Browse files Browse the repository at this point in the history
  • Loading branch information
rchodava committed Apr 14, 2017
2 parents 6c3c989 + d995c13 commit b15f343
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package foundation.stack.datamill.configuration;

import java.lang.annotation.*;

/**
* Annotation to be used together with a {@link foundation.stack.datamill.configuration.impl.ConstantsClassSource} to
* specify String values. For example, a constant declared in a constants class as:
* <code>
* \@BooleanValue(true) public static final String PROPERTY_NAME = "configuration/propertyName";
* </code>
* This defines a property called "configuration/propertyName" that has the boolean value true.
*
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface BooleanValue {
boolean value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package foundation.stack.datamill.configuration;

import java.lang.annotation.*;

/**
* Annotation to be used together with a {@link foundation.stack.datamill.configuration.impl.ConstantsClassSource} to
* specify String values. For example, a constant declared in a constants class as:
* <code>
* \@IntegerValue(16) public static final String PROPERTY_NAME = "configuration/propertyName";
* </code>
* This defines a property called "configuration/propertyName" that has the integer value 16.
*
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface IntegerValue {
int value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Target({ElementType.PARAMETER})
public @interface Named {
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class Properties {
/** @see PropertySourceChain#orConstantsClass(Class) */
public static <T> PropertySourceChain fromConstantsClass(Class<T> constantsClass) {
return fromSource(new ConstantsClassSource<>(constantsClass));
}

/** @see PropertySourceChain#orFile(String) */
public static PropertySourceChain fromFile(String path) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ public interface PropertySourceChain extends PropertySource {
*/
PropertySourceChain alias(String alias, String original);

/**
* Add a property source to the chain which retrieves properties from a constants interface or class. The constants
* are expected to
*
* @param constantsClass Constants class to add as a source.
*/
<T> PropertySourceChain orConstantsClass(Class<T> constantsClass);

/**
* Add a property source to the chain which retrieves properties specified in the file at the specified path.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package foundation.stack.datamill.configuration;

import java.lang.annotation.*;

/**
* Annotation to be used together with a {@link foundation.stack.datamill.configuration.impl.ConstantsClassSource} to
* specify String values. For example, a constant declared in a constants class as:
* <code>
* \@StringValue("value") public static final String PROPERTY_NAME = "configuration/propertyName";
* </code>
* This defines a property called "configuration/propertyName" that has the value "value".
*
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface StringValue {
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,27 @@ private <T> T constructWithConstructor(
return null;
}
} else {
logger.error("Could not build class {} as the following type was not found {}", clazz, parameterType);
StringBuilder parameterNames = new StringBuilder();

Named[] annotations = parameters[i].getAnnotationsByType(Named.class);
if (annotations != null && annotations.length > 0) {
for (Named annotation : annotations) {
if (parameterNames.length() > 0) {
parameterNames.append(", ");
}

parameterNames.append(annotation.value());
}
}

if (parameterNames.length() > 0) {
logger.error("Could not build class {} as the following named parameter was not found: {}",
clazz, parameterNames.toString());
} else {
logger.error("Could not build class {} as the following type was not found {}",
clazz, parameterType);
}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package foundation.stack.datamill.configuration.impl;

import foundation.stack.datamill.configuration.BooleanValue;
import foundation.stack.datamill.configuration.IntegerValue;
import foundation.stack.datamill.configuration.StringValue;
import javassist.Modifier;

import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class ConstantsClassSource<T> extends AbstractSource {
private static String getConstantValue(Field field) {
if (field != null && Modifier.isStatic(field.getModifiers())) {
try {
performSecure(() -> field.setAccessible(true));
return String.valueOf(field.get(null));
} catch (IllegalAccessException e) {
return null;
}
}

return null;
}

private static void performSecure(Runnable runnable) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<?>) () -> {
runnable.run();
return null;
});
} else {
runnable.run();
}
}

private final Class<T> constantsClass;
private Map<String, String> annotatedConstants;

public ConstantsClassSource(Class<T> constantsClass) {
this.constantsClass = constantsClass;
}

private Map<String, String> buildAnnotatedConstants() {
HashMap<String, String> constants = new HashMap<>();

Field[] fields = constantsClass.getDeclaredFields();
for (Field field : fields) {
String propertyName = getConstantValue(field);
if (propertyName != null) {
StringValue stringValue = field.getAnnotation(StringValue.class);
if (stringValue != null) {
constants.put(propertyName, stringValue.value());
} else {
BooleanValue booleanValue = field.getAnnotation(BooleanValue.class);
if (booleanValue != null) {
constants.put(propertyName, String.valueOf(booleanValue.value()));
} else {
IntegerValue integerValue = field.getAnnotation(IntegerValue.class);
if (integerValue != null) {
constants.put(propertyName, String.valueOf(integerValue.value()));
}
}
}
}
}

return constants;
}

@Override
protected Optional<String> getOptional(String name) {
if (annotatedConstants == null) {
annotatedConstants = buildAnnotatedConstants();
}

return Optional.ofNullable(annotatedConstants.get(name));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public Optional<String> getOptional(String name) {
return Optional.empty();
}

@Override
public <T> PropertySourceChain orConstantsClass(Class<T> constantsClass) {
return orSource(new ConstantsClassSource<>(constantsClass));
}

@Override
public PropertySource orDefaults(Action1<Defaults> defaultsInitializer) {
DefaultsSource defaults = new DefaultsSource();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,26 @@ public void systemProperties() {
assertEquals("value", Properties.fromSystem().getRequired("test").asString());
}

@Test
public void constantClasses() {
assertEquals("value", Properties.fromConstantsClass(ConstantsClass.class).get(ConstantsClass.property).get());
assertFalse(Properties.fromConstantsClass(ConstantsClass.class).get("property2").isPresent());
assertFalse(Properties.fromConstantsClass(ConstantsClass.class).get("config/instance").isPresent());

assertEquals("publicValue", Properties.fromConstantsClass(ConstantsClass.class).get(ConstantsClass.publicProperty).get());
assertEquals("privateValue", Properties.fromConstantsClass(ConstantsClass.class).get(ConstantsClass.privateProperty).get());
assertEquals("nonFinalValue", Properties.fromConstantsClass(ConstantsClass.class).get(ConstantsClass.nonFinalProperty).get());
assertEquals("1", Properties.fromConstantsClass(ConstantsClass.class).get(ConstantsClass.integerProperty).get());
assertEquals("true", Properties.fromConstantsClass(ConstantsClass.class).get(ConstantsClass.booleanProperty).get());

assertEquals("value", Properties.fromConstantsClass(ConstantsClass.class).getRequired("config/property").asString());

assertEquals("ifacePublic", Properties.fromConstantsClass(ConstantsClass.class).orConstantsClass(ConstantsInterface.class).get(ConstantsInterface.IFACE_PUBLIC_TEST).get());
assertEquals("2", Properties.fromConstantsClass(ConstantsClass.class).orConstantsClass(ConstantsInterface.class).get(ConstantsInterface.IFACE_INTEGER).get());
assertEquals("true", Properties.fromConstantsClass(ConstantsClass.class).orConstantsClass(ConstantsInterface.class).get(ConstantsInterface.IFACE_BOOLEAN).get());
assertEquals("true", Properties.fromConstantsClass(ConstantsClass.class).orConstantsClass(ConstantsInterface.class).get(ConstantsClass.booleanProperty).get());
}

@Test
public void files() {
assertEquals("value", Properties.fromFile("test.properties").get("test").get());
Expand Down Expand Up @@ -131,4 +151,20 @@ public void missingRequiredProperties() throws Exception {
} catch (IllegalArgumentException e) {
}
}

private static class ConstantsClass {
@StringValue("value") static final String property = "config/property";
@StringValue("publicValue") public static final String publicProperty = "config/public";
@StringValue("privateValue") private static final String privateProperty = "config/private";
@StringValue("nonFinalValue") static String nonFinalProperty = "config/nonFinal";
@IntegerValue(1) static String integerProperty = "config/integer";
@BooleanValue(true) static String booleanProperty = "config/boolean";
@StringValue("instanceValue") String instanceProperty = "config/instance";
}

private interface ConstantsInterface {
@StringValue("ifacePublic") String IFACE_PUBLIC_TEST = "iface/public";
@IntegerValue(2) String IFACE_INTEGER = "iface/integer";
@BooleanValue(true) String IFACE_BOOLEAN = "iface/boolean";
}
}

0 comments on commit b15f343

Please sign in to comment.