Tuesday, April 15, 2008

Grails UI for Integer Dates

Legacy tables often have dates kept in integer, zoned, or packed fields. This is unfortunate because the UI developer has to do all the validation and formatting of the numeric value. But there's a simple solution for Grails applications: create getter/setter methods to simulate a Date attribute.

Grails' generate-view will use the g:datePicker tag in the create and edit GSPs. Typically I replace g:datePicker with the RichUi tag.

In the following example, the domain has an integer sales date, in month/day/year format but getSaleDate() and setSaleDate() surfaces a Date attribute. The trick is to not present the integer date attribute for update (otherwise the value will overwrite the change made by setSaleDate()).


class Sale {
String last
int saleMDY
BigDecimal amount

Date getSaleDate() {
Calendar cal = Calendar.getInstance();
if (!saleMDY) {
return new Date()
}
int year = (saleMDY % 100)
int day = saleMDY / 100
int month = day / 100
day %= 100
cal.set((2000+year), (month-1), day)
return cal.time
}
void setSaleDate(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date)
int day = cal.get(Calendar.DAY_OF_MONTH)
int month = cal.get(Calendar.MONTH) + 1
int year = cal.get(Calendar.YEAR) % 100
saleMDY = (month * 10000) + (day * 100) + year
}
}

Friday, February 22, 2008

Integration testing for Grails controllers

I've been negligent in writing integration tests for my controllers, mostly because I ran into some issues and put off figuring it out. So while on a train I wrote a simple 24-line controller with action-closures that used the 5 basic operations most in my applications:

  1. render

  2. rended JSON

  3. return map

  4. redirect

  5. return string


And I threw in a service for good measure, and then wrote integration tests.
Here's a couple of points to remember:

  • When a map is returned, the map is available directly as the return value from the action

  • When JSON is returned, use the controller.response.contentAsString

  • When a String is returned, it is available directly as the return value from the action

  • When the action does a render, use the controller's ModelAndView's model property to test model data. Note that the view did not actually render but you can test the viewName property value. Also note that what you can't seem to be able to do is see how the page would ultimately be rendered.

  • When the action does a redirect, check the url with the controller's MockHttpServletResponse

  • To use a service, the test has to interject the service. Note there may be other things that Grails normally interjects for you that you may have to manually set



Here's the domain:

class Person {
String firstName, lastName, email
String toString() {"$id $firstName $lastName <$email>" }
}

Here's the controller:

class PersonController {
def list = {
[ personList: Person.list( params ) ]
}
def showWithReturnMap = {
def person = Person.get( params.id )
if(!person) {
flash.message = "Person not found with id ${params.id}"
redirect(action:list)
}
else { return [ person : person ] }
}
def showWithReturnString = {
return Person.get( params.id ).toString()
}
def showWithRender = {
render view:'show', model:[ person : Person.get( params.id ) ]
}
def showWithJSON = {
render Person.get( params.id ) as JSON
}

MathService mathService
def usesService = {
return mathService.add(params.a, params.b).toString()
}
}


And here's the integration test:

class PersonControllerTests extends GroovyTestCase {
// when true, Grails does a rollback after each test method
boolean transactional = false

void setUp() {
new Person(lastName:'Denoncourt', firstName:'Don', email:'ddenoncourt@cassevern.com').save()
}

void testShowWithReturnMap() {
def controller = new PersonController()
controller.params.id = 1
def model = controller.showWithReturnMap()
assertFalse "Person was found",
controller.flash.message ==~ /Person not found with id 1/
assertNotNull "Person returned as model", model
assertEquals "Person is a Denoncourt",
model.person.lastName, 'Denoncourt'
}

/* When a String is returned,
* it is available directly as the return value
* from the action
*/
void testShowWithReturnString() {
def controller = new PersonController()
controller.params.id = 1
def model = controller.showWithReturnString()
assertEquals "Don Denoncourt found",
model, "1 Don Denoncourt "
}

/* When the action does a render,
* use the controller's ModelAndView's model property
* to test model data
* Note that the view did not actually render but you can test
* the viewName property value
* What you can't seem to be able to do is see how the page
* would ultimately be rendered.
*/
void testShowWithRender() {
def controller = new PersonController()
controller.params.id = 1
controller.showWithRender()
assertTrue "Person was found",
!( controller.flash.message =~
/Person not found with id/
)
assertNotNull "Person returned in ModelAndView",
controller.modelAndView.model
assertEquals "Person is a Denoncourt",
controller.modelAndView.model.person.lastName,
'Denoncourt'
assertEquals "view should be show",
controller.modelAndView.viewName,
"/person/show"
}

/* When the action does a redirect, check the url with the controller's MockHttpServletResponse */
void testShowButRedirected() {
def controller = new PersonController()
controller.params.id = 9898
def model = controller.showWithReturnMap()
assertNotNull "Should have a flash message", controller.flash
assertTrue "Person should not be found",
controller.flash.message ==~
/Person not found with id 9898/
assertEquals "/person/list",
controller.response.redirectedUrl
}
/* When JSON is returned,
* use the controller.response.contentAsString
*/
void testShowWithJSON() {
def controller = new PersonController()
controller.params.id = 1
controller.showWithJSON()
assertEquals("""{"id":[1,"class":"Person",
"email":"ddenoncourt@cassevern.com",
"firstName":"Don","lastName":"Denoncourt]"}""",
controller.response.contentAsString)
}

/* to use a service,
* the test has to interject the service
*/
MathService mathService
void testUsesService() {
def controller = new PersonController()
controller.mathService = mathService
controller.params.a = 2
controller.params.b = 2
def model = controller.usesService()
assertEquals "2+2=4", model, "4"
}
}

