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()}")
}