Friday, August 22, 2008

Grails on WebLogic - Validation Errors

If you are deploying Grails on WebLogic 9/10 you must ensure that all of your domain classes MUST implement Serializable. If not, you won't see validation feedback on your form.

Also, as part of the standard Java Serializable interface, ensure that all members of the domain class must also be Serializable.

You will see a WebLogic log message:

<[weblogic.servlet.internal.WebAppServletContext@f7ca6d - appName: '_appsdir_MyApp_dir',

name: 'MyApp', context-path: '/MyApp'] could not deserialize the request scoped attribute with name: "org.codehaus.groovy.grails.ERRORS_MyDomainObj_19315431"
java.io.NotSerializableException: org.springframework.beans.factory.support.DefaultListableBeanFactory
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1081)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1375)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1347)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1290)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1079)
Truncated. see log file for complete stacktrace

Friday, July 18, 2008

Acegi Table Configuration

One of the main issues I had from the Grails site was using the often reserved words "user" and "role" for the main table names.

While I was working with Oracle, "user" was an issue but "role" was not. So, generated user but mapped to a table called "app_user".

Behind the scenes I had 4 tables:
1. app_user
2. role
3. role_app_user
4. requestmap

Also, generated a sequence called hibernate_sequence

I ran into intermittent problems where hibernate would not be able to identify the intersection table "role_app_user".

I decided to rename the "role" table to "app_role" and tell Grails the specific name of the intersection table. By adding the table name and the "joinTable" option to the mapping, we identify the link between the classes and their intersection table.

File: Role.groovy
class Role {
static hasMany = [people: User]

String description
String authority = 'ROLE_'

static constraints = {
authority(blank: false)
description()
}

static mapping = {
table "app_role"
people joinTable:[name:'ROLE_APP_USER', key:'id', column:'people_id']
}

}

File: User.groovy
class User {
static transients = ['pass']
static hasMany = [authorities: Role]
static belongsTo = Role

String username
String userRealName
String passwd
boolean enabled
String email
boolean emailShow
String description = ''
String pass = '[secret]'

static constraints = {
username(blank: false, unique: true)
userRealName(blank: false)
passwd(blank: false)
enabled()
description (nullable: true)
}

static mapping = {
table "app_user"
authorities joinTable:[name:'ROLE_APP_USER', key:'id', column:'authorities_id']
}
}

In preparation of moving this code to testing and production regions describe (Oracle's "desc object") to extract the tables and sequence for your DBA's convenience.

Thursday, May 29, 2008

Currying Closures

This threw my brain for a loop... Groovy code using a "Execute Around Method" pattern:

def tellFortunes(closure) {
Date date = new Date("11/15/2007")

//closure date, "Your day is filled with ceremony"
//closure date, "They're features, not bugs"
// You can curry to avoid sending date repeatedly

postFortune = closure.curry(date)

postFortune "Your day is filled with ceremony"
postFortune "They're features, not bugs"
}

tellFortunes() { date, fortune ->
println "Fortune for ${date} is '${fortune}'"
}

From "Programming Groovy" pps 92-93

Result is:
Fortune for Thu Nov 15 00:00:00 EST 2007 is 'Your day is filled with ceremony'
Fortune for Thu Nov 15 00:00:00 EST 2007 is 'They're features, not bugs'

  1. Basically tellFortunes() accepts date and fortune as parameters, defines a code block which prints the message with the date and fortune in it
  2. This calls postFortune(closure) accepting the code block (called a Closure in Groovy)
  3. the first use of postFortune binds the date to the inbound closure and holds a reference in postFortune
  4. the next two postFortunes pass the string (and the date by use of curry) to the code block thus printing the message each time

Wednesday, May 28, 2008

Grails Web Info - dated?

I have been working on a Grails project for the last few weeks. Form my previous posts, you can see that I am working with a legacy database with my primary table configured with a composite primary key.

I've been struggling with the insert and update aspects of writing to the database. Now, in all fairness I am not extremely familiar with Hibernate so I am relying on documentation and information from the web.

One challenge with the information on the web is that a lot of the posts are out of date. When I read anything earlier that the 2nd half of 2007, I tend to not take the suggestions. Many of those tell you to configure your hibernate.cfg.xml file. However, since version 1.0 of Grails many of those configuration options are available within the domain class.

One feature I just discovered from CK's Blog was the composite key setting "generator='assigned'" which is not in the Grails documentation.

Now to give this a spin...

Wednesday, May 14, 2008

Redirecting from index.gsp

To redirect to an initial page from the index.gsp

1. Create a TagLib file
  grails-app\taglib\MyAppTagLib.groovy

2. Define a method that routes to your first page
  class MyAppTagLib {
    def redirectMainPage = {
      response.sendRedirect("${request.contextPath}/myController/welcomePage/")
    }
  }


3. Change your index.gsp to contain only:
  <g:redirectMainPage/>

Note: One advantage to this approach is that you have a coding opportunity over simply mapping the "/" url.

For example, if you want to provide features or data based on a specific user's login id.

Tuesday, May 13, 2008

Legacy DB equals frustration

Been spending a lot of time trying to solve problems with Grails when you are using a legacy database. It could be a combination of factors but I am trying to do "simple" one to one mapping of a code table to a column value and I can not get any dynamic finders working!!!

So I tried a simple:
def results = MyMainDomain.findAll()
and I get:
No row with the given identifier exists: [MyLookupClass#someValueInLookupClass ]

When I change the variables back to Strings that message goes away.

Then I get results back with all rows (rowcount = to db row count) containing empty values!

Turns out: DO NOT define any columns in your composite primary key from other Domain Classes!!! I changed them to Strings and it worked.

Thursday, May 8, 2008

Composite Key in Domain Class - round 2

Still tinkering with the composite key issues.

I changed my previous approach a bit, I now define my own getId and setId methods to essentially serialize and deserialize the the key fields strung together

Created a delimiter
static final String delimiter = '~@'

Defined (overriding default) accessors:
def getIdAsMap() {
["fieldOne": fieldOne, "fieldTwo": fieldTwo]
}

def getId() {
fieldOne + delimiter + fieldTwo
}

def setId(String inString) {
def fieldList = new ArrayList()
def items = inString.split(delimiter)
items.each { fieldList.add (it) }
this.setFieldOne (fieldList[0])
this.setFieldTwo (fieldList[1])
getIdAsMap()
}

Its a little clunky but when you can pass around the map, use getIdAsMap(), in other cases the String is available with myclass.id


Inserting new rows requires a little tweak, by default with version: false set, GORM will try to perform a sql update on new records. To fix this, add insert:true to your save method

myClass.save(insert: true)