However, when working with GWT, you quickly find that the toolkit’s implementation of the Java API’s are incomplete, and that using types that Google hasn’t provided as translatable will result in a GWT compiler error.
We wanted to be able to use the java.net.URI and java.util.UUID classes in our client-side code, neither of which are supported by GWT. This tutorial describes a method for implementing client-side versions of JDK classes that GWT doesn’t support. Fortunately, GWT provides support for overriding one package implementation with another.
There have been some attempts to implement more of the JDK; for example see GWTx, but they are quite incomplete.
It is extremely useful, then, to have a clear technique for creating client-side versions of some JDK classes that are unavailable. GWT helpfully provides a mechanism for doing this (look down under “Overriding one package implementation with another”), but doesn’t tell you much about how to use it.
I went looking for some examples of how to use the “super-source” XML tag to create some client-side implementations of some java types, and found a couple of incomplete or confusing tutorials:
- Summary of Some of the Pecularities Involving GWT Eclipse Tooling and Super-Source
- Using the same model classes in Android, GWT and JPA (Part II)
But, none of these told me exactly how I was to accomplish this.
There’s a mechanism for this, as well, but again I couldn’t find good Google documentation on it. I did find How to use a CustomFieldSerializer in GWT, but it deals with entirely custom classes and not classes meant to act as JDK API classes.
We eventually figured this out and I thought I’d put this tutorial together in hopes that others can do the same if necessary.
- Maven, 2.2.1 or greater
- JDK 1.6 or better
- Eclipse (optional)
These can be downloaded from the websites of the respective projects.
If you don’t have an existing project, you can use the gwt-maven-plugin archetype to create one:
It will prompt you for group id, artifact id, version, and package names; for the example I used “com.example.gwt”, “SuperSource”, “1.0.0-SNAPSHOT”, and “com.example.gwt”.
You will need a working maven pom.xml file that includes the necessary GWT dependencies and the gwt-maven-plugin; the archetype may give you this, although it doesn’t use the latest GWT versions and in my experience produces some errors. The one I used, for example, looks like this:
Create source directories: src/main/java, src/main/resources, and src/main/webapp. You will need to provide the necessary html, css, and web.xml files for your implementation. The example code linked above includes sample sources for these taken directly from the archetype that the gwt-maven-plugin provides.
Create server-side service interface and implementation, and an EntryPoint class. The ones in the linked source are adapted from the “Greeting Service” that comes with the archetype.
Create a JDK-Emulation Module
Create a new module at the same level, in the resources directory, as your existing GWT module. In the example source, I called it “SuperSourceJre.gwt.xml”, and placed it in the src/main/resources/com/example/gwt directory next to the one created by the archetype. It should look like this:
The path in the super-source tag is arbitrary, but must match the the name of a directory directly under the package or directory where you created the new file; obviously you can also set the “rename-to” value to whatever you would like. What GWT does is take the package specified as source-path, and removes it from the front of the path when compiling anything under that directory. So files under “com/example/gwt/jre/java/net” will be compiled as if they belonged to the package “java.net”. This allows you to create classes that get “renamed” to classes in the core JDK. Note that the sub-path then must match the package of the class in the JDK.
Import this new module in your existing module(s). For example, in the existing “SuperSourceTest.gwt.xml” file, I added:
Create the Emulated Classes
In the attached example source code, I emulate both java.net.URI and java.util.UUID, and I also include some fancy JSNI native code to allow me to generate UUID’s on the client side if necessary. Of course, your needs might be different. The key is that only the methods you implement in your emulation classes are available to the client. Although it will appear as though your java code compiles when you are writing client-side code, it will fail GWT compilation.
The classes you need to emulate should be created in a package-dependent path in the resources directory under where you created your new emulation module. For example, I created the classes:
(The exception is created because the URI constructor throws URISyntaxException, so it also must be emulated.) Note that I put these in the src/main/resources directories instead of src/main/java: this is because Eclipse or other IDE’s and compilers will refuse to compile them as java, since they have invalid package specifications, so I put them in as “resources” so they end up in source and classes directories but nothing but GWT attempts to compile them.
Create the source code for these classes. Ideally, you would implement the entire functionality of the original JDK class yourself, but you at least need to emulate a default constructor and the minimum logic necessary for your client. The signatures of the methods you implement should mirror exactly the ones they are replacing in the JDK itself.
Here are the implementations I used:
At this point, GWT will happily compile these classes, but they can only be used on the client side (server-side code will use the original JDK classes). They can’t be passed as service arguments between client and server because GWT will see them as different because their serialized signatures are different.
GWT allows you to specify custom serialization for your classes. The method is pretty straightforward: you need to create a class in the same package as the class you want to serialize named [Class to Serialize]_CustomFieldSerializer. The name and package must be exact, or GWT won’t be able to find the serialization class.
In the serializer class, you must implement two methods:
You may also implement “instantiate” if default constructor instantiation is not adequate.
Because we want these serializers available on both the server and client side, they should be placed in src/main/java/… so they will be compiled by both GWT and the java compiler.
This is pretty straightforward, and works fine on the client. But there’s a major problem: the classes we want to emulate are in JDK core packages (java.net, java.util). On the server, the classloader will refuse to load any custom class in core packages (packages beginning with java or javax) as part of the JDK’s core sandboxing. So we can’t place our serializers in the right package.
Apparently this is a problem Google had as well, because their own emulation classes must also often have custom serializers. So they have created a “magic value” package name that their internal API checks for serializers as well as the raw package name. This value is “com.google.gwt.user.client.rpc.core”. The raw package is appended to this, so if GWT were looking for a serializer for “java.net.URI”, they would first check for “java.net.URI_CustomFieldSerializer” and, if none was found, check for “com.google.gwt.user.client.rpc.core.java.net.URI_CustomFieldSerializer” So if we place our serializers in such a package, GWT will find them automatically. Of course, this is internal GWT API to “use at your own risk”, but we haven’t found another way around this problem yet.
So to serialize our own URI and UUID classes, we build custom serializers as (note the src/main/java location and the special package):
These look like:
The code implementation for both of these is pretty straightforward: serialize the object using toString, and instantiate it from the string.
Utilizing the Classes
Obviously you will have your own specific uses of these classes. To test them, I created a simple DTO bean in the archetype’s shared package:
I then modified the archetype’s GreetingService and related implementation, changing greetServer to look like this:
The implementation was changed similarly, and echoes both echoes back incoming URI and UUID entries and generates some new random ones. This lets me make sure that the equals and hashcode implementations work reasonably well.
Then I modified the EntryPoint to use the new method and display the result. If you’re using gwt-maven-plugin to generate the async interfaces you’ll have to do a maven compile to have those interfaces generated before the code below will compile in Eclipse.
You should then compile this using mvn clean install gwt:run to make sure it works correctly in both hosted and production modes.