Guice with GWT

This article shows how to configure Guice with server side code in Google Web Toolkit (GWT) applications1. I use Guice 2.0 with GWT 1.7.0.

Steps include:

  • Obtain the Guice JAR files
  • Extend GWT’s RemoteServiceServlet
  • Extend Guice’s GuiceServletContextListener
  • Extend Guice’s ServletModule
  • Set all RemoteService relative paths to GWT.rpc
  • Configure GuiceFilter and your context listener in web.xml
  • (optional, but good) Your remote service implementation classes no longer have to extend RemoteServiceServlet

Phew! That seems like a long list, but I want to ensure I list every step. This approach is based on the excellent GuiceRemoteServiceServlet by Pavel Jbanov. His initial work pointed me in the right direction, making this much easier.

Get Guice

Grab guice-2.0.zip from the Guice downloads page. This ZIP contains all of the required JARs. You may also want guice-2.0-src.zip.

To use Guice with GWT, you’ll need two JARs: guice.jar and guice-servlet.jar. Just drop these into your war/WEB-INF/lib directory.

Extend GWT’s RemoteServiceServlet

This code comes almost verbatim from Pevel Jbanov’s blog, with the modifications mentioned in the first comment on that page. Here is the source:

@Singleton
public class GuiceRemoteServiceServlet extends RemoteServiceServlet {
  @Inject
  private Injector injector;

  @Override
  public String processCall(String payload) throws SerializationException {
    try {
      RPCRequest req = RPC.decodeRequest(payload, null, this);

      RemoteService service = getServiceInstance(
            req.getMethod().getDeclaringClass());

      return RPC.invokeAndEncodeResponse(service, req.getMethod(),
        req.getParameters(), req.getSerializationPolicy());
    } catch (IncompatibleRemoteServiceException ex) {
      log("IncompatibleRemoteServiceException in the processCall(String) method.",
          ex);
      return RPC.encodeResponseForFailure(null, ex);
    }
  }

  @SuppressWarnings({"unchecked"})
  private RemoteService getServiceInstance(Class serviceClass) {
    return (RemoteService) injector.getInstance(serviceClass);
  }
}

All GWT RPC requests will go through this single servlet. Thus, your remote service implementation classes will not have to extend RemoteServiceServlet.

  • GuiceFilter will inject the Injector
  • The @Singleton annotation is required for Guice-managed servlets

Extend Guice’s GuiceServletContextListener

This step creates a Guice-friendly servlet context listener.

public class MyGuiceContextListener extends GuiceServletContextListener {
  @Override
  protected Injector getInjector() {
    return Guice.createInjector(
      new MyServletModule(),
      new AnotherOneOfMyModules(), etc...);
  }
}

You can hook up as many Guice modules as you want. The first, MyServletModule, replaces much of what traditionally goes in web.xml.

Extend Guice’s ServletModule

In order to use constructor injection with servlets, Guice must construct your servlet instances. Thus, you no longer list each servlet in web.xml. Here is a simple module:

public class MyServletModule extends ServletModule {
    @Override
    protected void configureServlets() {
        serve("/MyApp/GWT.rpc").with(GuiceRemoteServiceServlet.class);

        // cannot use @ImplementedBy
        bind(MyService.class).to(MyServiceImpl.class);
    }
}

Go check out the Guice ServletModule documentation for a full explanation.

You cannot use @ImplementedBy on the MyService interface. That’s because MyService is in GWT client code, and the MyServiceImpl is in GWT server code. Instead, the binding is configured in MyServletModule as shown.

Remember — this replaces most of what you typically place in web.xml.

Set all RemoteService relative paths to GWT.rpc

All of your GWT remote services will dispatch to the GuiceRemoteServiceServlet shown earlier.

@RemoteServiceRelativePath("GWT.rpc")
public interface MyService extends RemoteService {
    ...
}

Create the web.xml

In old-style GWT, you list your remote service implementations in the GWT module XML file (MyApp.gwt.xml). If you have any servlets listed there, you can remove them. Your GWT module should look something like this:

