Friday, April 24, 2009

GWT RPC over arbitrary transports: Uber RPC

A frequent request that pops up in the GWT groups is how to run GWT RPC over non-XHR transport mechanisms, like script-tag injection, cross-domain POSTs, or OpenSocial Gadget's makeRequest().

There are a few ways to do this, including patching GWT itself, but the path of least resistance would be a module that you could inherit which would provide this functionality, without compromising or changing your client code in any way.

Step 1: Overwrite GWT ServiceInterfaceProxyGenerator


In order to handle alternate transport mechanisms, we need to take over generation of the RPC client stub that is created by the builtin generators, to do this, we add the following entries to a module file:

class="com.google.gwt.user.rebind.rpc.UberServiceInterfaceProxyGenerator">
class="com.google.gwt.user.client.rpc.RemoteService"/>


This tells GWT to invoke our UberServiceIntefaceProxyGenerator whenever someone calls GWT.create(RemoteService.class). There are some package-protected methods we need access to, so we arrange for our class to be in the com.google.gwt.user.rebind.rpc package. Next, we want to modify the generator to allow substitution of arbitrary client stub superclasses.

<define-property name="gwt.rpc.proxySuperclass"
values="org_timepedia_uberrpc_client_RpcServiceProxy"/>
<set-property name="gwt.rpc.proxySuperclass"
value="org_timepedia_uberrpc_client_RpcServiceProxy"/>

This construct is used to pass a compile time parameter to the generator as to which class will be used as a client stub, org.timepedia.uberrpc.client.RpcServiceProxy. Here is our proxy generator source, which exists merely to redirect to UberProxyCreator

public class UberServiceInterfaceProxyGenerator extends Generator {

@Override
public String generate(TreeLogger logger, GeneratorContext ctx,
String requestedClass) throws UnableToCompleteException {

logger.log(TreeLogger.WARN, "Running UberProxyCreator", null);

TypeOracle typeOracle = ctx.getTypeOracle();
assert (typeOracle != null);

JClassType remoteService = typeOracle.findType(requestedClass);
if (remoteService == null) {
logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
+ requestedClass + "'", null);
throw new UnableToCompleteException();
}

if (remoteService.isInterface() == null) {
logger.log(TreeLogger.ERROR, remoteService.getQualifiedSourceName()
+ " is not an interface", null);
throw new UnableToCompleteException();
}

UberProxyCreator proxyCreator = new UberProxyCreator(remoteService);

TreeLogger proxyLogger = logger.branch(TreeLogger.DEBUG,
"Generating client proxy for remote service interface '"
+ remoteService.getQualifiedSourceName() + "'", null);

return proxyCreator.create(proxyLogger, ctx);
}
}

Step 2; Override the superclass of the generated client proxy stub


This source was mostly copied unchanged from the original, except for the line which calls UberProxyCreator. The bulk of the work is done there. Again, I copied the source, but made just a few changes to the routine which creates the SourceWriter

private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx,
JClassType serviceAsync) {
JPackage serviceIntfPkg = serviceAsync.getPackage();
String packageName = serviceIntfPkg == null ? "" : serviceIntfPkg.getName();
PrintWriter printWriter = ctx
.tryCreate(logger, packageName, getProxySimpleName());
if (printWriter == null) {
return null;
}

ClassSourceFileComposerFactory composerFactory
= new ClassSourceFileComposerFactory(packageName, getProxySimpleName());

String[] imports = new String[]{RemoteServiceProxy.class.getCanonicalName(),
ClientSerializationStreamWriter.class.getCanonicalName(),
GWT.class.getCanonicalName(), ResponseReader.class.getCanonicalName(),
SerializationException.class.getCanonicalName()};
for (String imp : imports) {
composerFactory.addImport(imp);
}

String rpcSuper = null;
try {
// retrieve user-defined superclass from module file
rpcSuper = ctx.getPropertyOracle()
.getPropertyValue(logger, "gwt.rpc.proxySuperclass");
if (rpcSuper != null) {
rpcSuper = rpcSuper.replaceAll("_", ".");
}
} catch (Exception e) {
}

// allow defining a custom superclass to customize the RPC implementation
composerFactory.setSuperclass(rpcSuper);
composerFactory.addImplementedInterface(
serviceAsync.getErasedType().getQualifiedSourceName());

composerFactory.addImplementedInterface(
serviceAsync.getErasedType().getQualifiedSourceName());

return composerFactory.createSourceWriter(ctx, printWriter);
}

