Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config #49

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
93 changes: 92 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class HelloWorldApplication extends Application<HelloWorldConfiguration>
}
```

Lastly, you can enable auto configuration via package scanning.
You can enable auto configuration via package scanning.
```java
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {

Expand All @@ -69,6 +69,9 @@ public class HelloWorldApplication extends Application<HelloWorldConfiguration>
.build();

bootstrap.addBundle(guiceBundle);
// with AutoConfig enabled you don't need to add bundles or commands explicitly here.
// inherit from one of InjectedCommand, InjectedConfiguredCommand, or InjectedEnvironmentCommand
// to get access to all modules during injection.
}

@Override
Expand All @@ -83,6 +86,94 @@ public class HelloWorldApplication extends Application<HelloWorldConfiguration>
}
}
```
Configuration data will be auto-injected and named. Use the provided @Config annotation to specify
the path to the configuration data to be injected.
```java

public class HelloWorldConfiguration extends Configuration {
@JsonProperty
private String template;

@JsonProperty
private Person defaultPerson = new Person();

public String getTemplate() { return template; }

public Person getDefaultPerson() { return defaultPerson; }
}

public class Person {
@JsonProperty
private String name = "Stranger";
private String city = "Unknown";

public String getName() { return name; }
}

public class HelloWorldModule extends AbstractModule {

// configuration data is available for injection and named based on the fields in the configuration objects
@Inject
@Config("template")
private String template;

// defaultPerson.name will only be available if the Person class is defined within the package path
// set by addConfigPackages (see below)
@Inject
@Config("defaultPerson.name")
private String defaultName;

// A root config class may also be specified. The path provided will be relative to this root object.
@Inject
@Config(Person.class, "city")
private String defaultCity;

@Override
protected void configure() {
}
}
```

Modules will also be injected before being added. Field injections only, constructor based injections will not be available.
Configuration data and initialization module data will be available for injecting into modules.
```java


public class HelloWorldApplication extends Application<HelloWorldConfiguration> {

public static void main(String[] args) throws Exception {
new HelloWorldApplication().run(args);
}

@Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {

GuiceBundle<HelloWorldConfiguration> guiceBundle = GuiceBundle.<HelloWorldConfiguration>newBuilder()
.addInitModule(new BaseModule())
// bindings defined in the BaseModule or any configuration data is available for
// injection into HelloWorldModule fields
.addModule(new HelloWorldModule())
//Any resource, task, bundle, etc within this class path will be included automatically.
.enableAutoConfig(getClass().getPackage().getName())
//The contents of any config objects within this package path will be auto-injected.
.addConfigPackages(getClass().getPackage().getName())
.setConfigClass(HelloWorldConfiguration.class)
.build();

bootstrap.addBundle(guiceBundle);
}

@Override
public String getName() {
return "hello-world";
}

@Override
public void run(HelloWorldConfiguration helloWorldConfiguration, Environment environment) throws Exception {
}
}
```

If you are having trouble accessing your Configuration or Environment inside a Guice Module, you could try using a provider.

```java
Expand Down
6 changes: 6 additions & 0 deletions pom.xml
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
Expand Down
96 changes: 76 additions & 20 deletions src/main/java/com/hubspot/dropwizard/guice/AutoConfig.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package com.hubspot.dropwizard.guice;

import com.google.inject.ConfigurationException;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import io.dropwizard.Bundle;
import io.dropwizard.ConfiguredBundle;
import io.dropwizard.cli.Command;
import io.dropwizard.cli.ConfiguredCommand;
import io.dropwizard.cli.EnvironmentCommand;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.servlets.tasks.Task;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;

import com.google.common.base.Preconditions;
import com.google.inject.Injector;
import org.glassfish.jersey.server.model.Resource;
Expand All @@ -21,6 +28,7 @@
import javax.ws.rs.Path;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import java.util.Collection;
import java.util.Set;

