Last week, I posted a first article on GWT 2.1.1 RequestFactory, followed a few days later by one on GWT 2.1 Editors (and, as promised in the former, how to use them with RequestFactory). In this article, I'll deal with 3 topics I also promised to tackle: what changed since 2.1.0, how does RequestFactory compares to GWT-RPC, and finally how does it integrates with JSR-303 Bean Validation (feel free to jump directly your section of interest). But first, let's start with something I forgot to talk about in my previous article!
Chained persistence
When you're asked to retrieve a domain object from your datastore, you should use at most one instance of a given persisted object (e.g. if you're asked twice for the Person with identifier 1, you should return the same instance each time, or at least instances that live in the same context so that changes to one instance will be persisted when saving another instance representing the same entity). This is causing many incomprehensions. Fortunately, when using JPA or JDO, it's as easy as using a request-scoped transaction.
What's new in 2.1.1
RequestFactory saw its first stable release in GWT 2.1.0. In GWT 2.1.1, the server part was totally rewritten, and the client side was slightly reworked as well. But this is all transparent to the developer, and code that compiled and ran with GWT 2.1.0 should still work with GWT 2.1.1. The following will probably only be useful for people who started using RequestFactory in 2.1.0 and want to know which new things can benefit them.
Features GWT 2.1.1 provides are summed up in the release announcement:
- "A service layer, which includes support for non-static service objects": GWT 2.1.1 introduced the
Locator
andServiceLocator
interfaces, which allow you to, respectively, get rid of thefindXxx
static method on entities (along with providing ID and version without necessarily using thus-named domain object properties), and instantiate a service class to be able to use instance methods for services. - "EntityProxy are no longer the only type of proxies you can use on the client-side": GWT 2.1.1 adds the
ValueProxy
interface (and theBaseProxy
interface that's a common parent for bothEntityProxy
andValueProxy
). - "Multiple methods calls on a single request": in GWT 2.1.0 you could only have a single invocation on a give
RequestContext
before youfire()
it. This limitation has simply been removed.
How does RequestFactory compare to GWT-RPC
Contrary to GWT-RPC which shares the same objects on the client and server side, RequestFactory uses proxy interfaces on the client-side (whose implementation is automatically generated). Thus, your real objects stay on the server, and are therefore not subject to the constraint of being translatable to JavaScript (remember having to fight with Hibernate's enhanced collections and lazy-loading until hibernate4gwt and gilead?). That dychotomy also allows a single server-side domain object to be represented by different proxy interfaces on the client-side, each proxy exposing a different set of properties.
The drawback is of course that there's a bit of redundancy (having to re-declare methods on the client-side interface), but on the other hand it brings flexibility and, if you ask me, removing the "must be translatable" constraint is enough to justify it.
It's worth mentionning that the dichotomy actually already existed in GWT-RPC in the service interfaces (synchronous, implemented by your RemoteServiceServlet
, and asynchronous, used on the client-side).
Despite removing the "must be translatable" constraint, RequestFactory comes with a number of limitations regarding the types that can be transported: only primitive types and their boxed classes, enums, strings, dates (java.util.Date
), BigInteger
and BigDecimal
, other proxies, and finally java.util.List
and java.util.Set
of any of the above, can be used. RequestFactory doesn't (yet!) support polymorphism (which means you have to use the exact classes I listed above; you cannot for instance declare a property of type java.util.SortedSet
or java.sql.Timestamp
).
However, because of those limitations, RequestFactory does not use serialization policies computed at compile-time, which means that your client and server code are much more loosely coupled than with GWT-RPC. With RequestFactory, provided the changes were made in a compatible way, you can independently deploy your client and server code, including using DevMode in -noserver
mode against an already deployed application.
Actually, RequestFactory has all the advantages of De-RPC:
- the protocol is JSON-based, which makes deserialization very fast on the browser.
- client and server can use slightly different (but compatible) objects and still communicate (this was probably inspired by Protocol Buffers), which helps with hot-deployment.
- you don't have to compile and deploy your app each time you change a service or domain object to be able to debug using DevMode in the
-noserver
case.
…and it works on Google AppEngine.
Finally, although the API and concepts are highly different, RequestFactory and its new (in 2.1.1) support for ValueProxy
makes it possible to use it as a general-purpose RPC mechanism, at least to aid in migrating from GWT-RPC, and it's expected that De-RPC will die in favor of RequestFactory (don't worry, GWT-RPC will still be supported, we're only talking here about De-RPC, which never leaved the experimental state).
JSR-303 Bean Validation
When receiving a request to process, the RequestFactoryServlet
applies the operations on the domain objects and validates them before processing any invocation. The default behavior for that validation is to use javax.validation
, and that's why you might see an "Unable to initialize a JSR 303 Bean Validator
" error in your server logs if you do not have a JSR 303 implementation (such as Hibernate Validator) in your classpath, and why you must have the validation API in your classpath for RequestFactory to work, to begin with.
ConstraintViolation
s are reported on the client side as a set of Violation
s to the onViolation()
method of Receiver
s. Because validation is done before invocations are processed, all Receiver
s for a given RequestContext
will receive the violations, and the default implementation of onViolation()
is to call onFailure()
with a fatal ServerFailure
, which will by result in a RuntimeException
.
What's next?
In the next article, I expect to write about the ServiceLayerDecorator
s, that is, the internals of the RequestFactoryServlet
, where you can plug your own code to tweak a few things.