Google Web Toolkit and Equinox
My team has been interested for some time in exposing web-based interfaces to some of the capabilities wrapped up in our RCP plugins. At EclipseCon I attended the tutorial offered by the Google Web Toolkit (GWT) team and I thought "Gee, wouldn't it be neat if you could put a GWT-based interface on a servlet running in an Equinox http server?" As it turns out, it is neat, and here's a rough description of how to do it:
First, I downloaded the Equinox Jetty server plugins as discussed here:
Note that you have to use the Jetty stuff because org.eclipse.equinox.http doesn't support Servlet 2.4, which appears to be required by GWT.
Next, I wrapped up gwt-user.jar and gwt-dev-windows.jar in a plugin that I called com.google.gwt. One critical point here - the jars include javax.servlet, which is also included with Equinox. These classes conflict and cause a nasty classloader error. In order to avoid this, you just have to *not* export the javax.servlet packages from this new GWT plugin. Many thanks to Pascal of the Eclipse platform team for helping me to track this problem down in between sessions at EclipseCon!
Armed with Equinox and GWT, I made an example plugin containing a basic application that the GWT team presented at EclipseCon, called Chattr. In that plugin I extended the extension points provided by Equinox to register both the static html resources and the Chattr servlet, as shown below:
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.2"?> <plugin> <extension point="org.eclipse.equinox.http.registry.resources"> <resource alias="/" base-name="/www"/> </extension> <extension point="org.eclipse.equinox.http.registry.servlets"> <servlet alias="/org.eclipsecon.gwt.chattr.Chattr/chattr" class="org.eclipsecon.gwt.chattr.server.ChatServiceImpl"/> </extension> </plugin>
Finally, since GWT uses reflection to deserialize the classes passed to and from the servlet, the GWT plugin needs to be able to access the classes in the example plugin described above. Since the example plugin already depends on the GWT plugin, we can't create a direct dependency in the opposite direction. So, I used buddy classloading. In the GWT plugin, I put this:
In the example plugin, I put this:
Finally, I created an Equinox OSGI Framework run configuration, added my example plugin and all required plugins, and started it up. Pointing Firefox at the OSGI http port shows that everything is working fine. I confirmed that I could access classes in another plugin from my example plugin.
Of course, this doesn't allow for the magic of GWT self-hosting, but I think it's a great start.
Client side debugging
To run the application using that browser we need to create a new "Java Application" launch configuration and add gwt-dev-windows.jar to the classpath. We have to add the "src" folder to the user entries too as the browser needs access to the source code. The main class to be selected is the com.google.gwt.dev.GWTShell.
Another thing we need is to copy the nocache.html file from the compiled version into the www folder. This is required by the hosted browser and as we are not using the built-in server we need to provide this file by hand. This file is rarely modified, so we just need to do it once.
As we don't need the built-in web server from the hosted browser, we can safely add the "-noserver" argument in the launch configuration.
At this point we are ready to run both the server (Equinox) and the client (GWT hosted browser). It's amazing to debug server and client side at same time. Enjoy!
I have followed the instructions here and I ran into a few problems (when using GWT 1.4 and Equinox). I don't know if these problems are just specific to GWT 1.4. I posting these here in case others have these problems too:
- com.google.gwt needs to depend on javax.servlet. I seemed to get that "nasty" class cast exception without it.
- swt-*.dll and gwt-ll.dll need to be added to com.google.gwt. It seems that the hosted browser uses SWT, so the DLL is needed (I guess they use an older version of SWT)
- I could not find the nocache file anywhere. The instructions say "copy it from the compiled version to the www directory", however, I thought the www directory was the "compiled" version. Anyways, it seems to work without this.
FWIW, there are some class loading errors in GWT 1.4.10 (RC1) http://code.google.com/p/google-web-toolkit/issues/detail?id=1138
You should also try this in case you need an Eclipse plug-in. ;)
--gunnar 01:02, 4 July 2007 (EDT)
If you try and keep your equinox server running and just do updates to get new bundles, you will run into an issue. This is because GWT does some security checks to ensure that classes have follow the proper inheritance hierarchy, and they use the default class loader to check this (not the use Equinox uses). To fix this, I added
and put this in the service method of my servlet. When a request comes in, this ensures that the current thread has the proper class loader attached.
I'm sure there is a better way to do this, but this works for now.
(Actually, I just realized this is the same issue that Gunnar pointed out... :) )
Ian, I've opened issue 1888 a while ago to make GWT a better player with OSGi. However, it hasn't been addressed yet. The thing gets really nasty when it tries to load the GWT serialization policy and your GWT client side module is in a different bundle than your server side.
Can you elaborate a bit more on the security checks? What about a stack trace in an issue report in the GWT issue tracker? I suspect it's more a caching thing that they are doing for serialization/de-serialization.
--gunnar 01:36, 7 March 2008 (EST)