register
other register

Thursday, December 10, 2009

Groovy/Grails Exchange London 2009

Just attended the Groovy/Grails Exchange London 2009 conference. Podcast and slides can be found there as well.

SkillsMatter uploaded more photos. I am in the picture (sitting very front) ;)

It is a festival for those enthusiastic groovy/grails lovers to learn new things and exchange knowledge and practice.

Got quite quite a few point to take away into practice.

Guillaume LaForge introduced new features of Groovy 1.7. Try it out.

iWebKit, the mobile plugin for grails, is a must playing one when off work.

Burt Beckwith, the Spring Security (aka acegi) plugin developer, took the grails application into an enterprise level - Clustering a Grails Application for Scalability and Availability and t

Venkat Subramaniam is the live coding ninja. His presentation and demo style is just fantastic. The pace of his talk is like prison break season 1 -- I want to know what is coming next. Following his way, you increase your job security.

Christian Dupuis from SpringSource demonstrated the Groovy/Grails plugin for Eclipse. I can see lots of improvement which had been made, but there are still lots of features lacking. I am a Eclipse lover. Come one, Let's wave the Eclipse flag.

Graeme Rocher talked about cloud, plugin systems and Grails 1.2 new features. Just try it.

Tomas Lin demonstrated how to use flex as a front end to talk to grails. Brilliant.

Well, so many things to take on to digest. Looking forward to the QCon London 2010.

Groovy/Grails have a bright future for Java developers.

Wednesday, September 16, 2009

Java Regular Expression Experience.

If the input only allows the number format like the following: 1, 11, 1.1, 1.11, 11.1, 11.11.

