Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dropwizard and Guice: injecting Environment

I am currently building a Dropwizard + Guice + Jersey-based application where the database access is being handled by JDBI for the time being.

What I am trying to achieve is to have your typical enterprise architecture, where Resources access Service classes accessing a DAO class that in turn accesses the database. It would be nice to get all this wired up in a proper DI way, although I guess I can build my object graph in the run() method of the application if all else fails.

So, I'm running into this problem that has been mentioned here before: Getting a DBIFactory requires both the Environment and the Configuration, which somehow need to be available at the time when Guice does its injection magic and not at run()-time.

Being a Dropwizard and Guice noob, what I've managed to put together so far is that I need a Provider for my DAO objects, something to the tune of

public class UserDAOProvider implements Provider<UserDAO> {

    @Inject
    Environment environment;
    @Inject
    Configuration configuration;

    @Override
    public UserDAO get() {
        final DBIFactory factory = new DBIFactory();
        final (MyConfiguration) config = (MyConfiguration) configuration;
        DBI jdbi = null;
        try {
            jdbi = factory.build(environment, config.getDataSourceFactory(),
                    "mysql");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return jdbi.onDemand(UserDAO.class);
    }

}

Registering this as a singleton provider should let me then inject the UserDAO into my Services.

Now, how do we actually get the environment injected into the Provider? Currently I am stuck at Guice complaining about not finding a suitable constructor for the Environment, so it is trying to instantiate it and not grab it from Dropwizard itself.

It seems like this is doable; there is the dropwizard-guice package whose DropWizardEnvironmentModule is, I think, what I need. But I feel like I'm just missing some piece of the puzzle here for an understanding of how to put things together. I've not managed to find a complete working example so far...

like image 834
CptPicard Avatar asked Aug 30 '14 10:08

CptPicard


People also ask

What is Dropwizard Guice?

Dropwizard provides you with simple dependency injection mechanism, using HK2, out-of-the-box, and you can add support for more advanced DI by using Guice bundle.

How does Guice injector work?

Interface Injector. Builds the graphs of objects that make up your application. The injector tracks the dependencies for each type and uses bindings to inject them. This is the core of Guice, although you rarely interact with it directly.

What is the use of Dropwizard?

Dropwizard is an open-source Java framework used for the fast development of high-performance RESTful web services. It gathers some popular libraries to create the light-weight package. The main libraries that it uses are Jetty, Jersey, Jackson, JUnit, and Guava. Furthermore, it uses its own library called Metrics.

What is managed in Dropwizard?

A Dropwizard Environment consists of all the Resources, servlets, filters, Health Checks, Health, Jersey providers, Managed Objects, Tasks, and Jersey properties which your application provides.


2 Answers

I had the same issue as OP but using Hibernate rather than JDBI. My simple solution is applicable to JDBI, though - just switch DBIFactory for SessionFactory.

First add an injection provider for a singleton SessionFactory in your Guice module:

public class MyModule extends AbstractModule {

    private SessionFactory sessionFactory;

    @Override
    protected void configure() {
    }

    @Provides
    SessionFactory providesSessionFactory() {

        if (sessionFactory == null) {
             throw new ProvisionException("The Hibernate session factory has not yet been set. This is likely caused by forgetting to call setSessionFactory during Application.run()");
        }

       return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
}

You need to set the singleton SessionFactory from your application's run() method. In your case, using JDBI, this is where you would create and configure your DBIFactory before handing it over to the Guice module:

public void run(MyConfiguration configuration, Environment environment) {

    myModule.setSessionFactory(hibernateBundle.getSessionFactory());
    ...
}

Now SessionFactory can be injected wherever it is needed. I now use implicit binding for my DAO classes by just annotating the constructor with @Inject and injecting the SessionFactory singleton. I don't explicitly create providers for DAO classes:

@Singleton
public class WidgetDAO extends AbstractDAO<App> {

    @Inject
    public WidgetDAO(SessionFactory factory) {
        super(factory);
    }

    public Optional<Widget> findById(Long id) {
        return Optional.fromNullable(get(id));
    }
    ...
}

Now I can inject my DAO singleton instances into resources:

@Path("/widgets")
@Produces(MediaType.APPLICATION_JSON)
public class WidgetsResource {

    private final WidgetDAO widgetDAO;

    @Inject
    public WidgetsResource(WidgetDAO widgetDAO) {
        this.widgetDAO = widgetDAO;
    }
    ...
}

Note that this approach follows the Guice recommendation of injecting direct dependencies only. Don't try to inject Envrionment and Configuration just so that you can create a DBI factory - inject the prebuilt DBI factory itself.

like image 121
Robin Summerhill Avatar answered Oct 06 '22 01:10

Robin Summerhill


This is how I use Guice with Dropwizard. Inside your run() method add the line

Guice.createInjector(new ConsoleModule()); 

You cannot inject Environ

Create the class ConsoleModule

public class ConsoleModule extends AbstractModule {

    //configuration and env variable declaration

    public  ConsoleModule(ConsoleConfiguration consoleConfig, Environment env)
    {
        this.consoleConfig = consoleConfig;
        this.env= env;
    }

    protected void configure()
    {
        //You should not inject Configuration and Environment in your provider since you are mixing     
        //dropwizard framework stuff with Guice.Neverthless you will have to bind them in the below order

        bind(Configuration.class).toInstance(consoleConfig.class);
        bind(Environment.class).toInstance(env.class);
        bind(UserDAO.class).toProvider(UserDAOProvider.class).in(Singleton.class);
    }
}
like image 27
Abhijeet Kushe Avatar answered Oct 05 '22 23:10

Abhijeet Kushe