-
Notifications
You must be signed in to change notification settings - Fork 95
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Readd @config code. Move it to its own Guice module.
- Loading branch information
Dan Jasek
committed
Mar 2, 2015
1 parent
df973aa
commit 6113917
Showing
14 changed files
with
436 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
src/main/java/com/hubspot/dropwizard/guice/ConfigData/Config.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.hubspot.dropwizard.guice.ConfigData; | ||
|
||
import javax.inject.Qualifier; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Documented; | ||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
/** | ||
* Guice {@linkplain Qualifier qualifier} that is bound | ||
* to fields in Dropwizard configuration objects. | ||
*/ | ||
@Qualifier | ||
@Documented | ||
@Retention(RUNTIME) | ||
public @interface Config { | ||
|
||
/** The config path. */ | ||
String value(); | ||
|
||
/** The root config object to which the path is relative */ | ||
Class root() default void.class; | ||
} |
153 changes: 153 additions & 0 deletions
153
src/main/java/com/hubspot/dropwizard/guice/ConfigData/ConfigDataModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package com.hubspot.dropwizard.guice.ConfigData; | ||
|
||
import com.google.common.base.Function; | ||
import com.google.common.base.Joiner; | ||
import com.google.common.base.Preconditions; | ||
import com.google.common.collect.Lists; | ||
import com.google.common.collect.Maps; | ||
import com.google.inject.AbstractModule; | ||
import com.google.inject.Provider; | ||
import io.dropwizard.Configuration; | ||
import org.apache.commons.lang3.ArrayUtils; | ||
import org.apache.commons.lang3.ClassUtils; | ||
import org.apache.commons.lang3.reflect.FieldUtils; | ||
|
||
import java.lang.reflect.Field; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Map.Entry; | ||
|
||
import static com.google.common.base.Throwables.propagate; | ||
import static java.lang.String.format; | ||
|
||
/** | ||
* Binds fields in the configurationClasses. Names field using the | ||
* @Config qualifier. | ||
* @param <T> | ||
*/ | ||
public class ConfigDataModule<T extends Configuration> extends AbstractModule { | ||
private final T configuration; | ||
private final String[] configurationPackages; | ||
|
||
public ConfigDataModule(T configuration, | ||
String[] configurationPackages) { | ||
this.configuration = Preconditions.checkNotNull(configuration); | ||
Preconditions.checkNotNull(configurationPackages); | ||
this.configurationPackages = ensureTypeInPackages(configuration.getClass(), configurationPackages); | ||
} | ||
|
||
private String[] ensureTypeInPackages(Class<?> type, String[] packages) { | ||
String configName = type.getName(); | ||
for(String pack : packages) { | ||
if(configName.startsWith(pack)) return packages; | ||
} | ||
return ArrayUtils.add(packages, configName); | ||
} | ||
|
||
@Override | ||
protected void configure() { | ||
bindConfigs(); | ||
} | ||
|
||
private void bindConfigs() { | ||
HashMap<Class, String[]> roots = new HashMap<>(); | ||
roots.put(void.class, new String[0]); | ||
bindConfigs(configuration.getClass(), roots, Lists.<Class<?>>newArrayList()); | ||
} | ||
@SuppressWarnings("unchecked") | ||
private void bindConfigs(Class<?> config, Map<Class,String[]> roots, List<Class<?>> visited) { | ||
List<Class<?>> classes = Lists.newArrayList(ClassUtils.getAllSuperclasses(config)); | ||
classes.add(config); | ||
for(Class<?> cls: classes) { | ||
//Only ever use a given class as a root once. Additional uses will have conflicting paths. | ||
boolean useAsRoot = false; | ||
if(!visited.contains(cls)) { | ||
useAsRoot = true; | ||
visited.add(cls); | ||
} | ||
for(Field field: cls.getDeclaredFields()) { | ||
Class<?> type = field.getType(); | ||
final String name = field.getName(); | ||
|
||
Map<Class, String[]> newRoots = Maps.newHashMap(Maps.transformValues(roots, new Function<String[], String[]>() { | ||
@Override | ||
public String[] apply(String[] path) { | ||
String[] subpath = new String[path.length + 1]; | ||
System.arraycopy(path, 0, subpath, 0, path.length); | ||
subpath[path.length] = name; | ||
return subpath; | ||
} | ||
})); | ||
if(useAsRoot) newRoots.put(cls, new String[]{ name }); | ||
ConfigElementProvider provider = new ConfigElementProvider(newRoots.get(void.class)); | ||
|
||
for (Entry<Class, String[]> root : newRoots.entrySet()) { | ||
bind(type) | ||
.annotatedWith(new ConfigImpl(root.getKey(), Joiner.on(".").join(root.getValue()))) | ||
.toProvider(provider); | ||
} | ||
|
||
if(!type.isEnum() && isInConfigPackage(type)) | ||
bindConfigs(type, newRoots, visited); | ||
} | ||
} | ||
} | ||
|
||
private boolean isInConfigPackage(Class<?> type) { | ||
String name = type.getName(); | ||
if(name == null) return false; | ||
|
||
for(String pack : configurationPackages) { | ||
if(name.startsWith(pack)) return true; | ||
} | ||
return false; | ||
} | ||
|
||
private class ConfigElementProvider<U> implements Provider<U> { | ||
private final Field[] path; | ||
|
||
public ConfigElementProvider(String[] path) { | ||
this.path = new Field[path.length]; | ||
|
||
Class<?> cls = configuration.getClass(); | ||
for(int i=0; i<path.length; i++) { | ||
this.path[i] = findField(cls, path[i]); | ||
cls = this.path[i].getType(); | ||
} | ||
} | ||
|
||
private Field findField(final Class<?> cls, String name) { | ||
Field f; | ||
Class<?> search = cls; | ||
do { | ||
f = FieldUtils.getDeclaredField(search, name, true); | ||
if(f != null) | ||
return f; | ||
else | ||
search = search.getSuperclass(); | ||
|
||
} while(!search.equals(Object.class)); | ||
|
||
throw new IllegalStateException(format("Unable to find field %s on %s", name, cls.getName())); | ||
} | ||
|
||
@Override | ||
public U get() { | ||
Object obj = configuration; | ||
for(Field field: path) { | ||
try { | ||
obj = field.get(obj); | ||
if (obj == null) { | ||
return null; // Should cause an injection exception | ||
} | ||
|
||
} catch(IllegalAccessException e) { | ||
throw propagate(e); | ||
} | ||
} | ||
|
||
return (U) obj; | ||
} | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
src/main/java/com/hubspot/dropwizard/guice/ConfigData/ConfigImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.hubspot.dropwizard.guice.ConfigData; | ||
|
||
import com.hubspot.dropwizard.guice.ConfigData.Config; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
|
||
import java.io.Serializable; | ||
import java.lang.annotation.Annotation; | ||
|
||
public class ConfigImpl implements Config, Serializable { | ||
|
||
private final String value; | ||
private final Class root; | ||
|
||
public ConfigImpl(String value) { | ||
this.value = checkNotNull(value, "name"); | ||
this.root = void.class; | ||
} | ||
|
||
public ConfigImpl(Class root, String value) { | ||
this.value = checkNotNull(value, "name"); | ||
this.root = checkNotNull(root); | ||
} | ||
|
||
public String value() { | ||
return this.value; | ||
} | ||
|
||
public Class root() { | ||
return this.root; | ||
} | ||
|
||
public int hashCode() { | ||
// This is specified in java.lang.Annotation. | ||
return ((127 * "value".hashCode()) ^ value.hashCode()) + | ||
((127 * "root".hashCode()) ^ root.hashCode()); | ||
} | ||
|
||
public boolean equals(Object o) { | ||
if (!(o instanceof Config)) { | ||
return false; | ||
} | ||
|
||
Config other = (Config) o; | ||
return value.equals(other.value()) && | ||
root.equals(other.root()); | ||
} | ||
|
||
public String toString() { | ||
return "@" + Config.class.getName() + "(root=" + root + ", " + "value=" + value + ")"; | ||
} | ||
|
||
public Class<? extends Annotation> annotationType() { | ||
return Config.class; | ||
} | ||
|
||
private static final long serialVersionUID = 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.