Integrating Spring Framework in Dropwizard

Advertisements

The first thing that someone could think is why do I want to use Spring and Dropwizard together, well in my case, it was because the company for the one I was creating a new project already had a set of core modules/libraries with Dropwizard. For example, Generic Exceptions, Exceptions Mappers, JaxRS Filters, Health checks, and authentication framework and more.

We don’t want to re-write all those (already created and well tested) libraries but in Spring Framework “flavour”. And at the same time, we wanted to use Spring Framework in the new application and take advantage of its Dependency Injection, Aspect-Oriented (AOP), Spring Data modules, to simplify the development.

So, that was my question, is it possible to integration Spring Framework on Dropwizard? and the answer is, YES.

Dependency Injection in Dropwizard

Dropwizard doesn’t provide any new features by itself, instead of that Dropwizard is a glue-code framework. So, it joins a set of technologies (Webserver, Restful, Database Access, Metrics, Healthchecks) so simplify the development following some structure.

One of the things, that it doesn’t provide is a Dependency Injection framework. However, it’s very common to see google GUICE as the “standard” for it, but it’s not limited by only using GUICE. Therefore we are able to use other Dependency injection frameworks like Spring DI. Instead of using the GUICE bundled, we will create a Spring Bundle that provides the basic steps to start using Spring Dependency Injection in our application.

Creating a Spring Framework Bundle for Dropwizard

I will not take credit of the following code, it’s based on the Github repository: https://github.com/nhuray/dropwizard-spring

Unfortunately, the repository hasn’t had updates since 2014. Dropwizard has new versions, and some of its dependencies are old, for example, Jersey version 1.x, and the current one is 2.x. That’s why, I took that code as a reference, and changed some few things to be used with new versions.

public class SpringBundle<T extends Configuration> implements ConfiguredBundle<T> {
    private static final Logger LOG = LoggerFactory.getLogger(SpringBundle.class);
    public static final String CONFIGURATION_BEAN_NAME = "dw";
    public static final String ENVIRONMENT_BEAN_NAME = "dwEnv";
    private ConfigurableApplicationContext context;
    private boolean registerConfiguration;
    private boolean registerEnvironment;

    public SpringBundle(ConfigurableApplicationContext context) {
        this(context, false, false);
    }

    public SpringBundle(ConfigurableApplicationContext context, boolean registerConfiguration, boolean registerEnvironment) {
        if (registerConfiguration || registerEnvironment) {
            Preconditions.checkArgument(!context.isActive(), "Context must be not active in order to register configuration, environment or placeholder");
        }

        this.context = context;
        this.registerConfiguration = registerConfiguration;
        this.registerEnvironment = registerEnvironment;
    }

    public void run(T configuration, Environment environment) throws Exception {
        if (this.registerConfiguration) {
            this.registerConfiguration(configuration, this.context);
        }

        if (this.registerEnvironment) {
            this.registerEnvironment(environment, this.context);
        }

        if (!this.context.isActive()) {
            this.context.refresh();
        }

        this.registerManaged(environment, this.context);
        this.registerLifecycle(environment, this.context);
        this.registerServerLifecycleListeners(environment, this.context);
        this.registerTasks(environment, this.context);
        this.registerHealthChecks(environment, this.context);
        this.registerInjectableProviders(environment, this.context);
        this.registerProviders(environment, this.context);
        this.registerResources(environment, this.context);
    }

    public void initialize(Bootstrap<?> bootstrap) {
    }

    public ConfigurableApplicationContext getContext() {
        return this.context;
    }

    public void setRegisterConfiguration(boolean registerConfiguration) {
        this.registerConfiguration = registerConfiguration;
    }

    public void setRegisterEnvironment(boolean registerEnvironment) {
        this.registerEnvironment = registerEnvironment;
    }

