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);