class Test {
java.math.BigDecimal price

static constraints = {
price(blank: false, validator: {val, obj ->
// this will only allow those 33.33 format
if (!Pattern.matches("\\d{0,2}\\.\\d{0,2}", val.toString())
&& !Pattern.matches("\\d{0,2}", val.toString())) {
return ['digital']
}
}
)
}

Please refer to Java Regular Expression docs for more info.


In addition, make sure the mysql table column price with the right (enough) type: decimal (10, 2).

Tuesday, September 15, 2009

Calendar datepicker Plugin in Grails

Refer to Grails Calendar Plugin site for how to install and use the plugin.

<g:form>
<calendar:datePicker name="myDate" defaultValue="${new Date()}" dateFormat="%d/%m/%Y"/>
<g:actionSubmit name="myAction" value=Submit"/>

There will be some html and javascript code generated by the plugin when viewing page source. The code we care is:

...
<input type="text" id="myDate_value" name="myDate_value" readonly="true"/>
...

The submitted myDate will be in String format dd/mm/YYYY, and we will use myDate_value in our controller to retrieve value.

class MyController {
def myAction = {
if (params['myDate_value'] != null) {
def f = new SimpleDateFormat("dd/MM/yyyy")
def myDate = f.parse(params['myDate_value'])

// Testing
println myDate.class // it is java.util.Date
println myDate // Tue Sep 15 00:00:00 BST 2009

def test = Test.get(1)
println test.dateCreated.class //java.sql.Timestamp
println test.dateCreated // 2009-09-14 10:34:10.0

// Time comparison
println myDate > test.dateCreated // true
println myDate == test.dateCreated // false
println myDate < test.dateCreated // false
....
}
}
}
The unique constraint in Grails works fine out of the box when the field is not a primary key. But it cause exceptions if the field is a manually assigned primary key.
Also refer to Grails doc for unique constraint.

class Test
String id

static mapping = {
table 'test'
id generator:'assigned', column:'id'
}
static constraints = {
id(blank: false, unique: true)
}


Exception will be thrown when saving it with a duplicate id.



The solution is as below:

class TestController {
def save = {
def testInstance = new Test(params)
try {
if(!testInstance.hasErrors() && testInstance.save()) {
flash.message = "Test ${testInstance.id} created"
redirect(action:show,id:courseInstance.id)
}
else {
render(view:'create',model:[testInstance:testInstance])
}
}
catch (org.springframework.orm.hibernate3.HibernateSystemException e){
// Field in view to highlight using tag
testInstance.errors.rejectValue('id', 'test.id.notUnique')
render(view:'create',model:[testInstance:testInstance])
}
}
}

In the message.properties file, add one entry:

test.id.notUnique=The system has already had a id as you entered. Please provide a unique id.

Remove Cookie in Servlet

In Firefox, view the cookie info via Web Developer plugin. Make sure the path is correct, that is where the cookie located.



 


 


In Servlet, to remove a named cookie do:

Cookie myCookie = new Cookie("JSESSIONID", "");
myCookie.setMaxAge(0);
myCookie.setPath("/");
//myCookie.setPath("/" + grailsApplication.metadata['app.name']);
response.addCookie(myCookie);

Refer to the Java Servlet Cookie doc, set the MaxAge to 0 will delete the cookie.

Friday, August 14, 2009

Database Result to Groovy List, Map

Person --* Order --* LineItem *-- Product


def lineItemList = LineItem.findAll()
def t = []
def q = []
def lineItemMap = []
def orderMap = []
def packs
lineItemList.order.person.unique().each { person ->
def p = []
orders = lineItemList.findAll{it.order.person == person}.order
orders.unique().each { order ->
lineItemMap = lineItemList.findAll{it.order == order}
orderMap = [orderId: order.id, lineItemId: lineItemMap.id]
p.add(orderMap)
// Use the statement below to get all the products
//products = LineItem.getAll(lineItemMap.id).product
}
q = [personId: person.id, order: p]
t.add(q)
}


[[personId:1, order:[[orderId:1, lineItemId:[1, 2]], [orderId:2, lineItemId:[3, 4]]]]]

[[personId:1, order:[[orderId:1, lineItemId:[1, 2]]]], [personId:2, order:[[orderId:2, lineItemId:[3, 4]]]]]

Thursday, August 06, 2009

Domain Class (Primary Key) Update and Table Generation

If you set dbCreate = "update" in the DataSource.groovy, then every time you change the domain class, the grails will update the table for you. Sometimes if it doesn't (the primary key (strategy) is changed), then drop the table, and restart the grails application. And it will reflect the changes in the domain class.

To use a manually assigned value as a primary key rather than the default auto_increment one, do:

class Person {
String id

static mapping = {
id generator: 'assigned', column: 'id'
}
}

Monday, August 03, 2009

List (array) of parameter values

Sometimes in a web form, it is useful to allow inputting multiple values to be associated with one

In some scenarios, a web form can allow inputting multiple values to be associated with the same named field:

<form>
<input name="key" type="text">
<input name="key" type="text">
</form>

The posted url is like: ?key=value1&key=value2

In Grails, params['key'] is always used to get parameter value. The default behaviour will return:

a string if the key only has one value (i.e. ?key=value)
a List if the key has many values (i.e. ?key=value1&key=value2)

It causes problems when you are not sure how many values the key is associated with.

Solutions:

In Java Servlet API, there is a method for Interface ServletRequest

java.lang.String[] request getParameterValues(java.lang.String name)

Returns an array of String objects containing all of the values the given request parameter has, or null if the parameter does not exist.

request.getParameterValues('key')
class [Ljava.lang.String;

According to http://groovy.codehaus.org/JN1025-Arrays, we can use the following code to reconstruct the query string:

def queryArray = request.getParameterValues('key')
def queryString = ""
queryArray.each {
queryString += "&key=$it"
}




For the query string like: http://blabla.com/list?personId=1&personId=2

To accommodate this, in Grails scaffolded list closure:


def list = {
def personList
if (params['personId'] != null) {
personList = Person.getAll(Arrays.asList(request.getParameterValues('personId')))
}
else {
personList = Person.list()
}
}


To cast an array into a list, using:

Arrays.asList(array[])


Also refer to http://prideafrica.blogspot.com/2007/01/javalangclasscastexception.html for some useful info regarding [Ljava.lang.String.

Wednesday, July 29, 2009

GORM Lazy

If we have the domain relations (one Order has many Items, and one Item can appear in many Orders) modelled below:

Order --* LineItem *-- Item

If we want the toString() method of the LineItem class to return its relation classes attributes, then that's where lazy strategy comes in handy.


class LineItem {

// relation
Order order
Item item

static mapping = {
columns {
order lazy: false
item lazy: false
}
}

String toString() {
return "Order no: $this.order.id, Item: $this.item.name"
}
}

Tuesday, July 21, 2009

Get Session in Grails Service

In Grails, session variable is not available in service. But

import org.springframework.web.context.request.RequestContextHolder

def user = RequestContextHolder.currentRequestAttributes().getSession()?.user


Note: the RequestContextHolder is genuinely a normal web request. If the request is a scheduled job (using Quartz plutin), not a web request, then there will be exceptions like below:

=============================
2009-07-21 00:00:00,269 INFO [core.JobRunShell] Job GRAILS_JOBS.ExpireJob threw a JobExecutionException:
org.quartz.JobExecutionException: No thread-bound request found: Are you referring to request attributes outside of an actual
web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web
request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this
case, use RequestContextListener or RequestContextFilter to expose the current request. [See nested exception: java.lang.Ill
egalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request,
or processing a request outside of the originally receiving thread? If you are actually operating within a web request and st
ill receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use Req
uestContextListener or RequestContextFilter to expose the current request.]
at org.codehaus.groovy.grails.plugins.quartz.GrailsJobFactory$GrailsTaskClassJob.execute(GrailsJobFactory.java:81)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:525)
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of
an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating wit
hin a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortle
t: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:12
2)
at org.springframework.web.context.request.RequestContextHolder$currentRequestAttributes.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:43)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at EmailerService.sendEmails(EmailerService.groovy:64)
at EmailerService$sendEmails$0.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:43)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
at ExpireJob$_execute_closure1.doCall(ExpireJob.groovy:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:234)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1061)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:910)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:892)
at groovy.lang.Closure.call(Closure.java:279)
at groovy.lang.Closure.call(Closure.java:292)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1165)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1141)
at org.codehaus.groovy.runtime.dgm$87.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSi
te.java:270)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52)
============================


