Grails Remoting with Hessian, Burlap and HttpInvoker
A short while ago, a collegue and I decided to write an application with Grails and Eclipse RCP. We choose Grails for the ease of development, and Eclipse RCP (in favor of Flex and Plain Old HTML) because we wanted to give our users a solid and native look and feel, for which Eclipse RCP works really well.
Since the Eclipse RCP front-end would actually be a remote front-end, we needed some kind of kind of communication between the client and the server. One of our first idea's was to use XML-RPC, which is pretty well supported in Grails, but it would force us to to do some mapping between our domain and the XML. Since we wanted to use the same domain classes is Eclipse as in Grails (by exporting the Grails domain to external domain jars), we opted for a different approach: Burlap/Hessian.
Burlap / Hessian
Burlap and Hessian are two protocols developed by Caucho. They serve the same purpose, but Burlap is XML, while Hessian is binary. While there is actually no reason the use Burlap, since Hessian is much smaller and thus faster, we choose Burlap, since it's easier to debug (it's plain XML after all). Okay, so there is a reason to pick Burlap after all..
In our Eclipse RCP application, we could use Spring, which has out of the box support for Hessian and Burlap. In Grails, we could use the Grails Remoting plugin, which also has support for Burlap and Hessian (and HttpInvoker and RMI, but more about that later). So, we just needed a little bit of configuration, connect them to each other, and it would work without any problems. Right? Right! In theory, it should work out of the box. In practice, there was Hibernate. And Proxies. And also some bugs....
Grails uses Hibernate. That's not a problem: in fact, it's great! Why re-invent the wheel when there's a powerful framework available? However, there are some issues when using Hibernate in combination with remoting. This is not Grails specific, but it did occur in our application, and since our application was a Grails application, it troubled us! The problem when using Grails, however, is that you might not suspect that all the collections you use are actually Hibernate collections. Normally, in a web application, it doesn't matter: Hibernate collections work like any other collection. When doing remoting, however, things change a little...
ClassNotFoundException for Hibernate Lists
Because you're using Hibernate, and you use remoting, you'll need Hibernate on the client side, because of the Hibernate Collections. Either that, or make copies of the objects yourself. While you're at it, you can make DTO's for it, which provide additional benefits. However, this does impose a lot of extra work (manually copying, extra classes to maintain, etc), so we'd rather not do this.
Lazy initialization exception / Proxies
A simple solution for the above is ofcourse to add to libraries on the client. Maybe not the best solution, but it's a solution anyway. However, when you do have the libraries at the client, you might run into Lazy Initialization Exceptions, because of Hibernate proxies. The easiest way to solve this is to agree on what to fetch, eagerly fetch all entities, and access only those relationships you agreed upon. There is (ofcourse!) a small problem with this approach: by default, Hibernate outer joins fetches 'only' 3 levels deep, but I needed 4. The recommended values are in between 0 and 3, but there isn't a lot of data to transfer, so I've set the
hibernate.max_fetch_depth property in Grails to 4. This solved a lot! But, unfortunately, not everything...
As it seems, there are a lot of people using Grails to create web applications, but few of them use Grails for anything else, like remoting. There wasn't a lot of information to be found, except for the Wiki, and there was a slight problem: it didn't work. After 2 days of programming, I created more than 5 Jira issues on the remoting subject (see for example here, , or here. Most of the issues were blockers for Hessian and Burlap, or created a lot of noice hiding the real problem. So, after a couple of days without much progress, we had to talk..
After 2 days of remoting issues, we were at a T-junction, were we could either go left or right. Left would mean: look for completely alternative ways to do remoting, like XML-RPC or SOAP, and right would mean: investigate into our 2 alternative remoting options: RMI and HttpInvoker. After some discussion, we decided to give HttpInvoker a last chance: RMI is not firewall friendly, and XML-RPC/SOAP would mean a lot of extra work. And finally, we got some succes!
HttpInvoker allowed us to pass our tests! This meant it would be possible to retrieve a single object, a list of objects, and send information to the server! This worked great, all our tested passed! And we lived happily ever after.... or didn't we? I forgot to implement 1 more test: storing a complex object (like a Project class containing two Strings). Something with a ClassNotFoundException, even though we knew our classes were there...*sigh*. As a workaround, we didn't send any complex data, but only Strings, int, etc. I also created this issue here. While our application pretty much worked, it still had the complex object issue, which just didn't feel right. And after a week, our hero arrived: Peter Ledbrook made a patch for the issue, which worked immediately, which was really great!!
Despite all of our problems with Grails, the bugs, the remoting, with everything, it was a great learning experience. When all the bugs are fixed, Grails will make it's promise to be a high-productivity framework really come true. There's really no configuration needed, and when it works, it's great. I would certainly use something like HttpInvoker to do future remoting. It (at the moment) worked the best, can easily pass firewalls and is well supported (in Spring as well as in Grails). And above all, even though there was some frustration with the remoting plugin, it was really fun to develop the application!
While writing this blog, I got some status updates on the rest of the issues, which seems all to be fixed right now! A new version of the plugin is even released! This part of the power of the Grails Community: no framework is 100% correct or bugfree, but it's great that there are people like Peter Ledbrook who care to make Grails even better. Thanks again Peter, your support is great!