Step 3: Write your own Proxy


This is where you come in, since you have to decide how you're going to transport the RPC payload, such as putting it as a URL GET parameter, a POST parameter, or using an OpenSocial container. Here is some skeleton code showing you how to override the doInvoke method. This example is pseudo-code for how you'd do it using gadgets.io.makeRequest() in an OpenSocial container.

public class RpcServiceProxy extends RemoteServiceProxy {

protected GadgetRpcServiceProxy(String moduleBaseURL,
String remoteServiceRelativePath, String serializationPolicyName,
Serializer serializer) {
super(moduleBaseURL, remoteServiceRelativePath, serializationPolicyName,
serializer);
}

static boolean isReturnValue(String encodedResponse) {
return encodedResponse.startsWith("//OK");
}

/**
* Return true if the encoded response contains a checked
* exception that was thrown by the method invocation.
*
* @param encodedResponse
* @return true if the encoded response contains a checked
* exception that was thrown by the method invocation
*/
static boolean isThrownException(String encodedResponse) {
return encodedResponse.startsWith("//EX");
}

public static final String RPC_PAYLOAD_PARAM="rpcpayload";

@Override
protected Request doInvoke(
final RequestCallbackAdapter.ResponseReader responseReader, String methodName, int invocationCount,
String requestData, final AsyncCallback tAsyncCallback) {

try {
makeRequest(getServiceEntryPoint(), "text/x-gwt-rpc; charset=utf-8", requestData, new AsyncCallback() {
public void onFailure(Throwable throwable) {
tAsyncCallback.onFailure(new InvocationException("Unable to initiate the asynchronous service invocation -- check the network connection"));
}

public void onSuccess(String encodedResponse) {
try {
if(isReturnValue(encodedResponse)) {
tAsyncCallback.onSuccess((T) responseReader.read(createStreamReader(encodedResponse)));
}
else if(isThrownException(encodedResponse)) {
tAsyncCallback.onFailure((Throwable)responseReader.read(createStreamReader(encodedResponse)));

}
else {
tAsyncCallback.onFailure(new InvocationException("Unknown return value type"));
}
} catch (SerializationException e) {
tAsyncCallback.onFailure(new InvocationException("Failure deserializing object "+e));
}
}
});
} catch (Exception ex) {
InvocationException iex = new InvocationException(
"Unable to initiate the asynchronous service invocation -- check the network connection",
ex);
tAsyncCallback.onFailure(iex);
} finally {
if (RemoteServiceProxy.isStatsAvailable()
&& RemoteServiceProxy.stats(RemoteServiceProxy.bytesStat(methodName,
invocationCount, requestData.length(), "requestSent"))) {
}
}
return null;
}

private native void makeRequest(String serviceEntryPoint, String contentType,
String requestData, AsyncCallback tAsyncCallback) /*-{
var params = {};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;

params[gadgets.io.RequestParameters.AUTHORIZATION]=gadgets.io.AuthorizationType.SIGNED;
params[gadgets.io.RequestParameters.METHOD]=gadgets.io.MethodType.GET;

gadgets.io.makeRequest(serviceEntryPoint+"?"+@org.timepedia.uberrpc.client.UberRpcServiceProxy::RPC_PAYLOAD_PARAM+"="+encodeURIComponent(requestData), function(resp) {
if(resp.errors && resp.errors.length > 0) {
tAsyncCallback.@com.google.gwt.user.client.rpc.AsyncCallback::onFailure(Ljava/lang/Throwable;)(null)
}
else {
tAsyncCallback.@com.google.gwt.user.client.rpc.AsyncCallback::onSuccess(Ljava/lang/Object;)(resp.text);
}
}, params);
}-*/;
}

Step 4: Modify RemoteServiceServlet