public class AutoConfig {
Expand Down Expand Up @@ -55,34 +63,46 @@ public void run(Environment environment, Injector injector) {

public void initialize(Bootstrap<?> bootstrap, Injector injector) {
addBundles(bootstrap, injector);
addConfiguredBundles(bootstrap, injector);
addCommands(bootstrap, injector);
}

private void addManaged(Environment environment, Injector injector) {
Set<Class<? extends Managed>> managedClasses = reflections
.getSubTypesOf(Managed.class);
for (Class<? extends Managed> managed : managedClasses) {
environment.lifecycle().manage(injector.getInstance(managed));
logger.info("Added managed: {}", managed);
try {
environment.lifecycle().manage(injector.getInstance(managed));
logger.info("Added managed: {}", managed);
} catch (ConfigurationException e) {
logger.warn("Could not get instance of managed: {}", managed);
}
}
}

private void addTasks(Environment environment, Injector injector) {
Set<Class<? extends Task>> taskClasses = reflections
.getSubTypesOf(Task.class);
for (Class<? extends Task> task : taskClasses) {
environment.admin().addTask(injector.getInstance(task));
logger.info("Added task: {}", task);
try {
environment.admin().addTask(injector.getInstance(task));
logger.info("Added task: {}", task);
} catch (ConfigurationException e) {
logger.warn("Could not get instance of task: {}", task);
}
}
}

private void addHealthChecks(Environment environment, Injector injector) {
Set<Class<? extends InjectableHealthCheck>> healthCheckClasses = reflections
.getSubTypesOf(InjectableHealthCheck.class);
for (Class<? extends InjectableHealthCheck> healthCheck : healthCheckClasses) {
InjectableHealthCheck instance = injector.getInstance(healthCheck);
environment.healthChecks().register(instance.getName(), instance);
logger.info("Added injectableHealthCheck: {}", healthCheck);
try {
InjectableHealthCheck instance = injector.getInstance(healthCheck);
environment.healthChecks().register(instance.getName(), instance);
logger.info("Added injectableHealthCheck: {}", healthCheck);
} catch (ConfigurationException e) {
logger.warn("Could not get instance of InjectableHealthCheck: {}", healthCheck);
}
}
}

Expand All @@ -106,27 +126,63 @@ private void addResources(Environment environment) {
}
}

@SuppressWarnings("rawtypes")
private void addBundles(Bootstrap<?> bootstrap, Injector injector) {
Set<Class<? extends Bundle>> bundleClasses = reflections
.getSubTypesOf(Bundle.class);
for (Class<? extends Bundle> bundle : bundleClasses) {
bootstrap.addBundle(injector.getInstance(bundle));
logger.info("Added bundle class {} during bootstrap", bundle);
try {
bootstrap.addBundle(injector.getInstance(bundle));
logger.info("Added bundle class {} during bootstrap", bundle);
} catch (ConfigurationException e) {
logger.warn("Could not get instance of bundle: {}", bundle);
}
}
}

@SuppressWarnings("unchecked")
private void addConfiguredBundles(Bootstrap<?> bootstrap, Injector injector) {
Set<Class<? extends ConfiguredBundle>> configuredBundleClasses = reflections
.getSubTypesOf(ConfiguredBundle.class);
for (Class<? extends ConfiguredBundle> configuredBundle : configuredBundleClasses) {
if (configuredBundle != GuiceBundle.class) {
bootstrap.addBundle(injector.getInstance(configuredBundle));
logger.info("Added configured bundle class {} during bootstrap", configuredBundle);
Set<Class<? extends ConfiguredBundle>> configuredBundleClasses = reflections.getSubTypesOf(ConfiguredBundle.class);
for(Class<? extends ConfiguredBundle> bundle : configuredBundleClasses)
{
if(!bundle.equals(GuiceBundle.class))
{
try {
bootstrap.addBundle(injector.getInstance(bundle));
logger.info("Added configured bundle class {} during bootstrap", bundle);
} catch (ConfigurationException e) {
logger.warn("Could not get instance of configured bundle: {}", bundle);
}
}
}
}

private void addCommands(Bootstrap<?> bootstrap, Injector injector) {
Collection<Object> existingCommands = Collections2.transform(bootstrap.getCommands(),
new Function<Command, Object>() {
@Override
public Class<? extends Command> apply(Command input) {
return input.getClass();
}
});

Set<Class<? extends Command>> commandClasses = reflections.getSubTypesOf(Command.class);
//The SubTypesScanner does not resolve the entire ancestry of a class
//This won't get subtyped Commands. If this becomes a problem, a
//replacement Scanner could be written. It is getting a bit ridiculous
//with all the Injected commands as well.
commandClasses.addAll(reflections.getSubTypesOf(ConfiguredCommand.class));
commandClasses.addAll(reflections.getSubTypesOf(EnvironmentCommand.class));
commandClasses.addAll(reflections.getSubTypesOf(InjectedCommand.class));
commandClasses.addAll(reflections.getSubTypesOf(InjectedConfiguredCommand.class));
commandClasses.addAll(reflections.getSubTypesOf(InjectedEnvironmentCommand.class));
for(Class<? extends Command> command : commandClasses) {
if(existingCommands.contains(command)) continue;
try {
bootstrap.addCommand(injector.getInstance(command));
logger.info("Added command class {} during bootstrap", command);
} catch (ConfigurationException e) {
logger.warn("Could not get instance of command: {}", command);
}
}
}

private void addParamConverterProviders(Environment environment) {
Set<Class<? extends ParamConverterProvider>> providerClasses = reflections
.getSubTypesOf(ParamConverterProvider.class);
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/hubspot/dropwizard/guice/ConfigData/Config.java
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;
}
Loading