Friday, February 25, 2011

RIA in Grails using Ext JS

I've been looking for quite some time now for a client-side library that'd allow for creation of rich internet applications (RIA). I've evaluated (however briefly) Flex and Silverlight at first but this is not what I've had in mind. I wanted something that will just work, without any sophisticated plugins that get outdated (Flash) or are not supported on all platforms (Silverlight).

Knowing what Google did to make real applications possible in the browser I've looked at GWT and again this is not what I've had in mind. I dismissed Silverlight and Flash for the reason that I wanted to code in JavaScript and have the whole power of that language at hand with the ability to use CSS and HTML as I please. Coding in Java is, how should I put it, a pain in the a$$. I personally hate this language and there's nothing that will change it. Oh, and btw this also means that all the frameworks that build on GWT (like Vaadin to name just one) fall into the same category.

And then I stumbled upon Ext JS. Man that was a surprise! First the object model baked in into the framework turned out to be perfectly suited for real-life applications. The GUI itself looks just gorgeous! There's a client-side part for direct communication with server (Ext.Direct) and a model for data access (the Ext.data.Store and descendants). But that's not all! There's a JsonStore that can be configured to work with RESTful JSON service!

As you might have guessed I'm using Ext JS with a Grails backend. Those two play so nicely together it's almost to good to be true but to make things even simpler I've created a Grails plugin called json-rest-api that taps into the domain objects and exposes them in a RESTful way for the JsonStore to consume.

Now creating an application with editable grid and all the usual CRUD functionality takes less than 5 minutes!

Check out the plugin here and the two examples here and here. You'll be amazed!

Stay tuned for more examples and tricks on using Ext JS with Grails!

9 comments:

Matthias Hryniszak said...

For those of you that are fixed on programming in Java there's a GWT port of the library called Ext GWT.

Similarly for those that love ASP.NET there's a .NET wrapper around the library called Ext.NET (http://ext.net)

Blazej Bucko said...

There's one problem though... It's 595$ per user if you don't want to publish your application as OpenSource.

Matthias Hryniszak said...

It's not $595 if you take out the support which is not needed if you're brainiac enough IMHO :D

Plush everything else costs money as well (Flex Builder, Expression Blend or even Visual Studio for that matter).

Blazej Bucko said...

That's true. But still... $330 (per seat) is not 0$ :) You can always code without Flex Builder and AFAIK there's free version of VS. And to clarify: I'm not saying that Flex/Silverlight is better. I'm just emphasising that Ext.JS is not free :)

Matthias Hryniszak said...

Rogger that :)

I'd personally argue that "being able to" and "actually doing it" (in the case of free tools for SL and Flex) are 2 different things. But I get your point :)

Whan you can do is to make your app open source and charge for support. This is how lots of companies do things (take PostgreSQL for one).

Plus as far as I can tell every web-enabled system consists of 2 segments: a browser application and server backend. Remember that thanks to the JSONP technology they don't have to be the same app (if you're worried about exposing your business code)...

Hector Villarreal said...

Hey man, first of all, great work on the plugin.

However, we're having some problems... I don't know if its a problem with the plugin or our code...

So here's what we have.

//Class Person

class Person {
static hasMany = [albums:Album]

static expose = 'person'

String name
}

//Class Album

class Album {
static belongsTo = [person:Person]

static expose = 'album'

String name
}

//Bootstrap

def person1 = new Person(name:"Hector")
.addToAlbums(new Album(name:"Tool"))
.addToAlbums(new Album(name:"NIN"))
.save()


now, everything works fine until now.

When I start the server I can access the person domain object easily by doing
http://localhost:8080/test/api/person

but when I try to access the Albums domain class, the plugin freaks out and I get the following stack trace.

2011-04-07 14:09:52,239 [http-8080-1] ERROR errors.GrailsExceptionResolver - Exception occurred when processing request: [GET] /salmon/api/album
Stacktrace follows:
org.codehaus.groovy.grails.web.servlet.mvc.exceptions.ControllerExecutionException: Executing action [list] of controller [org.grails.plugins.rest.JsonRestApiController] in plugin [json-rest-api] caused exception: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Exception in JSONDomainMarshaller
at java.lang.Thread.run(Thread.java:619)
Caused by: org.apache.commons.lang.UnhandledException: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Exception in JSONDomainMarshaller
at org.grails.plugins.rest.JsonRestApiController$_closure1.doCall(JsonRestApiController.groovy:24)
at org.grails.plugins.rest.JsonRestApiController$_closure1.doCall(JsonRestApiController.groovy)
... 1 more
Caused by: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Exception in JSONDomainMarshaller
at org.grails.plugins.rest.JSONDomainMarshaller.marshalObject(JSONDomainMarshaller.groovy:42)
at org.grails.plugins.rest.JSONDomainMarshaller.marshalObject(JSONDomainMarshaller.groovy)
at grails.converters.JSON.value(JSON.java:198)
at grails.converters.JSON.convertAnother(JSON.java:161)
at grails.converters.JSON.value(JSON.java:198)
at grails.converters.JSON.convertAnother(JSON.java:161)
at grails.converters.JSON.value(JSON.java:198)
at grails.converters.JSON.render(JSON.java:133)
... 3 more


Maybe the plugin doesn't handle the belongsTo relationships correctly??

Thanks!

this plugin will save us a bunch of time, we're developing an ExtJS app with a Grails backend as well :)

I appreciate your help.

Matthias Hryniszak said...

First of all thanks for giving this plugin a try!

Up until now there's been no interest in making this plugin support relations of any kind. This means it's not been tested with any relations whatsoever.

I'll have a look at what the options are to implement this case. For now my first idea is to exclude everything that's a relation (which in fact is what this plugin tries to achieve in the first place since it is a one-to-one mapper between a URI and a domain class (with the use of the respective HTTP methods for obvious operations).

Like I said I'll take a look at what the problem is and will let you know.

Hector Villarreal said...

Thanks for your response.

So, we figured that if we set lazy loading to false on the relations, the plugin will work, the problem is that now each time we call the object it will bring with it a whole bunch of information we don't really need.

This brings me to another question.

We're kinda new to the whole RESTful application structure and maybe we're not looking at things in the correct way.

So, you mention that this plugin is designed to act upon a single object with no relations... would it be a good idea to create a "composite" domain object specific for a datatable (where each of its columns would be an attribute in the domain object)?

Basically, the composite domain object will include attributes from several different domain objects that already exist in our model and just relate to them internally.

Do you think this is a good idea or would you recommend some other technique?

Thanks again!

Matthias Hryniszak said...

That's actually a very good idea! And in fact it "should be" already possible with the api static. Granted, the "api" thingy is a read-only feature right now but there's nothing standing in the way to extend it for read/write operations.

What I'd advice against is to for example send addresses with people as embedded objects. In fact it'd be great to have URLs like /person/1/address and /person/1/address/1 for the actual access to people's addresses but you can always include the address id in person's representation (along with some summary if you want) so that it is possible to traverse those relations. Remember that in RESTful architecture everything has to have its URI so sending "everything" in one batch would be _kind of_ in violation of that principle. But don't hold me accountable for this interpretation - it might be wrong all along.