Integrating Guice into Dropwizard

3,992 views
Skip to first unread message

Gili T.

unread,
Nov 7, 2012, 1:36:32 AM11/7/12
to dropwiz...@googlegroups.com
Hi,

There are serious problems integrating Guice into Dropwizard. I will start off by sharing what worked for me:

public class Main extends Service<Configuration>
{
public static void main(String[] args) throws Exception
{
new Main().run(args);
}

@Override
protected void initialize(Configuration configuration, Environment environment)
throws Exception
{
environment.addServletListeners(new JerseyContextListener());

environment.addServlet(NotFoundServlet.class, configuration.getHttpConfiguration().
getRootPath());
environment.addFilter(GuiceFilter.class, configuration.getHttpConfiguration().getRootPath());
}

@Override
public ServletContainer getJerseyContainer(DropwizardResourceConfig resourceConfig,
Configuration serviceConfig)
{
return null;
}
}

/**
 * A servlet that returns HTTP 404 for all methods.
 */
public class NotFoundServlet implements Servlet
{
private ServletConfig config;

@Override
public void init(ServletConfig config) throws ServletException
{
this.config = config;
}

@Override
public ServletConfig getServletConfig()
{
return config;
}

@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;

try
{
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
}
catch (ClassCastException e)
{
throw new ServletException("non-HTTP request or response");
}
response.sendError(404);
}

@Override
public String getServletInfo()
{
return getClass().getName();
}

@Override
public void destroy()
{
}
}

public class JerseyContextListener extends GuiceServletContextListener
{
private Injector injector;

@Override
protected Injector getInjector()
{
return Guice.createInjector(new JerseyModule());
}
}

public class JerseyModule extends JerseyServletModule
{
@Override
protected void configureServlets()
{
Map<String, String> params = new HashMap<>(10);
params.put(JSONConfiguration.FEATURE_POJO_MAPPING, "true");
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, JerseyModule.class.getPackage().
getName());
params.put(ServletContainer.PROPERTY_WEB_PAGE_CONTENT_REGEX, "/(images|css|js)/.*");
params.put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
CachedEntityFilter.class.getName());
filter("/*").through(GuiceContainer.class, params);
}
}


The problem with the above code, although it works, is that you cannot configure ResourceConfig using Dropwizard and pass it into GuiceContainer. Instead, we are forced to pass filter parameters into GuiceContainer and totally disregarding Dropwizard's configuration. We lose type-safety and Dropwizard's nice API in the process.

The point of this discussion thread is to brainstorm on possible solutions. And with that, I turn the table to you :)

Thanks,
Gili

Coda Hale

unread,
Nov 7, 2012, 2:30:05 AM11/7/12
to dropwiz...@googlegroups.com
I'd strongly suggest checking out 0.6.0-SNAPSHOT before continuing further. It moves the JerseyContainer to Environment, among other things.

---
Coda Hale
http://codahale.com

cowwoc

