Saturday, May 23, 2009

Converting a Java/Flex application to Grails/Flex

I have a small reference application to CRUD ( create, read, update and delete ) notes from a back end service using a simple flex application. This reference application was originally written with Spring 2.5, later upgraded to 3, and Hibernate 3.x with MySql 5. As new versions of these technologies come out, I integrate it into this simple reference application. I use it as a sanity check while integrating all of those technologies because the application is not too difficult.

I decided to try to migrate it to a Grails platform to get a sense for how difficult that might be. I am going to share the steps that I took to migrate my Tomcat/Spring3/Hibernate3/MySql5 application to Grails/MySql5.

Since I already had a database schema from the Java implementation I decide to leverage that DDL so that both applications could run against the same backend.

Since I knew that the user interface was a Flex interface, I installed the flex plugin.

grails install-plugin flex
This plugin allows one to expose a Grails service as a Flex BlazeDS service by simply adding: static expose = ['flex-remoting'] to the service.

The 3 methods that the Java service exposed were:
* getAllNotes
* saveOrUpdateNote
* deleteNode

Therefore, to maintain the same Flex UI I needed to expose the same API.

The service ultimately ended up looking like the following:
package com.redpointtech.services
import com.redpointtech.domain.Note
class RedpointNotesService {

boolean transactional = true
static expose = ['flex-remoting']

def getAllNotes() {
def allNotes = Note.findAll()
return allNotes
}

def saveOrUpdateNote(Note note) {
def noteid = note.id
println "NOTE ID TO SAVE: " + noteid
// until I can figure out how to get unsavedValue:0 to
// work I need to do my own unsaved value.
if( noteid == 0 ) {
note.id = null;
}
note.save()
}

def deleteNote(Long noteId) {
def note = Note.findById(nodeId)
note?.delete()
}
}



Take special note of the check for a zero noteid. I could not immediately figure out how to tell grails that the 'unsaved-value' is 0 instead of the default null, so I had to make that programmatic. I am still looking into this, but if you know the answer please leave a comment.

To my pleasant surprise, the service was very small and the only extra work to expose the service to Flex was to add the 'static expose = ['flex-remoting']'. Anyone that has exposed Flex services knows that you have to add a servlet to the web.xml, update the flex-remote.xml, add a Spring factory to get the service. While not hard, certainly more work that added one line to a service. Please note, that I did NOT update the web.xml at all.

Next I turned my attention to the Note domain object. I used the the grails create-domain command to create the Note domain object.

To keep this as minimal as possible to start I did not add any constraints - clearly you would normally.

The Note domain class just keeps track of the authors name, the text of the note and the date the note was created. There is no mapping to a User domain object because I purposefully wanted to keep this reference application very simple.

Below is the Grails domain object:
package com.redpointtech.domain
class Note{

//Long id
String author
String text
Date createDate

static constraints = {
}

static mapping = {
table 'notes'
version false
id unsavedValue: 0
text column: "note_text"
createDate column: "create_date"
}

}



There are a couple of aspects to note:
* I commented out the id property because by convention Grails gives every domain object an id property.
* I had to add a mapping section because I was not able to use the grails default values.

If you look up at the service class again, you will see methods on the Note class like 'findAll' but you do not see them here. This is because GORM, which is Grails' Object Relational Mapping which sits on top of hibernate, creates many of the standard methods for you and dynamically adds them at runtime. This dramatically decreases the number of DAO methods you need to write - and really all but eliminates the DAO layer all together.

At this point, I now I have a service and a domain object that represents the Notes. Because I am using a MySql database and not the supported out of the box HSQL DB, I have a little more configuration to do.

The grails-app/conf/DataSource.groovy file contains the datasource definitions for all of the environments.

Below is the version of DataSource.groovy that I used:
dataSource {
pooled = true
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""
}
hibernate {
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider'
}
// environment specific settings
environments {
development {
dataSource {
pooled = false
driverClassName = "com.mysql.jdbc.Driver"
dbCreate = "update" // one of 'create', 'create-drop','update'
url = "jdbc:mysql://localhost/test"
username = "root"
password = "root"
logSql="true"
dialect = org.hibernate.dialect.MySQL5InnoDBDialect
}
hibernate {
show_sql = true
hibernate.format_sql = true
}
}
hsqldevelopment {
dataSource {
dbCreate = "create-drop" // one of 'create', 'create-drop','update'
url = "jdbc:hsqldb:mem:devDB"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:hsqldb:mem:testDb"
}
}
production {
dataSource {
dbCreate = "update"
url = "jdbc:hsqldb:file:prodDb;shutdown=true"
}
}
}

You can see in the environments section, I altered the development datasource to use MySql instead of HSQL. This should be pretty standard datasource configuration for MySql.

The last thing I needed to do was drop the mysql JDBC jar into the lib directory - and it was complete.

In the end I went from absolute zero, to a working Grails application that gave me the same functionality as my Tomcat/Spring/Hibernate application in less than an hour. I have to say, I was very impressed with the speed of development in the grails environment.

My company is currently working on a full featured, consumer facing Flex/Grails application and Grails has still not let me down even in this more challenging real world environment.

Every time I use Grails, I am more convinced of its viability as a production ready framework. It probably helps that it is built upon production ready frames of Spring and Hibernate.