It’s been a while since I’ve been active in GWT programming, but I recently picked up an old GWT + App Engine project and dusted it off. This time around, I need a service layer that will work with native mobile apps as well as the GWT desktop app: in other words, a REST+JSON API.
The server side
I decided to use Jersey on the server, which makes it incredibly easy to create REST services, especially in combination with the fabulous objectify-appengine. Here’s one of my services:
@Path("api/user") @Produces(MediaType.APPLICATION_JSON) public class UserDao extends ObjectifyDao<User> { @GET @Path("me") public User getMe() { return AuthFilter.getUser(); // long live ThreadLocal! } }
All the other standard CRUD methods (list, get, delete, etc.) are inherited from a generic DAO. A servlet filter verifies user authentication, XSRF tokens, etc. At only one class per entity, I find this much more agreeable than the four classes per entity I used to write with gwt-dispatch (Action, Result, Handler, DAO).
The GWT REST+JSON client
On the client side, GWT developers have long needed something which combines:
- The convenience of GWT-RPC (automatic serialization of pretty much everything)
- JSON data format
- Simple verb-based service APIs
- Benefits of the command pattern such as request caching and batching
- Minimal boilerplate
RestyGWT delivers on all counts.
The interface is actually simpler than GWT-RPC because you don’t have to define a synchronous and asynchronous method, only async (although you can reuse the server interface and call it using DirectRestService if it helps you sleep better). Here’s a sample interface using RestyGWT. This is a generic CRUD API which I simply extend for each entity class.
public interface RestApi<T> extends RestService { @GET @Path("own") public void getForOwner(MethodCallback<T> callback); @GET @Path("get") public void get(@QueryParam("id")Long id, MethodCallback<T> callback); @GET @Path("all") public void listAll(MethodCallback<ListResponse<T>> callback); @POST @Path("save") public void save(T obj, MethodCallback<T> callback); ... }
Here’s a sample service API and the GWT code that calls it:
public class UserPrefsService { private static final UserPrefsRestService service = GWT.create(UserPrefsRestService.class); @Path("/api/userPrefs") public interface UserPrefsRestService extends RestApi<UserPrefs> { // just in case you missed it, all the CRUD methods are inherited } private UserPrefs prefs; public void loadUserPrefs() { service.getForOwner(new AppCallback<UserPrefs>() { @Override public void handleSuccess(UserPrefs result) { prefs = result; App.getEventBus().fireEvent(new UserPrefsLoadedEvent(prefs)); } }); } }
I really like the benefits of the Command pattern from the old gwt-dispatch framework such as the ability to do caching and queuing centrally. The downside of the Command pattern is the proliferation of classes associated with turning every API method into two or more classes (in the case of gwt-dispatch, an Action and Result class for every service method). GWT’s RequestFactory (the GWT team’s “final answer” to RPC after GWT-RPC and deRPC) used generators to remove some of the boilerplate, but I wasn’t in love with having to call someSingleton.getRequestFactory().someRequest().create(BeanProxy.class) each and every time I wanted a new Bean. And it uses a proprietary data format, not REST+JSON.
This is where RestyGWT really shines. It lets you define simple API interfaces using methods, but uses a Command pattern dispatcher under the covers with an ingenious filter mechanism for adding capabilities like XSRF protection, caching, and retries. The magic is possible thanks to RestyGWT’s generator classes which transform your annotated service methods into MethodRequest classes that get sent by a dispatcher. To add caching capability to all your APIs, for example, just put this in your GWT onModuleLoad():
DispatcherFactory factory = new DispatcherFactory(); org.fusesource.restygwt.client.Defaults.setDispatcher(factory.cachingDispatcher());
For a look at some of the other dispatcher options, have a look at the RestyGWT User Guide and the example DispatchFactory class.
Tips for success with Jersey + RestyGWT
Just a couple final notes to ease your migration to Jersey + RestyGWT.
If you’re using RestyGWT with Jersey + Jackson on the server, you’ll want to set the default date format to avoid Date serialization errors. Put this in your GWT’s onModuleLoad():
Defaults.setDateFormat(null);
Similarly, recent versions of Jersey complain about “self-referencing cycles” or some such with Objectify’s Ref properties, so you’ll probably want to annotate those in your server-side entities with @JsonIgnore. If you need the id of a related object, use a differently named getter instead. Jersey will create a corresponding JSON property. Example of an Objectified entity on the server:
package com.my.server.domain; @Entity public class UserPrefs { @Id private long id; @JsonIgnore private Ref<User> ownerKey; ... public long getOwnerId() { return ownerKey.get().getId(); } }
Then on the client you can reference the ownerId property:
package com.my.client.domain; public class UserPrefs { public Long id; public long ownerId; ... }
I would prefer to use the same object representation on client and server, but RestyGWT makes using DTOs about as painless as possible. The client-side object is a class not an interface, so you can create a new instance anywhere you need it vs. having to GWT.create(BeanProxy.class). I could probably hack Jersey and maybe objectify-appengine to automatically replace all Refs with a long id, but honestly, it’s easy enough just to copy the server entities and replace Refs with long ids on the client.
Also, here’s a pair of classes you may find useful for sending any type of list from server to client. For security purposes, a JSON response should always return a root object, not an array directly. The ListWrapper (server) and ListResponse (client) serve as this root object, having a single field containing the list.
package com.my.server.domain; /** * Wraps a List<T> in a JSON root object. */ public class ListWrapper<T> { private List<T> list; public ListWrapper(List<T> list) { this.list = list; } public List<T> getList() { return list; } }
Use it like this in one of your Jersey-annotated methods:
@Path("all") @GET public ListWrapper<Subscription> findAll() { User user = AuthFilter.getUser(); List<Subscription> userAll = this.listByOwner(user); return new ListWrapper<Subscription>(userAll); }
Using ListWrapper this way has the additional advantage of causing Objectify to fetch any lazily loaded properties such as Refs used in the getOwnerId() method above while producing the JSON. If you were to return the List directly, this would not occur!
Here’s the corresponding representation on the client:
package com.my.client.domain; public class ListResponse<T> { public List<T> list; }
See the RestApi example earlier for how it’s used.
Summary
Jersey and RestyGWT in combination make a powerful and easy way to create a REST API for your GWT client and mobile apps, combining all the benefits of a request dispatcher with straightforward service interfaces and minimal boilerplate.
Happy coding!