String-based RPC between GWT and PHP
This is an extension of the technique I used in my Google Web Toolkit / PHP communication tutorials. After playing with it more and trying to simplify the code, this is what I have been using.
Most of my applications have been improving forms for existing web sites with PHP back-ends. As such, this technique passes strings with a minimum of encoding. It’s just enough to get data into PHP, interpret it and to allow for returning data to GWT appropriately.
Here is the sample code for this tutorial. The file contains:
- ServerComm.java: Client-side RPC class
- PanelRPC.java: Sample code illustrating recursion of widgets on a panel
- SingleItemRPC.java: Sample code showing an RPC call with a single argument
- ServerComm.php: Server-side RPC class
- UsingRPC.php: Sample PHP corresponding to sample Java files
Java Side
To identify the information to be sent via RPC, the ServerComm class defines an interface ServerComm.ServerData. This interface lists four methods: getName, getValue, onSuccess and onFailure. getName and getValue supply the data which will be passed to the server. onSuccess only gets called when the server returns data for that object. onFailure would be called if the HTTPRequest call fails, although this situation has yet to occur for me.
RPC calls are made via the ServerComm.requestUpdate method. This static method takes a string as the first parameter. The string identifies the server function to call. Prior to making RPC calls, it is necessary to call ServerComm.setURL() with the appropriate URL. Note that changing the URL could cause a queued request to fail. This functionality was primarily to allow modularization of the RPC function rather than switching URLs during operation, so there are no safeguards on that behavior.
The second parameter of requestUpdate can be either a Widget or a ServerComm.ServerData object. If the Widget is a panel, it’s children will be traversed looking for ServerData interfaces. When one is found, its name and value get added to the request and a reference is stored to allow a return value to be passed back. See PanelRPC.java for an illustration of this usage.
If the second parameter to requestUpdate is a ServerData object, its name and value are simply added to the request. See SingleItemRPC.java for an example of this type of call.
PHP Side
The PHP support is implemented as a simple PHP class. I call it as a static class as it is more of a helper function than a rigorous class definition. The class requires an array of defined RPC functions. This array has a format such as:
$HandlerArray = array( “FnName” => Array(’Var1′, ‘Var2′, ‘Var3′), “FnName2″ => Array() );
The FnName’s correspond to the string portion of the Java call to requestUpdate. The array of variable names corresponds to the names returned by the getName function of the ServerData interface in Java. If no names are specified, all values get passed to the handler function. Handler functions need to be defined in PHP and have the same name as the RPC function. The HandlerArray defined above would necessitate two PHP functions: FnName() and FnName2(). Variables are passed as an associative array such as: array(’Var1′ => ‘Val’, … ).
Handler functions return values to the GWT client by returning an associative array similar to the passed argument array. Array values are allowed, i.e. return array( ‘var1′ => array(1,2,3,4,5,6) ) will result in var1.onSuccess() being called with a String[] with the six corresponding strings.
Implementation Notes
I used this type of approach to make it easy to do things like an amortization calculator. This is typically a form with five inputs. If three of the values are known, the final two can be calculated. Which three doesn’t matter. With this sort of RPC call, all five inputs get passed to the server, which can then figure out what to do with them. This can also be applied for server-side data validation. You can pass an entire panel of data, then only return updates to inputs with problems.
In a car-related form, where a VIN needs to be validated and possibly decoded, there are several options. The VIN itself could be passed alone, having an array returned with year/make/model data which the GWT client then writes into the appropriate inputs. Or a panel with all VIN-related inputs can be sent to the server, with each then getting a value back from the server.
Possible Problems
The only specific encoding used in RPC calls is JavaScript’s encodeURIComponent. If encodeURIComponent doesn’t exist for some reason, no encoding is performed. Escape is not an exact replacement, and it seems like a good chance that GWT-supported browsers will also support encodeURIComponent. If the encoding doesn’t happen, RPC success will depend on the data passed; ampersands being the worst case.
There is some asymmetry in data handling in this RPC implementation. There is no explicit support for arrays being passed from the client to the server, although they are supported from server to client (via String array). I had more use for the client receiving arrays than sending them, so that’s what this does.
RPC calls don’t end up passing any status data. An empty response from the server is technically valid. I plan to add something simple like ‘OK’ as the first line of the server response. Then perhaps a list such as ‘ERR,hash:error response’ to allow for throwing exceptions. On the PHP side, if a variable specified in the handler array is not passed from GWT in the RPC call, no errors are generated.
What about JSON or GWT-RPC?
JSON data is a valid payload for this code. It was funny that while JSON gets mentioned as an RPC technique, it doesn’t really support one thing: the RPC part. That sent me on a slight goose chase during initial development, so I wrote the part that was missing.
It would certainly be cool to tie straight in to the GWT RPC mechanism. But not this week.
Conclusion
I’ve tried to keep this simple enough to make sense, yet flexible enough to be useful. I have found it quite useful, and I keep discovering other ways to make use of it. Any links back to the site are appreciated. Feel free to shoot me an email (erik@…) with any questions or feedback.
Thanks to Marcin for telling me about a bug in the attached sources, which I have updated. You may also want to uncomment the DeferredCommand lines in ServerComm.java. I prefer the approach, and only have it uncommented because timers (and therefore DeferredCommand) didn’t work under Win2K.