├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── org │ └── skife │ └── config │ ├── Bully.java │ ├── CaseInsensitiveEnumCoercible.java │ ├── Coercer.java │ ├── Coercible.java │ ├── CommonsConfigSource.java │ ├── Config.java │ ├── ConfigReplacements.java │ ├── ConfigSource.java │ ├── ConfigurationObjectFactory.java │ ├── DataAmount.java │ ├── DataAmountUnit.java │ ├── Default.java │ ├── DefaultCoercibles.java │ ├── DefaultNull.java │ ├── Description.java │ ├── ExactMatchEnumCoercible.java │ ├── Param.java │ ├── Separator.java │ ├── ServletFilterConfigSource.java │ ├── SimplePropertyConfigSource.java │ └── TimeSpan.java └── test └── java └── org └── skife └── config ├── BadConfig.java ├── CoercionConfig.java ├── Config1.java ├── Config2.java ├── Config3.java ├── Config4.java ├── Config5.java ├── ConfigEnum.java ├── EnumeratedConfig1.java ├── MultiConfig.java ├── Props.java ├── ReplacementConfig1.java ├── TestArrays.java ├── TestBadConfig.java ├── TestCaseInsensitiveEnumCoercible.java ├── TestClasses.java ├── TestCoercion.java ├── TestCollections.java ├── TestCommonsConfig.java ├── TestConfigurationObjectFactory.java ├── TestCustomCoercion.java ├── TestDataAmount.java ├── TestDefaultCoercibles.java ├── TestDefaultNull.java ├── TestDefaultsPresent.java ├── TestEmptyValue.java ├── TestEnums.java ├── TestExposeMappedReplacements.java ├── TestFile.java ├── TestMultiConfig.java ├── TestNoFinal.java ├── TestServletFilterConfigSource.java ├── TestTimeSpan.java ├── TestVariousPropertyTypes.java ├── Wibble.java └── WibbleConfig.java /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | report 3 | *.ipr 4 | *.iws 5 | *.iml 6 | .clover 7 | .idea 8 | build 9 | out 10 | .classpath 11 | .project 12 | .settings 13 | *~ 14 | .java-version 15 | test-output 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | Create an interface for your config object: 4 | 5 | public interface MyConfig 6 | { 7 | @Config("foo") 8 | String getFoo(); 9 | 10 | @Config("blah") 11 | int getBlah(); 12 | 13 | @Config("what") 14 | @Default("none") 15 | String getWhat(); 16 | } 17 | 18 | Set the properties that we mapped with `@Config` above (or simply call `System.getProperties()`): 19 | 20 | Properties props = new Properties(); 21 | props.setProperty("foo", "hello"); 22 | props.setProperty("blah", "123"); 23 | 24 | Then create the config object from the properties: 25 | 26 | ConfigurationObjectFactory factory = new ConfigurationObjectFactory(props); 27 | MyConfig conf = factory.build(MyConfig.class); 28 | 29 | # Default values 30 | 31 | Using `@Default()` can set arbitrary default values. To set `null` as the default value, use the `@DefaultNull`annotation. 32 | 33 | # Advanced usage 34 | 35 | @Config({"what1", "what2"}) 36 | @Default("none") 37 | String getWhat(); 38 | 39 | will look at `what1` first, then at `what2` and finally fall back to the default. 40 | 41 | # Parameterized configs 42 | 43 | enum StateType { 44 | FILESYSTEM, 45 | MEMORY 46 | } 47 | 48 | @Config("${state_type}.size") 49 | @DefaultNull 50 | String getParameterizedConfig(@Param("state_type") StateType e); 51 | 52 | @Config("${state_type}.${source}.path") 53 | @DefaultNull 54 | String getDoubleParameterizedConfig(@Param("state_type") StateType e, @Param("source") String source); 55 | 56 | 57 | Use it like the following: 58 | 59 | myConfig.getParameterizedConfig(StateType.MEMORY); // reads from MEMORY.size 60 | myConfig.getDoubleParameterizedConfig(StateType.FILESYSTEM, "backend"); // reads from FILESYSTEM.backend.path 61 | 62 | 63 | # Type support 64 | 65 | Config-magic supports these types: 66 | 67 | * Primitive types: `boolean`, `byte`, `short`, `integer`, `long`, `float`, `double`. 68 | * Enums. Note that config-magic by default ignores the case for enum values. 69 | * `java.lang.String`. 70 | * `java.net.URI`. 71 | * `java.lang.Class` and simple wildcard extensions (`java.lang.Class`, `java.lang.Class` - config-magic will type check that the type passed as a property conforms to the wildcard type), but not more complex wildcard or parameterized types (e.g. `java.lang.Class` or `java.lang.Class>`). 72 | * `org.skipe.config.TimeSpan`: constructed from short textual representation like "5d" (or alias "5 days"); units supported are: 73 | * ms (alias 'milliseconds') 74 | * s ('seconds') 75 | * m ('minutes') 76 | * h ('hours') 77 | * d ('days') 78 | * Any instantiable class that has a public constructor with a single `Object` parameter. This is useful for instance for [joda-time](http://joda-time.sourceforge.net/)'s `DateTime` objects. 79 | * Any instantiable class that has a public constructor with a single `String` parameter. This is useful for instance for `java.lang.File`. 80 | * Any class that has a static `valueOf` method with a single `String` parameter and the class as its return type. 81 | 82 | # Maven dependency 83 | 84 | To use config-magic in Maven projects: 85 | 86 | 87 | org.skife.config 88 | config-magic 89 | 0.11 90 | 91 | 92 | # Mailing List 93 | 94 | We have a [mailing list](http://groups.google.com/group/config-magic) for development and users. 95 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | org.skife.config 4 | config-magic 5 | jar 6 | 0.23-SNAPSHOT 7 | config-magic 8 | http://github.com/brianm/config-magic 9 | 10 | A configuration object convenience library. 11 | 12 | 13 | UTF-8 14 | 15 | 16 | 17 | 18 | Apache License 2.0 19 | http://www.apache.org/licenses/LICENSE-2.0.html 20 | repo 21 | 22 | 23 | 24 | 25 | scm:git:https://github.com/brianm/config-magic.git 26 | https://github.com/brianm/config-magic 27 | HEAD 28 | 29 | 30 | 31 | 32 | 33 | sonatype-nexus-snapshots 34 | Sonatype Nexus Snapshots 35 | http://oss.sonatype.org/content/repositories/snapshots 36 | 37 | 38 | 39 | sonatype-nexus-staging 40 | Nexus Release Repository 41 | http://oss.sonatype.org/service/local/staging/deploy/maven2/ 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | commons-configuration 50 | commons-configuration 51 | 1.6 52 | true 53 | 54 | 55 | 56 | cglib 57 | cglib-nodep 58 | 2.2 59 | 60 | 61 | 62 | org.slf4j 63 | slf4j-api 64 | 1.5.6 65 | 66 | 67 | 68 | org.slf4j 69 | slf4j-simple 70 | 1.5.6 71 | true 72 | 73 | 74 | 75 | javax.servlet 76 | servlet-api 77 | 2.5 78 | provided 79 | true 80 | 81 | 82 | 83 | junit 84 | junit 85 | 4.13.1 86 | test 87 | 88 | 89 | 90 | joda-time 91 | joda-time 92 | 1.6.2 93 | test 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-compiler-plugin 102 | 2.3.2 103 | 104 | 1.6 105 | 1.6 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-resources-plugin 112 | 2.5 113 | 114 | UTF-8 115 | 116 | 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-assembly-plugin 121 | 2.2.2 122 | 123 | gnu 124 | 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-source-plugin 130 | 2.2.1 131 | 132 | 133 | attach-sources 134 | 135 | jar-no-fork 136 | 137 | 138 | 139 | 140 | 141 | org.apache.maven.plugins 142 | maven-javadoc-plugin 143 | 2.9.1 144 | 145 | 146 | attach-javadocs 147 | 148 | jar 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.apache.maven.plugins 156 | maven-shade-plugin 157 | 1.5 158 | 159 | 160 | package 161 | 162 | shade 163 | 164 | 165 | 166 | 167 | cglib:cglib-nodep 168 | 169 | 170 | 171 | 172 | net.sf.cglib 173 | org.skife.config.cglib 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | org.apache.maven.plugins 182 | maven-release-plugin 183 | 2.5.3 184 | 185 | forked-path 186 | 187 | 188 | 189 | 190 | org.sonatype.plugins 191 | nexus-staging-maven-plugin 192 | 1.6.7 193 | true 194 | 195 | sonatype-nexus-staging 196 | https://oss.sonatype.org/ 197 | true 198 | 199 | 200 | 201 | 202 | org.apache.maven.plugins 203 | maven-gpg-plugin 204 | 1.5 205 | 206 | 207 | sign-artifacts 208 | verify 209 | 210 | sign 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | release-sign-artifacts 221 | 222 | 223 | performRelease 224 | true 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | brianm 238 | Brian McCallister 239 | brianm@skife.org 240 | 241 | 242 | martint 243 | Martin Traverso 244 | 245 | 246 | arosien 247 | Adam Rosien 248 | 249 | 250 | hgschmie 251 | Henning Schmiedehausen 252 | 253 | 254 | cowtowncoder 255 | Tatu Saloranta 256 | tatu@fasterxml.com 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/Bully.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.reflect.Array; 4 | import java.lang.reflect.Constructor; 5 | import java.lang.reflect.ParameterizedType; 6 | import java.lang.reflect.Type; 7 | import java.lang.reflect.WildcardType; 8 | import java.util.ArrayList; 9 | import java.util.Collection; 10 | import java.util.Collections; 11 | import java.util.HashMap; 12 | import java.util.LinkedHashSet; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Set; 16 | 17 | class Bully 18 | { 19 | /** All explicit type conversions that config magic knows about. Every new bully will know about those. */ 20 | private static final List> TYPE_COERCIBLES; 21 | 22 | /** Catchall converters. These will be run if no specific type coercer was found. */ 23 | private static final List> DEFAULT_COERCIBLES; 24 | 25 | static { 26 | final List> typeCoercibles = new ArrayList>(); 27 | final List> defaultCoercibles = new ArrayList>(); 28 | 29 | typeCoercibles.add(DefaultCoercibles.BOOLEAN_COERCIBLE); 30 | typeCoercibles.add(DefaultCoercibles.BYTE_COERCIBLE); 31 | typeCoercibles.add(DefaultCoercibles.SHORT_COERCIBLE); 32 | typeCoercibles.add(DefaultCoercibles.INTEGER_COERCIBLE); 33 | typeCoercibles.add(DefaultCoercibles.LONG_COERCIBLE); 34 | typeCoercibles.add(DefaultCoercibles.FLOAT_COERCIBLE); 35 | typeCoercibles.add(DefaultCoercibles.DOUBLE_COERCIBLE); 36 | typeCoercibles.add(DefaultCoercibles.STRING_COERCIBLE); 37 | 38 | // Look Brian, now it groks URIs. ;-) 39 | typeCoercibles.add(DefaultCoercibles.URI_COERCIBLE); 40 | 41 | defaultCoercibles.add(DefaultCoercibles.CASE_INSENSITIVE_ENUM_COERCIBLE); 42 | defaultCoercibles.add(DefaultCoercibles.VALUE_OF_COERCIBLE); 43 | defaultCoercibles.add(DefaultCoercibles.STRING_CTOR_COERCIBLE); 44 | defaultCoercibles.add(DefaultCoercibles.OBJECT_CTOR_COERCIBLE); 45 | 46 | TYPE_COERCIBLES = Collections.unmodifiableList(typeCoercibles); 47 | DEFAULT_COERCIBLES = Collections.unmodifiableList(defaultCoercibles); 48 | } 49 | 50 | /** 51 | * The instance specific mappings from a given type to its coercer. This needs to be two-level because the 52 | * catchall converters will generate specific instances of their coercers based on the type. 53 | */ 54 | private final Map, Coercer> mappings = new HashMap, Coercer>(); 55 | 56 | /** 57 | * All the coercibles that this instance knows about. This list can be extended with user mappings. 58 | */ 59 | private final List> coercibles = new ArrayList>(); 60 | 61 | public Bully() 62 | { 63 | coercibles.addAll(TYPE_COERCIBLES); 64 | } 65 | 66 | /** 67 | * Adds a new Coercible to the list of known coercibles. This also resets the current mappings in this bully. 68 | */ 69 | public void addCoercible(final Coercible coercible) 70 | { 71 | coercibles.add(coercible); 72 | mappings.clear(); 73 | } 74 | 75 | public synchronized Object coerce(Type type, String value, Separator separator) { 76 | if (type instanceof Class) { 77 | Class clazz = (Class)type; 78 | 79 | if (clazz.isArray()) { 80 | return coerceArray(clazz.getComponentType(), value, separator); 81 | } 82 | else if (Class.class.equals(clazz)) { 83 | return coerceClass(type, null, value); 84 | } 85 | else { 86 | return coerce(clazz, value); 87 | } 88 | } 89 | else if (type instanceof ParameterizedType) { 90 | ParameterizedType parameterizedType = (ParameterizedType)type; 91 | Type rawType = parameterizedType.getRawType(); 92 | 93 | if (rawType instanceof Class) { 94 | Type[] args = parameterizedType.getActualTypeArguments(); 95 | 96 | if (args != null && args.length == 1) { 97 | if (args[0] instanceof Class) { 98 | return coerceCollection((Class)rawType, (Class)args[0], value, separator); 99 | } 100 | else if (args[0] instanceof WildcardType) { 101 | return coerceClass(type, (WildcardType)args[0], value); 102 | } 103 | } 104 | } 105 | } 106 | throw new IllegalStateException(String.format("Don't know how to handle a '%s' type for value '%s'", type, value)); 107 | } 108 | 109 | private boolean isAssignableFrom(Type targetType, Class assignedClass) { 110 | if (targetType instanceof Class) { 111 | return ((Class)targetType).isAssignableFrom(assignedClass); 112 | } 113 | else if (targetType instanceof WildcardType) { 114 | WildcardType wildcardType = (WildcardType)targetType; 115 | 116 | // Class 117 | for (Type upperBoundType : wildcardType.getUpperBounds()) { 118 | if (!Object.class.equals(upperBoundType)) { 119 | if ((upperBoundType instanceof Class) && !((Class)upperBoundType).isAssignableFrom(assignedClass)) { 120 | return false; 121 | } 122 | } 123 | } 124 | } 125 | return true; 126 | } 127 | 128 | private Class coerceClass(Type type, WildcardType wildcardType, String value) { 129 | if (value == null) { 130 | return null; 131 | } 132 | else { 133 | try { 134 | Class clazz = Class.forName(value); 135 | 136 | if (!isAssignableFrom(wildcardType, clazz)) { 137 | throw new IllegalArgumentException("Specified class " + clazz + " is not compatible with required type " + type); 138 | } 139 | return clazz; 140 | } 141 | catch (Exception ex) { 142 | throw new IllegalArgumentException(ex); 143 | } 144 | } 145 | } 146 | 147 | private Object coerceArray(Class elemType, String value, Separator separator) { 148 | if (value == null) { 149 | return null; 150 | } 151 | else if (value.length() == 0) { 152 | return Array.newInstance(elemType, 0); 153 | } 154 | else { 155 | String[] tokens = value.split(separator == null ? Separator.DEFAULT : separator.value()); 156 | Object targetArray = Array.newInstance(elemType, tokens.length); 157 | 158 | for (int idx = 0; idx < tokens.length; idx++) { 159 | Array.set(targetArray, idx, coerce(elemType, tokens[idx])); 160 | } 161 | return targetArray; 162 | } 163 | } 164 | 165 | @SuppressWarnings({ "rawtypes", "unchecked" }) 166 | private Object coerceCollection(Class containerType, Class elemType, String value, Separator separator) { 167 | if (value == null) { 168 | return null; 169 | } 170 | else { 171 | Collection result = null; 172 | 173 | if (Set.class.equals(containerType)) { 174 | result = new LinkedHashSet(); 175 | } 176 | else if (Collection.class.equals(containerType) || List.class.equals(containerType)) { 177 | result = new ArrayList(); 178 | } 179 | else if (Collection.class.isAssignableFrom(containerType)) { 180 | try { 181 | final Constructor ctor = containerType.getConstructor(); 182 | 183 | if (ctor != null) { 184 | result = (Collection)ctor.newInstance(); 185 | } 186 | } 187 | catch (Exception ex) { 188 | // handled below 189 | } 190 | } 191 | if (result == null) { 192 | throw new IllegalStateException(String.format("Don't know how to handle a '%s' container type for value '%s'", containerType, value)); 193 | } 194 | if (value.length() > 0) { 195 | for (String token : value.split(separator == null ? Separator.DEFAULT : separator.value())) { 196 | result.add(coerce(elemType, token)); 197 | } 198 | } 199 | return result; 200 | } 201 | } 202 | 203 | private Object coerce(Class clazz, String value) { 204 | Coercer coercer = getCoercerFor(coercibles, clazz); 205 | if (coercer == null) { 206 | coercer = getCoercerFor(DEFAULT_COERCIBLES, clazz); 207 | 208 | if (coercer == null) { 209 | throw new IllegalStateException(String.format("Don't know how to handle a '%s' type for value '%s'", clazz, value)); 210 | } 211 | } 212 | return coercer.coerce(value); 213 | } 214 | 215 | private Coercer getCoercerFor(final List> coercibles, final Class type) 216 | { 217 | Coercer typeCoercer = mappings.get(type); 218 | if (typeCoercer == null) { 219 | for (Coercible coercible : coercibles) { 220 | final Coercer coercer = coercible.accept(type); 221 | if (coercer != null) { 222 | mappings.put(type, coercer); 223 | typeCoercer = coercer; 224 | break; 225 | } 226 | } 227 | } 228 | return typeCoercer; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/CaseInsensitiveEnumCoercible.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Do case insensitive string comparisons for determination of enum value matches. 7 | */ 8 | public class CaseInsensitiveEnumCoercible implements Coercible 9 | { 10 | public Coercer accept(final Class clazz) 11 | { 12 | if (!clazz.isEnum()) { 13 | return null; 14 | } 15 | 16 | final Enum[] values; 17 | try { 18 | values = (Enum[]) clazz.getMethod("values").invoke(null); 19 | } 20 | catch (Exception e) { 21 | throw new IllegalStateException("World seems to be broken, unable to access .values() static method", e); 22 | } 23 | 24 | return new Coercer() 25 | { 26 | public Object coerce(String value) 27 | { 28 | if (value == null) { 29 | return null; 30 | } 31 | for (Object o : values) { 32 | if (value.equalsIgnoreCase(o.toString())) { 33 | return o; 34 | } 35 | } 36 | throw new IllegalStateException("No enum value of " + Arrays.toString(values) + " matches [" + value + "]"); 37 | } 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/Coercer.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | /** 4 | * Coerces a given string value to a type. 5 | */ 6 | public interface Coercer 7 | { 8 | T coerce(String value); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/Coercible.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | /** 4 | * Returns a Coercer to convert String values into 5 | * the given type. The interface accepts Class<?> because 6 | * the type can be {@link java.lang.Object}. 7 | */ 8 | public interface Coercible 9 | { 10 | Coercer accept(Class clazz); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/CommonsConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import org.apache.commons.configuration.Configuration; 4 | 5 | public class CommonsConfigSource implements ConfigSource 6 | { 7 | private final Configuration config; 8 | 9 | public CommonsConfigSource(Configuration config) { 10 | this.config = config; 11 | } 12 | 13 | public String getString(String propertyName) 14 | { 15 | final String [] strings = config.getStringArray(propertyName); 16 | if (strings == null || strings.length == 0) { 17 | return null; 18 | } 19 | final StringBuilder sb = new StringBuilder(); 20 | for (int i = 0; i < strings.length; i++) { 21 | sb.append(strings[i]); 22 | if (i < strings.length - 1) { 23 | sb.append(','); 24 | } 25 | } 26 | return sb.toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/Config.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | import java.lang.annotation.ElementType; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface Config 11 | { 12 | String [] value(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/ConfigReplacements.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * If a configuration bean is created with mapped replacement values via 10 | * {@link ConfigurationObjectFactory#buildWithReplacements(Class, java.util.Map)}, 11 | * this annotation designates a method which should present the provided Map. 12 | * The map may not be changed and is not necessarily the same instance as the original. 13 | * If a key is provided, the return is instead the value for that key. 14 | */ 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target(ElementType.METHOD) 17 | public @interface ConfigReplacements 18 | { 19 | static final String DEFAULT_VALUE = "__%%%noValue%%%__"; 20 | 21 | /** 22 | * The key to look up in the replacement map, if any. 23 | */ 24 | String value() default DEFAULT_VALUE; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/ConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | public interface ConfigSource 4 | { 5 | String getString(String propertyName); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/ConfigurationObjectFactory.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import net.sf.cglib.proxy.Callback; 4 | import net.sf.cglib.proxy.CallbackFilter; 5 | import net.sf.cglib.proxy.Enhancer; 6 | import net.sf.cglib.proxy.Factory; 7 | import net.sf.cglib.proxy.MethodInterceptor; 8 | import net.sf.cglib.proxy.MethodProxy; 9 | import net.sf.cglib.proxy.NoOp; 10 | 11 | import java.lang.annotation.Annotation; 12 | import java.lang.reflect.Method; 13 | import java.lang.reflect.Modifier; 14 | import java.util.ArrayList; 15 | import java.util.Collections; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.Properties; 20 | import java.util.concurrent.ConcurrentHashMap; 21 | import java.util.concurrent.ConcurrentMap; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | public class ConfigurationObjectFactory 26 | { 27 | private static final Logger logger = LoggerFactory.getLogger(ConfigurationObjectFactory.class); 28 | private static final ConcurrentMap, Factory> factories = new ConcurrentHashMap, Factory>(); 29 | private final ConfigSource config; 30 | private final Bully bully; 31 | 32 | public ConfigurationObjectFactory(Properties props) 33 | { 34 | this(new SimplePropertyConfigSource(props)); 35 | } 36 | 37 | public ConfigurationObjectFactory(ConfigSource config) 38 | { 39 | this.config = config; 40 | this.bully = new Bully(); 41 | } 42 | 43 | public void addCoercible(final Coercible coercible) 44 | { 45 | this.bully.addCoercible(coercible); 46 | } 47 | 48 | 49 | public T buildWithReplacements(Class configClass, Map mappedReplacements) 50 | { 51 | return internalBuild(configClass, mappedReplacements); 52 | } 53 | 54 | public T build(Class configClass) 55 | { 56 | return internalBuild(configClass, null); 57 | } 58 | 59 | private T internalBuild(Class configClass, Map mappedReplacements) 60 | { 61 | final List callbacks = new ArrayList(); 62 | final Map slots = new HashMap(); 63 | callbacks.add(NoOp.INSTANCE); 64 | 65 | int count = 1; 66 | 67 | // Hook up a toString method that prints out the settings for that bean if possible. 68 | final Method toStringMethod = findToStringMethod(configClass); 69 | if (toStringMethod != null) { 70 | slots.put(toStringMethod, count++); 71 | callbacks.add(new ConfigMagicBeanToString(callbacks)); 72 | } 73 | 74 | // Now hook up the actual value interceptors. 75 | for (final Method method : configClass.getMethods()) { 76 | if (method.isAnnotationPresent(Config.class)) { 77 | final Config annotation = method.getAnnotation(Config.class); 78 | slots.put(method, count++); 79 | 80 | if (method.getParameterTypes().length > 0) { 81 | if (mappedReplacements != null) { 82 | throw new RuntimeException("Replacements are not supported for parameterized config methods"); 83 | } 84 | buildParameterized(callbacks, method, annotation); 85 | } 86 | else { 87 | buildSimple(callbacks, method, annotation, mappedReplacements, null); 88 | } 89 | } 90 | else if (method.isAnnotationPresent(ConfigReplacements.class)) { 91 | final ConfigReplacements annotation = method.getAnnotation(ConfigReplacements.class); 92 | 93 | slots.put(method, count++); 94 | 95 | if (ConfigReplacements.DEFAULT_VALUE.equals(annotation.value())) { 96 | Map fixedMap = mappedReplacements == null ? 97 | Collections.emptyMap() : Collections.unmodifiableMap(mappedReplacements); 98 | 99 | callbacks.add(new ConfigMagicFixedValue(method, "annotation: @ConfigReplacements", fixedMap, false)); 100 | } else { 101 | buildSimple(callbacks, method, null, mappedReplacements, annotation); 102 | } 103 | } 104 | else if (Modifier.isAbstract(method.getModifiers())) { 105 | throw new AbstractMethodError(String.format("Method [%s] is abstract and lacks an @Config annotation", 106 | method.toGenericString())); 107 | } 108 | } 109 | 110 | 111 | if (factories.containsKey(configClass)) { 112 | Factory f = factories.get(configClass); 113 | return configClass.cast(f.newInstance(callbacks.toArray(new Callback[callbacks.size()]))); 114 | } 115 | else { 116 | Enhancer e = new Enhancer(); 117 | e.setSuperclass(configClass); 118 | e.setCallbackFilter(new ConfigMagicCallbackFilter(slots)); 119 | e.setCallbacks(callbacks.toArray(new Callback[callbacks.size()])); 120 | T rt = configClass.cast(e.create()); 121 | factories.putIfAbsent(configClass, (Factory) rt); 122 | return rt; 123 | } 124 | } 125 | 126 | private void buildSimple(List callbacks, Method method, Config annotation, 127 | Map mappedReplacements, ConfigReplacements mapAnnotation) 128 | { 129 | String assignedFrom = null; 130 | String[] propertyNames = new String[0]; 131 | String value = null; 132 | 133 | // Annotation will be null for an @ConfigReplacements, in which case "value" will 134 | // be preset and ready to be defaulted + bullied 135 | if (annotation != null) { 136 | propertyNames = annotation.value(); 137 | 138 | if (propertyNames == null || propertyNames.length == 0) { 139 | throw new IllegalArgumentException("Method " + 140 | method.toGenericString() + 141 | " declares config annotation but no field name!"); 142 | } 143 | 144 | 145 | for (String propertyName : propertyNames) { 146 | if (mappedReplacements != null) { 147 | propertyName = applyReplacements(propertyName, mappedReplacements); 148 | } 149 | value = config.getString(propertyName); 150 | 151 | // First value found wins 152 | if (value != null) { 153 | assignedFrom = "property: '" + propertyName + "'"; 154 | logger.info("Assigning value [{}] for [{}] on [{}#{}()]", 155 | new Object[] { value, propertyName, method.getDeclaringClass().getName(), method.getName() }); 156 | break; 157 | } 158 | } 159 | } else { 160 | if (mapAnnotation == null) { 161 | throw new IllegalStateException("Neither @Config nor @ConfigReplacements provided, this should not be possible!"); 162 | } 163 | String key = mapAnnotation.value(); 164 | value = mappedReplacements == null ? null : mappedReplacements.get(key); 165 | 166 | if (value != null) { 167 | assignedFrom = "@ConfigReplacements: key '" + key + "'"; 168 | logger.info("Assigning mappedReplacement value [{}] for [{}] on [{}#{}()]", 169 | new Object[] { value, key, method.getDeclaringClass().getName(), method.getName() }); 170 | } 171 | } 172 | 173 | final boolean hasDefault = method.isAnnotationPresent(Default.class); 174 | final boolean hasDefaultNull = method.isAnnotationPresent(DefaultNull.class); 175 | boolean hasForValue = false; 176 | if(hasDefault) { 177 | String[] forvalue = method.getAnnotation(Default.class).forValues(); 178 | if (forvalue != null && forvalue.length > 0) { 179 | hasForValue = true; 180 | } 181 | } 182 | if (hasDefault && hasDefaultNull) { 183 | throw new IllegalArgumentException(String.format("@Default and @DefaultNull present in [%s]", method.toGenericString())); 184 | } 185 | 186 | boolean useMethod = false; 187 | 188 | // 189 | // This is how the value logic works if no value has been set by the config: 190 | // 191 | // - if the @Default annotation is present, use its value. 192 | // - if the @Default annotation is present and has the argument 'forValues', use the default value if any of the forValues matches the property value 193 | // - if the @DefaultNull annotation is present, accept null as the value 194 | // - otherwise, check whether the method is not abstract. If it is not, mark the callback that it should call the method and 195 | // ignore the passed in value (which will be null) 196 | // - if all else fails, throw an exception. 197 | // 198 | 199 | if (value == null) { 200 | if (hasDefault) { 201 | value = method.getAnnotation(Default.class).value(); 202 | assignedFrom = "annotation: @Default"; 203 | 204 | logger.info("Assigning default value [{}] for {} on [{}#{}()]", 205 | new Object[] { value, propertyNames, method.getDeclaringClass().getName(), method.getName() }); 206 | } 207 | else if (hasDefaultNull) { 208 | logger.info("Assigning null default value for {} on [{}#{}()]", 209 | new Object[] { propertyNames, method.getDeclaringClass().getName(), method.getName() }); 210 | assignedFrom = "annotation: @DefaultNull"; 211 | } 212 | else { 213 | // Final try: Is the method is actually callable? 214 | if (!Modifier.isAbstract(method.getModifiers())) { 215 | useMethod = true; 216 | assignedFrom = "method: '" + method.getName() + "()'"; 217 | logger.info("Using method itself for {} on [{}#{}()]", 218 | new Object[] { propertyNames, method.getDeclaringClass().getName(), method.getName() }); 219 | } 220 | else { 221 | throw new IllegalArgumentException(String.format("No value present for '%s' in [%s]", 222 | prettyPrint(propertyNames, mappedReplacements), 223 | method.toGenericString())); 224 | } 225 | } 226 | } 227 | else if (value != null && hasForValue) { 228 | String[] forvalue = method.getAnnotation(Default.class).forValues(); 229 | for (String val : forvalue) { 230 | if (value.equalsIgnoreCase(val)) { 231 | value = method.getAnnotation(Default.class).value(); 232 | assignedFrom = "annotation: @Default"; 233 | 234 | logger.info("Assigning default for_value [{}] for {} on [{}#{}()]", 235 | new Object[] { value, propertyNames, method.getDeclaringClass().getName(), method.getName() }); 236 | 237 | break; 238 | } 239 | } 240 | } 241 | 242 | final Object finalValue = bully.coerce(method.getGenericReturnType(), value, method.getAnnotation(Separator.class)); 243 | callbacks.add(new ConfigMagicFixedValue(method, assignedFrom, finalValue, useMethod)); 244 | } 245 | 246 | private String applyReplacements(String propertyName, Map mappedReplacements) 247 | { 248 | for (String key : mappedReplacements.keySet()) { 249 | String token = makeToken(key); 250 | String replacement = mappedReplacements.get(key); 251 | propertyName = propertyName.replace(token, replacement); 252 | } 253 | return propertyName; 254 | } 255 | 256 | private void buildParameterized(List callbacks, Method method, Config annotation) 257 | { 258 | String defaultValue = null; 259 | 260 | final boolean hasDefault = method.isAnnotationPresent(Default.class); 261 | final boolean hasDefaultNull = method.isAnnotationPresent(DefaultNull.class); 262 | 263 | if (hasDefault && hasDefaultNull) { 264 | throw new IllegalArgumentException(String.format("@Default and @DefaultNull present in [%s]", method.toGenericString())); 265 | } 266 | 267 | if (hasDefault) { 268 | defaultValue = method.getAnnotation(Default.class).value(); 269 | } 270 | else if (!hasDefaultNull) { 271 | throw new IllegalArgumentException(String.format("No value present for '%s' in [%s]", 272 | prettyPrint(annotation.value(), null), 273 | method.toGenericString())); 274 | } 275 | 276 | final Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 277 | final List paramTokenList = new ArrayList(); 278 | for (Annotation[] parameterTab : parameterAnnotations) { 279 | for (Annotation parameter : parameterTab) { 280 | if (parameter.annotationType().equals(Param.class)) { 281 | Param paramAnnotation = (Param) parameter; 282 | paramTokenList.add(makeToken(paramAnnotation.value())); 283 | break; 284 | } 285 | } 286 | } 287 | 288 | if (paramTokenList.size() != method.getParameterTypes().length) { 289 | throw new RuntimeException(String.format("Method [%s] is missing one or more @Param annotations", 290 | method.toGenericString())); 291 | } 292 | 293 | final Object bulliedDefaultValue = bully.coerce(method.getGenericReturnType(), defaultValue, method.getAnnotation(Separator.class)); 294 | final String[] annotationValues = annotation.value(); 295 | 296 | if (annotationValues == null || annotationValues.length == 0) { 297 | throw new IllegalArgumentException("Method " + 298 | method.toGenericString() + 299 | " declares config annotation but no field name!"); 300 | } 301 | 302 | callbacks.add(new ConfigMagicMethodInterceptor(method, 303 | config, 304 | annotationValues, 305 | paramTokenList, 306 | bully, 307 | bulliedDefaultValue)); 308 | } 309 | 310 | private String makeToken(String temp) 311 | { 312 | return "${" + temp + "}"; 313 | } 314 | 315 | private String prettyPrint(String[] values, final Map mappedReplacements) 316 | { 317 | if (values == null || values.length == 0) { 318 | return ""; 319 | } 320 | final StringBuilder sb = new StringBuilder("["); 321 | 322 | for (int i = 0; i < values.length; i++) { 323 | sb.append(values[i]); 324 | if (i < (values.length - 1)) { 325 | sb.append(", "); 326 | } 327 | } 328 | sb.append(']'); 329 | if (mappedReplacements != null && mappedReplacements.size() > 0) { 330 | sb.append(" translated to ["); 331 | for (int i = 0; i < values.length; i++) { 332 | sb.append(applyReplacements(values[i], mappedReplacements)); 333 | if (i < (values.length - 1)) { 334 | sb.append(", "); 335 | } 336 | } 337 | sb.append("]"); 338 | } 339 | 340 | return sb.toString(); 341 | } 342 | 343 | private static final class ConfigMagicFixedValue implements MethodInterceptor 344 | { 345 | private final Method method; 346 | private final String assignedFrom; 347 | 348 | private final Handler handler; 349 | 350 | private ConfigMagicFixedValue(final Method method, final String assignedFrom, final Object value, final boolean callSuper) 351 | { 352 | this.method = method; 353 | this.assignedFrom = assignedFrom; 354 | 355 | // This is a workaround for broken cglib 356 | if (callSuper) { 357 | this.handler = new InvokeSuperHandler(); 358 | } 359 | else { 360 | handler = new FixedValueHandler(value); 361 | } 362 | } 363 | 364 | public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable 365 | { 366 | return handler.handle(methodProxy, o, objects); 367 | } 368 | 369 | private static interface Handler 370 | { 371 | Object handle(MethodProxy m, Object o, Object[] args) throws Throwable; 372 | } 373 | 374 | private static class InvokeSuperHandler implements Handler 375 | { 376 | public Object handle(MethodProxy m, Object o, Object[] args) throws Throwable 377 | { 378 | return m.invokeSuper(o, args); 379 | } 380 | } 381 | 382 | private static class FixedValueHandler implements Handler 383 | { 384 | private final Object finalValue; 385 | 386 | public FixedValueHandler(final Object finalValue) 387 | { 388 | this.finalValue = finalValue; 389 | } 390 | 391 | public Object handle(MethodProxy m, Object o, Object[] args) throws Throwable 392 | { 393 | return finalValue; 394 | } 395 | 396 | private transient String toStringValue = null; 397 | 398 | @Override 399 | public String toString() 400 | { 401 | if (toStringValue == null) { 402 | final StringBuilder sb = new StringBuilder("value: "); 403 | if (finalValue != null) { 404 | sb.append(finalValue.toString()); 405 | sb.append(", class: "); 406 | sb.append(finalValue.getClass().getName()); 407 | } 408 | else { 409 | sb.append(""); 410 | } 411 | toStringValue = sb.toString(); 412 | } 413 | 414 | return toStringValue; 415 | } 416 | } 417 | 418 | private transient String toStringValue = null; 419 | 420 | @Override 421 | public String toString() 422 | { 423 | if (toStringValue == null) { 424 | final StringBuilder sb = new StringBuilder(method.getName()); 425 | sb.append("(): "); 426 | sb.append(assignedFrom); 427 | sb.append(", "); 428 | sb.append(handler.toString()); 429 | toStringValue = sb.toString(); 430 | } 431 | 432 | return toStringValue; 433 | } 434 | } 435 | 436 | 437 | private static final class ConfigMagicCallbackFilter implements CallbackFilter 438 | { 439 | private final Map slots; 440 | 441 | private ConfigMagicCallbackFilter(final Map slots) 442 | { 443 | this.slots = slots; 444 | } 445 | 446 | public int accept(Method method) 447 | { 448 | return slots.containsKey(method) ? slots.get(method) : 0; 449 | } 450 | } 451 | 452 | private static final class ConfigMagicMethodInterceptor implements MethodInterceptor 453 | { 454 | private final Method method; 455 | private final ConfigSource config; 456 | private final String[] properties; 457 | private final Bully bully; 458 | private final Object defaultValue; 459 | private final List paramTokenList; 460 | 461 | private ConfigMagicMethodInterceptor(final Method method, 462 | final ConfigSource config, 463 | final String[] properties, 464 | final List paramTokenList, 465 | final Bully bully, 466 | final Object defaultValue) 467 | { 468 | this.method = method; 469 | this.config = config; 470 | this.properties = properties; 471 | this.paramTokenList = paramTokenList; 472 | this.bully = bully; 473 | this.defaultValue = defaultValue; 474 | } 475 | 476 | public Object intercept(final Object o, 477 | final Method method, 478 | final Object[] args, 479 | final MethodProxy methodProxy) throws Throwable 480 | { 481 | for (String property : properties) { 482 | if (args.length == paramTokenList.size()) { 483 | for (int i = 0; i < args.length; ++i) { 484 | property = property.replace(paramTokenList.get(i), String.valueOf(args[i])); 485 | } 486 | String value = config.getString(property); 487 | if (value != null) { 488 | logger.info("Assigning value [{}] for [{}] on [{}#{}()]", 489 | new Object[] { value, property, method.getDeclaringClass().getName(), method.getName() }); 490 | return bully.coerce(method.getGenericReturnType(), value, method.getAnnotation(Separator.class)); 491 | } 492 | } 493 | else { 494 | throw new IllegalStateException("Argument list doesn't match @Param list"); 495 | } 496 | } 497 | logger.info("Assigning default value [{}] for {} on [{}#{}()]", 498 | new Object[] { defaultValue, properties, method.getDeclaringClass().getName(), method.getName() }); 499 | return defaultValue; 500 | } 501 | 502 | private transient String toStringValue = null; 503 | 504 | @Override 505 | public String toString() 506 | { 507 | if (toStringValue == null) { 508 | toStringValue = method.getName() + ": " + super.toString(); 509 | } 510 | 511 | return toStringValue; 512 | } 513 | } 514 | 515 | private Method findToStringMethod(final Class clazz) 516 | { 517 | try { 518 | return clazz.getMethod("toString", new Class [] {}); 519 | } 520 | catch (NoSuchMethodException nsme) { 521 | try { 522 | return Object.class.getMethod("toString", new Class [] {}); 523 | } 524 | catch (NoSuchMethodException nsme2) { 525 | throw new IllegalStateException("Could not intercept toString method!", nsme); 526 | } 527 | } 528 | } 529 | 530 | private static final class ConfigMagicBeanToString implements MethodInterceptor 531 | { 532 | private final List callbacks; 533 | 534 | private transient String toStringValue = null; 535 | 536 | private ConfigMagicBeanToString(final List callbacks) 537 | { 538 | this.callbacks = callbacks; 539 | } 540 | 541 | public Object intercept(final Object o, 542 | final Method method, 543 | final Object[] args, 544 | final MethodProxy methodProxy) throws Throwable 545 | { 546 | if (toStringValue == null) { 547 | final StringBuilder sb = new StringBuilder(); 548 | 549 | for (int i = 2; i < callbacks.size(); i++) { 550 | sb.append(callbacks.get(i).toString()); 551 | 552 | if (i < callbacks.size() - 1) { 553 | sb.append("\n"); 554 | } 555 | } 556 | toStringValue = sb.toString(); 557 | } 558 | 559 | return toStringValue; 560 | } 561 | } 562 | } 563 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/DataAmount.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class DataAmount 7 | { 8 | private final long value; 9 | private final DataAmountUnit unit; 10 | private final long numBytes; 11 | private static final Pattern SPLIT = Pattern.compile("^(\\d+)\\s*([a-zA-Z]+)$"); 12 | private static final Pattern NUM_ONLY = Pattern.compile("^(\\d+)$"); 13 | 14 | public DataAmount(String spec) 15 | { 16 | Matcher m = SPLIT.matcher(spec); 17 | if (!m.matches()) { 18 | // #7: allow undecorated unit to mean basic bytes 19 | m = NUM_ONLY.matcher(spec); 20 | if (!m.matches()) { 21 | throw new IllegalArgumentException(String.format("%s is not a valid data amount", spec)); 22 | } 23 | unit = DataAmountUnit.BYTE; 24 | value = numBytes = Long.parseLong(spec); 25 | } else { 26 | String number = m.group(1); 27 | String type = m.group(2); 28 | this.value = Long.parseLong(number); 29 | this.unit = DataAmountUnit.fromString(type); 30 | this.numBytes = unit.getFactor() * value; 31 | } 32 | } 33 | 34 | public DataAmount(long value, DataAmountUnit unit) 35 | { 36 | this.value = value; 37 | this.unit = unit; 38 | this.numBytes = unit.getFactor() * value; 39 | } 40 | 41 | /** 42 | * @since 0.15 43 | */ 44 | public DataAmount(long rawBytes) 45 | { 46 | value = numBytes = rawBytes; 47 | unit = DataAmountUnit.BYTE; 48 | } 49 | 50 | public long getValue() 51 | { 52 | return value; 53 | } 54 | 55 | public DataAmountUnit getUnit() 56 | { 57 | return unit; 58 | } 59 | 60 | public long getNumberOfBytes() 61 | { 62 | return numBytes; 63 | } 64 | 65 | public DataAmount convertTo(DataAmountUnit newUnit) 66 | { 67 | return new DataAmount(numBytes / newUnit.getFactor(), newUnit); 68 | } 69 | 70 | @Override 71 | public String toString() 72 | { 73 | return value + unit.getSymbol(); 74 | } 75 | 76 | @Override 77 | public int hashCode() 78 | { 79 | final int prime = 31; 80 | int result = 1; 81 | result = prime * result + (int)(numBytes ^ (numBytes >>> 32)); 82 | result = prime * result + unit.hashCode(); 83 | result = prime * result + (int)(value ^ (value >>> 32)); 84 | return result; 85 | } 86 | 87 | @Override 88 | public boolean equals(Object obj) 89 | { 90 | if (this == obj) { 91 | return true; 92 | } 93 | if (obj == null) { 94 | return false; 95 | } 96 | if (getClass() != obj.getClass()) { 97 | return false; 98 | } 99 | DataAmount other = (DataAmount)obj; 100 | 101 | return numBytes == other.numBytes; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/DataAmountUnit.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | public enum DataAmountUnit 4 | { 5 | BYTE("B", 1l), 6 | 7 | KIBIBYTE("KiB", 1024l), 8 | MEBIBYTE("MiB", 1024l*1024l), 9 | GIBIBYTE("GiB", 1024l*1024l*1024l), 10 | TEBIBYTE("TiB", 1024l*1024l*1024l*1024l), 11 | PEBIBYTE("PiB", 1024l*1024l*1024l*1024l*1024l), 12 | EXIBYTE("EiB", 1024l*1024l*1024l*1024l*1024l*1024l), 13 | 14 | KILOBYTE("kB", 1000l), 15 | MEGABYTE("MB", 1000l*1000l), 16 | GIGABYTE("GB", 1000l*1000l*1000l), 17 | TERABYTE("TB", 1000l*1000l*1000l*1000l), 18 | PETABYTE("PB", 1000l*1000l*1000l*1000l*1000l), 19 | EXABYTE("EB", 1000l*1000l*1000l*1000l*1000l*1000l); 20 | 21 | private final String symbol; 22 | private final long factor; 23 | 24 | private DataAmountUnit(String symbol, long factor) 25 | { 26 | this.symbol = symbol; 27 | this.factor = factor; 28 | } 29 | 30 | public String getSymbol() 31 | { 32 | return symbol; 33 | } 34 | 35 | public long getFactor() 36 | { 37 | return factor; 38 | } 39 | 40 | public static DataAmountUnit fromString(String text) 41 | { 42 | for (DataAmountUnit unit : DataAmountUnit.values()) { 43 | if (unit.symbol.equals(text)) { 44 | return unit; 45 | } 46 | } 47 | throw new IllegalArgumentException("Unknown unit '" + text + "'"); 48 | } 49 | 50 | public static DataAmountUnit fromStringCaseInsensitive(String origText) 51 | { 52 | final String text = origText.toLowerCase(); 53 | for (DataAmountUnit unit : DataAmountUnit.values()) { 54 | if (unit.symbol.equals(text)) { 55 | return unit; 56 | } 57 | } 58 | throw new IllegalArgumentException("Unknown unit '" + origText + "'"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/Default.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | import java.lang.annotation.ElementType; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface Default 11 | { 12 | String value(); 13 | String[] forValues() default {}; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/DefaultCoercibles.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.Modifier; 7 | import java.net.URI; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | final class DefaultCoercibles 12 | { 13 | 14 | private DefaultCoercibles() 15 | { 16 | } 17 | 18 | public static final Coercible CASE_INSENSITIVE_ENUM_COERCIBLE = new CaseInsensitiveEnumCoercible(); 19 | 20 | static final Coercible BOOLEAN_COERCIBLE = new Coercible() { 21 | public Coercer accept(final Class clazz) { 22 | if (Boolean.class.isAssignableFrom(clazz) || Boolean.TYPE.isAssignableFrom(clazz)) { 23 | return DefaultCoercibles.BOOLEAN_COERCER; 24 | } 25 | return null; 26 | } 27 | }; 28 | 29 | static final Coercible BYTE_COERCIBLE = new Coercible() { 30 | public Coercer accept(final Class clazz) { 31 | if (Byte.class.isAssignableFrom(clazz) || Byte.TYPE.isAssignableFrom(clazz)) { 32 | return DefaultCoercibles.BYTE_COERCER; 33 | } 34 | return null; 35 | } 36 | }; 37 | 38 | static final Coercible SHORT_COERCIBLE = new Coercible() { 39 | public Coercer accept(final Class clazz) { 40 | if (Short.class.isAssignableFrom(clazz) || Short.TYPE.isAssignableFrom(clazz)) { 41 | return DefaultCoercibles.SHORT_COERCER; 42 | } 43 | return null; 44 | } 45 | }; 46 | 47 | static final Coercible INTEGER_COERCIBLE = new Coercible() { 48 | public Coercer accept(final Class clazz) { 49 | if (Integer.class.isAssignableFrom(clazz) || Integer.TYPE.isAssignableFrom(clazz)) { 50 | return DefaultCoercibles.INTEGER_COERCER; 51 | } 52 | return null; 53 | } 54 | }; 55 | 56 | static final Coercible LONG_COERCIBLE = new Coercible() { 57 | public Coercer accept(final Class clazz) { 58 | if (Long.class.isAssignableFrom(clazz) || Long.TYPE.isAssignableFrom(clazz)) { 59 | return DefaultCoercibles.LONG_COERCER; 60 | } 61 | return null; 62 | } 63 | }; 64 | 65 | static final Coercible FLOAT_COERCIBLE = new Coercible() { 66 | public Coercer accept(final Class clazz) { 67 | if (Float.class.isAssignableFrom(clazz) || Float.TYPE.isAssignableFrom(clazz)) { 68 | return DefaultCoercibles.FLOAT_COERCER; 69 | } 70 | return null; 71 | } 72 | }; 73 | 74 | static final Coercible DOUBLE_COERCIBLE = new Coercible() { 75 | public Coercer accept(final Class clazz) { 76 | if (Double.class.isAssignableFrom(clazz) || Double.TYPE.isAssignableFrom(clazz)) { 77 | return DefaultCoercibles.DOUBLE_COERCER; 78 | } 79 | return null; 80 | } 81 | }; 82 | 83 | static final Coercible STRING_COERCIBLE = new Coercible() { 84 | public Coercer accept(final Class clazz) { 85 | if (String.class.equals(clazz)) { 86 | return DefaultCoercibles.STRING_COERCER; 87 | } 88 | return null; 89 | } 90 | }; 91 | 92 | static final Coercible URI_COERCIBLE = new Coercible() { 93 | public Coercer accept(final Class clazz) { 94 | if (URI.class.equals(clazz)) { 95 | return DefaultCoercibles.URI_COERCER; 96 | } 97 | return null; 98 | } 99 | }; 100 | 101 | /** 102 | * A Coercible that accepts any type with a static valueOf(String) method. 103 | */ 104 | static final Coercible VALUE_OF_COERCIBLE = new Coercible() { 105 | 106 | private Map, Coercer> coercerMap = new HashMap, Coercer>(); 107 | 108 | public Coercer accept(final Class type) 109 | { 110 | if (coercerMap.containsKey(type)) { 111 | // If a key exists, the value always gets returned. If a null value is in the map, 112 | // the type was seen before and deemed not worthy. 113 | return coercerMap.get(type); 114 | } 115 | 116 | Coercer coercer = null; 117 | try { 118 | // Method must be 'static valueOf(String)' and return the type in question. 119 | Method candidate = type.getMethod("valueOf", String.class); 120 | if (!Modifier.isStatic(candidate.getModifiers())) { 121 | // not static. 122 | candidate = null; 123 | } 124 | else if (!candidate.getReturnType().isAssignableFrom(type)) { 125 | // does not return the right type. 126 | candidate = null; 127 | } 128 | 129 | if (candidate != null) { 130 | final Method valueOfMethod = candidate; 131 | 132 | coercer = new Coercer() { 133 | public Object coerce(final String value) 134 | { 135 | try { 136 | return value == null ? null : valueOfMethod.invoke(null, value); 137 | } 138 | catch (Exception e) { 139 | throw convertException(e); 140 | } 141 | } 142 | }; 143 | } 144 | } 145 | catch(NoSuchMethodException nsme) { 146 | // Don't do anything, the class does not have a method. 147 | } 148 | 149 | coercerMap.put(type, coercer); 150 | return coercer; 151 | } 152 | }; 153 | 154 | /** 155 | * A Coercible that accepts any type with a c'tor that takes a single string parameter. 156 | */ 157 | static final Coercible STRING_CTOR_COERCIBLE = new Coercible() { 158 | 159 | private Map, Coercer> coercerMap = new HashMap, Coercer>(); 160 | 161 | public Coercer accept(final Class type) 162 | { 163 | if (coercerMap.containsKey(type)) { 164 | // If a key exists, the value always gets returned. If a null value is in the map, 165 | // the type was seen before and deemed not worthy. 166 | return coercerMap.get(type); 167 | } 168 | 169 | 170 | Coercer coercer = null; 171 | try { 172 | final Constructor ctor = type.getConstructor(String.class); 173 | 174 | coercer = new Coercer() { 175 | public Object coerce(final String value) 176 | { 177 | try { 178 | return value == null ? null : ctor.newInstance(value); 179 | } 180 | catch (Exception e) { 181 | throw convertException(e); 182 | } 183 | } 184 | }; 185 | } 186 | catch(NoSuchMethodException nsme) { 187 | // Don't do anything, the class does not have a matching c'tor 188 | } 189 | 190 | coercerMap.put(type, coercer); 191 | return coercer; 192 | } 193 | }; 194 | 195 | /** 196 | * A Coercible that accepts any type with a c'tor that takes a single Object parameter. 197 | * 198 | * This one was lovingly prepared and added for Jodatime DateTime objects. 199 | */ 200 | static final Coercible OBJECT_CTOR_COERCIBLE = new Coercible() { 201 | 202 | private Map, Coercer> coercerMap = new HashMap, Coercer>(); 203 | 204 | public Coercer accept(final Class type) 205 | { 206 | if (coercerMap.containsKey(type)) { 207 | // If a key exists, the value always gets returned. If a null value is in the map, 208 | // the type was seen before and deemed not worthy. 209 | return coercerMap.get(type); 210 | } 211 | 212 | 213 | Coercer coercer = null; 214 | try { 215 | final Constructor ctor = type.getConstructor(Object.class); 216 | 217 | coercer = new Coercer() { 218 | public Object coerce(final String value) 219 | { 220 | try { 221 | return ctor.newInstance(value); 222 | } 223 | catch (Exception e) { 224 | throw convertException(e); 225 | } 226 | } 227 | }; 228 | } 229 | catch(NoSuchMethodException nsme) { 230 | // Don't do anything, the class does not have a matching c'tor 231 | } 232 | 233 | coercerMap.put(type, coercer); 234 | return coercer; 235 | } 236 | }; 237 | 238 | static final Coercer BOOLEAN_COERCER = new Coercer() { 239 | public Boolean coerce(final String value) { 240 | return value != null ? Boolean.valueOf(value) : null; 241 | } 242 | }; 243 | 244 | static final Coercer BYTE_COERCER = new Coercer() { 245 | public Byte coerce(final String value) { 246 | return value != null ? Byte.valueOf(value) : null; 247 | } 248 | }; 249 | 250 | static final Coercer SHORT_COERCER = new Coercer() { 251 | public Short coerce(final String value) { 252 | return value != null ? Short.valueOf(value) : null; 253 | } 254 | }; 255 | 256 | static final Coercer INTEGER_COERCER = new Coercer() { 257 | public Integer coerce(final String value) { 258 | return value != null ? Integer.valueOf(value) : null; 259 | } 260 | }; 261 | 262 | static final Coercer LONG_COERCER = new Coercer() { 263 | public Long coerce(final String value) { 264 | return value != null ? Long.valueOf(value) : null; 265 | } 266 | }; 267 | 268 | static final Coercer FLOAT_COERCER = new Coercer() { 269 | public Float coerce(final String value) { 270 | return value != null ? Float.valueOf(value) : null; 271 | } 272 | }; 273 | 274 | static final Coercer DOUBLE_COERCER = new Coercer() { 275 | public Double coerce(final String value) { 276 | return value != null ? Double.valueOf(value) : null; 277 | } 278 | }; 279 | 280 | static final Coercer STRING_COERCER = new Coercer() { 281 | public String coerce(final String value) { 282 | return value; 283 | } 284 | }; 285 | 286 | static final Coercer URI_COERCER = new Coercer() { 287 | public URI coerce(final String value) { 288 | return value != null ? URI.create(value) : null; 289 | } 290 | }; 291 | 292 | public static final RuntimeException convertException(final Throwable t) 293 | { 294 | if (t instanceof RuntimeException) { 295 | return (RuntimeException) t; 296 | } 297 | else if (t instanceof InvocationTargetException) { 298 | return convertException(((InvocationTargetException)t).getTargetException()); 299 | } 300 | else { 301 | return new RuntimeException(t); 302 | } 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/DefaultNull.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | import java.lang.annotation.ElementType; 7 | 8 | /** 9 | * Allows assigning "null" as a default value for a property. 10 | */ 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target(ElementType.METHOD) 13 | public @interface DefaultNull 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/Description.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface Description 11 | { 12 | String value() default ":nodoc:"; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/ExactMatchEnumCoercible.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public class ExactMatchEnumCoercible implements Coercible 6 | { 7 | public Coercer accept(Class clazz) 8 | { 9 | if (!clazz.isEnum()) { 10 | return null; 11 | } 12 | try { 13 | final Method m = clazz.getMethod("valueOf", String.class); 14 | return new Coercer() 15 | { 16 | public Object coerce(String value) 17 | { 18 | if (value == null) { 19 | return null; 20 | } 21 | try { 22 | return m.invoke(null, value); 23 | } 24 | catch (Exception e) { 25 | throw DefaultCoercibles.convertException(e); 26 | } 27 | } 28 | }; 29 | } 30 | catch (NoSuchMethodException e) { 31 | throw new IllegalStateException(".valueOf(String) missing! World broken!", e); 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/Param.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Param 11 | { 12 | String value(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/Separator.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface Separator 11 | { 12 | public static String DEFAULT = "\\s*,\\s*"; 13 | 14 | String value() default DEFAULT; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/ServletFilterConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import javax.servlet.FilterConfig; 4 | 5 | import org.skife.config.ConfigSource; 6 | 7 | /** 8 | * A Filter configuration based config for Config Magic. 9 | */ 10 | public class ServletFilterConfigSource implements ConfigSource 11 | { 12 | private final FilterConfig filterConfig; 13 | 14 | public ServletFilterConfigSource(final FilterConfig filterConfig) 15 | { 16 | this.filterConfig = filterConfig; 17 | } 18 | 19 | public String getString(final String propertyName) 20 | { 21 | return filterConfig.getInitParameter(propertyName); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/SimplePropertyConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | public class SimplePropertyConfigSource implements ConfigSource 6 | { 7 | private final Properties props; 8 | 9 | public SimplePropertyConfigSource(Properties props) 10 | { 11 | this.props = props; 12 | } 13 | 14 | public String getString(String propertyName) 15 | { 16 | return props.getProperty(propertyName); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/skife/config/TimeSpan.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.HashMap; 4 | import java.util.concurrent.TimeUnit; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class TimeSpan 9 | { 10 | private final long period; 11 | private final TimeUnit unit; 12 | private final long millis; 13 | 14 | private static final Pattern SPLIT = Pattern.compile("^(\\d+)\\s?(\\w+)$"); 15 | 16 | private static final HashMap UNITS = new HashMap(); 17 | static { 18 | UNITS.put("ms", TimeUnit.MILLISECONDS); 19 | UNITS.put("millisecond", TimeUnit.MILLISECONDS); 20 | UNITS.put("milliseconds", TimeUnit.MILLISECONDS); 21 | UNITS.put("s", TimeUnit.SECONDS); 22 | UNITS.put("second", TimeUnit.SECONDS); 23 | UNITS.put("seconds", TimeUnit.SECONDS); 24 | UNITS.put("m", TimeUnit.MINUTES); 25 | UNITS.put("min", TimeUnit.MINUTES); 26 | UNITS.put("minute", TimeUnit.MINUTES); 27 | UNITS.put("minutes", TimeUnit.MINUTES); 28 | UNITS.put("h", TimeUnit.HOURS); 29 | UNITS.put("hour", TimeUnit.HOURS); 30 | UNITS.put("hours", TimeUnit.HOURS); 31 | UNITS.put("d", TimeUnit.DAYS); 32 | UNITS.put("day", TimeUnit.DAYS); 33 | UNITS.put("days", TimeUnit.DAYS); 34 | } 35 | 36 | public TimeSpan(String spec) 37 | { 38 | Matcher m = SPLIT.matcher(spec); 39 | if (!m.matches()) { 40 | throw new IllegalArgumentException(String.format("%s is not a valid time spec", spec)); 41 | } 42 | String number = m.group(1); 43 | String type = m.group(2); 44 | period = Long.parseLong(number); 45 | unit = UNITS.get(type); 46 | if (unit == null) { 47 | throw new IllegalArgumentException(String.format("%s is not a valid time unit in %s", type, spec)); 48 | } 49 | millis = TimeUnit.MILLISECONDS.convert(period, unit); 50 | } 51 | 52 | public TimeSpan(long period, TimeUnit unit) 53 | { 54 | this.period = period; 55 | this.unit = unit; 56 | this.millis = TimeUnit.MILLISECONDS.convert(period, unit); 57 | } 58 | 59 | public long getMillis() { 60 | return millis; 61 | } 62 | 63 | public long getPeriod() 64 | { 65 | return period; 66 | } 67 | 68 | public TimeUnit getUnit() 69 | { 70 | return unit; 71 | } 72 | 73 | @Override 74 | public String toString() 75 | { 76 | switch (unit) { 77 | case SECONDS: 78 | return period + "s"; 79 | case MINUTES: 80 | return period + "m"; 81 | case HOURS: 82 | return period + "h"; 83 | case DAYS: 84 | return period + "d"; 85 | default: 86 | return period + "ms"; 87 | } 88 | } 89 | 90 | @Override 91 | public int hashCode() 92 | { 93 | return 31 + (int)(millis ^ (millis >>> 32)); 94 | } 95 | 96 | @Override 97 | public boolean equals(Object obj) 98 | { 99 | if (this == obj) { 100 | return true; 101 | } 102 | if (obj == null) { 103 | return false; 104 | } 105 | if (getClass() != obj.getClass()) { 106 | return false; 107 | } 108 | TimeSpan other = (TimeSpan)obj; 109 | 110 | return millis == other.millis; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/BadConfig.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | interface BadConfig 4 | { 5 | @Config({}) 6 | String getBadOption(); 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/CoercionConfig.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.net.URI; 4 | 5 | interface CoercionConfig 6 | { 7 | @Config("the-url") 8 | URI getURI(); 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/Config1.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | interface Config1 4 | { 5 | @Config("stringOption") 6 | String getStringOption(); 7 | 8 | @Config("booleanOption") 9 | boolean getBooleanOption(); 10 | 11 | @Config("boxedBooleanOption") 12 | Boolean getBoxedBooleanOption(); 13 | 14 | @Config("byteOption") 15 | byte getByteOption(); 16 | 17 | @Config("boxedByteOption") 18 | Byte getBoxedByteOption(); 19 | 20 | @Config("shortOption") 21 | short getShortOption(); 22 | 23 | @Config("boxedShortOption") 24 | Short getBoxedShortOption(); 25 | 26 | @Config("integerOption") 27 | int getIntegerOption(); 28 | 29 | @Config("boxedIntegerOption") 30 | Integer getBoxedIntegerOption(); 31 | 32 | @Config("longOption") 33 | long getLongOption(); 34 | 35 | @Config("boxedLongOption") 36 | Long getBoxedLongOption(); 37 | 38 | @Config("floatOption") 39 | float getFloatOption(); 40 | 41 | @Config("boxedFloatOption") 42 | Float getBoxedFloatOption(); 43 | 44 | @Config("doubleOption") 45 | double getDoubleOption(); 46 | 47 | @Config("boxedDoubleOption") 48 | Double getBoxedDoubleOption(); 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/Config2.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | class Config2 4 | { 5 | public int invocationCount = 0; 6 | 7 | public int getInvocationCount() 8 | { 9 | return invocationCount; 10 | } 11 | 12 | // optional w/ default value 13 | @Config("option") 14 | public String getOption() 15 | { 16 | ++invocationCount; 17 | return "default"; 18 | } 19 | } -------------------------------------------------------------------------------- /src/test/java/org/skife/config/Config3.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | interface Config3 4 | { 5 | // required 6 | @Config("option") 7 | public String getOption(); 8 | 9 | public abstract String getOption2(); 10 | } -------------------------------------------------------------------------------- /src/test/java/org/skife/config/Config4.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | interface Config4 4 | { 5 | String getOption(); 6 | } 7 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/Config5.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | public interface Config5 4 | { 5 | @Config("foo") 6 | String getFoo(); 7 | 8 | @Config("bar") 9 | int getBar(); 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/ConfigEnum.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | public enum ConfigEnum 4 | { 5 | ONE, 6 | TWO, 7 | THREE; 8 | 9 | public String toString() 10 | { 11 | return name().toLowerCase(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/EnumeratedConfig1.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | public interface EnumeratedConfig1 4 | { 5 | @Config("option.${type}") 6 | @Default("default") 7 | public String getStringOption(@Param("type") ConfigEnum type); 8 | 9 | @Config("another-option.${type}.${s}") 10 | @Default("default") 11 | public String getStringOption2Types(@Param("type") ConfigEnum type, @Param("s") String selector); 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/MultiConfig.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | interface MultiConfig 4 | { 5 | @Config("singleOption") 6 | @Default("failed!") 7 | String getSingleOption(); 8 | 9 | @Config({"singleOption"}) 10 | @Default("failed!") 11 | String getSingleOption2(); 12 | 13 | @Config({"multiOption1", "multiOption2"}) 14 | @Default("failed!") 15 | String getMultiOption1(); 16 | 17 | @Config({"doesNotExistOption", "multiOption2"}) 18 | @Default("failed!") 19 | String getMultiOption2(); 20 | 21 | @Config({"doesNotExistOption", "alsoDoesNotExistOption"}) 22 | @Default("theDefault") 23 | String getMultiDefault(); 24 | 25 | @Config({"${key}ExtOption", "defaultOption"}) 26 | @Default("failed!") 27 | String getReplaceOption(); 28 | } 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/Props.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | public class Props 6 | { 7 | public static Properties of(String key, String value) 8 | { 9 | Properties props = new Properties(); 10 | props.put(key, value); 11 | return props; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/ReplacementConfig1.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | public interface ReplacementConfig1 4 | { 5 | @Config("option.${type}") 6 | @Default("default") 7 | public String getStringOption(); 8 | 9 | @Config("another-option.${type}.${s}") 10 | @Default("default") 11 | public String getStringOption2Types(); 12 | } -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestArrays.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | import org.junit.After; 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class TestArrays 10 | { 11 | private ConfigurationObjectFactory cof; 12 | 13 | @Before 14 | public void setUp() 15 | { 16 | cof = new ConfigurationObjectFactory(new Properties()); 17 | } 18 | 19 | @After 20 | public void tearDown() 21 | { 22 | cof = null; 23 | } 24 | 25 | @Test 26 | public void testClassDefault() 27 | { 28 | EmptyClass ec = cof.build(EmptyClass.class); 29 | 30 | Assert.assertArrayEquals(new String[] { "one", "three", "two" }, ec.getValue()); 31 | } 32 | 33 | @Test 34 | public void testAbstractClassDefault() 35 | { 36 | EmptyAbstractClass ec = cof.build(EmptyAbstractClass.class); 37 | 38 | Assert.assertArrayEquals(new TestEnum[] { TestEnum.TWO, TestEnum.ONE }, ec.getValue()); 39 | } 40 | 41 | @Test 42 | public void testInterface() 43 | { 44 | EmptyInterface ec = cof.build(EmptyInterface.class); 45 | 46 | Assert.assertArrayEquals(new float[] { 1.0f, 2.0f }, ec.getValue(), 0.0f); 47 | } 48 | 49 | @Test 50 | public void testClassDefaultNull() 51 | { 52 | EmptyClassDefaultNull ec = cof.build(EmptyClassDefaultNull.class); 53 | 54 | Assert.assertNull(ec.getValue()); 55 | } 56 | 57 | @Test 58 | public void testAbstractClassDefaultNull() 59 | { 60 | EmptyAbstractClassDefaultNull ec = cof.build(EmptyAbstractClassDefaultNull.class); 61 | 62 | Assert.assertNull(ec.getValue()); 63 | } 64 | 65 | @Test 66 | public void testInterfaceDefaultNull() 67 | { 68 | EmptyInterfaceDefaultNull ec = cof.build(EmptyInterfaceDefaultNull.class); 69 | 70 | Assert.assertNull(ec.getValue()); 71 | } 72 | 73 | @Test 74 | public void testInterfaceDefaultEmptyString() 75 | { 76 | EmptyInterfaceEmptyString ec = cof.build(EmptyInterfaceEmptyString.class); 77 | 78 | Assert.assertArrayEquals(new int[0], ec.getValue()); 79 | } 80 | 81 | @Test 82 | public void testDifferentSeparator() 83 | { 84 | DifferentSeparator ec = cof.build(DifferentSeparator.class); 85 | 86 | Assert.assertArrayEquals(new float[] { 1.0f, 2.0f }, ec.getValue(), 0.0f); 87 | } 88 | 89 | public static enum TestEnum 90 | { 91 | ONE, 92 | TWO, 93 | THREE 94 | } 95 | 96 | public static class EmptyClass 97 | { 98 | @Config("value") 99 | @Default("one, three, two") 100 | public String[] getValue() 101 | { 102 | return null; 103 | } 104 | } 105 | 106 | public static abstract class EmptyAbstractClass 107 | { 108 | @Config("value") 109 | @Default("TWO, ONE") 110 | public abstract TestEnum[] getValue(); 111 | } 112 | 113 | public static interface EmptyInterface 114 | { 115 | @Config("value") 116 | @Default("1.0, 2.0") 117 | public float[] getValue(); 118 | } 119 | 120 | public static class EmptyClassDefaultNull 121 | { 122 | @Config("value") 123 | @DefaultNull 124 | public int[] getValue() 125 | { 126 | return null; 127 | } 128 | } 129 | 130 | public static abstract class EmptyAbstractClassDefaultNull 131 | { 132 | @Config("value") 133 | @DefaultNull 134 | public String[] getValue() 135 | { 136 | return null; 137 | } 138 | } 139 | 140 | public static interface EmptyInterfaceDefaultNull 141 | { 142 | @Config("value") 143 | @DefaultNull 144 | public TestEnum[] getValue(); 145 | } 146 | 147 | public static interface EmptyInterfaceEmptyString 148 | { 149 | @Config("value") 150 | @Default("") 151 | public int[] getValue(); 152 | } 153 | 154 | public static interface DifferentSeparator 155 | { 156 | @Config("value") 157 | @Separator("\\s*;\\s*") 158 | @Default("1.0 ; 2.0") 159 | public float[] getValue(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestBadConfig.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class TestBadConfig 10 | { 11 | ConfigurationObjectFactory c = null; 12 | 13 | @Before 14 | public void setUp() 15 | { 16 | this.c = new ConfigurationObjectFactory(new Properties()); 17 | } 18 | 19 | @After 20 | public void tearDown() 21 | { 22 | this.c = null; 23 | } 24 | 25 | @Test(expected=IllegalArgumentException.class) 26 | public void testBadConfig() 27 | { 28 | BadConfig bc = c.build(BadConfig.class); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestCaseInsensitiveEnumCoercible.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.hamcrest.CoreMatchers.equalTo; 6 | import static org.junit.Assert.assertThat; 7 | import static org.junit.Assert.fail; 8 | 9 | public class TestCaseInsensitiveEnumCoercible 10 | { 11 | @Test 12 | public void testHappyPath() throws Exception 13 | { 14 | ConfigurationObjectFactory cof = new ConfigurationObjectFactory(Props.of("creamer", "half_and_half")); 15 | 16 | Coffee coffee = cof.build(Coffee.class); 17 | assertThat(coffee.getCreamer(), equalTo(Creamer.HALF_AND_HALF)); 18 | } 19 | 20 | 21 | @Test(expected = IllegalStateException.class) 22 | public void testNoMatch() throws Exception 23 | { 24 | ConfigurationObjectFactory cof = new ConfigurationObjectFactory(Props.of("creamer", "goat_milk")); 25 | 26 | Coffee coffee = cof.build(Coffee.class); 27 | fail("should have raised an illegal state exception"); 28 | } 29 | 30 | @Test(expected = IllegalArgumentException.class) 31 | public void testExactMatch() throws Exception 32 | { 33 | ConfigurationObjectFactory cof = new ConfigurationObjectFactory(Props.of("creamer", "whole_milk")); 34 | cof.addCoercible(new ExactMatchEnumCoercible()); 35 | 36 | Coffee coffee = cof.build(Coffee.class); 37 | fail("should have raised an exception"); 38 | } 39 | 40 | public static abstract class Coffee 41 | { 42 | @Config("creamer") 43 | public abstract Creamer getCreamer(); 44 | } 45 | 46 | public static enum Creamer 47 | { 48 | HEAVY_CREAM, HALF_AND_HALF, WHOLE_MILK, SKIM_MILK, GROSS_WHITE_POWDER 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestClasses.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Properties; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class TestClasses 11 | { 12 | @Test 13 | public void testRawType() 14 | { 15 | WithRawType config = new ConfigurationObjectFactory(Props.of("theClazz", Object.class.getName())).build(WithRawType.class); 16 | 17 | Assert.assertEquals(config.getTheClazz(), Object.class); 18 | } 19 | 20 | @Test(expected = IllegalArgumentException.class) 21 | public void testRawTypeNotFound() 22 | { 23 | new ConfigurationObjectFactory(Props.of("theClazz", "does.not.Exist")).build(WithRawType.class); 24 | } 25 | 26 | @Test(expected = IllegalArgumentException.class) 27 | public void testRawTypeIllegal() 28 | { 29 | new ConfigurationObjectFactory(Props.of("theClazz", "not a class")).build(WithRawType.class); 30 | } 31 | 32 | @Test 33 | public void testRawTypeWithDefault() 34 | { 35 | WithRawTypeAndDefault config = new ConfigurationObjectFactory(new Properties()).build(WithRawTypeAndDefault.class); 36 | 37 | Assert.assertEquals(config.getTheClazz(), Object.class); 38 | } 39 | 40 | @Test 41 | public void testRawTypeWithNullDefault() 42 | { 43 | WithRawType config = new ConfigurationObjectFactory(new Properties()).build(WithRawType.class); 44 | 45 | Assert.assertNull(config.getTheClazz()); 46 | } 47 | 48 | @Test(expected = IllegalArgumentException.class) 49 | public void testRawTypeWithNotFoundDefault() 50 | { 51 | new ConfigurationObjectFactory(new Properties()).build(WithRawTypeAndUndefinedDefault.class); 52 | } 53 | 54 | @Test(expected = IllegalArgumentException.class) 55 | public void testRawTypeWithIllegalDefault() 56 | { 57 | new ConfigurationObjectFactory(new Properties()).build(WithRawTypeAndIllegalDefault.class); 58 | } 59 | 60 | @Test 61 | public void testUnspecifiedType() 62 | { 63 | WithUnspecifiedType config = new ConfigurationObjectFactory(Props.of("theClazz", Foo.class.getName())).build(WithUnspecifiedType.class); 64 | 65 | Assert.assertEquals(config.getTheClazz(), Foo.class); 66 | } 67 | 68 | @Test 69 | public void testExtends() 70 | { 71 | WithExtends config = new ConfigurationObjectFactory(Props.of("theClazz", Foo.class.getName())).build(WithExtends.class); 72 | 73 | Assert.assertEquals(config.getTheClazz(), Foo.class); 74 | } 75 | 76 | @Test 77 | public void testExtendsWithSubClass() 78 | { 79 | WithExtends config = new ConfigurationObjectFactory(Props.of("theClazz", FooSub.class.getName())).build(WithExtends.class); 80 | 81 | Assert.assertEquals(config.getTheClazz(), FooSub.class); 82 | } 83 | 84 | @Test(expected = IllegalArgumentException.class) 85 | public void testExtendsWithSuperClass() 86 | { 87 | new ConfigurationObjectFactory(Props.of("theClazz", FooSuper.class.getName())).build(WithExtends.class); 88 | } 89 | 90 | @Test(expected = IllegalArgumentException.class) 91 | public void testExtendsWithUnrelatedClass() 92 | { 93 | new ConfigurationObjectFactory(Props.of("theClazz", Properties.class.getName())).build(WithExtends.class); 94 | } 95 | 96 | @Test 97 | public void testNestedExtends() 98 | { 99 | WithNestedExtends config = new ConfigurationObjectFactory(Props.of("theClazz", FooList.class.getName())).build(WithNestedExtends.class); 100 | 101 | Assert.assertEquals(config.getTheClazz(), FooList.class); 102 | } 103 | 104 | @Test 105 | public void testNestedExtendsWithSubClass() 106 | { 107 | WithNestedExtends config = new ConfigurationObjectFactory(Props.of("theClazz", FooSubList.class.getName())).build(WithNestedExtends.class); 108 | 109 | Assert.assertEquals(config.getTheClazz(), FooSubList.class); 110 | } 111 | 112 | @Test 113 | public void testNestedExtendsWithSuperClass() 114 | { 115 | WithNestedExtends config = new ConfigurationObjectFactory(Props.of("theClazz", FooSuperList.class.getName())).build(WithNestedExtends.class); 116 | 117 | Assert.assertEquals(config.getTheClazz(), FooSuperList.class); 118 | } 119 | 120 | @Test 121 | public void testNestedExtendsWithUnrelatedClass() 122 | { 123 | WithNestedExtends config = new ConfigurationObjectFactory(Props.of("theClazz", StringList.class.getName())).build(WithNestedExtends.class); 124 | 125 | Assert.assertEquals(config.getTheClazz(), StringList.class); 126 | } 127 | 128 | public static interface WithRawType 129 | { 130 | @SuppressWarnings("rawtypes") 131 | @Config("theClazz") 132 | @DefaultNull 133 | Class getTheClazz(); 134 | } 135 | 136 | public static interface WithRawTypeAndDefault 137 | { 138 | @SuppressWarnings("rawtypes") 139 | @Config("theClazz") 140 | @Default("java.lang.Object") 141 | Class getTheClazz(); 142 | } 143 | 144 | public static interface WithRawTypeAndUndefinedDefault 145 | { 146 | @SuppressWarnings("rawtypes") 147 | @Config("theClazz") 148 | @Default("does.not.Exist") 149 | Class getTheClazz(); 150 | } 151 | 152 | public static interface WithRawTypeAndIllegalDefault 153 | { 154 | @SuppressWarnings("rawtypes") 155 | @Config("theClazz") 156 | @Default("not a class") 157 | Class getTheClazz(); 158 | } 159 | 160 | public static interface WithUnspecifiedType 161 | { 162 | @Config("theClazz") 163 | Class getTheClazz(); 164 | } 165 | 166 | public static interface WithExtends 167 | { 168 | @Config("theClazz") 169 | Class getTheClazz(); 170 | } 171 | 172 | public static interface WithNestedExtends 173 | { 174 | @Config("theClazz") 175 | Class> getTheClazz(); 176 | } 177 | 178 | public static class FooSuper {} 179 | public static class Foo extends FooSuper {} 180 | public static class FooSub extends Foo {} 181 | 182 | @SuppressWarnings("serial") 183 | public static class FooSuperList extends ArrayList {}; 184 | @SuppressWarnings("serial") 185 | public static class FooList extends ArrayList {}; 186 | @SuppressWarnings("serial") 187 | public static class FooSubList extends ArrayList {}; 188 | @SuppressWarnings("serial") 189 | public static class StringList extends ArrayList {}; 190 | } 191 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestCoercion.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | 5 | import java.net.URI; 6 | import java.util.Properties; 7 | 8 | import org.junit.After; 9 | import org.junit.Assert; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | public class TestCoercion 14 | { 15 | private ConfigurationObjectFactory c = null; 16 | 17 | @Before 18 | public void setUp() 19 | { 20 | this.c = new ConfigurationObjectFactory(new Properties() {{ 21 | setProperty("the-url", "http://github.org/brianm/config-magic"); 22 | }}); 23 | } 24 | 25 | @After 26 | public void tearDown() 27 | { 28 | this.c = null; 29 | } 30 | 31 | @Test(expected=IllegalStateException.class) 32 | public void testBadConfig() 33 | { 34 | c.build(WibbleConfig.class); 35 | } 36 | 37 | @Test 38 | public void testGoodConfig() 39 | { 40 | CoercionConfig cc = c.build(CoercionConfig.class); 41 | Assert.assertThat(cc.getURI(), is(URI.create("http://github.org/brianm/config-magic"))); 42 | } 43 | 44 | @Test 45 | public void testEmptyURI() 46 | { 47 | final EmptyUriConfig euc1 = new EmptyUriConfig() {}; 48 | Assert.assertNull(euc1.getTheUri()); 49 | 50 | final EmptyUriConfig euc2 = c.build(EmptyUriConfig.class); 51 | Assert.assertNull(euc2.getTheUri()); 52 | } 53 | 54 | public static abstract class EmptyUriConfig 55 | { 56 | @Config("the-uri") 57 | @DefaultNull 58 | public URI getTheUri() 59 | { 60 | return null; 61 | } 62 | } 63 | 64 | @Test 65 | public void testNullDouble() 66 | { 67 | final NullDoubleConfig ndc1 = new NullDoubleConfig() {}; 68 | Assert.assertNull(ndc1.getTheNumber()); 69 | 70 | final NullDoubleConfig ndc2 = c.build(NullDoubleConfig.class); 71 | Assert.assertNull(ndc2.getTheNumber()); 72 | } 73 | 74 | 75 | public static abstract class NullDoubleConfig 76 | { 77 | @Config("the-number") 78 | @DefaultNull 79 | public Double getTheNumber() 80 | { 81 | return null; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestCollections.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collection; 5 | import java.util.Collections; 6 | import java.util.HashSet; 7 | import java.util.LinkedHashSet; 8 | import java.util.List; 9 | import java.util.Properties; 10 | import java.util.Set; 11 | import org.junit.After; 12 | import org.junit.Assert; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | 16 | public class TestCollections 17 | { 18 | private ConfigurationObjectFactory cof; 19 | 20 | @Before 21 | public void setUp() 22 | { 23 | cof = new ConfigurationObjectFactory(new Properties()); 24 | } 25 | 26 | @After 27 | public void tearDown() 28 | { 29 | cof = null; 30 | } 31 | 32 | @Test 33 | public void testClassWithListDefault() 34 | { 35 | EmptyClassList ec = cof.build(EmptyClassList.class); 36 | 37 | Assert.assertEquals(Arrays.asList("one", "three", "two"), ec.getValue()); 38 | } 39 | 40 | @Test 41 | public void testClassWithCollectionDefault() 42 | { 43 | EmptyClassCollection ec = cof.build(EmptyClassCollection.class); 44 | 45 | Assert.assertEquals(Arrays.asList("one", "three", "two"), ec.getValue()); 46 | } 47 | 48 | @Test 49 | public void testAbstractClassDefault() 50 | { 51 | EmptyAbstractClass ec = cof.build(EmptyAbstractClass.class); 52 | 53 | Assert.assertEquals(new HashSet(Arrays.asList(TestEnum.TWO, TestEnum.ONE)), ec.getValue()); 54 | } 55 | 56 | @Test 57 | public void testInterface() 58 | { 59 | EmptyInterface ec = cof.build(EmptyInterface.class); 60 | 61 | Assert.assertEquals(new LinkedHashSet(Arrays.asList("one", "two")), ec.getValue()); 62 | } 63 | 64 | @Test 65 | public void testClassDefaultNull() 66 | { 67 | EmptyClassDefaultNull ec = cof.build(EmptyClassDefaultNull.class); 68 | 69 | Assert.assertNull(ec.getValue()); 70 | } 71 | 72 | @Test 73 | public void testAbstractClassDefaultNull() 74 | { 75 | EmptyAbstractClassDefaultNull ec = cof.build(EmptyAbstractClassDefaultNull.class); 76 | 77 | Assert.assertNull(ec.getValue()); 78 | } 79 | 80 | @Test 81 | public void testInterfaceDefaultNull() 82 | { 83 | EmptyInterfaceDefaultNull ec = cof.build(EmptyInterfaceDefaultNull.class); 84 | 85 | Assert.assertNull(ec.getValue()); 86 | } 87 | 88 | @Test 89 | public void testInterfaceDefaultEmptyString() 90 | { 91 | EmptyInterfaceEmptyString ec = cof.build(EmptyInterfaceEmptyString.class); 92 | 93 | Assert.assertEquals(Collections.emptyList(), ec.getValue()); 94 | } 95 | 96 | @Test 97 | public void testDifferentSeparator() 98 | { 99 | DifferentSeparator ec = cof.build(DifferentSeparator.class); 100 | 101 | Assert.assertEquals(new HashSet(Arrays.asList(TestEnum.TWO, TestEnum.ONE)), ec.getValue()); 102 | } 103 | 104 | public static enum TestEnum 105 | { 106 | ONE, 107 | TWO, 108 | THREE 109 | } 110 | 111 | public static class EmptyClassList 112 | { 113 | @Config("value") 114 | @Default("one, three, two") 115 | public List getValue() 116 | { 117 | return Collections.emptyList(); 118 | } 119 | } 120 | 121 | public static class EmptyClassCollection 122 | { 123 | @Config("value") 124 | @Default("one, three, two") 125 | public Collection getValue() 126 | { 127 | return Collections.emptyList(); 128 | } 129 | } 130 | 131 | public static abstract class EmptyAbstractClass 132 | { 133 | @Config("value") 134 | @Default("TWO, ONE") 135 | public abstract Set getValue(); 136 | } 137 | 138 | public static interface EmptyInterface 139 | { 140 | @Config("value") 141 | @Default("one, two") 142 | public LinkedHashSet getValue(); 143 | } 144 | 145 | public static class EmptyClassDefaultNull 146 | { 147 | @Config("value") 148 | @DefaultNull 149 | public List getValue() 150 | { 151 | return null; 152 | } 153 | } 154 | 155 | public static abstract class EmptyAbstractClassDefaultNull 156 | { 157 | @Config("value") 158 | @DefaultNull 159 | public Set getValue() 160 | { 161 | return null; 162 | } 163 | } 164 | 165 | public static interface EmptyInterfaceDefaultNull 166 | { 167 | @Config("value") 168 | @DefaultNull 169 | public List getValue(); 170 | } 171 | 172 | public static interface EmptyInterfaceEmptyString 173 | { 174 | @Config("value") 175 | @Default("") 176 | public List getValue(); 177 | } 178 | 179 | 180 | public static interface DifferentSeparator 181 | { 182 | @Config("value") 183 | @Separator("\\s*!\\s*") 184 | @Default("TWO ! ONE") 185 | public Set getValue(); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestCommonsConfig.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import static junit.framework.Assert.assertEquals; 4 | 5 | import java.util.Properties; 6 | 7 | import org.apache.commons.configuration.ConfigurationConverter; 8 | import org.junit.Test; 9 | 10 | /** 11 | * 12 | */ 13 | public class TestCommonsConfig 14 | { 15 | @Test 16 | public void testFoo() throws Exception { 17 | Properties props = new Properties(); 18 | props.setProperty("hello", "world"); 19 | final ConfigSource cs = new CommonsConfigSource(ConfigurationConverter.getConfiguration(props)); 20 | assertEquals("world", cs.getString("hello")); 21 | } 22 | 23 | @Test 24 | public void testCommas() throws Exception { 25 | Properties props = new Properties(); 26 | props.setProperty("hello", "world,brian,someone else"); 27 | final ConfigSource cs = new CommonsConfigSource(ConfigurationConverter.getConfiguration(props)); 28 | assertEquals("world,brian,someone else", cs.getString("hello")); 29 | } 30 | 31 | @Test 32 | public void testEscapedCommas() throws Exception { 33 | Properties props = new Properties(); 34 | props.setProperty("hello", "world\\, brian\\, someone else"); 35 | final ConfigSource cs = new CommonsConfigSource(ConfigurationConverter.getConfiguration(props)); 36 | assertEquals("world, brian, someone else", cs.getString("hello")); 37 | } 38 | 39 | @Test 40 | public void testEmpty() throws Exception { 41 | Properties props = new Properties(); 42 | final ConfigSource cs = new CommonsConfigSource(ConfigurationConverter.getConfiguration(props)); 43 | assertEquals(null, cs.getString("hello")); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestConfigurationObjectFactory.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Collections; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Properties; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.fail; 12 | 13 | /** 14 | * 15 | */ 16 | public class TestConfigurationObjectFactory 17 | { 18 | @Test 19 | public void testMultipleReplacements() throws Exception 20 | { 21 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 22 | {{ 23 | setProperty("another-option.a.1", "A1"); 24 | setProperty("another-option.a.2", "A2"); 25 | setProperty("another-option.b.1", "B1"); 26 | setProperty("another-option.b.2", "B2"); 27 | }}); 28 | 29 | ReplacementConfig1 r; 30 | Map replacementsMap = new HashMap(); 31 | 32 | replacementsMap.put("type", "a"); 33 | replacementsMap.put("s", "1"); 34 | r = c.buildWithReplacements(ReplacementConfig1.class, replacementsMap); 35 | assertEquals(r.getStringOption2Types(), "A1"); 36 | 37 | replacementsMap.put("type", "a"); 38 | replacementsMap.put("s", "2"); 39 | r = c.buildWithReplacements(ReplacementConfig1.class, replacementsMap); 40 | assertEquals(r.getStringOption2Types(), "A2"); 41 | 42 | replacementsMap.put("type", "b"); 43 | replacementsMap.put("s", "1"); 44 | r = c.buildWithReplacements(ReplacementConfig1.class, replacementsMap); 45 | assertEquals(r.getStringOption2Types(), "B1"); 46 | 47 | replacementsMap.put("type", "b"); 48 | replacementsMap.put("s", "2"); 49 | r = c.buildWithReplacements(ReplacementConfig1.class, replacementsMap); 50 | assertEquals(r.getStringOption2Types(), "B2"); 51 | } 52 | 53 | @Test 54 | public void testReplacement() throws Exception 55 | { 56 | Map replacementsMap = new HashMap(); 57 | replacementsMap.put("type", "first"); 58 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 59 | {{ 60 | setProperty("option.first", "1st"); 61 | setProperty("option.second", "2nd"); 62 | }}); 63 | ReplacementConfig1 r = c.buildWithReplacements(ReplacementConfig1.class, replacementsMap); 64 | assertEquals(r.getStringOption(), "1st"); 65 | 66 | replacementsMap.put("type", "second"); 67 | r = c.buildWithReplacements(ReplacementConfig1.class, replacementsMap); 68 | assertEquals(r.getStringOption(), "2nd"); 69 | } 70 | 71 | @Test 72 | public void testFoo() throws Exception 73 | { 74 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 75 | {{ 76 | setProperty("hello", "world"); 77 | setProperty("theValue", "value"); 78 | }}); 79 | Thing t = c.build(Thing.class); 80 | assertEquals(t.getName(), "world"); 81 | } 82 | 83 | @Test 84 | public void testSameConfigObjectClassUsedForEachInstance() throws Exception 85 | { 86 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 87 | {{ 88 | setProperty("hello", "world"); 89 | setProperty("theValue", "value"); 90 | }}); 91 | Thing t = c.build(Thing.class); 92 | Thing t2 = c.build(Thing.class); 93 | 94 | assertEquals(t.getClass(), t2.getClass()); 95 | } 96 | 97 | @Test 98 | public void testSameConfigObjectClassUsedForEachInstanceEvenWithDifferentCOFs() throws Exception 99 | { 100 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 101 | {{ 102 | setProperty("hello", "world"); 103 | setProperty("theValue", "value"); 104 | }}); 105 | 106 | ConfigurationObjectFactory c2 = new ConfigurationObjectFactory(new Properties() 107 | {{ 108 | setProperty("hello", "world"); 109 | setProperty("theValue", "value"); 110 | }}); 111 | 112 | Thing t = c.build(Thing.class); 113 | Thing t2 = c2.build(Thing.class); 114 | 115 | assertEquals(t.getClass(), t2.getClass()); 116 | } 117 | 118 | @Test 119 | public void testSameConfigObjectClassUsedForEachInstance2() throws Exception 120 | { 121 | 122 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 123 | {{ 124 | setProperty("t1.hello", "world"); 125 | setProperty("t1.value", "value"); 126 | setProperty("t2.hello", "brian"); 127 | setProperty("t2.value", "another-value"); 128 | }}); 129 | ThingParam t1 = c.buildWithReplacements(ThingParam.class, Collections.singletonMap("thing", "t1")); 130 | ThingParam t2 = c.buildWithReplacements(ThingParam.class, Collections.singletonMap("thing", "t2")); 131 | 132 | 133 | assertEquals(t1.getClass(), t2.getClass()); 134 | 135 | assertEquals("world", t1.getHello()); 136 | assertEquals("value", t1.getValue()); 137 | 138 | assertEquals("brian", t2.getHello()); 139 | assertEquals("another-value", t2.getValue()); 140 | } 141 | 142 | private interface ThingParam 143 | { 144 | 145 | @Config("${thing}.hello") 146 | String getHello(); 147 | 148 | @Config("${thing}.value") 149 | String getValue(); 150 | } 151 | 152 | 153 | @Test 154 | public void testEnum() throws Exception 155 | { 156 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 157 | {{ 158 | setProperty("option.one", "1"); 159 | setProperty("option.two", "2"); 160 | }}); 161 | EnumeratedConfig1 t = c.build(EnumeratedConfig1.class); 162 | assertEquals(t.getStringOption(ConfigEnum.ONE), "1"); 163 | assertEquals(t.getStringOption(ConfigEnum.TWO), "2"); 164 | assertEquals(t.getStringOption(ConfigEnum.THREE), "default"); 165 | } 166 | 167 | @Test 168 | public void testMultiParameters() throws Exception 169 | { 170 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 171 | {{ 172 | setProperty("another-option.one.a", "1-x"); 173 | setProperty("another-option.two.b", "2-y"); 174 | }}); 175 | EnumeratedConfig1 t = c.build(EnumeratedConfig1.class); 176 | assertEquals(t.getStringOption2Types(ConfigEnum.ONE, "a"), "1-x"); 177 | assertEquals(t.getStringOption2Types(ConfigEnum.TWO, "b"), "2-y"); 178 | assertEquals(t.getStringOption2Types(ConfigEnum.ONE, "dummy"), "default"); 179 | } 180 | 181 | @Test 182 | public void testDefaultValue() throws Exception 183 | { 184 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties()); 185 | Thing t = c.build(Thing.class); 186 | assertEquals(t.getName(), "woof"); 187 | } 188 | 189 | @Test 190 | public void testDefaultViaImpl() throws Exception 191 | { 192 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties()); 193 | Config2 config = c.build(Config2.class); 194 | assertEquals(config.getOption(), "default"); 195 | } 196 | 197 | @Test 198 | public void testProvidedOverridesDefault() throws Exception 199 | { 200 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 201 | {{ 202 | setProperty("option", "provided"); 203 | }}); 204 | 205 | Config2 config = c.build(Config2.class); 206 | assertEquals(config.getOption(), "provided"); 207 | } 208 | 209 | @Test 210 | public void testMissingDefault() throws Exception 211 | { 212 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties()); 213 | try { 214 | c.build(Config3.class); 215 | fail("Expected exception due to missing value"); 216 | } 217 | catch (Throwable e) { 218 | } 219 | } 220 | 221 | @Test 222 | public void testDetectsAbstractMethod() throws Exception 223 | { 224 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties()); 225 | try { 226 | c.build(Config4.class); 227 | fail("Expected exception due to abstract method without @Config annotation"); 228 | } 229 | catch (AbstractMethodError e) { 230 | } 231 | } 232 | 233 | @Test 234 | public void testTypes() 235 | { 236 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() 237 | {{ 238 | setProperty("stringOption", "a string"); 239 | setProperty("booleanOption", "true"); 240 | setProperty("boxedBooleanOption", "true"); 241 | setProperty("byteOption", Byte.toString(Byte.MAX_VALUE)); 242 | setProperty("boxedByteOption", Byte.toString(Byte.MAX_VALUE)); 243 | setProperty("shortOption", Short.toString(Short.MAX_VALUE)); 244 | setProperty("boxedShortOption", Short.toString(Short.MAX_VALUE)); 245 | setProperty("integerOption", Integer.toString(Integer.MAX_VALUE)); 246 | setProperty("boxedIntegerOption", Integer.toString(Integer.MAX_VALUE)); 247 | setProperty("longOption", Long.toString(Long.MAX_VALUE)); 248 | setProperty("boxedLongOption", Long.toString(Long.MAX_VALUE)); 249 | setProperty("floatOption", Float.toString(Float.MAX_VALUE)); 250 | setProperty("boxedFloatOption", Float.toString(Float.MAX_VALUE)); 251 | setProperty("doubleOption", Double.toString(Double.MAX_VALUE)); 252 | setProperty("boxedDoubleOption", Double.toString(Double.MAX_VALUE)); 253 | }}); 254 | 255 | Config1 config = c.build(Config1.class); 256 | assertEquals("a string", config.getStringOption()); 257 | assertEquals(true, config.getBooleanOption()); 258 | assertEquals(Boolean.TRUE, config.getBoxedBooleanOption()); 259 | assertEquals(Byte.MAX_VALUE, config.getByteOption()); 260 | assertEquals(Byte.valueOf(Byte.MAX_VALUE), config.getBoxedByteOption()); 261 | assertEquals(Short.MAX_VALUE, config.getShortOption()); 262 | assertEquals(Short.valueOf(Short.MAX_VALUE), config.getBoxedShortOption()); 263 | assertEquals(Integer.MAX_VALUE, config.getIntegerOption()); 264 | assertEquals(Integer.valueOf(Integer.MAX_VALUE), config.getBoxedIntegerOption()); 265 | assertEquals(Long.MAX_VALUE, config.getLongOption()); 266 | assertEquals(Long.valueOf(Long.MAX_VALUE), config.getBoxedLongOption()); 267 | assertEquals(Float.MAX_VALUE, config.getFloatOption(), 0); 268 | assertEquals(Float.valueOf(Float.MAX_VALUE), config.getBoxedFloatOption()); 269 | assertEquals(Double.MAX_VALUE, config.getDoubleOption(), 0); 270 | assertEquals(Double.valueOf(Double.MAX_VALUE), config.getBoxedDoubleOption()); 271 | } 272 | 273 | public abstract static class Thing 274 | { 275 | @Config("hello") 276 | @Default("woof") 277 | public abstract String getName(); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestCustomCoercion.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import static org.hamcrest.CoreMatchers.equalTo; 4 | import static org.hamcrest.CoreMatchers.is; 5 | import static org.hamcrest.CoreMatchers.notNullValue; 6 | 7 | import java.util.Properties; 8 | 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | 12 | public class TestCustomCoercion 13 | { 14 | @Test(expected=IllegalStateException.class) 15 | public void testNoConverterConfig() 16 | { 17 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() {{ 18 | setProperty("the-url", "http://github.org/brianm/config-magic"); 19 | }}); 20 | 21 | c.build(WibbleConfig.class); 22 | } 23 | 24 | @Test 25 | public void testWithConverterConfig() 26 | { 27 | ConfigurationObjectFactory c = new ConfigurationObjectFactory(new Properties() {{ 28 | setProperty("the-url", "http://github.org/brianm/config-magic"); 29 | }}); 30 | 31 | c.addCoercible(new WibbleCoercible()); 32 | 33 | WibbleConfig wc = c.build(WibbleConfig.class); 34 | 35 | Assert.assertThat(wc, is(notNullValue())); 36 | 37 | Wibble w = wc.getWibble(); 38 | Assert.assertThat(w, is(notNullValue())); 39 | 40 | Assert.assertThat(w.getURL(), equalTo("http://github.org/brianm/config-magic")); 41 | } 42 | 43 | private static class WibbleCoercible implements Coercible 44 | { 45 | public Coercer accept(Class clazz) 46 | { 47 | if (Wibble.class.equals(clazz)) { 48 | return new Coercer() { 49 | public Wibble coerce(final String value) { 50 | Wibble w = new Wibble(); 51 | w.setURL(value); 52 | 53 | return w; 54 | } 55 | }; 56 | } 57 | return null; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestDataAmount.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | import org.junit.After; 6 | import org.junit.Assert; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class TestDataAmount 11 | { 12 | private ConfigurationObjectFactory cof; 13 | 14 | @Before 15 | public void setUp() 16 | { 17 | cof = new ConfigurationObjectFactory(new Properties()); 18 | } 19 | 20 | @After 21 | public void tearDown() 22 | { 23 | cof = null; 24 | } 25 | 26 | @Test 27 | public void testKiloBytes() 28 | { 29 | DataAmount amount = new DataAmount("20kB"); 30 | Assert.assertEquals(DataAmountUnit.KILOBYTE, amount.getUnit()); 31 | Assert.assertEquals(20L * 1000, amount.getNumberOfBytes()); 32 | // and space allowed now as well 33 | amount = new DataAmount("20 kB"); 34 | Assert.assertEquals(DataAmountUnit.KILOBYTE, amount.getUnit()); 35 | Assert.assertEquals(20L * 1000, amount.getNumberOfBytes()); 36 | 37 | ClassWithKilobytes ec = cof.build(ClassWithKilobytes.class); 38 | 39 | Assert.assertEquals(DataAmountUnit.KILOBYTE, ec.getValue().getUnit()); 40 | Assert.assertEquals(10L * 1000, ec.getValue().getNumberOfBytes()); 41 | } 42 | 43 | @Test 44 | public void testRawBytes() 45 | { 46 | DataAmount amt = new DataAmount("1024"); 47 | Assert.assertEquals(DataAmountUnit.BYTE, amt.getUnit()); 48 | Assert.assertEquals(1024L, amt.getNumberOfBytes()); 49 | 50 | amt = new DataAmount(2000); 51 | Assert.assertEquals(DataAmountUnit.BYTE, amt.getUnit()); 52 | Assert.assertEquals(2000L, amt.getNumberOfBytes()); 53 | } 54 | 55 | public static abstract class ClassWithKilobytes 56 | { 57 | @Config("value") 58 | @Default("10kB") 59 | public abstract DataAmount getValue(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestDefaultCoercibles.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import static org.hamcrest.CoreMatchers.equalTo; 4 | import static org.hamcrest.CoreMatchers.is; 5 | import static org.hamcrest.CoreMatchers.notNullValue; 6 | 7 | import java.net.MalformedURLException; 8 | import java.net.URL; 9 | import java.sql.Date; 10 | 11 | import org.joda.time.DateTime; 12 | import org.junit.Assert; 13 | import org.junit.Test; 14 | 15 | public class TestDefaultCoercibles 16 | { 17 | @Test 18 | public void testValueOfCoercible1() 19 | { 20 | Coercer c = DefaultCoercibles.VALUE_OF_COERCIBLE.accept(Date.class); 21 | 22 | Assert.assertThat(c, is(notNullValue())); 23 | 24 | Object result = c.coerce("2010-11-21"); 25 | 26 | Assert.assertEquals(Date.class, result.getClass()); 27 | Assert.assertThat((Date) result, equalTo(Date.valueOf("2010-11-21"))); 28 | } 29 | 30 | @Test 31 | public void testValueOfCoercible2() 32 | { 33 | Coercer c = DefaultCoercibles.VALUE_OF_COERCIBLE.accept(Long.class); 34 | 35 | Assert.assertThat(c, is(notNullValue())); 36 | 37 | Object result = c.coerce("4815162342"); 38 | 39 | Assert.assertEquals(Long.class, result.getClass()); 40 | Assert.assertThat((Long) result, is(4815162342L)); 41 | } 42 | 43 | @Test 44 | public void testStringCtor1() throws MalformedURLException 45 | { 46 | Coercer c = DefaultCoercibles.STRING_CTOR_COERCIBLE.accept(URL.class); 47 | 48 | Assert.assertThat(c, is(notNullValue())); 49 | 50 | Object result = c.coerce("http://www.cnn.com/"); 51 | 52 | Assert.assertEquals(URL.class, result.getClass()); 53 | Assert.assertThat((URL) result, equalTo(new URL("http://www.cnn.com/"))); 54 | } 55 | 56 | @Test 57 | public void testStringCtor2() 58 | { 59 | Coercer c = DefaultCoercibles.STRING_CTOR_COERCIBLE.accept(StringBuilder.class); 60 | 61 | Assert.assertThat(c, is(notNullValue())); 62 | 63 | Object result = c.coerce("Ich bin zwei Oeltanks."); 64 | 65 | Assert.assertEquals(StringBuilder.class, result.getClass()); 66 | Assert.assertThat(result.toString(), is("Ich bin zwei Oeltanks.")); 67 | } 68 | 69 | @Test 70 | public void testObjectCtor1() 71 | { 72 | Coercer c = DefaultCoercibles.OBJECT_CTOR_COERCIBLE.accept(DateTime.class); 73 | 74 | Assert.assertThat(c, is(notNullValue())); 75 | 76 | Object result = c.coerce("2010-11-22T01:58Z"); 77 | 78 | Assert.assertEquals(DateTime.class, result.getClass()); 79 | Assert.assertThat((DateTime) result, equalTo(new DateTime("2010-11-22T01:58Z"))); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestDefaultNull.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | import org.junit.After; 6 | import org.junit.Assert; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class TestDefaultNull 11 | { 12 | private ConfigurationObjectFactory cof = null; 13 | 14 | @Before 15 | public void setUp() 16 | { 17 | cof = new ConfigurationObjectFactory(new Properties()); 18 | } 19 | 20 | @After 21 | public void tearDown() 22 | { 23 | cof = null; 24 | } 25 | 26 | @Test 27 | public void testClass() 28 | { 29 | EmptyClass ec = cof.build(EmptyClass.class); 30 | 31 | Assert.assertNull(ec.getValue()); 32 | } 33 | 34 | @Test 35 | public void testInterface() 36 | { 37 | EmptyInterface ec = cof.build(EmptyInterface.class); 38 | 39 | Assert.assertNull(ec.getValue()); 40 | } 41 | 42 | 43 | @Test(expected = IllegalArgumentException.class) 44 | public void testDoubleFeature() 45 | { 46 | cof.build(DoubleFeature.class); 47 | } 48 | 49 | public static interface EmptyInterface 50 | { 51 | @Config("value") 52 | @DefaultNull 53 | String getValue(); 54 | } 55 | 56 | public static abstract class EmptyClass 57 | { 58 | @Config("value") 59 | @DefaultNull 60 | public abstract String getValue(); 61 | } 62 | 63 | 64 | public static class DoubleFeature 65 | { 66 | @Config("value") 67 | @DefaultNull 68 | @Default("value-default") 69 | public String getValue() 70 | { 71 | return "default-value"; 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestDefaultsPresent.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | import org.junit.After; 6 | import org.junit.Assert; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class TestDefaultsPresent 11 | { 12 | private ConfigurationObjectFactory cof = null; 13 | 14 | @Before 15 | public void setUp() 16 | { 17 | cof = new ConfigurationObjectFactory(new Properties()); 18 | } 19 | 20 | @After 21 | public void tearDown() 22 | { 23 | cof = null; 24 | } 25 | 26 | @Test 27 | public void testClassDefault() 28 | { 29 | EmptyClass ec = cof.build(EmptyClass.class); 30 | 31 | Assert.assertEquals("default-value", ec.getValue()); 32 | } 33 | 34 | @Test 35 | public void testAbstractClassDefault() 36 | { 37 | EmptyAbstractClass ec = cof.build(EmptyAbstractClass.class); 38 | 39 | Assert.assertEquals("default-value", ec.getValue()); 40 | } 41 | 42 | @Test 43 | public void testClassDefaultNull() 44 | { 45 | EmptyClassDefaultNull ec = cof.build(EmptyClassDefaultNull.class); 46 | 47 | Assert.assertNull(ec.getValue()); 48 | } 49 | 50 | @Test 51 | public void testAbstractClassDefaultNull() 52 | { 53 | EmptyAbstractClassDefaultNull ec = cof.build(EmptyAbstractClassDefaultNull.class); 54 | 55 | Assert.assertNull(ec.getValue()); 56 | } 57 | 58 | @Test 59 | public void testClassDefaultForValues() 60 | { 61 | Properties p = new Properties(); 62 | p.setProperty("value", "auto"); 63 | cof = new ConfigurationObjectFactory(p); 64 | EmptyForValueClass ec = cof.build(EmptyForValueClass.class); 65 | Assert.assertEquals("auto-value", ec.getValue()); 66 | } 67 | 68 | @Test 69 | public void testClassDefaultForValuesFailed() 70 | { 71 | Properties p = new Properties(); 72 | p.setProperty("value", "23"); 73 | cof = new ConfigurationObjectFactory(p); 74 | EmptyForValueClass ec = cof.build(EmptyForValueClass.class); 75 | Assert.assertNotEquals("auto-value", ec.getValue()); 76 | } 77 | 78 | @Test 79 | public void testClassDefaultForValuesWithNoResult() 80 | { 81 | Properties p = new Properties(); 82 | p.setProperty("value", "auto"); 83 | cof = new ConfigurationObjectFactory(p); 84 | EmptyClass ec = cof.build(EmptyClass.class); 85 | Assert.assertEquals("auto", ec.getValue()); 86 | } 87 | 88 | public static class EmptyForValueClass 89 | { 90 | @Config("value") 91 | @Default(value = "auto-value", forValues = {"auto", "AUTO"}) 92 | public String getValue() 93 | { 94 | return "value-auto"; 95 | } 96 | } 97 | 98 | 99 | public static class EmptyClass 100 | { 101 | @Config("value") 102 | @Default("default-value") 103 | public String getValue() 104 | { 105 | return "value-default"; 106 | } 107 | } 108 | 109 | 110 | public static abstract class EmptyAbstractClass 111 | { 112 | @Config("value") 113 | @Default("default-value") 114 | public String getValue() 115 | { 116 | return "value-default"; 117 | } 118 | } 119 | 120 | 121 | public static class EmptyClassDefaultNull 122 | { 123 | @Config("value") 124 | @DefaultNull 125 | public String getValue() 126 | { 127 | return "value-default"; 128 | } 129 | } 130 | 131 | public static abstract class EmptyAbstractClassDefaultNull 132 | { 133 | @Config("value") 134 | @DefaultNull 135 | public String getValue() 136 | { 137 | return "value-default"; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestEmptyValue.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | import org.junit.After; 6 | import org.junit.Assert; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class TestEmptyValue 11 | { 12 | private ConfigurationObjectFactory cof = null; 13 | 14 | @Before 15 | public void setUp() 16 | { 17 | cof = new ConfigurationObjectFactory(new Properties()); 18 | } 19 | 20 | @After 21 | public void tearDown() 22 | { 23 | cof = null; 24 | } 25 | 26 | @Test(expected = IllegalArgumentException.class) 27 | public void testClass() 28 | { 29 | cof.build(EmptyClass.class); 30 | } 31 | 32 | @Test(expected = IllegalArgumentException.class) 33 | public void testInterface() 34 | { 35 | cof.build(EmptyInterface.class); 36 | } 37 | 38 | @Test 39 | public void testDefaultClass() 40 | { 41 | EmptyDefaultClass ec = cof.build(EmptyDefaultClass.class); 42 | 43 | Assert.assertEquals("default-value", ec.getValue()); 44 | } 45 | 46 | @Test 47 | public void testAbstractDefaultClass() 48 | { 49 | EmptyAbstractClass ec = cof.build(EmptyAbstractClass.class); 50 | 51 | Assert.assertEquals("default-value", ec.getValue()); 52 | } 53 | 54 | public static interface EmptyInterface 55 | { 56 | @Config("value") 57 | String getValue(); 58 | } 59 | 60 | public static abstract class EmptyClass 61 | { 62 | @Config("value") 63 | public abstract String getValue(); 64 | } 65 | 66 | public static abstract class EmptyAbstractClass 67 | { 68 | @Config("value") 69 | public String getValue() 70 | { 71 | return "default-value"; 72 | } 73 | } 74 | 75 | 76 | public static class EmptyDefaultClass 77 | { 78 | @Config("value") 79 | public String getValue() 80 | { 81 | return "default-value"; 82 | } 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestEnums.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | import org.junit.After; 6 | import org.junit.Assert; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class TestEnums 11 | { 12 | private ConfigurationObjectFactory cof; 13 | 14 | @Before 15 | public void setUp() 16 | { 17 | cof = new ConfigurationObjectFactory(new Properties()); 18 | } 19 | 20 | @After 21 | public void tearDown() 22 | { 23 | cof = null; 24 | } 25 | 26 | @Test 27 | public void testClassDefault() 28 | { 29 | EmptyClass ec = cof.build(EmptyClass.class); 30 | 31 | Assert.assertEquals(TestEnum.ONE, ec.getValue()); 32 | } 33 | 34 | @Test 35 | public void testAbstractClassDefault() 36 | { 37 | EmptyAbstractClass ec = cof.build(EmptyAbstractClass.class); 38 | 39 | Assert.assertEquals(TestEnum.TWO, ec.getValue()); 40 | } 41 | 42 | @Test 43 | public void testInterface() 44 | { 45 | EmptyInterface ec = cof.build(EmptyInterface.class); 46 | 47 | Assert.assertEquals(TestEnum.THREE, ec.getValue()); 48 | } 49 | 50 | @Test 51 | public void testClassDefaultNull() 52 | { 53 | EmptyClassDefaultNull ec = cof.build(EmptyClassDefaultNull.class); 54 | 55 | Assert.assertNull(ec.getValue()); 56 | } 57 | 58 | @Test 59 | public void testAbstractClassDefaultNull() 60 | { 61 | EmptyAbstractClassDefaultNull ec = cof.build(EmptyAbstractClassDefaultNull.class); 62 | 63 | Assert.assertNull(ec.getValue()); 64 | } 65 | 66 | @Test 67 | public void testInterfaceDefaultNull() 68 | { 69 | EmptyInterfaceDefaultNull ec = cof.build(EmptyInterfaceDefaultNull.class); 70 | 71 | Assert.assertNull(ec.getValue()); 72 | } 73 | 74 | public static enum TestEnum 75 | { 76 | ONE, 77 | TWO, 78 | THREE 79 | } 80 | 81 | public static class EmptyClass 82 | { 83 | @Config("value") 84 | @Default("ONE") 85 | public TestEnum getValue() 86 | { 87 | return TestEnum.ONE; 88 | } 89 | } 90 | 91 | 92 | public static abstract class EmptyAbstractClass 93 | { 94 | @Config("value") 95 | @Default("TWO") 96 | public abstract TestEnum getValue(); 97 | } 98 | 99 | public static class EmptyClassDefaultNull 100 | { 101 | @Config("value") 102 | @DefaultNull 103 | public TestEnum getValue() 104 | { 105 | return TestEnum.THREE; 106 | } 107 | } 108 | 109 | public static interface EmptyInterface 110 | { 111 | @Config("value") 112 | @Default("THREE") 113 | public TestEnum getValue(); 114 | } 115 | 116 | public static abstract class EmptyAbstractClassDefaultNull 117 | { 118 | @Config("value") 119 | @DefaultNull 120 | public TestEnum getValue() 121 | { 122 | return null; 123 | } 124 | } 125 | 126 | public static interface EmptyInterfaceDefaultNull 127 | { 128 | @Config("value") 129 | @DefaultNull 130 | public TestEnum getValue(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestExposeMappedReplacements.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Properties; 9 | 10 | import org.junit.Test; 11 | 12 | public class TestExposeMappedReplacements 13 | { 14 | 15 | public static interface ReplacementConfig 16 | { 17 | @Config("wat.${a}") 18 | @DefaultNull 19 | String getWat(); 20 | 21 | @ConfigReplacements 22 | Map getMap(); 23 | 24 | @ConfigReplacements("a") 25 | @Default("invalid") 26 | String getAString(); 27 | 28 | @ConfigReplacements("b") 29 | @Default("999") 30 | int getBInt(); 31 | 32 | @ConfigReplacements("x") 33 | @DefaultNull 34 | String getDefaultNull(); 35 | 36 | @ConfigReplacements("y") 37 | @Default("3") 38 | int getDefault3(); 39 | } 40 | 41 | @Test 42 | public void testExposeReplacements() 43 | { 44 | Properties properties = new Properties(); 45 | properties.put("wat.1", "xyzzy"); 46 | 47 | ConfigurationObjectFactory factory = new ConfigurationObjectFactory(properties); 48 | Map map = new HashMap(); 49 | 50 | map.put("a", "1"); 51 | map.put("b", "2"); 52 | 53 | ReplacementConfig config = factory.buildWithReplacements(ReplacementConfig.class, map); 54 | assertEquals("xyzzy", config.getWat()); 55 | assertEquals(map, config.getMap()); 56 | } 57 | 58 | @Test 59 | public void testNoReplacements() 60 | { 61 | ConfigurationObjectFactory factory = new ConfigurationObjectFactory(new Properties()); 62 | 63 | ReplacementConfig config = factory.build(ReplacementConfig.class); 64 | assertTrue(config.getMap().isEmpty()); 65 | } 66 | 67 | @Test 68 | public void testKeyReplacement() 69 | { 70 | ConfigurationObjectFactory factory = new ConfigurationObjectFactory(new Properties()); 71 | Map map = new HashMap(); 72 | 73 | map.put("a", "1"); 74 | map.put("b", "2"); 75 | 76 | ReplacementConfig config = factory.buildWithReplacements(ReplacementConfig.class, map); 77 | assertEquals("1", config.getAString()); 78 | assertEquals(2, config.getBInt()); 79 | } 80 | 81 | @Test 82 | public void testDefaultValues() 83 | { 84 | ConfigurationObjectFactory factory = new ConfigurationObjectFactory(new Properties()); 85 | ReplacementConfig config = factory.build(ReplacementConfig.class); 86 | assertEquals(null, config.getDefaultNull()); 87 | assertEquals(3, config.getDefault3()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestFile.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.io.File; 4 | import java.util.Properties; 5 | 6 | import org.junit.After; 7 | import org.junit.Assert; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | public class TestFile 12 | { 13 | private ConfigurationObjectFactory cof; 14 | 15 | @Before 16 | public void setUp() 17 | { 18 | cof = new ConfigurationObjectFactory(new Properties() {{ 19 | setProperty("file2", ".."); 20 | }}); 21 | } 22 | 23 | @After 24 | public void tearDown() 25 | { 26 | cof = null; 27 | } 28 | 29 | @Test 30 | public void testClassDefault() 31 | { 32 | EmptyClass ec = cof.build(EmptyClass.class); 33 | 34 | Assert.assertEquals(new File("."), ec.getFile()); 35 | } 36 | 37 | @Test 38 | public void testAbstractClassDefault() 39 | { 40 | EmptyAbstractClass ec = cof.build(EmptyAbstractClass.class); 41 | 42 | Assert.assertEquals(new File(".."), ec.getFile()); 43 | } 44 | 45 | @Test 46 | public void testAbstractClassDefaultNull() 47 | { 48 | EmptyAbstractClassDefaultNull ec = cof.build(EmptyAbstractClassDefaultNull.class); 49 | 50 | Assert.assertNull(ec.getFile()); 51 | } 52 | 53 | public static class EmptyClass 54 | { 55 | @Config("file1") 56 | @Default(".") 57 | public File getFile() 58 | { 59 | return null; 60 | } 61 | } 62 | 63 | public abstract static class EmptyAbstractClass 64 | { 65 | @Config("file2") 66 | public abstract File getFile(); 67 | } 68 | 69 | public abstract static class EmptyAbstractClassDefaultNull 70 | { 71 | @Config("file3") 72 | @DefaultNull 73 | public abstract File getFile(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestMultiConfig.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | 5 | import java.util.Collections; 6 | import java.util.Properties; 7 | 8 | import org.junit.After; 9 | import org.junit.Assert; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | public class TestMultiConfig 14 | { 15 | ConfigurationObjectFactory c = null; 16 | 17 | @Before 18 | public void setUp() 19 | { 20 | this.c = new ConfigurationObjectFactory(new Properties() {{ 21 | setProperty("singleOption", "the-single-value"); 22 | setProperty("multiOption1", "the-multi-option1-value"); 23 | setProperty("multiOption2", "the-multi-option2-value"); 24 | setProperty("fooExtOption", "the-fooExt-option-value"); 25 | setProperty("barExtOption", "the-barExt-option-value"); 26 | setProperty("defaultOption", "the-default-option-value"); 27 | }}); 28 | } 29 | 30 | @After 31 | public void tearDown() 32 | { 33 | this.c = null; 34 | } 35 | 36 | @Test 37 | public void testSimple() 38 | { 39 | MultiConfig mc = c.build(MultiConfig.class); 40 | Assert.assertThat(mc.getSingleOption(), is("the-single-value")); 41 | } 42 | 43 | @Test 44 | public void testSimple2() 45 | { 46 | MultiConfig mc = c.build(MultiConfig.class); 47 | Assert.assertThat(mc.getSingleOption2(), is("the-single-value")); 48 | } 49 | 50 | @Test 51 | public void testMultiOption1() 52 | { 53 | MultiConfig mc = c.build(MultiConfig.class); 54 | Assert.assertThat(mc.getMultiOption1(), is("the-multi-option1-value")); 55 | } 56 | 57 | @Test 58 | public void testMultiOption2() 59 | { 60 | MultiConfig mc = c.build(MultiConfig.class); 61 | Assert.assertThat(mc.getMultiOption2(), is("the-multi-option2-value")); 62 | } 63 | 64 | @Test 65 | public void testMultiDefault() 66 | { 67 | MultiConfig mc = c.build(MultiConfig.class); 68 | Assert.assertThat(mc.getMultiDefault(), is("theDefault")); 69 | } 70 | 71 | @Test 72 | public void testMultiReplace1() 73 | { 74 | MultiConfig mc = c.buildWithReplacements(MultiConfig.class, Collections.singletonMap("key", "foo")); 75 | Assert.assertThat(mc.getReplaceOption(), is("the-fooExt-option-value")); 76 | } 77 | 78 | @Test 79 | public void testMultiReplace2() 80 | { 81 | MultiConfig mc = c.buildWithReplacements(MultiConfig.class, Collections.singletonMap("key", "bar")); 82 | Assert.assertThat(mc.getReplaceOption(), is("the-barExt-option-value")); 83 | } 84 | 85 | @Test 86 | public void testMultiReplaceDefault() 87 | { 88 | MultiConfig mc = c.buildWithReplacements(MultiConfig.class, Collections.singletonMap("key", "baz")); 89 | Assert.assertThat(mc.getReplaceOption(), is("the-default-option-value")); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestNoFinal.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class TestNoFinal 9 | { 10 | @Test(expected=IllegalArgumentException.class) 11 | public void testExplodeOnFinal() 12 | { 13 | ConfigurationObjectFactory cof = new ConfigurationObjectFactory(new Properties()); 14 | cof.build(EmptyClass.class); 15 | } 16 | 17 | public static final class EmptyClass 18 | { 19 | @Config("value") 20 | public String getValue() 21 | { 22 | return "default-value"; 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestServletFilterConfigSource.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | 5 | import java.util.Enumeration; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import javax.servlet.FilterConfig; 10 | import javax.servlet.ServletContext; 11 | 12 | import org.apache.commons.collections.iterators.IteratorEnumeration; 13 | import org.junit.Assert; 14 | import org.junit.Test; 15 | 16 | public class TestServletFilterConfigSource 17 | { 18 | @Test 19 | public void simpleTest() 20 | { 21 | final MockFilterConfig mfc = new MockFilterConfig(); 22 | mfc.put("foo", "hello, world"); 23 | mfc.put("bar", "23"); 24 | 25 | final ServletFilterConfigSource sfcs = new ServletFilterConfigSource(mfc); 26 | 27 | final ConfigurationObjectFactory configurationObjectFactory = new ConfigurationObjectFactory(sfcs); 28 | 29 | final Config5 config = configurationObjectFactory.build(Config5.class); 30 | 31 | Assert.assertThat(config.getFoo(), is("hello, world")); 32 | Assert.assertThat(config.getBar(), is(23)); 33 | } 34 | 35 | private static class MockFilterConfig implements FilterConfig 36 | { 37 | private final Map parameters = new HashMap(); 38 | 39 | private void put(String key, String value) 40 | { 41 | parameters.put(key, value); 42 | } 43 | 44 | public String getFilterName() { 45 | return "bogus"; 46 | } 47 | 48 | public String getInitParameter(String name) { 49 | return parameters.get(name); 50 | } 51 | 52 | @SuppressWarnings("unchecked") 53 | public Enumeration getInitParameterNames() { 54 | return new IteratorEnumeration(parameters.keySet().iterator()); 55 | } 56 | 57 | public ServletContext getServletContext() { 58 | return null; 59 | } 60 | 61 | } 62 | } 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestTimeSpan.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Properties; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import org.junit.After; 7 | import org.junit.Assert; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | public class TestTimeSpan 12 | { 13 | private ConfigurationObjectFactory cof; 14 | 15 | @Before 16 | public void setUp() 17 | { 18 | cof = new ConfigurationObjectFactory(new Properties()); 19 | } 20 | 21 | @After 22 | public void tearDown() 23 | { 24 | cof = null; 25 | } 26 | 27 | @Test 28 | public void testMilliSeconds() 29 | { 30 | ClassWithMilliseconds ec = cof.build(ClassWithMilliseconds.class); 31 | 32 | Assert.assertEquals(5, ec.getValue().getPeriod()); 33 | Assert.assertEquals(TimeUnit.MILLISECONDS, ec.getValue().getUnit()); 34 | Assert.assertEquals(new TimeSpan(5, TimeUnit.MILLISECONDS), ec.getValue()); 35 | Assert.assertEquals(5, ec.getValue().getMillis()); 36 | } 37 | 38 | @Test 39 | public void testSeconds() 40 | { 41 | ClassWithSeconds ec = cof.build(ClassWithSeconds.class); 42 | 43 | Assert.assertEquals(5, ec.getValue().getPeriod()); 44 | Assert.assertEquals(TimeUnit.SECONDS, ec.getValue().getUnit()); 45 | Assert.assertEquals(new TimeSpan(5, TimeUnit.SECONDS), ec.getValue()); 46 | Assert.assertEquals(TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS), ec.getValue().getMillis()); 47 | } 48 | 49 | @Test 50 | public void testMinutes() 51 | { 52 | ClassWithMinutes ec = cof.build(ClassWithMinutes.class); 53 | 54 | Assert.assertEquals(5, ec.getValue().getPeriod()); 55 | Assert.assertEquals(TimeUnit.MINUTES, ec.getValue().getUnit()); 56 | Assert.assertEquals(new TimeSpan(5, TimeUnit.MINUTES), ec.getValue()); 57 | Assert.assertEquals(TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES), ec.getValue().getMillis()); 58 | } 59 | 60 | @Test 61 | public void testHours() 62 | { 63 | ClassWithHours ec = cof.build(ClassWithHours.class); 64 | 65 | Assert.assertEquals(5, ec.getValue().getPeriod()); 66 | Assert.assertEquals(TimeUnit.HOURS, ec.getValue().getUnit()); 67 | Assert.assertEquals(new TimeSpan(5, TimeUnit.HOURS), ec.getValue()); 68 | Assert.assertEquals(TimeUnit.MILLISECONDS.convert(5, TimeUnit.HOURS), ec.getValue().getMillis()); 69 | } 70 | 71 | @Test 72 | public void testDays() 73 | { 74 | ClassWithDays ec = cof.build(ClassWithDays.class); 75 | 76 | Assert.assertEquals(5, ec.getValue().getPeriod()); 77 | Assert.assertEquals(TimeUnit.DAYS, ec.getValue().getUnit()); 78 | Assert.assertEquals(new TimeSpan(5, TimeUnit.DAYS), ec.getValue()); 79 | Assert.assertEquals(TimeUnit.MILLISECONDS.convert(5, TimeUnit.DAYS), ec.getValue().getMillis()); 80 | } 81 | 82 | // for [Issue-5] 83 | @Test 84 | public void testAliases() 85 | { 86 | Assert.assertEquals(new TimeSpan("5ms"), new TimeSpan("5milliseconds")); 87 | Assert.assertEquals(new TimeSpan("1ms"), new TimeSpan("1 millisecond")); 88 | Assert.assertEquals(new TimeSpan("7s"), new TimeSpan("7seconds")); 89 | Assert.assertEquals(new TimeSpan("1s"), new TimeSpan("1second")); 90 | Assert.assertEquals(new TimeSpan("15m"), new TimeSpan("15minutes")); 91 | Assert.assertEquals(new TimeSpan("1m"), new TimeSpan("1minute")); 92 | Assert.assertEquals(new TimeSpan("7m"), new TimeSpan("7min")); 93 | Assert.assertEquals(new TimeSpan("25h"), new TimeSpan("25hours")); 94 | Assert.assertEquals(new TimeSpan("1h"), new TimeSpan("1hour")); 95 | Assert.assertEquals(new TimeSpan("31d"), new TimeSpan("31days")); 96 | Assert.assertEquals(new TimeSpan("1d"), new TimeSpan("1day")); 97 | } 98 | 99 | // for [Issue-6] 100 | @Test 101 | public void testWhitespace() 102 | { 103 | ClassWithTimespanWithWhitespace ec = cof.build(ClassWithTimespanWithWhitespace.class); 104 | // "5 h" 105 | Assert.assertEquals(5, ec.getValue().getPeriod()); 106 | Assert.assertEquals(TimeUnit.HOURS, ec.getValue().getUnit()); 107 | Assert.assertEquals(new TimeSpan(5, TimeUnit.HOURS), ec.getValue()); 108 | Assert.assertEquals(TimeUnit.MILLISECONDS.convert(5, TimeUnit.HOURS), ec.getValue().getMillis()); 109 | 110 | Assert.assertEquals(new TimeSpan("5ms"), new TimeSpan("5 milliseconds")); 111 | Assert.assertEquals(new TimeSpan("5s"), new TimeSpan("5 seconds")); 112 | Assert.assertEquals(new TimeSpan("5m"), new TimeSpan("5 minutes")); 113 | Assert.assertEquals(new TimeSpan("5d"), new TimeSpan("5 days")); 114 | } 115 | 116 | @Test(expected = IllegalArgumentException.class) 117 | public void testNoUnit() 118 | { 119 | cof.build(ClassWithTimespanWithoutUnit.class); 120 | } 121 | 122 | @Test(expected = IllegalArgumentException.class) 123 | public void testIllegalUnit() 124 | { 125 | cof.build(ClassWithTimespanWithIllegalUnit.class); 126 | } 127 | 128 | public static abstract class ClassWithMilliseconds 129 | { 130 | @Config("value") 131 | @Default("5ms") 132 | public abstract TimeSpan getValue(); 133 | } 134 | 135 | public static abstract class ClassWithSeconds 136 | { 137 | @Config("value") 138 | @Default("5s") 139 | public abstract TimeSpan getValue(); 140 | } 141 | 142 | public static abstract class ClassWithSeconds2 143 | { 144 | @Config("value") 145 | @Default("5seconds") 146 | public abstract TimeSpan getValue(); 147 | } 148 | 149 | public static abstract class ClassWithMinutes 150 | { 151 | @Config("value") 152 | @Default("5m") 153 | public abstract TimeSpan getValue(); 154 | } 155 | 156 | public static abstract class ClassWithHours 157 | { 158 | @Config("value") 159 | @Default("5h") 160 | public abstract TimeSpan getValue(); 161 | } 162 | 163 | public static abstract class ClassWithDays 164 | { 165 | @Config("value") 166 | @Default("5d") 167 | public abstract TimeSpan getValue(); 168 | } 169 | 170 | public static abstract class ClassWithTimespanWithoutUnit 171 | { 172 | @Config("value") 173 | @Default("5") 174 | public abstract TimeSpan getValue(); 175 | } 176 | 177 | public static abstract class ClassWithTimespanWithIllegalUnit 178 | { 179 | @Config("value") 180 | @Default("5x") 181 | public abstract TimeSpan getValue(); 182 | } 183 | 184 | public static abstract class ClassWithTimespanWithWhitespace 185 | { 186 | @Config("value") 187 | @Default("5 h") 188 | public abstract TimeSpan getValue(); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/TestVariousPropertyTypes.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | import java.util.Collections; 4 | import java.util.Properties; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class TestVariousPropertyTypes 11 | { 12 | private ConfigurationObjectFactory c = null; 13 | private StrangeConfig sc = null; 14 | 15 | @Before 16 | public void setUp() 17 | { 18 | final Properties p = new Properties(); 19 | p.setProperty("single-property", "single-property-value"); 20 | p.setProperty("multi.first.property", "multi-first-value"); 21 | p.setProperty("double.second.property", "double-second-value"); 22 | p.setProperty("test.value.property", "test-value-value"); 23 | p.setProperty("test.default.property", "test-default-value"); 24 | c = new ConfigurationObjectFactory(p); 25 | sc = c.buildWithReplacements(StrangeConfig.class, Collections.singletonMap("key", "value")); 26 | } 27 | 28 | 29 | @Test 30 | public void testWithDefault() 31 | { 32 | Assert.assertEquals("default-is-set", sc.getHasDefault()); 33 | } 34 | 35 | @Test 36 | public void testWithDefaultNull() 37 | { 38 | Assert.assertNull(sc.getHasDefaultNull()); 39 | } 40 | 41 | @Test 42 | public void testCallMethod() 43 | { 44 | Assert.assertEquals("called getCalledMethod()", sc.getCalledMethod()); 45 | } 46 | 47 | @Test 48 | public void testMultiProperty() 49 | { 50 | Assert.assertEquals("multi-first-value", sc.getMultiProperty()); 51 | } 52 | 53 | @Test 54 | public void testDoubleProperty() 55 | { 56 | Assert.assertEquals("double-second-value", sc.getDoubleProperty()); 57 | } 58 | 59 | @Test 60 | public void testKeyedProperty() 61 | { 62 | Assert.assertEquals("test-value-value", sc.getKeyedProperty()); 63 | } 64 | 65 | public static abstract class StrangeConfig 66 | { 67 | @Config("has-default") 68 | @Default("default-is-set") 69 | public String getHasDefault() 70 | { 71 | return "called getHasDefault()"; 72 | } 73 | 74 | @Config("has-default-null") 75 | @DefaultNull 76 | public String getHasDefaultNull() 77 | { 78 | return "called getHasDefault()"; 79 | } 80 | 81 | @Config("get-called-method") 82 | public String getCalledMethod() 83 | { 84 | return "called getCalledMethod()"; 85 | } 86 | 87 | @Config("single.property") 88 | public String getSingleProperty() 89 | { 90 | return "called getSingleProperty()"; 91 | } 92 | 93 | @Config({"multi.first.property", "multi.second.property"}) 94 | public String getMultiProperty() 95 | { 96 | return "called getMultiProperty()"; 97 | } 98 | 99 | @Config({"double.first.property", "double.second.property"}) 100 | public String getDoubleProperty() 101 | { 102 | return "called getDoubleProperty()"; 103 | } 104 | 105 | @Config({"test.${key}.property", "test.default.property"}) 106 | public String getKeyedProperty() 107 | { 108 | return "called getKeyedProperty()"; 109 | } 110 | } 111 | } 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/Wibble.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | public class Wibble 4 | { 5 | private String url = null; 6 | 7 | public void setURL(final String url) 8 | { 9 | this.url = url; 10 | } 11 | 12 | public String getURL() 13 | { 14 | return url; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/org/skife/config/WibbleConfig.java: -------------------------------------------------------------------------------- 1 | package org.skife.config; 2 | 3 | 4 | interface WibbleConfig 5 | { 6 | @Config("the-url") 7 | Wibble getWibble(); 8 | } 9 | --------------------------------------------------------------------------------