Saturday, February 16, 2008

GORM insert and update auto-set of createUser and updateUser

Our company has a standard of using the following attributes on all database tables:

String createUser
Date createDate
String updateUser
Date updateDate


But we did not want to put code in all controller's to set these fields on insert or update. Grails has a nice facilities where you specify code in the beforeInsert and beforeUpdate closures to set the dates:

def beforeInsert = {
createDate = new Date()
}
def beforeUpdate = {
updateDate = new Date()
}

Grails will call these closures automatically. But the issue is setting the createUser and updateUser. What we did is create an abstract base class (concrete makes Grails look for a table called base):

abstract class Base {

String createUser = ''
Date createDate = new Date()
String updateUser = ''
Date updateDate = new Date()

def beforeInsert = {
setCreateUsername() // must be injected in login controller
createDate = new Date()
delegate.createUser = this.createUser
delegate.updateUser = this.updateUser
}

def beforeUpdate = {
setUpdateUsername() // must be injected in login controller
updateDate = new Date()
delegate.updateUser = this.updateUser
}
}


Each of our domain classes extended Base (so they did not have to specify the 4 fields.)
Then, when the user login is handled by the Login controller, we inject the setCreateUsername and updateUsername "methods" as closures that set the username with the HttpSession value:


def user = User.findWhere(userId:params.userId, userPwd:params.userPwd)
session.user = user
Base.metaClass.setCreateUsername = {
createUser = session.user.userId
updateUser = session.user.userId
}
Base.metaClass.setUpdateUsername = {
updateUser = session.user.userId
}


And the four columns are automatically set on all insert and updates.

Grails WebFlow and explicit events

Grails WebFlow enables the ability to keep a context specific to a set of pages. Grails webflow is built on top of Spring webflow but, as always, Grails adds ease of use with a DSL.
When do you need webflow? Whenever you find yourself stuffing things in the session context.
One side note, if you are putting something in the session that is used in the very next request, use the flash context, as it retains items in the flash scope for two request cycles (unless the item is refreshed on the second request.

A webflow is contained in one controller. Reading existing webflows is fairly easy but, until you become comfortable with a few terms, writing webflows can be problematic. Things that you need to understand are Flow Scopes, View States, and Action States.

To get started with webflow, read, in its entirety, the documentation at WebFlow and then download and play with the book-flow
sample application. If you do not know how to check out a Subversion, email me and I'll walk you through it.

Grails WebFlow seems to be limited somewhat in GSP tags: g:submitButton and g:link. Remember that in the Grails RESTful architecture, URLs follow the domain/controller/action convention to identify the controller and closure (the action) to handle the request. WebFlows adds the concept of an event. Strangely the g:submitButton's name attribute defines the event but the g:link uses the more obvious event attribute. But what if you want to use an image button or invoke a request from a JavaScript event?

If you look at the HTML generated from the g:submitButton or g:link tags you will see a request parameter with a name the is prefixed with _eventId_. The string that follows the second underscore identifies the WebFlow event.
So for an application that requires an image, a click of which should be handled by the edit event of a webflow:

<input class="edit" name="_eventId_edit"id="_eventId_edit"
value="Edit" type="image"
src="/FAB/images/skin/database_edit.png"
/>

To have a select list that runs the createMop (method of payment) event when a user select a item from the method of payment types list:

<g:select id='mopType' name='mopType'
onchange="location='/FAB/document/documentWork?_eventId_createMop=createMop&'+Form.serialize(\'docLogForm\');"
from='${(BootStrap.mopTypes.entrySet().value)}'
keys='${(BootStrap.mopTypes.entrySet().key)}'
/>

Notice the use of the serialize method of the Form object. The serialize method comes from the prototype JavaScript library.

Monday, December 10, 2007

Grails Eclipse Plugin, very basic functionality

The Grails generation utilities are designed to run from a system command line. But I keep running into folks that just don't want to go to DOS. They want a GUI. Well, someone has created an Eclipse plugin (which works with WDSc 7.0). It is available at:

http://www.groovy-news.org/e/page/axelclk?entry=eclipse_grails_plugin_very_early

Please don't forget to restart Eclipse with the clean option after copying the plugin to your Eclipse directory.
For WDSc users that means copying the downloaded org.codehaus.grails.eclipse.externaltools_1.0.0.jar file to
C:\Program Files\IBM\SDP70\plugins
Then ending WDSc and, in a DOS window:

cd C:\Program Files\IBM\SDP70
eclipse -clean


The plugin is invoked by right-mousing on your Grails project in Eclipse and selecting Grails.

It is limited to the following options:

  • Create Domain Class

  • Create Controller

  • Create Testsuite

  • Create Domain Class

  • Create Taglib

  • Generate Controller

  • Generate Views

  • Generate All

  • Create Webtest

  • Run Webtest

  • Test Application

  • Run Application



Note too that there's an issue with Run Application that you need to be aware of: While its output goes to the Eclipse console, clicking on the red square "terminate" icon does not stop the launched JVM, which prevents you from running the application again (you'll get a TCP/IP bind error as port 8080 is allocated to the JVM.)
A simple solution for this is to bring up Windows Task Manager (with Control-Alt-Delete,) click on the Processes tab, find the image name of Java (you can sort by clicking on the Image Name tab,) select java.exe, and finally click the End Process button.
If you have multiple java.exe processes listed, be careful because you may terminate a process that's doing something that you may not want to terminate.

