With all of the tutorials and examples for Grails there is often no reference to packaging your Groovy classes.
So, I decided to convert all of my project into packages including Acegi.
Using IntelliJ 7.04 & JetGroovy plugin v1.6.18593, I was able to refactor most of my classes without issue. Service classes required me to add the imports manually... no problem there. Of course its odd that I had to manually add imports to java.util.*
Acegi classes required to add the package to the class declaration within grails-app/conf/SecurityConfig.groovy
loginUserDomainClass = "com.mycorp.projname.security.User"
authorityDomainClass = "com.mycorp.projname.security.Role"
requestMapClass = "com.mycorp.projname.security.Requestmap"
(thanks to Burt Beckwith)
Wednesday, August 27, 2008
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
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:
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.
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'
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'
- Basically tellFortunes() accepts date and fortune as parameters, defines a code block which prints the message with the date and fortune in it
- This calls postFortune(closure) accepting the code block (called a Closure in Groovy)
- the first use of postFortune binds the date to the inbound closure and holds a reference in postFortune
- 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...
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.
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.
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)
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)
UI changes - on the fly?
I love the rapid feedback from Grails, but too much of a good thing...
I was changing some of the field presentation from a <input to <g:select and it was not showing up on the page.
So:
Refreshed the browser page - nope
Shut down the app, at the command prompt typed 'grails clean', restarted - negative
Finally I closed the browser and reopened it, and viola!
I was changing some of the field presentation from a <input to <g:select and it was not showing up on the page.
So:
Refreshed the browser page - nope
Shut down the app, at the command prompt typed 'grails clean', restarted - negative
Finally I closed the browser and reopened it, and viola!
Tuesday, May 6, 2008
Composite Key in Domain Class
Using the default id column in the database works great. However, if you are facing a "legacy" database design and unable to alter the table definition, you have a bit of work to do.
Defining your domain class:
1. The class must implement Serializable
class MyClass implements Serializable {
...
static mapping = {
table "my_table"
version false
id composite: ["myFieldOne","myFieldTwo"]
...
2. The scaffolding will by default setup all communication via the id column. The id column in this case is a serialized value. However, there is an issue with Grails v1.0.2 that does not deserialize the value properly. So, for now you have to work around using the primary key:
3. Add new Method to domain class - define a map of the values
def getPK ( ) {
["myFieldOne":myFieldOne, "myFieldTwo":myFieldTwo]
}
4. List view - Add a link on the page to show details for the current record
<span class="actionButton">
<g:link action="show" params="${myClass.getPK()}">Detail </g:link>
</span>
NOTE: "id=" was changed to "params=" and you are passing in the map returned from myClass.getPK()
5. MyClassController.groovy - modify the retrieval code in the show method
def show = {
def myClass = MyClass.get( new MyClass(params) ) //here are your inbound params
if(!myClass) {
flash.message = "MyClass not found with ${params}"
redirect(action:list)
}
else { return [ myClass: myClass] }
}
Defining your domain class:
1. The class must implement Serializable
class MyClass implements Serializable {
...
static mapping = {
table "my_table"
version false
id composite: ["myFieldOne","myFieldTwo"]
...
2. The scaffolding will by default setup all communication via the id column. The id column in this case is a serialized value. However, there is an issue with Grails v1.0.2 that does not deserialize the value properly. So, for now you have to work around using the primary key:
3. Add new Method to domain class - define a map of the values
def getPK ( ) {
["myFieldOne":myFieldOne, "myFieldTwo":myFieldTwo]
}
4. List view - Add a link on the page to show details for the current record
<span class="actionButton">
<g:link action="show" params="${myClass.getPK()}">Detail </g:link>
</span>
NOTE: "id=" was changed to "params=" and you are passing in the map returned from myClass.getPK()
5. MyClassController.groovy - modify the retrieval code in the show method
def show = {
def myClass = MyClass.get( new MyClass(params) ) //here are your inbound params
if(!myClass) {
flash.message = "MyClass not found with ${params}"
redirect(action:list)
}
else { return [ myClass: myClass] }
}
Wednesday, April 23, 2008
Java Programmer looking at Groovy
Some things that might help you when looking over Groovy code with "Java" eyes:
- everything is an object!
- method parenthesis are optional
- semicolons at the end of the line are optional
- elements in brackets are either a List() or a Map()
- Lists are comma separated elements
- def myList = ["one", "two", "three"]
- Maps are name value : pairs separated by commas
- def myMap = [dairy:"cheese", meat:"chicken", shopper:"Mario"]
- the return keyword is optional
- by default the result of the last statement in a code block is returned
- The questionmark protects you from null pointer exceptions
- println myObj?.myAttribute will print myAttribute if myObj is not null, no error thrown
- Strings can be single or double quoted
- Double quoted strings can have variables nested with using the ${ var } syntax or simply $var
- def var = "Crusty"
- println "The clown $var unnerves me"
- The ampersand allows you to call a method from an object
- println myObj.&someMethod
- The at symbol allows you to directly address a classes attribute
- myObj.@fieldName = "somevalue"
- A range of elements can be defined using an elipsis
- (1..10).each {println "toe number $it"}
- Now that opened up a couple of other items....
- Code in braces is called a closure (see other blog "What are Groovy Closures?")
- the keyword "it" is the default iterator for a closure
- The double less than symbol is used for appending with StringBuffer, Lists, Sockets, Files & Writers
- Named Parameters
- For example, create a new user with a name and ssn provided
- new User (name:"Gorge", ssn: "123-45-9999")
- Parameter Defaults
- def myMethod (String name, String type="chef") { //somecodehere }
What are Groovy Closures?
One of the more difficult concepts that I had to get my head around as a Java developer was the concept of Groovy closures.
Closures:
Closures:
- are classes (defined by Closure.class)
- like defining new Closure() {}
- are similar to anonymous classes
- are defined with braces { and }
- provide one default iterator parameter (if none specified) called "it" for iterator
- (1..3).each {println "number $it" }
- can accept parameters
- (1..3).each {someNum -> println "Some number $someNum" }
- can be predefined with a variable name and invoked (they are classes after all)
- def myClosure = {x, y -> println "you sent $x and $y" }
- myClosure 'Feet', 'Hans'
- prints: you sent Feet and Hans
- do not require an input parameter if its not used, do not provide one
- def myClosure = { -> "returning this dude" }
- println myClosure
Tuesday, April 22, 2008
Grails IntelliJ Plugin
The Grails plugin for IntelliJ Idea 7.0 was updated to version 1.5. I was using the previous versions and they were troublesome. The 1.5 release seems to be working well so far.
Plugin Site: http://www.jetbrains.com/idea/features/groovy_grails.html
One point of note, I could not find a way to perform "grails clean" within IntelliJ so I have to have a command prompt open to clean the project now and again when things don't seem to be behaving as expected. Sometimes you have to clean when one of your domain classes changes significantly.
Plugin Site: http://www.jetbrains.com/idea/features/groovy_grails.html
One point of note, I could not find a way to perform "grails clean" within IntelliJ so I have to have a command prompt open to clean the project now and again when things don't seem to be behaving as expected. Sometimes you have to clean when one of your domain classes changes significantly.
New Grails User
A friend of mine, Ken Rimple, recently turned me onto Groovy on Grails. When I first heard of it, I wasn't interested in learning another something on something language/framework pair. But after a quick spin through the tutorial I was hooked.
The best thing about it is that its all based in Java and deploys as a war on your favorite web server. As a matter of fact it builds upon many of the top Java technologies such as Hibernate, Spring, log4J, JUnit, Jetty, HSQL, Quartz, Sitemesh and ANT.
Now that I have my prototype ready, the devil will be in the details to get it finely polished.
The best thing about it is that its all based in Java and deploys as a war on your favorite web server. As a matter of fact it builds upon many of the top Java technologies such as Hibernate, Spring, log4J, JUnit, Jetty, HSQL, Quartz, Sitemesh and ANT.
Now that I have my prototype ready, the devil will be in the details to get it finely polished.
- Ken Rimple's Blog: http://www.rimple.com/tech/
- Groovy Site: http://groovy.codehaus.org/
- Grails Site: http://grails.codehaus.org/
Subscribe to:
Posts (Atom)