<module rename-to='MyApp'>
  <inherits name='com.google.gwt.user.User'/>
  etc...

  <entry-point class='com.abc.whatever.client.MyEntryPoint'/>
</module>

Your web.xml should list the Guice filter and your custom Guice context listener.


<web-app>
  <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.yourcompany.server.MyGuiceContextListener</listener-class>
  </listener>

  <!-- NOTE: List all Servlets in MyServletModule for Guice injection -->

  <welcome-file-list>
    <welcome-file>Css.html</welcome-file>
  </welcome-file-list>
</web-app>

Clean up Remote Service Implementations

Your GWT remote service implementations do not have to extend RemoteServiceServlet any more.

Old code:

public class MyServiceImpl
    extends RemoteServiceServlet implements MyService {
  ...
}

New code:

public class MyServiceImpl implements MyService {

  private final AnInterface anInterface;

  // Guice injection here!
  @Inject
  public MyServiceImpl(AnInterface anInterface) {
    this.anInterface = anInterface;
  }
  ...
}

Summary

Once all of the above pieces are in place, you can use normal Guice dependency injection in all of your GWT remote service implementation classes. This includes both constructor and setter injection.

Please let me know if you find any mistakes, and I’ll be sure to update this article.

1 To use Guice with GWT client code, check out the GIN project. (I have not tried GIN)

Updates

Sep 17, 2009

Changed: RPCRequest req = RPC.decodeRequest(payload);
To this: RPCRequest req = RPC.decodeRequest(payload, null, this);


Jesse Wilson Says:

Eric, you continue to be awesome. I’ve linked this from here:
http://code.google.com/docreader/#p=google-guice&s=google-guice&t=ExternalDocumentation

If you’d like to contribute an extension to the framework that supplies this stuff, it would be quite welcome.

Jesse Kuhnert Says:

Mmmm that code looks purty. Can’t believe I’m currently stuck with f-ing spring xml files currently.

Siegfried Says:

Is it possible to download the whole project somewhere?

rl337 Says:

In GuiceRemoteServiceServlet, you’ll run into SerializationPolicy issues if you decode your requests as shown in the blog post. You COULD just make all of your rpc classes implement IsSerializable, but the right way to do it (so that you don’t end up using the LegacySerializationPolicy) is to change your decode from:

RPCRequest req = RPC.decodeRequest(payload);

to read this like so:

RPCRequest req = RPC.decodeRequest(payload, null, this);

where we don’t care about an interface to restrict our instantiation to so we pass null, and we use our class as the SerializationPolicyProvider.

Eric Burke Says:

@rl337 thanks for the tip, I updated the code to do it that way.

Ravi Says:

Hey Eric,

Thank you for putting it all together. I am on a GWT project and would like to use it. But I cannot use GWT for all Service implementations. I want to use it for a new SvcImpl I am writing. Is it possible to use Guice selectively. And is so how? Can you give me an example…?

I tried to follow the example above but get an exception while trying to run in hosted mode. Have you been able to use it in Hosted Mode?

Also, is it possible to download the sample project above from somewhere?

Look forward to your reply and thanks once again for this article.

Ravi

Ian Smith Says:

Thanks very much for the excellent idea and working code! I used your solution above to also solve another problem–handling the setup of the guice injector (usually done context listener) when running a GWTTestCase. GWTTestCase sets up it’s servlet container with no hook back to stick the context listener on. So, I just override the init() method in this class and since it’s the only servlet to create the Injector needed for the test case. Then, you can put your servlet in a servlet tag in you .gwt.xml and it all just works…

thanks
again

Here is a sketch if it helps:

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);

// this code is the critical connector for dealing with GWTTestCase
// the ONLY case where you arrive here without the injector having
// been already injected is when you are running in a GWTTestCase
// because it gives you no hook to allow you to attach your context
// listener
if (injector == null) {
// create your context listener here
// you will have to expose the “getInjector” method as public
// assign the inject from the getInjector method
}
}

Pavel Jbanov Says:

Thanks Eric for mentioning my work. :)

