Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Google Guice working with JaxRS (Jersey)

I have a basic JAXRS service that I can expose easily, but for once I wish to use a dependency injection API and I suspect Google Guice will be one of the best. With this in mind, I have tried to integrate it, but the documentation is a little heavy going and I've been having to hunt around to try and find the right combination of

  • Web.xml
  • Context Listener (should I use ServletContainer or GuiceContainer)
  • Service
  • Whether to annotate the service with @Singleton or @Request or nothing (should I annotate with @Singleton - docs say I should but then says it defaults to request scope)
  • Whether to annotate the constructor parameters with @InjectParam

But currently I get errors from Google Guice and they change based on whether I use the @InjectParam annotation or not.

If I annotate with @InjectParam then I get

       Mar 29, 2013 9:52:04 PM com.sun.jersey.spi.inject.Errors processErrorMessages
   SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
   SEVERE: The class com.hillingar.server.dao.interfaces.UserDao is an interface and cannot be instantiated.
   SEVERE: Missing dependency for constructor public com.hillingar.server.SessionUtility(com.hillingar.server.dao.interfaces.UserDao) at parameter index 0

If I don't annotate then I get

    Mar 29, 2013 9:54:59 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
  SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 0
  SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 1

This is my web.xml

    <?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <filter>
        <filter-name>guiceFilter</filter-name>
        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>guiceFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>com.hillingar.server.ServletContextListener</listener-class>
    </listener>

    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

This is my ServletContextListener

package com.hillingar.server;

import java.util.logging.Logger;

import javax.servlet.ServletContextEvent;

import com.google.inject.Guice;
import com.google.inject.Singleton;
import com.hillingar.server.dao.jdbcImpl.UserJdbc;
import com.hillingar.server.dao.interfaces.UserDao;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.servlet.ServletContainer;

public class ServletContextListener implements javax.servlet.ServletContextListener {

    Logger logger = Logger.getLogger(this.getClass().getName());

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
    }

        /*
         * Covered in URL
         * https://code.google.com/p/google-guice/wiki/ServletModule
         */
    @Override
    public void contextInitialized(ServletContextEvent arg0) {

            // Note the user of JerseyServletModule instead of ServletModule
            // otherwise the expected constructor injection doesn't happen
            // (just the default constructor is called)
            Guice.createInjector(new JerseyServletModule() {
                @Override
                protected void configureServlets() {

                    /*
                     * Note: Every servlet (or filter) is required to be a 
                     * @Singleton. If you cannot annotate the class directly, 
                     * you must bind it using bind(..).in(Singleton.class), 
                     * separate to the filter() or servlet() rules. 
                     * Mapping under any other scope is an error. This is to 
                     * maintain consistency with the Servlet specification. 
                     * Guice Servlet does not support the 
                     * deprecated SingleThreadModel.
                     */
                    bind(SecurityFilter.class).in(Singleton.class);
                    bind(ServletContainer.class).in(Singleton.class);

                    /*
                     * Filter Mapping
                     * 
                     * This will route every incoming request through MyFilter, 
                     * and then continue to any other matching filters before 
                     * finally being dispatched to a servlet for processing.
                     * 
                     */

                    // SECURITY - currently disabled
                    // filter("/*").through(SecurityFilter.class);

                    /*
                     * Registering Servlets
                     * 
                     * This registers a servlet (subclass of HttpServlet) called 
                     * ServletContainer, the same one that I would have used in 
                     * the web.xml file, to serve any web requests with the 
                     * path /rest/*  i.e. ...
                     * 
                        <servlet>
                            <servlet-name>ServletAdaptor</servlet-name>
                            <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
                            <load-on-startup>1</load-on-startup>
                        </servlet>
                        <servlet-mapping>
                            <servlet-name>ServletAdaptor</servlet-name>
                            <url-pattern>/rest/*</url-pattern>
                        </servlet-mapping>

                     */
                    serve("/rest/*").with(ServletContainer.class); // JAX-RS

                    // Using this and it starts bitching about
                    // com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.
                    // So presumably wants an Application class that enumerates 
                    // all my services?
                    //serve("/rest/*").with(GuiceContainer.class);

                    /*
                     * Bindings
                     */
                    bind(UserDao.class).to(UserJdbc.class);
                    bind(SessionUtility.class);
                }
            });
    }

}