unread,
Nov 7, 2012, 3:13:07 AM11/7/12
to dropwiz...@googlegroups.com

    I'll check it out, but the problem remains that GuiceContainer doesn't allow passing ResourceConfig to its (ServletContainer) superclass.

    If I understand correctly:

  • There are two ways to configure the ResourceConfig used by ServletContainer.
    • Either pass an instance to ServletContainer's constructor, or
    • Update an existing ResourceConfig using the filter parameters (ServletContainer is a filter)
  • Dropwizard would like to use the first mechanism (pass ResourceConfig to ServletContainer's constructor) and I favor this approach as well because it is type-safe (filter parameters are String-based) and offers a cleaner API.
  • GuiceContainer only offers the second mechanism. It's easy to understand why: Guice can't mix @Injected and user-supplied arguments.

    I am aware of the following options, but there are obviously others:

  • Use Filter init parameters when using Guice. Use Dropwizard's API when not using Guice. I dislike this approach because it isn't consistent.
  • Find a way to @Inject ResourceConfig, have GuiceContainer's constructor to pass it to the superclass. I'm not sure how easy it is to do this (if at all possible).
  • Create an alternative to ServletFilter, GuiceContainer. This is actually my favorite choice because of annoying bugs like http://code.google.com/p/google-guice/issues/detail?id=618 and http://code.google.com/p/google-guice/issues/detail?id=635
    • In theory, all we'd need to do is invoke addFilter(new GuiceContainerReplacement(injector, resourceConfig)).
    • GuiceContainerReplacement would bind Jersey to Guice's Injector, but its constructor would *not* be injected (as GuiceContainer is now). This would allow us to pass ResourceConfig up to the superclass as we would like.
    What do you think?

Gili

Kim Betti

unread,
Nov 7, 2012, 11:08:21 AM11/7/12
to dropwiz...@googlegroups.com
Are (current) snapshots being published to any public Maven repository?

Derek Stainer

unread,
Nov 7, 2012, 11:45:16 AM11/7/12
to dropwiz...@googlegroups.com
We're getting our snapshots from here: 


Derek

Gili T.

unread,
Nov 7, 2012, 12:17:04 PM11/7/12
to dropwiz...@googlegroups.com
Eureka!

I managed to integrate Guice into Dropwizard 0.6.0-SNAPSHOT using the third approach.

  • It turns out that you cannot drop ServletFilter because it's responsible for maintaining @RequestScope and other important things.
  • It also turns out that JerseyServletModule requires GuiceContainer.class to be injectable. This means you cannot simply pass a singleton into JerseyServletModule.filter().
Here is what I did:
  • JerseyServletModule remains unchanged
  • Replace GuiceContainer(Injector) with three constructors that mirror and delegate to the ServletContainer constructors
  • The private Injector field changes from "private final Injector injector" to "@Inject private Injector injector". In other words, we use property injection instead of constructor injection. I believe this is a lesser evil to the current situation.
That's it! Here is the updated GuiceContainer:

@Singleton
public class GuiceContainer extends ServletContainer {
    
    private static final long serialVersionUID = 1931878850157940335L;

    @Inject
    private Injector injector;
    private WebApplication webapp;

    public class ServletGuiceComponentProviderFactory extends GuiceComponentProviderFactory {
        public ServletGuiceComponentProviderFactory(ResourceConfig config, Injector injector) {
            super(config, injector);
        }
        
        @Override
        public Map<Scope, ComponentScope> createScopeMap() {
            Map<Scope, ComponentScope> m = super.createScopeMap();

            m.put(ServletScopes.REQUEST, ComponentScope.PerRequest);
            return m;
        }
    }
    /**
     * Creates a new container.
     */
    public GuiceContainer() {
    }
    /**
     * Creates a new container.
     *
     * @param app the JAX-RS application
     */
    public GuiceContainer(Application app) {
      super(app);
    }
    /**
     * Creates a new container.
     *
     * @param app the JAX-RS application
     */
    public GuiceContainer(Class<? extends Application> app) {
      super(app);
    }

    @Override
    protected ResourceConfig getDefaultResourceConfig(Map<String, Object> props,
            WebConfig webConfig) throws ServletException {
        return new DefaultResourceConfig();
    }

    @Override
    protected void initiate(ResourceConfig config, WebApplication webapp) {
        this.webapp = webapp;
        webapp.initiate(config, new ServletGuiceComponentProviderFactory(config, injector));
    }

    public WebApplication getWebApplication() {
        return webapp;
    }
}

Here is a sample application:

public class Main extends Service<Configuration>
{
public static void main(String[] args) throws Exception
{
new Main().run(args);
}

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

@Override
public void run(Configuration configuration, Environment environment) throws Exception
{
                // Add resources using dropwizard. Horay!
environment.addResource(RootResource.class);

                // Pass the resourceConfig into GuiceContainer
GuiceContainer container = new GuiceContainer(environment.getJerseyResourceConfig());
environment.setJerseyServletContainer(container);

environment.addServletListeners(new JerseyContextListener(container, new UserModule()));
environment.addFilter(GuiceFilter.class, configuration.getHttpConfiguration().getRootPath());
}
}

public class JerseyContextListener extends GuiceServletContextListener
{
private final Injector injector;

public JerseyContextListener(GuiceContainer container, Module... modules)
{
this.injector = Guice.createInjector(Lists.asList(new JerseyModule(container), modules));
}

@Override
protected Injector getInjector()
{
return injector;
}
}

public class JerseyModule extends JerseyServletModule
{
private final GuiceContainer container;
/**
* Creates a new JerseyModule.
* <p/>
* @param container a container binding Jersey to Guice
*/
public JerseyModule(GuiceContainer container)
{
this.container = container;
}

@Override
protected void configureServlets()
{
bind(GuiceContainer.class).toInstance(container);
filter("/*").through(GuiceContainer.class);
}
}

Gili T.

unread,
Nov 8, 2012, 4:58:23 PM11/8/12
to dropwiz...@googlegroups.com
I just made an interesting discovery. It turns out you don't need JerseyContextListener at all. It's sufficient for you to invoke:

  Guice.createInjector(Lists.asList(new JerseyModule(container), modules));

from within the Service. Doing so will cause Guice to invoke Injector.injectMembers(container) which will allow GuiceContainer to behave as expected when it gets an HTTP request. You don't need to do anything with the returned Injector.

As far as I can tell:

GuiceFilter starts/stops ServletScopes
ServletModule populates a Module with key/value pairs related to servlets, filters and their URIs (GuiceContainer is bound here)
I haven't yet figured out how GuiceFilter and ServletModule are linked to one another. I see GuiceFilter delegates to a filter pipeline but it's not clear how that pipeline is populated.

Gili

David Morgantini

unread,
Nov 21, 2012, 7:11:13 AM11/21/12
to dropwiz...@googlegroups.com
This works really nicely for me.  The only thing I did was extract the service code into a bundle such that I could test the service run method without having to deal with the complexities of guice.

Elias Torres

unread,
Nov 27, 2012, 10:28:42 PM11/27/12
to dropwiz...@googlegroups.com
Gili,

I almost have everything working. However, when I wanted to change my rootPath, I started getting 404s on requests that were working after following your instructions.

If I remove all of the guice-related glue, DW respects rootPath correctly in 0.6.0. I'm not sure if it helps, but I had Guice working with rootPath in 0.5.1.

This is all I added. Would you mind trying the same and see if it works for you? 

http:
  rootPath:  /v1/*

-Elias

cowwoc

unread,
Nov 27, 2012, 10:30:45 PM11/27/12
to dropwiz...@googlegroups.com

    Sorry Elias. I no longer use DropWizard in my project. I plan on revisiting it in the future...

Good luck,
Gili

GG

unread,
Nov 29, 2012, 12:20:54 PM11/29/12
to dropwiz...@googlegroups.com

If it helps others, dropwizard-guice package worked well for my team's needs and is available in maven central:  https://github.com/jaredstehler/dropwizard-guice

cowwoc

unread,
Nov 29, 2012, 12:56:06 PM11/29/12
to dropwiz...@googlegroups.com

    I think it's important to clarify what dropwizard-guice doesn't do. It doesn't integrate GuiceFilter with dropwizard, which means you cannot use @RequestScoped and other useful stuff.

Gili

Tatu Saloranta

unread,
Nov 29, 2012, 1:03:09 PM11/29/12
to dropwiz...@googlegroups.com
I think it'd be great to have a wiki page for known DropWizard
extensions. I think this is a very good way to extend the platform
(sorry! can't resist using the term here, at risk of sounding
enterprise-y).
Ideally new integration pieces would live outside of core package,
whether within main project or not. This can speed up development and
progress a lot, while keeping core mean & lean.

-+ Tatu +-

Elias Torres

unread,
Nov 29, 2012, 3:22:11 PM11/29/12
to dropwiz...@googlegroups.com
I have a pull request coming with my fixes. Hang tight Guicers!

Sent from my iPhone

Elias Torres

unread,
Nov 29, 2012, 9:01:29 PM11/29/12
to dropwiz...@googlegroups.com
I have submitted a pull request to have a sub-module with Guice support.


I also have a stand-alone example with fixes to Gili's code and making sure we do have the GuiceFilter enabled to support RequestScoped items. In the pull request you'll find a bundle that can be called during the initialize phase.

bootstrap.addBundle(new GuiceBundle<HelloWorldConfiguration>() {
  @Override
  protected Collection<? extends Module> configureModules(HelloWorldConfiguration configuration) {
    return Lists.newArrayList(new HelloWorldModule(configuration));
  }
});

-Elias

Elias Torres

unread,
Nov 29, 2012, 9:07:20 PM11/29/12
to dropwiz...@googlegroups.com
I forgot to point to the stand-alone example.


-Elias

cowwoc

unread,
Nov 29, 2012, 9:08:09 PM11/29/12
to dropwiz...@googlegroups.com

    Very nice. Keep up the good work! :)

Gili

Elias Torres

unread,
Nov 29, 2012, 10:59:08 PM11/29/12
to dropwiz...@googlegroups.com
Thanks Gili you did most of the work. ;-)

Just to keep everyone posted. Coda suggested we hosted the extension separately since Guice is not used at Yammer and I agreed it was a good idea.


I did borrow Jared Stehler's auto config suggestion and made it available optionally if anybody wants it.

My last improvement to the extension was to make sure we supported injection on all of Jersey's objects such as HttpRequestContext and so on with RequestScope.

-Elias

S Ahmed

unread,
Nov 30, 2012, 2:26:37 PM11/30/12
to dropwiz...@googlegroups.com
Thanks!

I've only used spring before, where exactly are the key bits that are using guice and wiring up/injects of the implementation bits together.

Elias Torres

unread,
Nov 30, 2012, 6:43:36 PM11/30/12
to dropwiz...@googlegroups.com
All of the binding happens within the modules. You'd bind a class/interface to an instance or provider and so on.

-Elias

S Ahmed

unread,
Nov 30, 2012, 9:19:49 PM11/30/12
to dropwiz...@googlegroups.com
So say I have:

public interface UserSevice {
  User getUserById(int id);
   ...
}

public class UserServiceImpl implements UserService {
  @Inject
  private UserDao userDao
}


public class HelloWorldResource {

  @Inject
  private UserService userService;

}


So now I need to wireup:

UserDao => UserDaoImpl
UserService => UserServiceImpl

Where would I do this? Just create a Services Module that wires up all my services (UserService, SearchService, etc.) and add it to the initialize in HelloWorldService like:

.addModule(new HelloWorldModule())
.addModule(new ServicesModule())


Or am I looking at this wrong, the UserService should be a property of my configuration like getTemplate?

Elias Torres

unread,
Dec 1, 2012, 12:12:48 PM12/1/12
to dropwiz...@googlegroups.com
Exactly. Inside your ServicesModule you'd bind:

public class ServicesModule extends AbstractModule {

public void configure() {
  binder().bind(UserDao.class).to(UserDaoImpl.class);
  binder().bind(UserService.class).to(UserServiceImpl.class);
}

}

Done.

Panagiotis Partheniadis

unread,
Jan 22, 2013, 4:34:02 AM1/22/13
to dropwiz...@googlegroups.com

Hello!

I'm trying hard to get to the point using the dropwizard-guice extension but i have a simple problem with my DAO :

This is the code before the use of DI:

final DBI db = factory.build(environment, connectionsServiceConf.getDatabaseConfiguration(), "postgresql");
environment.addResource(new ConnectionResource(db.onDemand(ConnectionDAO.class));

So, my DAO implementation is the one returned from the db.OnDemand method. How im i supposed to inject this instance when the AbstractModule subclass is registered during the initialize phase (and before the run phase) where the DBI cannot be available? 

Elias Torres

unread,
Jan 22, 2013, 8:52:22 AM1/22/13
to dropwiz...@googlegroups.com
Here's a gist explaining how I do it.


You also don't have add the resources directly, since the scanner in dropwizard-guice will automatically pick it up. In general, I let Guice construct my resources so it manages all of the injection. You can still decide to have singleton or per-request resources via annotations.

-Elias

Panagiotis Partheniadis

unread,
Jan 22, 2013, 11:36:25 AM1/22/13
to dropwiz...@googlegroups.com

Played nicely!

Thanks!

Panagiotis Partheniadis

unread,
Jan 29, 2013, 1:50:22 AM1/29/13
to dropwiz...@googlegroups.com

Hello again,

I'm using the Guice AOP facilities in order to enable logging using a MessageInterceptor and a @Logged annotation. Is there a way to somehow have access to a Request Header that i need to log? 

Elias Torres

unread,
Jan 29, 2013, 4:54:09 PM1/29/13
to dropwiz...@googlegroups.com
You have access to all of these properties found in JerseyServletModule.


WebApplication
WebApplication
Providers
FeaturesAndProperties
MessageBodyWorkers
ExceptionMapperContext
HttpContext
UriInfo
ExtendedUriInfo
HttpRequestContext
HttpHeaders
Request
SecurityContext
HttpResponseContext

You just need to make sure you are in the right scope (RequestScope).

-Elias


--
You received this message because you are subscribed to the Google Groups "dropwizard-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dropwizard-us...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Matt Veitas

unread,
Feb 3, 2013, 1:42:47 PM2/3/13
to dropwiz...@googlegroups.com
I came across this in GitHub:

Elias Torres

unread,
Feb 3, 2013, 2:31:11 PM2/3/13
to dropwiz...@googlegroups.com
Yes. I work with Mike Axiak at HubSpot. That's a fork of the dropwizard-guice we built for using dropwizard at our company.

Let me know if we can be of help.

-Elias

--

Panagiotis Partheniadis

unread,
Feb 7, 2013, 3:55:28 AM2/7/13
to dropwiz...@googlegroups.com

Hello,

I have the following problem. I need to implement an Integration Test for my service. Essentially, i need to start my service and make some calls to it using my Client Access Library for this Service. The start and stop of the service is done using an EmbeddedServerCommand as mentioned in an other thread. I also use the Hubspot dropwizard-guice integration and everything works fine. Nevertheless, it is apparent that in order to call the Service using my Client Access Library, i need to have access to the Http Client configuration along with the Service Port. From that information, i can provide my Library with the HttpClient required to make a call. I know that i can use direct values but i would rather not, as all the relevant information are already loaded in Guice by your Bundle. I just need some kind of access to it. The EmbeddedServerCommand implementation can be found at https://gist.github.com/tcovo/4512416

Can you take a look please?

andrew....@seekersolutions.com

unread,
Feb 25, 2013, 2:35:56 PM2/25/13
to dropwiz...@googlegroups.com
@Elias Torres: Am I missing something when trying to use your gist? I get a runtime error from configuresDBI(MyConfiguration, Environment) -- "Could not find a suitable constructor in com.yammer.dropwizard.config.Environment. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private."
Reply all
Reply to author
Forward
0 new messages