Updated 2011-02-10: I was wrong about optimistic locking. It was there in early versions of RequestFactory but never shipped in GA releases.
RequestFactory was already available in GWT 2.1 but really becomes usable starting with GWT 2.1.1.
RequestFactory is "an alternative to GWT-RPC for creating data-oriented services." It's been initially designed for CRUD on entities (domain objects with an identifier and version), but as of GWT 2.1.1 can actually also be used as a general-purpose RPC mechanism.
With RequestFactory, you have a set of domain objects and services on the server-side, and proxy and stub interfaces on the client-side, using annotations and naming conventions to map ones to the others. Note that a single server-side object can have several different mappings on the client-side.
Whichever layout you have on the server-side, RequestFactory clearly separates domain objects (that only have properties) from services (methods acting on those objects) on the client-side: there's not necessarily a 1:1 mapping between your client-side proxies and service stub interfaces and your server-side domain objects and services.
There are two kind of domain objects: entities and value objects. The difference is that entities have an identifier (and RequestFactory also mandates that they have a version, more on this later), and then RequestFactory can be used to update an existing object; while value objects are just that: values with properties, with no particular identity. Each one maps to a distinct base interface on the client-side:
ValueProxy, respectively. Apart from that, all a (client-side) proxy is allowed to have are property accessors (getters and setters).
The crux of RequestFactory, compared to classical RPC, are entities. On the client-side, RequestFactory will monitor your calls to property setters and only send a diff to the server, where the setters will be re-applied. This is why having an identifier and version are so important: the identifier allows RequestFactory to load the object so it can apply the diff to it, and the version is used to detect changes and fire update events (see below). When working with objects having many properties, and/or with many such objecs, this can significantly reduce the amount of data that goes over the wire (which also means RequestFactory is well-suite for mobile devices), while on the other hand, value objects are always sent with all their properties. Last, but not least, events will be dispatched on the client-side each time a domain object is persisted, updated or deleted on the server (it obviously only concerns entities, not value objects, and only those that have been sent in or returned from a given request).
Services are methods. A method can take as argument and/or return domain objects (entities or value objects) or simple values. Most common such methods are those that implement CRUD on entities, but if you only use value objects and simple values you have a general-purpose RPC mechanism.
RequestFactory supports many use cases, as a service method can be (on the server-side) either a static method, an instance method on a service object, or an instance method on a domain object.
Putting it all together
Keep in mind that RequestFactory makes use of code generation, so you'll always start (in your client code) by
GWT.create()ing a class, or rather in this case, an interface. This interface has to extend
RequestFactory, and define no-args methods that return service stubs. Those method are factories: each call will return a new instance (hence the name of the interface:
A service stub is an interface extending
RequestContext and defining service methods. The interface has to be annotated with
@ServiceName to map it to a server-side class. Those service methods have the same signature as their server-side counter-part, with the exception of the return type: it will be either
Request<T> if the method lives in the server-side class referenced by the
@ServiceName annotation, or an
InstanceRequest<P,T> if the method is on a domain object (of the type mapped to
P on the server-side). In any case, the
T type is the actual return type of the method. If you have instance methods on the server-side service object, you have to provide a
ServiceLocator class in the
@ServiceName annotation; RequestFactory will instantiate that class to then ask it for an intance of the service class; otherwise, all methods declared as
Request<T> will have to be static methods on the server-side class. As for the difference between
@ServiceName, the former uses class literals that have to be available at compile-time while the latter uses class names; if you use the
@Service interface, the
RequestFactoryGenerator will automatically validate that the declared client-side methods actually map to existing server-side methods.
RequestFactoryGenerator has found the service stub interfaces by examining the methods of the
RequestFactory root interface, and it'll similarly find the domain objects' proxy interfaces looking at the arguments and return types of the service methods.
A proxy is an interface that extends either
EntityProxy for an entity, or
ValueProxy for a value object, and define accessors for the object's properties (you don't have to provide a getter/setter pair for each property, you can have a getter without a setter; you however cannot have a setter without an associated getter). The interface has to be annotated with
@ProxyForName to map it to a server-side class (and similarly, the
RequestFactoryGenerator will validate at compile-time that declared property accessors actually map to existing server-side peers if you use the
@ProxyFor annotation, but won't if you use the
For entities, RequestFactory will have to load them by their identifier (so it can apply the diffs sent from the client before landing them to the service methods). To do so, it will either ask a
Locator (that it will first instantiate, of course) or fallback to using a static method named
Xxx is the name of the entity class). The
Locator will also provide the entity's identifier and version, defaulting
getVersion accessors respectively.
All those interfaces have to be available to your client code, and also compiled and deployed to your server, as RequestFactory will need the
@ProxyFor et al. at runtime (also note that the validation that is performed at compile-time if you use @Service and
@ProxyFor will be done at runtime with all four annotations). For this reason, they're often placed in a sub-package called shared.
OK, but how to use it now?
The first thing to do is to declare a servlet mapping for the
RequestFactoryServlet at path
/gwtRequest (this is the default, it can be changed). That's all you have to do on the server side.
On the client side, you'll start by
RequestFactory root interface, and then immediately
initialize() it before you can use it. The
initialize() method expects an
EventBus where the
EntityProxyChange events will be dispatched, and an optional
RequestTransport. If the
RequestTransport is omitted, a
DefaultTransport will be used, with the default servlet path of
GWT.getHostPageBaseURL() + "gwtServlet" (in other words, this is where you have a chance of changing those default values).
Next you'll create a
RequestContext (service stub) by calling one of the methods you defined on the
RequestFactory root interface. As for the
RequestFactory interface, the name
RequestContext is not innocent: a
RequestContext is a kind of builder for your request, this is where you'll queue entity property setters and service method calls. It represents the context in which things are done before you send them all in one bach request to the server.
On that context, as I said above, you'll be able to queue entity property setter calls. For that, you first have to
edit() a proxy, as these are immutable by default (this holds true for value objects too, even though their property setters are not monitored like entities' ones). A proxy can only be edited by one
RequestContext at a time; it however doesn't matter how many time you call
edit() on the same proxy. Note that
edit() doesn't update the given proxy in-place, it instead returns a new object. If you want to create a new proxy instead of editing an existing one, you can use the
create() method. The returned object is mutable so you don't have to
edit() it before you modify it.
You'll also be able to queue service method calls. To do that, you'll simply call the method on the service stub, passing the appropriate arguments. If you're calling an
InstanceRequest<P,T>, you'll also have to call using() on the returned
InstanceRequest to provide the object instance on which to call the method. You can further refine the invocation using the
with() method which controls which properties of the returned entities will be sent back to the client (this helps reduce the response payload size), and the
to() method where you'll give a
Receiver<T> to actually receive the method's returned value.
When you're done setting up your
fire() it to actually send everything to the server. One thing to remember is that whichever the order you called them, operations (property setter calls on entity proxies) will always be processed before invocations (service method calls). This means you can
create() an object,
save() it right away and then starts modifying it; when processed on the server, the object will first be applied its setters, then the
save() method will be called. However, the relative order of operations and invocations is respected (i.e. if you call
setFoo will be applied before setBar on the server too; and similarly for service method calls).
You can pass a
Receiver<Void> to the
RequestContext.fire() method, and it will be called after the
Receivers for each service method invocation. Note however that its
onFailure will be called only in case of general failure, so in most case only its
onSuccess method will be called, whether the distinct invocations are successful or not.
As a shortcut, you can also call a
fire() directly on a
Request object; and passing a
Receiver to the
fire() method is equivalent to calling
fire() sequentially. I'd only recommend doing this if you have only a single invocation in your
In the following article(s), I'll deal with the changes between GWT 2.1.0 and 2.1.1, how RequestFactory compares to GWT-RPC, JSR 303 validation, integration with the GWT Editor framework,
ServiceLayerDecorators (or how to tweak the server-side processing, for example to integrate nicely with Guice or Spring) and other advanced techniques.