This is my UserService

package com.hillingar.server.rest;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;

import com.hillingar.server.SessionUtility;
import com.hillingar.server.dao.interfaces.UserDao;
import com.hillingar.server.model.User;
import com.hillingar.server.model.dto.AuthenticationResponse;

@Path("/user")
@Produces("application/json")
@Consumes({"application/xml","application/json"})
@Singleton // <-- Added Singleton here
public class UserService {

    private UserDao userDao;
    private SessionUtility sessionManager;

        /*
           Error if I annotate with @InjectParam...

           Mar 29, 2013 9:52:04 PM com.sun.jersey.spi.inject.Errors processErrorMessages
           SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
           SEVERE: The class com.hillingar.server.dao.interfaces.UserDao is an interface and cannot be instantiated.
           SEVERE: Missing dependency for constructor public com.hillingar.server.SessionUtility(com.hillingar.server.dao.interfaces.UserDao) at parameter index 0

           Error If I don't annotate at all...
            Mar 29, 2013 9:54:59 PM com.sun.jersey.spi.inject.Errors processErrorMessages
            SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
              SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 0
              SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 1

           (both output Initiating Jersey application, version 'Jersey: 1.13 06/29/2012 05:14 PM')
        */
    @Inject
    public UserService(UserDao userDao, SessionUtility sessionManager) {
        this.userDao = userDao;
                this.sessionManager = sessionManager;
    }

    @GET
    public List<User> test(@Context HttpServletRequest hsr) {
                // USER DAO IS ALWAYS NULL - CONSTRUCTOR INJECTION NOT WORKING
        User loggedInUser = userDao.findBySessionId(hsr.getSession().getId());
                ...
        return users;
    }

}
like image 915
Devology Ltd Avatar asked Mar 29 '13 22:03

Devology Ltd


People also ask

Does Google use Guice?

Use of Google Guice for implementing dependency injection in application is very easy and it does it beautifully. It's used in Google APIs so we can assume that it's highly tested and reliable code.

How does Guice injector work?

Using GuiceIn each of your constructors that need to have something injected in them, you just add an @Inject annotation and that tells Guice to do it's thing. Guice figures out how to give you an Emailer based on the type. If it's a simple object, it'll instantiate it and pass it in.

How do you inject a Guice constructor?

To use it, annotate the constructor with the @Inject annotation. This constructor should accept class dependencies as parameters. Most constructors will then assign the parameters to final fields. If your class has no @Inject -annotated constructor, Guice will use a public, no-arguments constructor if it exists.

What does bind mean in Guice?

A binding is an object that corresponds to an entry in the Guice map. You add new entries into the Guice map by creating bindings.


1 Answers

Changed the ServletContextListener to

package com.hillingar.server;

import java.util.logging.Logger;

import javax.servlet.ServletContextEvent;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.google.inject.servlet.GuiceServletContextListener;
import com.hillingar.server.dao.jdbcImpl.UserJdbc;
import com.hillingar.server.dao.interfaces.UserDao;
import com.hillingar.server.rest.UserService;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.servlet.ServletContainer;

// (1) Extend GuiceServletContextListener
public class ServletContextListener extends GuiceServletContextListener {

    Logger logger = Logger.getLogger(this.getClass().getName());

    // (1) Override getInjector
    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new JerseyServletModule() {
            @Override
            protected void configureServlets() {
                bind(SecurityFilter.class).in(Singleton.class);
                bind(UserService.class);// .in(Singleton.class);
                bind(ServletContainer.class).in(Singleton.class);

                // (2) Change to using the GuiceContainer
                serve("/rest/*").with(GuiceContainer.class); // <<<<---

                bind(UserDao.class).to(UserJdbc.class);
                bind(SessionUtility.class);
            }
        });
    }
}
like image 124
Devology Ltd Avatar answered Nov 14 '22 23:11

Devology Ltd