    private void registerManaged(Environment environment, ConfigurableApplicationContext context) {
        Map<String, Managed> beansOfType = context.getBeansOfType(Managed.class);
        Iterator var4 = beansOfType.keySet().iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            Managed managed = (Managed)beansOfType.get(beanName);
            environment.lifecycle().manage(managed);
            LOG.info("Registering managed: " + managed.getClass().getName());
        }

    }

    private void registerLifecycle(Environment environment, ConfigurableApplicationContext context) {
        Map<String, LifeCycle> beansOfType = context.getBeansOfType(LifeCycle.class);
        Iterator var4 = beansOfType.keySet().iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            if (!beanName.equals("dwEnv")) {
                LifeCycle lifeCycle = (LifeCycle)beansOfType.get(beanName);
                environment.lifecycle().manage(lifeCycle);
                LOG.info("Registering lifeCycle: " + lifeCycle.getClass().getName());
            }
        }

    }

    private void registerServerLifecycleListeners(Environment environment, ConfigurableApplicationContext context) {
        Map<String, ServerLifecycleListener> beansOfType = context.getBeansOfType(ServerLifecycleListener.class);
        Iterator var4 = beansOfType.keySet().iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            if (!beanName.equals("dwEnv")) {
                ServerLifecycleListener serverLifecycleListener = (ServerLifecycleListener)beansOfType.get(beanName);
                environment.lifecycle().addServerLifecycleListener(serverLifecycleListener);
                LOG.info("Registering serverLifecycleListener: " + serverLifecycleListener.getClass().getName());
            }
        }

    }

    private void registerTasks(Environment environment, ConfigurableApplicationContext context) {
        Map<String, Task> beansOfType = context.getBeansOfType(Task.class);
        Iterator var4 = beansOfType.keySet().iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            Task task = (Task)beansOfType.get(beanName);
            environment.admin().addTask(task);
            LOG.info("Registering task: " + task.getClass().getName());
        }

    }

    private void registerHealthChecks(Environment environment, ConfigurableApplicationContext context) {
        Map<String, HealthCheck> beansOfType = context.getBeansOfType(HealthCheck.class);
        Iterator var4 = beansOfType.keySet().iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            HealthCheck healthCheck = (HealthCheck)beansOfType.get(beanName);
            environment.healthChecks().register(beanName, healthCheck);
            LOG.info("Registering healthCheck: " + healthCheck.getClass().getName());
        }

    }

    private void registerInjectableProviders(Environment environment, ConfigurableApplicationContext context) {
        Map<String, InjectionResolver> beansOfType = context.getBeansOfType(InjectionResolver.class);
        Iterator var4 = beansOfType.keySet().iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            InjectionResolver injectableProvider = (InjectionResolver)beansOfType.get(beanName);
            environment.jersey().register(injectableProvider);
            LOG.info("Registering injectable provider: " + injectableProvider.getClass().getName());
        }

    }

    private void registerProviders(Environment environment, ConfigurableApplicationContext context) {
        Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(Provider.class);
        Iterator var4 = beansWithAnnotation.keySet().iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            Object provider = beansWithAnnotation.get(beanName);
            environment.jersey().register(provider);
            LOG.info("Registering provider : " + provider.getClass().getName());
        }

    }

    private void registerResources(Environment environment, ConfigurableApplicationContext context) {
        Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(Path.class);
        Iterator var4 = beansWithAnnotation.keySet().iterator();

        while(var4.hasNext()) {
            String beanName = (String)var4.next();
            Object resource = beansWithAnnotation.get(beanName);
            environment.jersey().register(resource);
            LOG.info("Registering resource : " + resource.getClass().getName());
        }

    }

    private void registerConfiguration(T configuration, ConfigurableApplicationContext context) {
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("dw", configuration);
        LOG.info("Registering Dropwizard Configuration under name : dw");
    }

    private void registerEnvironment(Environment environment, ConfigurableApplicationContext context) {
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("dwEnv", environment);
        LOG.info("Registering Dropwizard Environment under name : dwEnv");
    }
}

How to use it?

Now that we have created the SpringBundle, it’s time to use it in our application.

public class HelloApp extends Service<HelloAppConfiguration> {

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

    @Override
    public void initialize(Bootstrap<HelloAppConfiguration> bootstrap) {
      // register configuration, environment and placeholder
      bootstrap.addBundle(new SpringBundle(applicationContext(), true, true, true));
    }

    @Override
    public void run(HelloAppConfiguration configuration, Environment environment) throws Exception {
      // doing nothing
    }


    private ConfigurableApplicationContext applicationContext() throws BeansException {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.scan("my.package.with.springconfiguration");
      return context;
    }
}
    

So, In the Dropwizard entry point class, we added the bundle with the following code

Advertisements

bootstrap.addBundle(new SpringBundle(applicationContext(), true, true, true));

And then in the AnnotationConfigApplicationContext (from spring) instance, we indicate the package or class where I want the Spring Context to scan for Configurations. For example a class like this one:

package my.package.with.springconfiguration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = {})
public class MySpringConfiguration {
	
}

In the above code, we can define some other packages to scan for Spring Components, Repositories, Services, enable JPA, Spring Data, Spring Security and more.

Conclusion

That’s all. I hope some more people could take advantage of this integration in their projects.

References:

Advertisements

Leave a Reply

Your email address will not be published. Required fields are marked *