I like your version, it’s much cleaner.

Awesome! It worked like a charm, thanks a lot Eric.

In case someone wants an example, my GWT/Guice/JPA app can be downloaded from Github:
http://github.com/tiagofernandez/bazooka

The slight difference is the server-side is implemented with Scala instead of Java.

Craig Baker Says:

Thanks for writing this up. No small error to fix in this post. @RemoteServiceRelative should be @RemoteServiceRelativePath.

Cheers.

Eric Burke Says:

@Craig Thanks! I fixed that just now.

Ming Zhu Says:

Without using the extends RemoteServiceServlet, how can I use method like getServletContext().getServerInfo() and getThreadLocalRequest().getHeader(”User-Agent”)? Thanks! -Ming

Eric Burke Says:

If you need to use HttpServletRequest, HttpSession, etc, you simply @Inject those objects wherever you need them. This is mentioned here: http://code.google.com/p/google-guice/wiki/ServletModule

Ming Zhu Says:

Is this what I should do?
Old code:

@SuppressWarnings(”serial”)
public class GreetingServiceImpl extends RemoteServiceServlet implements
GreetingService {
public String greetServer(String input) {
String serverInfo = getServletContext().getServerInfo();
String userAgent = getThreadLocalRequest().getHeader(”User-Agent”);
return “Hello, ” + input + “!I am running ” + serverInfo
+ “.It looks like you are using:” + userAgent;
}
}

New code:

@SuppressWarnings(”serial”)
public class GreetingServiceImpl implements GreetingService {

HttpServletRequest request;
HttpServlet servlet;

@Inject
public GreetingServiceImpl(HttpServletRequest request, HttpServlet servlet) {
this.request = request;
this.servlet = servlet;
}

public String greetServer(String input) {
String serverInfo = servlet.getServletContext().getServerInfo();
String userAgent = request.getHeader(”User-Agent”);
return “Hello, ” + input + “!I am running ” + serverInfo
+ “.It looks like you are using:” + userAgent;
}

}

Eric Burke Says:

You probably need to inject Provider<HttpServletRequest> for this reason from that same page I linked to: “Note that if you want access to any of the request or session scoped classes from singletons or other wider-scoped instances, you should inject a Provider<T> instead.”

Ming Zhu Says:

Here is my modified code. I have to a cast to request. Does this look right? Thanks! -Ming

private Provider request;
private HttpServlet servlet;

@Inject
public GreetingServiceImpl(Provider request, HttpServlet servlet) {
this.request = request;
this.servlet = servlet;
}
public String greetServer(String input) {
String serverInfo = servlet.getServletContext().getServerInfo();
String userAgent = ((HttpServletRequest) request).getHeader(”User-Agent”);
return “Hello, ” + input + “!I am running ” + serverInfo
+ “.It looks like you are using:” + userAgent;
}

Toine Says:

Hello

thx for the Info
Did you also tried to implement JPA and EJB3 with it

regards toine

Toine Says:

Hello again

I tried to use it but i get the error
Error 404: SRVE0190E: File not found: /bw_gxt_01/GWT.rpc

protected void configureServlets() {
serve(”/bw_gxt_01/GWT.rpc”).with(GuiceRemoteServiceImpl.class);
System.out.println(”BwServletModel.configureServlets()”);
bind(ListService.class).to(ListServiceImpl.class);
}
@RemoteServiceRelativePath(”GWT.rpc”)
public interface ListService extends RemoteService {

Am i forgetting something
regards toine

Peter Putzig Says:

Solution is simple. I forgot to adapt the the path in serve()

serve( “/myweb/GWT.rpc” ).with( GuiceRemoteServiceServlet.class );

Thanks for this blog post! I’ve just tried this approach with GWT 2.0 and it works perfectly. But I did need aopalliance.jar in addition to the two JARs you mentioned.

Question:
- Is it possible to avoid “/MyApp/” in serve(”/MyApp/GWT.rpc”)? Given that this is just the name of the web application, it would be nice if one could avoid this redundancy somehow.