The last step is to modify RemoteServiceServlet so that it understands the new transport formats you've devised. Here's an example of one that would handle the incoming OpenSocial makeRequest(). This one handles GET or POST requests with the incoming payload as a form parameter.

public class GadgetServiceServlet extends RemoteServiceServlet {

@Override
protected void doGet(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws ServletException, IOException {
doPost(httpServletRequest, httpServletResponse);
}

@Override
protected String readContent(HttpServletRequest httpServletRequest)
throws ServletException, IOException {
String str = httpServletRequest.getMethod().equals("POST") ? RPCServletUtils
.readContentAsUtf8(httpServletRequest, false) : httpServletRequest
.getParameter(RpcServiceProxy.RPC_PAYLOAD_PARAM);
String ustr = URLDecoder.decode(str);
return ustr;
}
}

To use, you'd just subclass this servlet.

The Sky's the Limit


Once you get to this point, you can start imagining all of the cool things you can do. Cross-domain POSTs using the window.name trick. OAuth signed RPC requests, verified in the servlet, done by OpenSocial containers. FaceBook integration. RPC over JSON, Protocol Buffers, Thrift. I'm trying to cobble together some of this stuff for a future UberRPC module, but due to talks I'm giving at the upcoming Google I/O, I'm a little too swamped to make them release worthy at this point.

Much credit goes to Alex Epshteyn for his original proposal on the GWT Contributors list, which I picked up (over an inferior method I had been using to make Gadgets work), and integrated into a more graceful override of the default RPC behavior.

If you're going to Google I/O, I will be doing two talks this year. One on Progressive Enhancement using GWT and a new library I've written, and a second on Building an Application on Google's Open Stack, which is a walkthrough of a sophisticated GWT app which leverages about a dozen Google APIs.

-Ray

Tuesday, April 7, 2009

Small update to GWT Exporter

I recently discovered a pretty critical bug in GWT Exporter that can cause an infinite loop doing export. This was fixed in version 2.06 which available in the trunk and maven repository.

-Ray

Google AppEngine and GWT now a marriage made in heaven

The announcement that Google AppEngine now supports Java is incredible news. Not just because it opens the doors to running arbitrary JVM languages like Scala, JRuby, PHP, etc on AppEngine, but because of the ability to wire up Java on the client, and Java on the server, through Google Web Toolkit. You can use all of your familiar Java tools for editing, debugging, testing, and packaging.

With the new system, you can write a POJO, add JPA or JDO annotations, and write server-side logic to persist these POJOs in either a RDBMS like MySQL, or in BigTable/AppEngine. Moreover, you can export your DAO or logic interfaces through GWT RPC, and call them directly from the client, seamlessly, and painlessly.

Almost Painlessly


The one hitch you'll encounter as a GWT developer is trying to serialize or deserialize persistence capable types. This is nothing new for GWT developers who have tried this with Hibernate before, and there are workarounds such as Hibernate4GWT. This problem occurs because the persistence classes are enhanced with an extra field to hold state which enables them to work when detached from the persistence context. GWT RPC computes its own CRC based on the fields of a class in order to ensure compatibility between server and client and the extra field causes problems.

In general, when it comes to sending serialized ORM POJOs down the wire, I think it's a risky practice, because you're likely to pull in a lot more of the reachable object tree than you bargained for unless you're careful. A better approach might be to use DTOs based on ProtocolBuffers.

However, it is sometimes nice to do it if your POJOs are relatively flat and you want to rapidly prototype. If your insist on using your ORM'ed POJOs over RPC, there is a trick to making it work.

Making JDO/JPA enhanced classes work over GWT RPC


The first step to making things work is to disable detachable objects and tag your class as serializable.

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "false")
public class MyPojo implements Serializable {

}

This does two things. First, it tells the persistence engine that you'll be managing object identity, usually through a primary key, and secondly, when your POJO is accessed outside of a transaction/session, you want it to be transient not detached. A detached object remembers where it came from, so that after modifications, it can be reattached and merged back into the datastore. A transient object forgets that it once came from the datastore, and thus if you try to repersist it, you'll end up inserting a copy.

