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

Not a JComboBox Bug

Here is a little Java program that creates a JComboBox with three identical “Green” items:

public class ComboDemo extends JFrame {     public static void main(String... args) {         SwingUtilities.invokeLater(new Runnable() {             public void run() {                 new ComboDemo().setVisible(true);             }         });     }      public ComboDemo() {         super("Duplicates are Dumb");         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);          JComboBox combo = new JComboBox(new String[] {                 "Red", "Green", "Blue", "Green", "Yellow", "Green"         });         add(new JLabel("Choose your favorite color:"), BorderLayout.NORTH);         add(combo, BorderLayout.SOUTH);         pack();     } }

As you can see, the user really has no way to know which “Green” is the correct selection:

Combo with Duplicate Items

Some people want to display multiple identical items in combo boxes. I’ve had customers ask me for this feature — and I refused. If memory serves correctly, they were having keyboard navigation problems because focus would get stuck on the first of the duplicate items. Their database contained duplicate descriptions, and they wanted me to fix the alleged focus “bug” in the combo box rather than eliminate the duplicate descriptions in the database. (eventually, reason prevailed and we made the descriptions unique).

This has even been reported as a bug, and the ensuing comments are interesting to read. For example:

Good God sun!!! Just because you dont’ see why people would use the same string more than once in a JComboBox, it doesn’t mean that it isn’t need people like us!! Fix it please!!!

Yikes…imagine a GUI with these choices:

a) Yes b) Yes c) No

Um…

Apple’s Swing implementation has an interesting approach. When you click on one of the identical items, they are all selected:

Leopard Combo Selection

This seems like a reasonable way for JComboBox to behave when given unreasonable data.

GWT IDE Selection

I’m starting a new GWT project at work, so I wanted to select the best IDE for the job. I installed the very latest versions of these IDEs along with their respective GWT plugins:

  • Eclipse 3.5 (Galileo)
  • NetBeans 6.7
  • IDEA 8.1

I’ll warn you right now that this is not an in-depth review or comparison. I looked just deep enough to satisfy my own curiosity and to choose a tool for my own coding.

Eclipse

Eclipse has an advantage in that Google supplies the Google Plugin for Eclipse, ensuring the plugin generates the exact same directory layout as the GWT command line tools. After installing Eclipse and the plugin, I generated a sample GWT project and it ran perfectly in hosted mode.

This is where things went badly, however. As you may know, GWT services consist of two interfaces and a class:

  • MyService
  • MyServiceAsync
  • MyServiceImpl

For my first (and only) trick, I performed a Rename refactoring on MyService. Eclipse renamed that interface, but not the other interface or implementation class. Instead, Eclipse immediately lit up with errors, forcing me to manually rename the Async interface and Impl class. Epic fail.

NetBeans

NetBeans also supports GWT through the gwt4nb plugin. I generated a project and noted it creates a slightly different directory layout, but nothing that would introduce problems.

Like Eclipse, NetBeans failed the Rename refactoring test. Unlike Eclipse, however, NetBeans did not immediately indicate errors in the mismatched class and interface names. Thus, errors would only manifest later at runtime. I gave up very quickly.

IDEA

Unlike the other two IDEs, IDEA’s plugin does not yet support the new directory layout introduced in GWT 1.6. I initially assumed this would be a big problem, since you also see deprecation warnings when you launch your app in hosted mode.

In reality, however, the directory layout differences only manifest themselves in the output directories. The source directories are the same, so a simple Ant buildfile can produce a more standard GWT 1.7 WAR-style layout.

Of the three IDEs, IDEA is the only one to properly implement the Rename refactoring. When you rename your Service interface, the other interface and class both rename in unison.

Also, IDEA does a kick-ass job of keeping CSS styles in sync with styles referenced in GWT code. As I discovered today, when I define a style name in Java code, IDEA shows it in red and can automatically insert a new style class into my CSS file. I also refactored a <div> identifier in my HTML file, and IDEA correctly refactored the String reference in the Java source code!

Conclusion

I’ve used IDEA for years, and was initially put off by the fact that IDEA 8.1 does not yet fully support the new GWT 1.6 and 1.7 directory layout. In practice, this is a non-issue, and IDEA 9 will support the latest GWT conventions. IDEA’s first rate refactoring, along with tight integration with CSS classes and HTML identifiers really set it apart from the other IDEs.

Ominous New Direction for Java?

Java SE 6 Update 14 (gotta love those names) is now available. According to the release notes, a new “Garbage First (G1) Garbage Collector” is enabled. To use it:

-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC

Do so with caution, however. As @kimchy noted on Twitter, the release notes include this statement:

Although G1 is available for use in this release, note that production use of G1 is only permitted where a Java support contract has been purchased.

What do you think?

Phipps is Wrong

Sun’s Simon Phipps is wronger than wrong:

Whether you agree with Sun policing it or not, Java compatibility has served us all very well for over a decade. That includes being sure as a developer that all core classes are present on all platforms. Creating sub-sets of the core classes in the Java platform was forbidden for a really good reason, and it’s wanton and irresponsible to casually flaunt the rules.

He’s talking about Google’s JRE Class White List for Google App Engine. The white list is a subset of JRE classes that work on Google’s App Engine hosting environment.

I seriously doubt Google set out to “casually flaunt the rules”. I find it far more likely that Google sought to support as much of Java as possible within the confines of their data centers.

Progress

Java is over ten years old, yet I write this blog on WordPress, a PHP application. PHP is ubiquitous and cheap with virtually any ISP, Java is not. Java hosting is expensive and consumes a lot of server resources. For this reason, server-side Java is largely limited to in-house deployments on company servers.

Now Google offers a way to cheaply host server-side Java applications that gracefully scale to hundreds…perhaps thousands of servers. This is wonderful news!

Yes, Google only supports a subset of Java. Rather than calling this “wanton and irresponsible”, how about recognizing this as an engineering tradeoff? We programmers give up some flexibility in return for the ability to scale cheaply and easily.

Other Examples

There are other ways to scale Java to many servers. None are possible without tradeoffs. MapReduce tools like Hadoop require a completely different way of designing your applications and are not appropriate for most applications.

Terracotta tries really hard to be transparent, but you won’t have much luck running it with Java WebStart apps. That’s one of the engineering tradeoffs you live with. Much like Google App Engine’s white list, Terracotta has a list of Non-Portable Classes. Those are the breaks with distributed computing.

GWT

Much like App Engine, GWT also supports a subset of the full JRE. That’s because GWT needs to generate JavaScript to run in the browser.

Is GWT another example of Google casually flaunting the rules? Really? GWT is one of the most innovative, cutting edge products I’ve ever seen. It is a marvel of ingenuity and can only support a subset of the JRE because of its nature.

Fully supporting every JRE class and method in a sandboxed environment like GWT is impossible.

Java ME

Sun’s own Java ME supports a subset of the JRE, currently limited to JRE 1.3. See CLDC and MIDP.

Some people say Sun chose a subset of JRE 1.3 as an engineering tradeoff because mobile devices and embedded systems cannot support the full JRE.

But we all know the truth. Sun is wanton and irresponsible to casually flaunt the rules with Java ME. Shame on you, Sun.

JavaFX Fail

JavaFX never seems to work for me. I assumed it was because I use a Mac, but developers assure me the examples work on Leopard. Not my Leopard, apparently.

Step 1

Click the Launch button at this page. This is on Leopard with Firefox version 3.0.7.

Step 2

Receive error.

Step 3

The details:

This happened to me the other day on a completely different web site with a similar kind of example. The error message — Server response code: 500 — makes it look like the problem is on Sun’s end.

Not good enough.

UPDATE

Now it works. This makes me believe the frequent errors I see are on Sun’s servers.

A Swing JComboBox for Enums

Here is a quick and dirty combo box class I wrote the other day. It displays all of the values from a Java enum.

public class EnumCombo<E extends Enum<E>> extends JComboBox {   private final Class<E> enumClass;    public EnumCombo(Class<E> enumClass) {     this.enumClass = enumClass;      for (E e : enumClass.getEnumConstants()) {       addItem(e);     }   }    public E getSelectedItem() {     return enumClass.cast(super.getSelectedItem());   } }

Using the combo box looks like this:

EnumCombo<Status> statusCombo = new EnumCombo<Status>(Status.class);

You’ll probably want to override toString() in the enum to display friendly values in the combo box.

Sun Expands Crapware Program

Remember when I wrote about the Java 6 Bundled Yahoo Toolbar crapware? Well, it just got a lot worse. Effective immediately, Sun will now bundle the Microsoft Live Powered Search-Powered Toolbar — also known as crapware — with JRE downloads.

REDMOND, Wash. — Nov. 10, 2008 — Microsoft Corp. today announced a search distribution deal with Sun Microsystems Inc. to offer the MSN Toolbar, powered by Microsoft Live Search, to U.S.-based Internet Explorer users when they download the Java™ Runtime Environment (JRE™), effective as of today.

Through this agreement, Internet Explorer users downloading Sun’s JRE will have the option to download the MSN Toolbar and have one-click access to Live Search features, as well as news, entertainment, sports and more from the MSN network and direct access to Windows Live Hotmail and Windows Live Messenger.

Here’s another take on this development.