If you are logging this scheduled job, then do:

try {
def user = RequestContextHolder.currentRequestAttributes().getSession()?.user
if (user != null) {
log.info("Email: ${mail.dump()}, action: sendEmails by ${user} on ${new Date()}")
}
else {
log.info("Email: ${mail.dump()}, action: sendEmails by system on ${new Date()}")
}
}
catch (java.lang.IllegalStateException e) {
log.info("Email: ${mail.dump()}, action: sendEmails by system on ${new Date()}")
}

Friday, July 03, 2009

Select Column Name with Space

If the column name has space in it, then use backtick operator like this `column name`

Migrate Access to MySQL to Fit in Grails

1. Access Tables Preparation

Open the Access database and modify the table names and column names with the following convention:
  • Modify the table names to table_name (e.g. account_manager) format if necessary. GRAG will generate a domain class named: TableName.
  • Modify column names to column_name (e.g. contact_address) format if necessary. GRAG will generate a field named: columnName.
  • Create an autoNumber id as the primary key if the table doesn't have one
  • If the table's primary key is not named id, then change it to id. GRAG will generate an id field with the mapping like: id generator:'identity', column:'id'
  • Modify foreign keys in other tables as table_name_id. GRAG will create a relation for this.

2. Migrate Access to MySQL


MySQL Migration Toolkit to migrate MS Access to MySQL (http://dev.mysql.com/downloads/gui-tools/5.0.html). It is an automatic process with step by step instructions. The tool will generate dbCreate.sql and dbInserts.sql, remember to save them. Then:
  • Modify the dbCreate.sql to add foreign keys which GRAG will create relations for the generated domain class, and add version int(10) field, otherwise a 'version false' statement will be generated in the mapping, because this isn't available by default for legacy databases
  • Re run the dbCreate.sql script to re-generate the tables.
  • Re run the dbInserts.sql script to populate the data.

3. Generate the Domain Classes

Then you can start using Grails Application Generator (GRAG) (http://grag.sourceforge.net/) to generate the domain models. It is not perfect, but at least it saves some manul typings.

Open the generated domain classes to:
  • check the generated field to see wheter it comforms to the lowerCaseCapital format (e.g. contactAddress)
  • If the id is an auto_increment field, then comment out the id field, and comment out id generator:'identity', column:'id' statement.
  • If the id is a user assigned value field (e.g. characters), then don't comment out the id field. Also, change the id generator in the mapping to: id generator:'assigned', column:'id'
  • modify relations if necessary (i.e. hasMany, belongsTo, or association like: OtherDomain otherDomain)
  • comment out constraint for a start
  • modify the toString method to return some meaningful value.

4. Configuration

Change dbCreate to 'update' in the DataSource.groovy so that the data

5. Testing

runt he "grails console", then for each domain class, test it with the following script:

def a = DomainClass.get('KK')
a.dump()

6. Generate Skeletons Code

grails generate-all *

This will generate the controllers and gsp view pages for all the domain classes.

7. Modify assigned id field for controller and gsp

If the id is a user assigned id rather than an auto incremented id, then for the controller, explicitly assign the id:



def save = {
def nameInstance = new Name(params)
nameInstance.id = params.id
if(! nameInstance.hasErrors() && nameInstance.save()) {
...
}
}
For the create.gsp page, add the id input field:

            
<tr class='prop'>
<td valign='top' style='text-align:left;' width='20%'>
<label for='id'>ID:</label>
</td>
<td valign='top' style='text-align:left;' width='80%'
class='${hasErrors(bean:nameInstance,field:'id','errors')}'>
<input type='text' name='id' value='${nameInstance?.id}' />
</td>
</tr>

There is also a very good post regarding Hosting Grails to your Legacy Database

Tuesday, June 30, 2009

Digesting belongsTo


class Face {
String name
Nose nose
static constraints = {
nose (nullable: true)
}

String toString() {
return name
}
}

class Nose {
String name
static belongsTo = [face: Face]
String toString() {
return name
}
}

def a = new Face(name: 'joe')
def b = new Nose(name: 'blog')
a.b = b
a.save(flush: true)
b.save(flush: true)


// test
def face = Face.findWhere (name: 'joe')
println face.nose

def nose = Nose.findWhere(name: 'blog')
println nose.face

// You must set nullable: true for nose in the constraint, otherwise the save won't work and it doesn't throws any exception
face.nose = null
face.save()
println face



In the above relationship, the foreign key (i.e. nose_id) will added in the Face side. But you can still get hold of face from the nose, because nose belongsTo face.

Friday, June 26, 2009

No row with the given identifier exists

No row with the given identifier exists

class A {
B b
}

class B {
String name
}


If you have an A has B relationship. If from the database end, A says has B and with B's id, but actually that B's id doesn't exist in B's table. Then it will complains: No row with the given identifier exists

A workaround of this is to use ignoreNoFound like below

class A {
B b

static mapping = {
b ignoreNotfound : true
}
}

Friday, March 20, 2009

Submit an array as a parameter

When you have an array of values to submit like the code below:

<g:each in="${people}" var="person">

<tr><td>
<input type="checkbox" name="emails[]" value="${person.email}" />
<td></tr>
</g:each>

When submiting this form, the emails[] will be submitted as an array/list object like [Ljava.lang.String;@151313a. To get the value out of the object, do:

params['emails[]'].toString()

That will get the value like: [joe@foo.com, blog@foo.com]

Tuesday, January 20, 2009

Remote Deploy Grails Application


Ant 1.6 or later support scp, ssh tasks.

Download the lastest ant.
Set ANT_HOME variable.
Add %ANT_HOME%\bin in the PATH variable (in Windows environment).

Download the JSch jar to the %ANT_HOME%\lib

Add the remote task in the build.xml in the Grails project:

<target name="remoteDeploy" depends="war" description="-->Deploy the war to the remote server">
<scp file="LockerJet.war" trust="yes" todir="username:password@remotehost:/PATH_TO_TOMCAT/webapps"/>
</target>

Note: set trust="yes", otherwise it will thrown "reject HostKey" or "java.net.UnknownHostException"

Run the task by:

ant remoteDeploy

===============

There is another Grails remote deploy using GANT post for reference.

Friday, January 16, 2009

Clear Flash Message in Grails

According to the Grails documentation:

"Flash is a temporary storage map that stores objects within the session for the next request and the next request only, automatically clearing our the objects held there after the next request completes.

The flash object is essentially a map (a hash) which you can use to store key value pairs. These values are transparently stored inside the session and then cleared at the end of the next request.

This pattern allows you to use HTTP redirects (which is useful for redirect after post and retain values that can retrieved from the flash object."

But just in case the flash message isn't cleared for some unknow reason, there is the code to rescue in your controller:

flash.clear()

Thursday, January 15, 2009

Command Object and Error Messages

"Grails command Objects provide a simple mechanism to validate from fields that do not map directly to domain objects. "

Command Object

class JetCommand {
  String number
  String name
 
  static constraints = {
    number(blank: false, nullable: false, matches: "\\d+")
    name(nullable: false)
  }
}

The command object class may be defined under src/groovy/ or under grails-app/controllers/. The command object class may also be defined in the same source file as the controller that uses it.

If a custom error message is preferred, then look at the corresponding error code for the above constraints. Add custom error messages in the grails-app/i18n/messages.properties file:

jetCommand.number.blank=The number can't be blank
jetCommand.number.matches.invalid=The number can only be digital numbers
jetCommand.name.nullable=The name can't be null


Controller:

class JetController {
  def jet = { JetCommand cmd ->
  if (cmd.hasErrors()) {
    cmd.errors.allErrors.each {
    println it
  }
}

// You don't have to do the following code, because the Grails will handle it for you.
// If you do the following code, there will be duplicate error messages 

/**
if (cmd.errors.hasFieldErrors("name")) { 
cmd.errors.rejectValue('choice', 'searchCommand.name.nullable') 
}
         if (cmd.errors.hasFieldErrors("number")) {
                 if (cmd.errors.getFieldValue("number").isEmpty()) {
                     cmd.errors.rejectValue('number', 'searchCommand.number.blank')
                 }
                 else {
                     cmd.errors.rejectValue('number', 'searchCommand.number.matches.invalid')
                 }
             }
**/
render (view: 'admin', model:[jetCommand: cmd, key1: object1, key2: object2])
}
}
}


GSP page

You can display the error messages in the GSP page:


  


Customised Validator:

You can also add a customised validator:

static constraints = {
  number(validator: {
    if (it.isEmpty() || it == null || !(it ==~ /\d+/)) {
      return ['numberJet']  // numberJet is the error code
    }
  })
}

In this case, in the messages.properties, you should add a error message with the error code:

jetCommand.number.numberJet=Number field is not valid

Wednesday, January 14, 2009

Redirect Params in Grails

Grails params contain controller, action, and other parameters like below:

["_action_search":"Search", "choice":"Locker Register", "action":"search", "numb
er":"1", "controller":"lockerRegister"]

So when doing redirect with all the params like below:

class JetController {

def search = {

redirect (action: "jetAction", params: params)
}

def jetAction { 
// some code here
}
}

It might be in a endless redirect loop. A workaround is to manually specify the parameters your want to redirect. For example: 

redirect (action: "testAction", controller: "testController", params:[p1: 'value1', p2: 'value2']) 

Put Multiple Objects in the Returning Model in The Controller

In controller, you can put multiple objects in a model and return it. You can also pass on the parameters from the request to the model on returning it. For example:

class JetController {

def test = {
return ['key1': object1, 'key2': object2, 'key3': params.person.id]
}
}

Restart the Application After Changing the Dynamic Finders in Grails

When changing the dynamic finders (i.e. find, findAll, findBy) while the grails application is running, you need to restart the application to reflect the changes for the queries. 

If you set  "logSql=true" in your conf/DataSource.groovy, then you can monitor whether  sql queries reflect your changes.