Friday, November 30, 2007

Grails template for Web-enabling RPG

The Grails Utilities for System i includes a utility to generate RPG call beans from SQL External Stored Procedures that are implemented with RPG. The utility creates both Java classes and Groovy classes. There is a Groovy and a Java class that wrappers a call to the stored procedure and there is (currently just Java) a class wrapper for the returned result set. The generated Java code can then be used in Java applications and the Groovy (and Java) can be used in Grails apps.

I'm planning on, first of all, adding the Groovy class wrapper to hold the result set values. But also I'm going to add the generation of a Grails domain class to hold the input parameters for the RPG call bean class (which calls the stored procedure.) The idea is that then it will be easy to generate a GSP prompt for the input parameters. Also, the generated domain class will have Grails constraints to validate user input.

But, you ask, isn't a domain coupled with a table? Only if you use the domain CRUD methods. What I do is use domain.validate() to use Grails' fantastic validation feature then call the RPG call bean.

So, going the extra mile (which, with Groovy is really only a couple of yards,) I plan on extending the tools with:

  • A domain class generator, that will also gen constraints for user input for RPG parameters

  • A controller template to generate Grails actions to invoke the Groovy RPG Call beans

  • A GSP template to generate an RPG Call Bean prompt form and a result form



While the generated code and GSP will function, in practice it really wouldn't be used in production. Rather you'd use it to test your RPG invocation and to have example, runnable code that can be refactored into production quality applications.

Thursday, November 29, 2007

Groovy Futures

I wanted to provide a bit of history going back to Spring 2007 when I first realized that the language called Groovy and the framework called Grails had bright futures. I kept hearing about Groovy on various technical podcasts (such as The Java Posse so I started to look curious about this new language. Then I listened to a podcast by Scott Davis The Groovy Programming Language and Scott convinced me that Groovy was indeed, as it's name suggests, pretty cool.

So I began to listen to all the podcasts I could find on Groovy and Grails. The Grails Podcast provided plenty (it now has 50.) Another site that has a number of great Grails Podcasts is About Groovy. My favorite is Neal Ford's

I also began to play with Groovy and Grails. I did what everyone should do and ran the Quick Start and then a couple Tutorials.

My timing was pretty good because four books on Groovy and Grails had just become available:


Groovy in Action is my favorite. Java developers should read that book cover-to-cover. Non-Java developers need only read, initially, through chapter 6. Then read the rest of the book after they've worked with Groovy for awhile.

The Definitive Guide to Grails is just what it says. It was written by the creator of Grails. I would recommend that you read Chapter 1 and Chapter 2 of Groovy in Action before reading The Definitive Guide to Grails as the Groovy intro in the in action book is very well written.

Getting Started with Grails is tutorial based. Jason Rudolph does a very nice job. You might even read this before working your way through The Definitive Guide to Grails

Groovy Programming was written as a college text and, while thorough, it is a bit boring.

Anyway, after reading all the books and doing the various tutorials and playing with some ad hoc applications, I started to work with System i legacy database tables. And I ran into a few issues. I've resolved those issues, using the Groovy language and the Grails philosophy of "convention over configuration." I developed several tools and launched an open-source project Grails400Utils: Grails Utilities for the IBM System i

I published an article The Search for the Holy Web Development Grail(s) in the November issue of System i News. And I did my own Groovy and Grails podcast What's in a name: Groovy and Grails - The implementation, language and more

I've also developed and deployed two Grails applications that are now in production. I am currently working on three other commercial Grails applications.

The future for me is Groovy (and Grails) and I'm continuing to develop the Grails Utilities for the IBM System i while being billable doing GonG development (although I can still be coerced to do Java coding.)