This has a major downside in terms of ease of use, but it does prevent the enhancer from injecting hidden fields into your class to manage detached state, and it is these hidden fields which break GWT RPC compatibility.

But I don't want copies!


In using transient objects, you'll break a desired design pattern, which is to fetch an object through RPC, modify its properties, and send the same object back through RPC to be merged into the datastore. A quick and dirty work around is to use reflection to lookup an attached object on the server, copy all of the persistent fields from the transient object, and then merge the persistent object. Here's a prototype class that does this (but not recursively, so it doesn't handle anything but primitive fields):

public class PersistenceHelper {
public static Object findPrimaryKey(T tInstance) {
if (tInstance == null) {
return null;
}
for (Field l : tInstance.getClass().getDeclaredFields()) {
if (l.getAnnotation(PrimaryKey.class) != null
|| l.getAnnotation(Id.class) != null) {
l.setAccessible(true);
try {
return l.get(tInstance);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
return new IllegalArgumentException(
"Class " + tInstance.getClass().getName()
+ " does not have a method called getId()");
}

public static void copyPersistentFields(Object entity, T tInstance)
throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
for (Method f : tInstance.getClass().getMethods()) {
if (f.getName().startsWith("set") && Character
.isUpperCase(f.getName().charAt(3))) {
f.setAccessible(true);
Method getter = tInstance.getClass()
.getMethod("get" + f.getName().substring(3));
getter.setAccessible(true);
f.invoke(entity, getter.invoke(tInstance));
}
}
}
}


The way you'd typically use this is as follows:

public T mergeTransient(T tInstance) {
EntityManager e = em.get();
if(e.contains(tInstance)) {
e.persist(tInstance);
return tInstance;
} else {
Object primaryKey = PersistenceHelper.findPrimaryKey(tInstance);
if(primaryKey != null) {
Object entity = e.find(tInstance.getClass(), primaryKey);
if(entity == null) {
e.persist(tInstance);
return tInstance;
}
else {
try {
PersistenceHelper.copyPersistentFields(entity, tInstance);
} catch (IllegalAccessException e1) {
e1.printStackTrace();
throw new IllegalArgumentException("Can't copy fields from transient class to persistent class.");
} catch (NoSuchMethodException e1) {
throw new IllegalArgumentException("Can't copy fields from transient class to persistent class.");
} catch (InvocationTargetException e1) {
throw new IllegalArgumentException("Can't copy fields from transient class to persistent class.");
}
e.persist(entity);
return (T) entity;
}
} else {
// primary key may be null, assume insert
e.persist(tInstance);
return tInstance;
}
}
}

Less than ideal


After experimenting with this pattern, I've come to the conclusion that although it works, I don't feel warm and cozy serializing instances out of the datastore, I like to have full control over what I'm sending down to the client so I can optimize for size and speed. However, I don't want to write boilerplate sychronization code for DTOs. In a later article, I'll detail a pattern for using ProtocolBuffers with GWT and a DSL for terse/concise manipulation of them.

It's still awesome


Even though there are some issue surrounding using RPC seamlessly with the Datastore ORM later, it completely trumps the time saved not having to do ANY configuration AT ALL to deploy an application. Words cannot describe how much of a time saver this is. No messing around with apt-get. No editing Apache configs. No Setting up log rotation and archiving. No dealing with backups. No bother with figuring out the right way to shard your db for your expected growth. No need to harden your own machines and firewalls. No need to provision anything but the application ID.

To be sure, there are still things you can't do on AppEngine. I can't run JNI. I can't launch threads. I can't use Java2D/JAI/JavaSound. And probably most relevant, I can't host long-running comet sessions. And if you really really need to do those, you can rent a server somewhere to do it.

However, the majority of applications don't use these capabilies, and for these developers, AppEngine is an epic win.

Thursday, April 2, 2009

GWT's type system is more powerful than Java

This may come as a shock to some people. Isn't GWT just Java syntax you say? Yes, it is, and it does not extend the Java grammar in anyway. Yet, it is nonetheless true that GWT is more powerful than Java.

The Overlay Type


The reason why GWT is more powerful is because it is actually a unification of two type systems, Java and Javascript. And while it seems that these are relatively walled off from one another, there is a bridge that unites the two, and that is the GWT Overlay Type system.

The overlay system in essence, permits the 'overlay' or attachment of Java types to Javascript objects. And since you can pass Java objects into Javascript and back, this means you can in fact, overlay types on Java as well.

Categories and Extension Methods


Some languages have a facility called Categories or Extension Methods, modern examples include Objective-C, Groovy, and C#. A category allows you to pretend as if an existing reference implements additional methods, when it actually doesn't.

In reality, they are syntax sugar for invocation of static utility functions. That is:

SomeType x = new SomeType();
SomeTypeUtils.doSomething(x);

becomes

SomeType x = new SomeType();
x.doSomething();

Transparently, behind the scenes, the compiler rewrites the invocation of x.doSomething() into SomeTypeUtils.doSomething(x). GWT's JavaScriptObject overlays are essentially Categories, as the compiler transparently rewrites methods that appear to exist on an Overlay into static method calls on the underlying JavaScriptObject. This is one reason why JSO methods have to be effectively final, as there is no polymorphism allowed.

This all sounds very interesting and theoretical, but what's the practical benefit?

Type-Safe Enumerations with Zero Overhead


Let's say you want to write a method that can set the display property of an element to one of the legal values, and only the legal values. Traditional approaches would include using a Java enum and writing a setter method that accepts only this type:

enum DisplayType {
BLOCK, INLINE, TABLE, NONE;
}

public static void setDisplay(Element e, DisplayType t) {
e.getStyle().setProperty("display", t.name().toLowerCase());
}

Unfortunately, this will generate a lot of bloat, since each enum value is a class, the class must be initialized by a static initializer, and the ultimate CSS property value string has to be obtained through method calls that might not inline because of polymorphic dispatch.

Think about what we want here. Don't we really just want to create a subclass of java.lang.String, create a bunch of this String subclass constants, and write a method to accept that type? Unfortunately, java.lang.String is final. You can't subclass it in Java.

But you can in GWT, and I'll show you how!

Turn a String into a JSO



public class DisplayType extends JavaScriptObject {
protected DisplayType() {}
public native static DisplayType make(String str) /*-{
return str;
}-*/;

public native String value() /*-{
return this;
}-*/;
}

For brevity, I left out the extra code to support Hosted Mode. In hosted mode, you must wrap the 'str' argument in an array, e.g. [str] and fetch it using return this[0], but that's not important. What this code is effectively doing is casting a String into a DisplayType and imposing an additional categorical method on this reference, which is value(). Keep in mind, the value() is never actually attached to the prototype of the underlying Javascript object.

To use,

DisplayType NONE = DisplayType.make("none");
DisplayType BLOCK = DisplayType.make("block");
DisplayType INLINE = DisplayType.make("inline");
DisplayType TABLE = DisplayType.make("TABLE");

Now, what is the output of the GWT compiler when compiling element.getStyle().setProperty("display", BLOCK.value())? Here it is:

element.style['display']='block';

In fact, even calling the setDisplay() method with various DisplayType enums results in inlined assignments to the element.style property with no method calls!

Adding methods to Numbers


In Groovy, Scala, and some languages, you can even add methods to primitive integers. Using GWT overlay types, you can even do this!

public class Int extends JavaScriptObject {
protected Int() {}
public static native Int make(int x) /*-{
return x;
}-*/;

final public native int value() /*-{
return this;
}-*/;

final public Int square() {
return make(value() * value());
}
}

int val = (int) Duration.currentTimeMillis();
Int x = Int.make(val);
Int sq = x.square();
Window.alert(String.valueOf(sq.value()));

And what do you think the generated code looks like? Try this:

val = (new Date()).getTime();
x = val;
sq = x * x;
$wnd.alert('' + sq);

There you have it. I successfully added a method to a primitive 'int' called square() without using a wrapper, and with no overhead whatsoever. This opens the doors to implementing primitive 'wrappers' for GWT for int, double, float, etc, that are not wrappers at all and have no overhead, which would be very useful in many circumstances where Integer, Double, Float, in java.lang are too heavy.

So, can we conclude from this that GWT is more powerful than Java? ;-)

-Ray