<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-12369778</id><updated>2011-09-08T09:32:57.084-07:00</updated><title type='text'>Foxgem's Groovy Notebook</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-12369778.post-6216482230789869971</id><published>2010-12-12T02:42:00.000-08:00</published><updated>2010-12-12T02:48:41.751-08:00</updated><title type='text'>Complement of mrhaki's "Run Java Application From Build Script"</title><content type='html'>If you want to know how to run java app in build.gradle, you should check &lt;a href="http://mrhaki.blogspot.com/2010/09/gradle-goodness-run-java-application.html"&gt;mrhaki's post: Run Java Application From Build Script&lt;/a&gt;. That post gave me big help on this task. In this post, I will give a complement on a topic that missed in that post.&lt;br /&gt;In my app, I have some code that needs data from command line, such as:&lt;br /&gt;&lt;pre&gt;    def input= new DataInputStream(System.in)&lt;br /&gt;    println "Work Dir:"&lt;br /&gt;    def rootDir= input.readLine()&lt;br /&gt;   println "Test Plan:"&lt;br /&gt;   def testplan= input.readLine()&lt;br /&gt;&lt;/pre&gt;When I applied the method I learned from mrhaki's post, I could run my app, but have no chance to input some data from command line. The app exited with an exception because it did not pause and got null from command line! After digging the gradle api doc, I found I could set the value of standardInput property of JavaExec task. So I made some change to my build.gradle:&lt;br /&gt;&lt;pre&gt;task (processplan, dependsOn: classes, type: JavaExec) {&lt;br /&gt;   main= '....'&lt;br /&gt;   classpath= sourceSets.main.runtimeClasspath&lt;br /&gt;   standardInput= System.in&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Note that the last line. I set the value of standardInput to "System.in". Then I ran the build task again. This time, the app stopped to wait for user's inputting. And app worked fine!&lt;br /&gt;So, If your app needs user to input some data from command line, you'd better set the value of standardInput. Hope this tip is help for you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-6216482230789869971?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/6216482230789869971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=6216482230789869971' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/6216482230789869971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/6216482230789869971'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2010/12/complement-of-mrhakis-run-java.html' title='Complement of mrhaki&apos;s &quot;Run Java Application From Build Script&quot;'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-5951956859335723332</id><published>2010-06-14T01:23:00.000-07:00</published><updated>2010-06-14T01:32:58.281-07:00</updated><title type='text'>Fix "junitvmwatcherXXX.properties could not be found" during grails unit test</title><content type='html'>Recently, I forked grails-core to fix the &lt;a href="http://jira.codehaus.org/browse/GRAILS-5876"&gt;GRAILS-5876&lt;/a&gt;. But, when I modified and ran "gradlew testSingleMockUtils", I got a java.io.FileNotFoundException:&lt;br /&gt;&lt;blockquote&gt;java.io.FileNotFoundException: D:\grails\grails-core\junitvmwatcher476409304.properties(no such file or directory)&lt;/blockquote&gt;Compile phase (sources compile and tests compile) was ok, the exception was thrown from the Unit Test phase, and the unit test didn't be launched at all. I tried another build approach, "ant -Dtest=MockUtils test", and got the same exception.&lt;br /&gt;This is very confusing, beacause I launched unit tests successfully under grails 1.2. Google could not give me any suggest this time. Taking a close look at the output on the screen, I found some messages which showed the memory was not enough for running unit tests. That is it!&lt;br /&gt;I compared the unit-test.xml in my local git repository with the one in grails 1.2 dist, and found there was some diffrence in the target "run-test" between these two files:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;my local git repository:&lt;br /&gt;&lt;/li&gt;&lt;pre&gt;  &amp;lt;target name="run-test" depends="defineTestingScope, build-test,-jar-app-files" unless="_skipTests_"&amp;gt;&lt;br /&gt;  &amp;lt;delete dir="${grails.test.reports}" /&amp;gt;&lt;br /&gt;  &amp;lt;mkdir dir="${grails.test.reports}" /&amp;gt;&lt;br /&gt;  &amp;lt;echo&amp;gt;Test being run ${test} from ${grails.test.build}&amp;lt;/echo&amp;gt;&lt;br /&gt;  &amp;lt;junit fork="true" forkmode="perBatch" clonevm="true" printsummary="on"&amp;gt;&lt;br /&gt;      &amp;lt;!-- only fork VMs as needed --&amp;gt;&lt;br /&gt;      &amp;lt;sysproperty key="grails.cli.testing" value="true"/&amp;gt;&lt;br /&gt;      &lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;&amp;lt;jvmarg line="-server -Dgroovy.grails.joint=${groovy.grails.joint} ${args.memory} ${args.debug}"/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;li&gt;grails 1.2 dist:&lt;/li&gt;&lt;pre&gt;  &amp;lt;target name="run-test" depends="defineTestingScope, build-test,-jar-app-files" unless="_skipTests_"&amp;gt;&lt;br /&gt;  &amp;lt;delete dir="${grails.test.reports}" /&amp;gt;&lt;br /&gt;  &amp;lt;mkdir dir="${grails.test.reports}" /&amp;gt;&lt;br /&gt;  &amp;lt;echo&amp;gt;Test being run ${test} from ${grails.test.build}&amp;lt;/echo&amp;gt;&lt;br /&gt;  &amp;lt;junit fork="true" forkmode="perBatch" clonevm="true" printsummary="on"&amp;gt;&lt;br /&gt;      &amp;lt;!-- only fork VMs as needed --&amp;gt;&lt;br /&gt;      &amp;lt;sysproperty key="grails.cli.testing" value="true"/&amp;gt;&lt;br /&gt;      &lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;&amp;lt;jvmarg value="-server" /&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;     &amp;lt;jvmarg value="-Xmx1G" /&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;     &amp;lt;jvmarg value="-Xms256m" /&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;     &amp;lt;jvmarg value="-XX:MaxPermSize=256m"/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;     &amp;lt;jvmarg value="-Dgroovy.grails.joint=${groovy.grails.joint}" /&amp;gt;&lt;/span&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;/ul&gt;The solution is obvious. Replacing the last statement in the former build script with the last 5 statements in the latter, I could run the unit test successfully again. Now, let's fix the gradle build script. Open the unit-test.gradle, and search the following statement:&lt;br /&gt;&lt;blockquote&gt;options.forkOptions.jvmArgs = ["-server", "-Xmx1500m", "-Xms256m", "-XX:MaxPermSize=256m", "-Dgroovy.grails.joint=true"]&lt;/blockquote&gt;Change &lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;"-Xmx1500m"&lt;/span&gt; to &lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;"-Xmx1G"&lt;/span&gt;, and save the file. Then you should run "gradlew testSingleMockUtils" successfully.&lt;br /&gt;There are other reasons can cause "junitvmwatcherXXX.properties could not be found", you could get some advices from google using the key works "ant junitvmwatcherXXX.properties".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-5951956859335723332?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/5951956859335723332/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=5951956859335723332' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/5951956859335723332'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/5951956859335723332'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2010/06/fix-junitvmwatcherxxxproperties-could.html' title='Fix &quot;junitvmwatcherXXX.properties could not be found&quot; during grails unit test'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-389773244987987875</id><published>2010-06-12T21:50:00.000-07:00</published><updated>2010-06-12T21:54:52.232-07:00</updated><title type='text'>Return partial Domain Class</title><content type='html'>Reading the description of &lt;a href="http://jira.codehaus.org/browse/GRAILS-5791"&gt;GRAILS-5791&lt;/a&gt;, I found &lt;a href="http://stackoverflow.com/questions/1700668/grails-jsonp-callback-without-id-and-class-in-json-file/1701258"&gt;this interesting question&lt;/a&gt;. The asker expects the JSON returned by "domain as JSON" not including some properties he lists.&lt;br /&gt;There are serval solutions, the comments following that question have list two ones. Here I will give my own one. In my way, I will add one method to the domain classes in the grails app: part, which returns a map including or excluding some properties of a domain class. Then you can render the result map as JSON. The following shows how to use this method:&lt;br /&gt;&lt;pre&gt;  def job= Job.get(1)&lt;br /&gt;&lt;br /&gt; //include&lt;br /&gt; render job.part(include:['categorys', 'source']) as JSON&lt;br /&gt;&lt;br /&gt; //except&lt;br /&gt; render job.part(except:['id', 'version']) as JSON&lt;br /&gt;&lt;/pre&gt;This sample is very intuitive. The first statement will result in a JSON &lt;b&gt;only&lt;/b&gt; including categorys and source; the JSON rendered by second statement will include all the properties of the domain class except id and version.&lt;br /&gt;Using part, you can make your life easier and also make your code more grace.&lt;br /&gt;Now, let's see how to adding the part method to the domain classes:&lt;br /&gt; &lt;ul&gt;&lt;li&gt;how: In groovy, in order to add a method to an object dynamiclly, we can use mop.&lt;/li&gt;&lt;li&gt;when: In grail app, there is a file named BootStrap.groovy, where is a good place to do some initialization works.&lt;/li&gt;&lt;/ul&gt;When we know how and when to do that job, here is the detail:&lt;br /&gt;&lt;pre&gt;import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor as Events&lt;br /&gt;&lt;br /&gt;class BootStrap {&lt;br /&gt; def grailsApplication&lt;br /&gt;&lt;br /&gt; def excludedProps = [Events.ONLOAD_EVENT,&lt;br /&gt;    Events.BEFORE_DELETE_EVENT, Events.AFTER_DELETE_EVENT,&lt;br /&gt;    Events.BEFORE_INSERT_EVENT, Events.AFTER_INSERT_EVENT,&lt;br /&gt;    Events.BEFORE_UPDATE_EVENT, Events.AFTER_UPDATE_EVENT]&lt;br /&gt;&lt;br /&gt;  def init = { servletContext -&gt;&lt;br /&gt;     grailsApplication.domainClasses.each{ domainClass -&gt;&lt;br /&gt;         domainClass.metaClass.part= { m -&gt;&lt;br /&gt;             def map= [:]&lt;br /&gt;             if(m.'include'){&lt;br /&gt;                 m.'include'.each{&lt;br /&gt;                     map[it]= delegate."${it}"&lt;br /&gt;                 }&lt;br /&gt;             }else if(m.'except'){&lt;br /&gt;                 m.'except'.addAll excludedProps&lt;br /&gt;                 def props= domainClass.persistentProperties.findAll {&lt;br /&gt;                     !(it.name in m.'except')&lt;br /&gt;                 }&lt;br /&gt;                 props.each{&lt;br /&gt;                     map[it.name]= delegate."${it.name}"&lt;br /&gt;                 }&lt;br /&gt;             }&lt;br /&gt;             return map&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt;  }&lt;br /&gt;  def destroy = {&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;You also can change this to a plugin, then you can use this feature across different grails apps. Hope you enjoy this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-389773244987987875?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/389773244987987875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=389773244987987875' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/389773244987987875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/389773244987987875'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2010/06/return-partial-domain-class.html' title='Return partial Domain Class'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-222079342685124835</id><published>2010-05-01T06:13:00.000-07:00</published><updated>2010-05-01T06:17:57.945-07:00</updated><title type='text'>Summary on Transaction Rollback in Grails</title><content type='html'>This post is a summary on how to rollback transactions in grails application, and highlights a stain in grails reference document.&lt;br /&gt;&lt;h3&gt;Rollback Programmatic Transactions&lt;/h3&gt;In Grails, we can use withTransaction method on domain classes to deal with programmatic transactions. This method takes a closure, which receives a TransactionStatus instance as its first argument. In order to rollback transactions, we can invoke the setRollbackOnly() method on TransactionStatus. Here is an example from grails reference document:&lt;br /&gt;&lt;pre&gt;Account.withTransaction { status -&amp;gt;&lt;br /&gt; def source = Account.get(params.from)&lt;br /&gt; def dest = Account.get(params.to)&lt;br /&gt; def amount = params.amount.toInteger()&lt;br /&gt;&lt;br /&gt; if(source.active) {&lt;br /&gt;     source.balance -= amount&lt;br /&gt;     if(dest.active) {&lt;br /&gt;         dest.amount += amount&lt;br /&gt;     }else {&lt;br /&gt;         status.setRollbackOnly()&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;h3&gt;Rollback Declarative Transactions&lt;/h3&gt;With service, it's not a big deal to implement declarative transactions in grails. If you want to rollback, all that you need to do is to throw a RuntimeException or its subclass in service method. The following code shows how to do this job:&lt;pre&gt;class DomainbService {&lt;br /&gt; boolean transactional = true&lt;br /&gt;&lt;br /&gt; def service2() {&lt;br /&gt;     new B().save()&lt;br /&gt;     throw new RuntimeException("save b failed!")&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;There is one thing you should notice: grails refernce document doesn't spot the exception you throw in method must be &lt;b&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;an RuntimeException object&lt;/span&gt;&lt;/b&gt;. Actually, if you throw an Exception object, the transaction doesn't rollback. You can have a test to verify this. And in spring reference document, you can find that you should throw an "&lt;b&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;unchecked&lt;/span&gt;&lt;/b&gt;" exception to trigger a rollback when you are using declarative transaction.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-222079342685124835?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/222079342685124835/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=222079342685124835' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/222079342685124835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/222079342685124835'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2010/05/summary-on-transaction-rollback-in.html' title='Summary on Transaction Rollback in Grails'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-4395889806776953951</id><published>2010-03-28T04:01:00.000-07:00</published><updated>2010-03-28T04:10:34.203-07:00</updated><title type='text'>A simple PageBus taglib</title><content type='html'>Today, javascript component is a must-be part in many web applications. As more and more such components are included in the web page, the difficulty of interaction between these components is increasing. There is a way to simplify this problem: Event. With it, what you need to do is to register your callback with the event that will be fired by your interested component, and to wait for the magic happened.&lt;br /&gt;&lt;br /&gt;All seems good, but "how to register your callback with the component you care about?" If there is a long distance between those two component, this register is not a easy work. That's why I choose &lt;a href="http://developer.tibco.com/pagebus/default.jsp"&gt;PageBus&lt;/a&gt; as the backbone of the interaction between components in my project.&lt;br /&gt;&lt;br /&gt;If you are familiar with jms, you can simply think pagebus as a jms implemention in the web page. Here is an intuitive picture from its document:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://public.blu.livefilestore.com/y1peb2UyeXxoDtSXrpiTX2syIS1-5xprcmozFth40IyXEwaNdYMlsU3Oj3tJu3K_qDuFchn4x8G4Resn-MiWwsUlQ/pagebus.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;The biggest benefit you can acquire from PageBus is full decoupling between those two parts of interaction. One component doesn't bother another. The whole interaction process is:&lt;br /&gt;   &lt;ol&gt;&lt;li&gt;subscriber registers a callback with a well-known topic&lt;br /&gt;   &lt;/li&gt;&lt;li&gt;publisher delivers a message to a well-known topic&lt;br /&gt;   &lt;/li&gt;&lt;li&gt;callback will be invoked when subscriber gets a message from the topic it registered.&lt;/li&gt;&lt;/ol&gt;Usage of PageBus is very simple, you just need:&lt;br /&gt;   &lt;ol&gt;&lt;li&gt;To Download it from &lt;a href="http://developer.tibco.com/gi/product_resources_pagebus2.jsp?tab=downloads"&gt;here&lt;/a&gt; and include pagebus.js in your page.&lt;br /&gt;   &lt;/li&gt;&lt;li&gt;To register:&lt;br /&gt;&lt;pre&gt;    window.PageBus.subscribe(topic, scope, function(sub, message, data){&lt;br /&gt;       .....&lt;br /&gt;   }, data);&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;To publish:&lt;br /&gt;&lt;pre&gt;    window.PageBus.publish(topic, message);&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;At the end of this post, I will create a simple PageBus taglib to show how to use it.&lt;br /&gt;&lt;p&gt;First, let's create a new Grails App, grails create-app pagebus.&lt;/p&gt;Next, enter your application directory and create a taglib, grails create-tag-lib pagebus.&lt;br /&gt;&lt;p&gt;In your taglib file, write down the code:&lt;/p&gt;&lt;pre&gt;class PagebusTagLib {&lt;br /&gt;  &lt;br /&gt;   static namespace= "pagebus"&lt;br /&gt;  &lt;br /&gt;   static final DEFAULT_TOPIC= "app.topic.default"&lt;br /&gt;  &lt;br /&gt;   def include= {&lt;br /&gt;       def js= """&lt;br /&gt;           &amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;               var App = {&lt;br /&gt;                 publish: function(topic, message){&lt;br /&gt;                      window.PageBus.publish(topic, message);&lt;br /&gt;                 },&lt;br /&gt;                 subscribe: function(topic, scope, fn, data){&lt;br /&gt;                     window.PageBus.subscribe(topic, scope, fn, data);&lt;br /&gt;                 }&lt;br /&gt;               }&lt;br /&gt;           &amp;lt;/script&amp;gt;&lt;br /&gt;           """&lt;br /&gt;       out &amp;lt;&amp;lt; g.javascript(src:'pagebus/pagebus.js')&amp;lt;&amp;lt; js&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;copy pagebus.js to the pagebus/web-app/js/pagebus.&lt;br /&gt;&lt;p&gt;In the app index.gsp, &lt;/p&gt;    &lt;ul&gt;&lt;li&gt;add &amp;lt;pagebus:include/&amp;gt; to &amp;lt;head&amp;gt; section;&lt;br /&gt;   &lt;/li&gt;&lt;li&gt;add a button in your &amp;lt;body&amp;gt; for publishing a "hello world!" message&lt;br /&gt;&lt;pre&gt;    &amp;lt;input type="button" value="publish"&lt;br /&gt;      onclick="App.publish('topic1', {message: 'Hello World!'})"&amp;gt;   &lt;/pre&gt;&lt;/li&gt;&lt;li&gt;register your callback to show the message the subscriber received.&lt;br /&gt;&lt;pre&gt;    &amp;lt;g:javascript&amp;gt;&lt;br /&gt;       App.subscribe("topic1", null, function(sub, message, data){&lt;br /&gt;                       alert(message.message);&lt;br /&gt;                   }, null);&lt;br /&gt;   &amp;lt;/g:javascript&amp;gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;Finally, run your app and test it. Now, you just create your first PageBus application!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-4395889806776953951?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/4395889806776953951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=4395889806776953951' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/4395889806776953951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/4395889806776953951'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2010/03/simple-pagebus-taglib.html' title='A simple PageBus taglib'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-2520200012475189959</id><published>2010-02-25T17:45:00.000-08:00</published><updated>2010-02-25T17:52:40.738-08:00</updated><title type='text'>Event Applied: When to minify js and css files in your grails projects</title><content type='html'>&lt;a href="http://oreilly.com/catalog/9780596529307"&gt;High Performance Web Sites&lt;/a&gt; is a great book discussing how to accelerate your web sites. The rule 10 in this book is "Minify JavaScript", which I think can be applied in my grails projects easily.&lt;br /&gt;There are some tools can be used to minify your js and css files, such as &lt;a href="http://crockford.com/javascript/jsmin"&gt;JSMIN&lt;/a&gt;, &lt;a href="http://dojotoolkit.org/docs/shrinksafe"&gt;Dojo compressor&lt;/a&gt;, Edwards' &lt;a href="http://dean.edwards.name/packer/"&gt;Packer&lt;/a&gt; and &lt;a href="http://yuilibrary.com/downloads/#yuicompressor"&gt;YUI Compressor&lt;/a&gt;. Since I was using &lt;a href="http://yuilibrary.com/downloads/"&gt;YUI&lt;/a&gt; these days, I decide to choose YUI Compressor to do this job.&lt;br /&gt;The first solution comes into my mind is to create a Grails Script to process js and css files by "grails create-script". On second thought, I think this is not an appropriate one:&lt;br /&gt; &lt;ul&gt;&lt;li&gt;If it changes the name of js or css files, you have to change the corresponding files name in your frontend source code. This is annoying and error-prone.&lt;/li&gt;   &lt;li&gt;If it doesn't change the files' name, it will modify the content of js and css source files. This can also make you unhappy: facing a minified js and css files, you can even hardly change the name of a variable. It is a big problem!&lt;/li&gt;&lt;/ul&gt;So the solution what I need is: a) which can minify js and css files, b) which can not change the name of files and the content of source code. By controlling the grails application build process, I can achieve my goal. Now, I can hear what you said: Event. That's  it! We just need to create an event handler to do this job, and the event we should focus on is : CreateWarStart，which will be fired before grails war your grails application.&lt;br /&gt;Since we know how to do it, let's do it:&lt;br /&gt; &lt;ol&gt;&lt;li&gt;Downloading &lt;a href="http://yuilibrary.com/downloads/#yuicompressor"&gt;YUI Compressor&lt;/a&gt;，which usage is simple："java -jar yuicompressor-x.x.x.jar inputfile -o outputfile".&lt;/li&gt;   &lt;li&gt;Unzipping it and copying YUI Compressor binary to lib directory in your grails project.&lt;/li&gt;   &lt;li&gt;Creating a "_Event.groovy" in your grails project's script directory：&lt;br /&gt;&lt;pre&gt;   eventCreateWarStart = { warLocation, stagingDir -&gt;&lt;br /&gt;     stagingDir.eachFileRecurse{&lt;br /&gt;         if(it.name.endsWith('.js')||it.name.endsWith('.css')){&lt;br /&gt;             def f= it.absolutePath&lt;br /&gt;          &lt;br /&gt;             println "Minizing ${f}......................"&lt;br /&gt;             ant.java(jar:"${basedir}/lib/yuicompressor-2.4.2.jar",&lt;br /&gt;                      fork: true){&lt;br /&gt;                 arg(value: "${f}")&lt;br /&gt;                 arg(value: "-o")&lt;br /&gt;                 arg(value: "${f}")&lt;br /&gt;             }&lt;br /&gt;             println "Minizing ${f}.................done!"&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;Only 3 steps, you can minify all the js and css files in your grails projects. Typing "grails war" to test it. Of course, it is not perfect: it always does minification and doesn't care if the js or css files have been minified. If there are many minified files in your projects, which could come from the js lib(such as yui/dojo/jquery) you include, it will waste your cpu time. The way to fix it is simple: you can define an excluding directory list, all directories in it will not be processed.&lt;br /&gt;Another interesting thing is: if there are some errors in your javascript source, YUI Compressor won't minify it(this won't break the build process). So you get an additional checking javascript function ;). Hope you enjoy it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-2520200012475189959?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/2520200012475189959/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=2520200012475189959' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/2520200012475189959'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/2520200012475189959'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2010/02/event-applied-when-to-minify-js-and-css.html' title='Event Applied: When to minify js and css files in your grails projects'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-7311610453302753046</id><published>2010-01-16T04:54:00.000-08:00</published><updated>2010-01-16T05:00:52.723-08:00</updated><title type='text'>A Note On Validation Non Domain classes</title><content type='html'>Validation non domain classes is a very cool grails feature, but when you use it, you should remember one thing: &lt;span style="font-weight: bold; color: rgb(0, 204, 204);"&gt;&lt;span style="color: rgb(51, 102, 255);"&gt;Invoking clearErrors method before re-validation, specially last validate invocation returns false&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;A piece of script means a lot of things:&lt;br /&gt;&lt;pre&gt;def u= new foxgem.test1.User1(name:'')&lt;br /&gt;assert u.validate() == false&lt;br /&gt;u.name='foxgem'&lt;br /&gt;assert u.validate() == false&lt;br /&gt;u.clearErrors()&lt;br /&gt;assert u.validate() == true&lt;/pre&gt;Here, there is a blank constraint on name property of foxgem.test1.User1. But the note is not necessary to Domain Class, so the second assert statement in the script will failed when foxgem.test1.User1 is a domain class.&lt;br /&gt;This is very confusing, I hope this will be imporved in the future of Grails.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-7311610453302753046?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/7311610453302753046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=7311610453302753046' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/7311610453302753046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/7311610453302753046'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2010/01/note-on-validation-non-domain-classes.html' title='A Note On Validation Non Domain classes'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-5603511707933164505</id><published>2009-11-19T21:27:00.000-08:00</published><updated>2009-11-19T21:32:00.207-08:00</updated><title type='text'>Getting Beans in Grails Console</title><content type='html'>It's convenient to run groovy snippets in Grails Console. At some time, these snippets might depend on some Service Object in your grails app. In Grails world, you can solve this kind of dependence using DI. Please check out "8.3 Dependency Injection and Services" in Grails reference document for more information. But now we are running groovy script in grails console, how can we get that service object we need? The reference document doesn't give us an answer directly.&lt;br /&gt;Since Grails is based on Spring, we can resort to ApplicationContext. Now the problem is how to get the ApplicationContext instance. After reading "%GRAILS_HOME%/scripts/Console.groovy", you can find a bind-variable named "ctx", which points to the ApplcationContext Object. So, we can use it to get the dependent object used by the script in the console. Here is an example:&lt;br /&gt;&lt;pre&gt;def executionService= ctx.getBean('executionService')&lt;br /&gt;def processInstances= executionService.createProcessInstanceQuery().processDefinitionId('StateSequence-1').list()&lt;br /&gt;def executions = processInstances.collect{&lt;br /&gt;  def activeNames= it.findActiveActivityNames()&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-5603511707933164505?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/5603511707933164505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=5603511707933164505' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/5603511707933164505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/5603511707933164505'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/11/getting-beans-in-grails-console.html' title='Getting Beans in Grails Console'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-9094622671335223386</id><published>2009-11-04T04:07:00.000-08:00</published><updated>2009-11-12T19:39:36.187-08:00</updated><title type='text'>JSecurity Mini Guide</title><content type='html'>This post is a summary on what I learned from my project during I was implementing its security function. The tools and their versions are:&lt;ul&gt;&lt;li&gt;Jsecurity 0.9&lt;/li&gt;&lt;li&gt;Grails 1.1.1&lt;/li&gt;&lt;li&gt;Grails JSecurity Plugin 0.4.1&lt;/li&gt;&lt;/ul&gt;Maybe the versions of the tools you are using are not the same as me, but I think that is not a big question and migration is not too difficult, ;)&lt;br /&gt;First, let's check grails-jsecurity-plugin. As I said in my &lt;a href="http://foxgem.blogspot.com/2009/10/how-to-get-actions-of-controller.html"&gt;last post&lt;/a&gt;, this plugin is awesome! If you decide to take jescurity as the fundation of your security function, you should also consider to examine this plugin. Trust me, it will sppeed up your development. Now, it's time to see how to use it:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;grails install-plugin jsecurity, or you can download it from http://grails.org/plugin/shiro, then grails install-plugin plugin-path&lt;/li&gt;&lt;li&gt;grails quick-start, this is the simplest way to use this plugin. In this step, you will get 6 security domain classes (including JsecPermission, JsecRole, JsecUser, JsecRolePermissionRel, JsecUserPermissionRel and JsecUserRoleRel), a DbRealm(JsecDbRealm), an AuthController and a login page. All of those classes will meet your need at most of time. If you are using ladp, you can try the LadpRealm provided by this plugin, using "grails create-ldap-realm".&lt;/li&gt;&lt;li&gt;grails create-filters filters-name, this filter will inteceptor all the request, if you want to protect something, this is a good place. To protect what you want to keep, you can leaveage the access control dsl made by this plugin:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;need authentication? accessControll { true }&lt;br /&gt;&lt;/li&gt;&lt;li&gt;has role? accessControll{ role('admin') }&lt;br /&gt;&lt;/li&gt;&lt;li&gt;is permitted? accessControll{ permission("${actionName}:${actionName}:${params.id}") }&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;At this point, you have implemented an auth controller and a login page, the security domain classes and a request inteceptor, the rest of the work are:&lt;br /&gt;&lt;li&gt;Using the tag lib to enhance your UI.&lt;/li&gt;&lt;li&gt;Configurating WildcardPermission as the permission class of your application. In BootStrap.groovy:&lt;br /&gt;&lt;pre&gt;new JsecPermission(type: 'org.jsecurity.authz.permission.WildcardPermission', possibleActions: '*').save()&lt;/pre&gt;Except WildcardPermission, You also can use AllPermission to represent a permission owned by administrator:&lt;br /&gt;&lt;pre&gt;new JsecPermission(type: 'org.jsecurity.authz.permission.AllPermission', possibleActions: '*').save()&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Implementing the CRUD of those security domain classes. Here, I will show you some samples:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;add user: new JsecUser(username: 'foxgem', passwordHash: new Sha1Hash('foxgem').toHex()).save()&lt;br /&gt;&lt;/li&gt;&lt;li&gt;add role: new JsecRole(name: 'admin').save()&lt;br /&gt;&lt;/li&gt;&lt;li&gt;assign a role to a user:&lt;br /&gt;&lt;/li&gt;&lt;pre&gt;def role= JsecRole.get(1)&lt;br /&gt;def user= JsecUser.get(1)&lt;br /&gt;new JsecUserRoleRel(role: role, user: user).save()&lt;br /&gt;&lt;/pre&gt;&lt;li&gt;assign a permission to a user:&lt;br /&gt;&lt;/li&gt;&lt;pre&gt;def user= JsecUser.get(1)&lt;br /&gt;def permission= JsecPermission.get(1)&lt;br /&gt;// In this sample, the target property is a wildcard permission string, eg: 'controller:action'.&lt;br /&gt;// Please to check WildcardPermission in JSecurity API document for more information.&lt;br /&gt;new JsecUserPermissionRel(permission: permission, user: user, target: params.target, actions: '*').save()&lt;/pre&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;Now, you have all the knowledge about grails jsecurity plugin. Next, you should understand the fundation of this plugin - JSecurity. The more you know about it, the more you can do with it. The following diagram shows the key concepts you must know, for more information, please read jsecurity api document.&lt;br /&gt;&lt;img src="http://bwttpa.blu.livefilestore.com/y1pLkfIkNOAdmx_xG-7Fz5SGD2TgeCECUp7Oq9zhV6BM6XGDKbxFnp8MmQKfHdPco2TnEY6M6iUNdsfFV009SwmEw/JSecurity.png" /&gt;&lt;br /&gt;At the end of this post, I will list some helpful resources:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.brucephillips.name/blog/index.cfm/2009/4/5/An-Introduction-to-Ki-formerly-JSecurity--A-Beginners--Tutorial-Part-1"&gt;An Introduction to Ki (formerly JSecurity) – A Beginner's Tutorial Part 1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.brucephillips.name/blog/index.cfm/2009/4/5/An-Introduction-to-Ki-formerly-JSecurity--A-Beginners--Tutorial-Part-2"&gt;An Introduction to Ki (formerly JSecurity) – A Beginner's Tutorial Part 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.brucephillips.name/blog/index.cfm/2009/4/5/An-Introduction-to-Ki-formerly-JSecurity--A-Beginners--Tutorial-Part-3"&gt;An Introduction to Ki (formerly JSecurity) – A Beginner's Tutorial Part 3&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.brucephillips.name/blog/index.cfm/2009/4/5/An-Introduction-to-Ki-formerly-JSecurity--A-Beginners--Tutorial-Part-4"&gt;An Introduction to Ki (formerly JSecurity) – A Beginner's Tutorial Part 4&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.brucephillips.name/blog/index.cfm/2009/5/1/An-Introduction-to-Ki-formerly-JSecurity--A-Beginners-Tutorial-Part-5"&gt;An Introduction to Ki (formerly JSecurity) – A Beginner's Tutorial Part 5&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.cacoethes.co.uk/blog/groovyandgrails/grails-jsecurity-plugin-version-0-4-released"&gt;Grails JSecurity plugin 0.4 released&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://grails.org/plugin/shiro"&gt;Grails Shiro Plugin Home&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.jsecurity.org/api/"&gt;jsecurity API&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-9094622671335223386?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/9094622671335223386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=9094622671335223386' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/9094622671335223386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/9094622671335223386'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/11/jsecurity-mini-guide.html' title='JSecurity Mini Guide'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-1412981266228747171</id><published>2009-10-20T23:42:00.000-07:00</published><updated>2009-10-20T23:45:55.797-07:00</updated><title type='text'>How to get actions of a controller</title><content type='html'>I'm doing something about security in my project using grails jsecurity plugin. To make this function user-friendly, I want to implement an ui that admin can assign a slected controller or its actions to an user or a role. It's not a big deal to list all controllers within a grails app: you can use "grailsApplication.controllerClasses". So, the next step is how to get actions of a controller.&lt;br /&gt;Google is a good teacher when you meet trouble. But I can google nothing. It seems that there is not any thread about my question. So, I checked grails api and found two things:&lt;br /&gt;1. GrailsControllerClass does not provide a method to list all actions.&lt;br /&gt;2. But GrailsControllerClass has a method to list all its possible URIs: getURIs().&lt;br /&gt;Since an URI is a combination of controller and action in grails, finally, I solved my problem using that method. Here is my solution:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def actions= []&lt;br /&gt;grailsApplication.getArtefact(ControllerArtefactHandler.TYPE, your controller name).URIs.each{&lt;br /&gt;   def parts= it.split('/')&lt;br /&gt;   if(parts.size()&amp;gt;2){&lt;br /&gt;        actions &amp;lt;&amp;lt; parts[2]&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;return (actions as Set)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By the way, Grails JSecurity Plugin is awesome! It reallys makes my life easier!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-1412981266228747171?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/1412981266228747171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=1412981266228747171' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/1412981266228747171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/1412981266228747171'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/10/how-to-get-actions-of-controller.html' title='How to get actions of a controller'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-5176754525883578001</id><published>2009-09-24T01:30:00.000-07:00</published><updated>2009-09-24T01:41:47.521-07:00</updated><title type='text'>More about Object Marshaller</title><content type='html'>Should I need to know something about Object Marshaller since Grails has done such good job in this aspect? Most of the time, the answer is not. But if you are doing something about RIA or Ajax, the answer is yes.&lt;br /&gt;Why? The reason is maybe:&lt;br /&gt;1. You want to include some properties in JSON output which was not included in default, such as version. By the way, you can include version through three config option in grails new version. You can find more details on this issue at &lt;a href="http://jira.codehaus.org/browse/GRAILS-4530"&gt;this&lt;/a&gt; or &lt;a href="http://foxgem.blogspot.com/2009/08/another-way-to-include-version-property.html"&gt;my last post&lt;/a&gt;.&lt;br /&gt;2. You like to format some property yourself, such as date formatting&lt;br /&gt;3. You want to output a object into a new JSON string, such as I want to output a relation property's toString() not its id.&lt;br /&gt;4. ...&lt;br /&gt;These days I was doing a RIA application using grails and ext. And, I met all the three requirements I said above. And here is what I got during this project:&lt;br /&gt;1. To implement your marshaller, you can implement ObjectMarshaller or you can use closure when you register it. If you want to use closure, you can do this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JSON.registerObjectMarshaller(Locale.class){&lt;br /&gt;  def rval= [:]&lt;br /&gt;  rval['ID']= it.toString()&lt;br /&gt;  rval['displayName']= it.getDisplayName()&lt;br /&gt;  return rval&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For method 1, implementing ObjectMarshaller, You can check DomainClassMarshaller.java. But if what you want is just to output the return value of toString() of relation property, you really don't have to do it from the ground up. You can extend DomainClassMarshaller:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class AnotherDomainClassMarshaller extends DomainClassMarshaller{&lt;br /&gt;  protected void asShortObject(Object refObj, JSON json,&lt;br /&gt;               GrailsDomainClassProperty idProperty,&lt;br /&gt;               GrailsDomainClass referencedDomainClass) throws ConverterException {&lt;br /&gt;      JSONWriter writer = json.getWriter();&lt;br /&gt;      writer.object();&lt;br /&gt;      writer.key("class").value(referencedDomainClass.getName());&lt;br /&gt;      writer.key("id").value(extractValue(refObj, idProperty));&lt;br /&gt;      writer.key("value").value(refObj.toString());&lt;br /&gt;      writer.endObject();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;2. After you create your marshaller, you should register it. And the best place in grails application is BootStrap.groovy:&lt;br /&gt;&lt;pre&gt;JSON.registerObjectMarshaller(your marshaller instance)&lt;/pre&gt;&lt;br /&gt;3. If you want to register more than one marshaller, &lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;you should provide a priority&lt;/span&gt;. &lt;span style="font-weight: bold;"&gt;REMEMBER IT!&lt;/span&gt; Yes, it is a trick. If you ignore it, you will get a surprise: &lt;span style="font-weight: bold;"&gt;Your marshaller registered just now did not work!&lt;/span&gt; After I digged the source of grails, DefaultConverterConfiguration.java, I found the truth: Grails hold all the object marshallers in a treeset, which use priority as a comparator, if you register two marshallers with the same priority, grails will register your marshaller with default-priority(=0) if priority is missing, the second marshaller will not be registered. So my last version of BootStrap.groovy is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;      JSON.registerObjectMarshaller(...)&lt;br /&gt;      JSON.registerObjectMarshaller(..., 1)&lt;br /&gt;      JSON.registerObjectMarshaller(..., 2)&lt;br /&gt;&lt;/pre&gt;But there is a problem I don't understand: the default ByteArrayMarshaller did not work, and I have to register it myself and provide a priority like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   JSON.registerObjectMarshaller(new org.codehaus.groovy.grails.web.converters.marshaller.json.ByteArrayMarshaller(), 1)&lt;br /&gt;&lt;/pre&gt;Who can tell me why?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-5176754525883578001?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/5176754525883578001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=5176754525883578001' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/5176754525883578001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/5176754525883578001'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/09/more-about-object-marshaller.html' title='More about Object Marshaller'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-6038955715195113321</id><published>2009-08-26T00:15:00.006-07:00</published><updated>2009-08-26T00:59:31.464-07:00</updated><title type='text'>Another way to include version property in your JSON</title><content type='html'>For those want to include domain class version property in JSON, you can get help from &lt;a href="http://jira.codehaus.org/browse/GRAILS-4530"&gt;GRAILS-4530&lt;/a&gt;: Chaning your grails version or Using the way provided by Sigi(See the description).&lt;br /&gt;But I found another way to get that. Instead of using a new DomainClassMarshaller, I just retrieve the DomainClassMarshaller from the ConverterConfiguration. Here is my code:&lt;br /&gt;&lt;pre&gt;import org.codehaus.groovy.grails.web.converters.configuration.ConvertersConfigurationHolder as CCH&lt;br /&gt;import org.codehaus.groovy.grails.web.converters.marshaller.json.DomainClassMarshaller as DCM&lt;br /&gt;import grails.converters.JSON&lt;br /&gt;&lt;br /&gt;class BootStrap {&lt;br /&gt;&lt;br /&gt;  def init = { servletContext -&amp;gt;&lt;br /&gt;&lt;br /&gt;     def cfg= CCH.getConverterConfiguration(JSON.class)&lt;br /&gt;     def marshellers= cfg.orderedObjectMarshallers&lt;br /&gt;     for(m in marshellers){&lt;br /&gt;         if(DCM.class.isAssignableFrom(m.class)){&lt;br /&gt;             m.includeVersion= true&lt;br /&gt;             break&lt;br /&gt;         }         &lt;br /&gt;     }&lt;br /&gt;  }&lt;br /&gt;  def destroy = {&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;pre&gt;Maybe it is not as simple as Sigi's solution, but I think it is the complement to this issue.&lt;/pre&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-6038955715195113321?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/6038955715195113321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=6038955715195113321' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/6038955715195113321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/6038955715195113321'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/08/another-way-to-include-version-property.html' title='Another way to include version property in your JSON'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-3920928345983532134</id><published>2009-08-24T00:08:00.000-07:00</published><updated>2009-08-24T22:12:49.970-07:00</updated><title type='text'>A new Grails powered site launched</title><content type='html'>A new Grails powered site launched last Friday:&lt;a href="http://trygroovy.appspot.com/"&gt; GroovyLive&lt;/a&gt;, which is a simple groovy console with a simple tutorial.&lt;br /&gt;Here are features of this simple Grails App:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Hosted on Google's GAE&lt;/li&gt;&lt;li&gt;Script history&lt;/li&gt;&lt;li&gt;Supporting hotkey. You can find these keys though the tooptips of the buttons of console.&lt;/li&gt;&lt;li&gt;A simple Groovy tutorial, although not including Groovy MOP.&lt;/li&gt;&lt;li&gt;The tutorial is intractive. You can try the script included in tutorial in a command shell, and the content will be the next chapter when the result of that script matches the answer.&lt;/li&gt;&lt;/ul&gt;It is powerd by:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.grails.org/"&gt;Grails&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.grails.org/plugin/app-engine"&gt;Grails AppEngine plugin&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://developer.yahoo.com/yui/"&gt;YUI&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://webcommand.googlecode.com/"&gt;WebCommand&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Since English is not my native language, if there are some error in my tutorial, pls point it to me.&lt;br /&gt;Thanks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-3920928345983532134?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/3920928345983532134/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=3920928345983532134' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/3920928345983532134'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/3920928345983532134'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/08/new-grails-powered-site-lauched.html' title='A new Grails powered site launched'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-727667219823415265</id><published>2009-07-17T18:53:00.000-07:00</published><updated>2009-07-17T19:00:53.725-07:00</updated><title type='text'>Using a list or a map to construct JSON dynamiclly</title><content type='html'>How do you contruct a dynamic JSON, especially when you only need to return some fields of an object not a whole object? Using a builder or constructing a string directly? Neither is my favorite.&lt;br /&gt;As time playing grails goes on, I find another way to build a JSON, only two steps:&lt;br /&gt;1. To fill a list or a map with your data.&lt;br /&gt;2. Using "as JSON".&lt;br /&gt;Here is a sample:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def toc= TutorialRepository.toc&lt;br /&gt;int i=1&lt;br /&gt;int j=1&lt;br /&gt;def tocJson=[]&lt;br /&gt;toc.each{ ch -&amp;gt;&lt;br /&gt;    def chJson= [:]&lt;br /&gt;    chJson['title']= ch.title&lt;br /&gt;    chJson['sections']= []&lt;br /&gt;    ch.sections.each{ se -&amp;gt;&lt;br /&gt;        def seJson= [:]&lt;br /&gt;        seJson['title']= se.title&lt;br /&gt;        seJson['pos']= &amp;quot;${i}:${j}&amp;quot;&lt;br /&gt;        chJson['sections'] &amp;lt;&amp;lt; seJson&lt;br /&gt;        j++&lt;br /&gt;    }&lt;br /&gt;    tocJson &amp;lt;&amp;lt; chJson&lt;br /&gt;    i++&lt;br /&gt;    j= 1&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;render tocJson as JSON&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's simple, right? And there are several advantages in this way building a JSON:&lt;br /&gt;1. It makes code clean.&lt;br /&gt;2. You can build more expressive JSON.&lt;br /&gt;3. It is more intuitive as the structure of the list(or the map) is the structure of the final JSON.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-727667219823415265?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/727667219823415265/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=727667219823415265' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/727667219823415265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/727667219823415265'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/07/using-list-or-map-to-construct-json.html' title='Using a list or a map to construct JSON dynamiclly'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-2341980244064868234</id><published>2009-07-12T19:26:00.000-07:00</published><updated>2009-07-12T19:36:04.260-07:00</updated><title type='text'>Enhancing your layout with YUI Layout Manager</title><content type='html'>These days, I did a little grails project. And in this project, I combined sitemesh with yui layout manager. The way is not too difficult ;)&lt;br /&gt;Here is code snippet from layout gsp, eg main.gsp:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;   ...&lt;br /&gt;   &amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;       YAHOO.util.Event.onDOMReady(function () {&lt;br /&gt;           var layout1 = new YAHOO.widget.Layout({&lt;br /&gt;               minWidth: 1000,&lt;br /&gt;               units: [&lt;br /&gt;                   { position: 'top', height: 45, body: 'top1' },&lt;br /&gt;                   { position: 'left', width: 190, resize: true, body: 'left1', gutter: '2px', header: leftHeader,&lt;br /&gt;                     collapse: true, scroll: true },&lt;br /&gt;                   { position: 'center' }&lt;br /&gt;               ]&lt;br /&gt;           });&lt;br /&gt;           layout1.on("render", function(){&lt;br /&gt;               var el = layout1.getUnitByPosition('center').get('wrap');&lt;br /&gt;               var layout2= new YAHOO.widget.Layout(el, {&lt;br /&gt;                   parent: layout1,&lt;br /&gt;                   units: [&lt;br /&gt;                       { position: 'top', height: 45, body: 'top2', gutter: '2px'  } ,&lt;br /&gt;                       { position: 'center', body: 'center2', gutter: '2px' },&lt;br /&gt;                       { position: 'bottom', height: bottomHeight, resize: true, body: 'bottom2',&lt;br /&gt;                       scroll: true, gutter: '2px' }&lt;br /&gt;                   ]&lt;br /&gt;               });&lt;br /&gt;               layout2.on("resize", function(){&lt;br /&gt;                   YAHOO.util.Dom.setStyle(center2ContentId, 'height', ((layout2.getSizes().center.h - 7) + 'px'));&lt;br /&gt;               });&lt;br /&gt;               layout2.render();&lt;br /&gt;           });&lt;br /&gt;           layout1.render();&lt;br /&gt;           &lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;init();&lt;/span&gt;&lt;br /&gt;       });&lt;br /&gt;   &amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body class="${pageProperty(name:'body.class')}"&amp;gt;&lt;br /&gt;   &amp;lt;div id="top1"&amp;gt;&lt;br /&gt;       &amp;lt;div id="logo"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;div id="left1"&amp;gt;&lt;br /&gt;       &lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;&amp;lt;g:pageProperty name="page.layout1.left.content"/&amp;gt;&lt;/span&gt;&lt;br /&gt;   &amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;div id="top2"&amp;gt;&lt;br /&gt;       &amp;lt;div id="toolbar"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;div id="center2"&amp;gt;&lt;br /&gt;       &lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;&amp;lt;g:pageProperty name="page.layout2.center.content"/&amp;gt;&lt;/span&gt;&lt;br /&gt;   &amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;div id="bottom2"&amp;gt;&lt;br /&gt;       &lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;&amp;lt;g:pageProperty name="page.layout2.bottom.content"/&amp;gt;&lt;/span&gt;&lt;br /&gt;   &amp;lt;/div&amp;gt;&lt;br /&gt;   &lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;&amp;lt;g:pageProperty name="page.other"/&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;init() and &amp;lt;g:pageProperty&amp;gt; is just placeholder, you can override it in your any gsp decorated by this layout. For example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;   &amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;       // your own js code is here&lt;br /&gt;       ...&lt;br /&gt;       // define your init function&lt;br /&gt;       var init= function(){...};&lt;br /&gt;   &amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;!-- define your page content --&amp;gt;&lt;br /&gt;&amp;lt;body class="yui-skin-sam"&amp;gt;&lt;br /&gt;   &amp;lt;content tag="layout1.left.content"&amp;gt;&amp;lt;/content&amp;gt;&lt;br /&gt;   &amp;lt;content tag="layout2.center.content"&amp;gt;&lt;br /&gt;       &amp;lt;div id="..."&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;/content&amp;gt;&lt;br /&gt;   &amp;lt;content tag="layout2.bottom.content"&amp;gt;&lt;br /&gt;       &amp;lt;div id="..."&amp;gt;&lt;br /&gt;           &amp;lt;div class="..."&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;       &amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;/content&amp;gt;&lt;br /&gt;   &amp;lt;content tag="other"&amp;gt;&lt;br /&gt;       &amp;lt;div id="how-to" style="text-align:left;"&amp;gt;&lt;br /&gt;           ......&lt;br /&gt;       &amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;/content&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It is noted that YUI Layout manager in this sample can also be replaced with othere ajax components lib(eg extjs or dojo) without too much hard work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-2341980244064868234?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/2341980244064868234/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=2341980244064868234' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/2341980244064868234'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/2341980244064868234'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/07/enhancing-your-layout-with-yui-layout.html' title='Enhancing your layout with YUI Layout Manager'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-6077289170865485936</id><published>2009-04-27T18:54:00.000-07:00</published><updated>2009-04-27T19:15:40.157-07:00</updated><title type='text'>How to change result of GrailsUI DataTable in an ajax way</title><content type='html'>DataTable is a good way to show data, but if you can give a way to your application users to change its result at rumtime they will get a better experience. GrailsUI DataTable taglib has an attribute named "params", which value is a query string and will be sent to your data reader since the first time DataTable sends request. So the thing becomes simple, if you can:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;change this attribute value at runtime&lt;/li&gt;&lt;br /&gt;&lt;li&gt;change your data reader to check this query string, filter data by it and return result as JSON that DataTable needs.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;Then you can change the content of DataTable as your users' wish.&lt;br /&gt;There are two implement styles - one is a server-side way; the other is an ajax way, which will be this blog's topic. Since the server-side way is not complicated, I think you can do it yourself. ;)&lt;br /&gt;The Domain class I will use in this example is following, and the fields used for filter are title and closed.&lt;br /&gt;&lt;pre&gt;class Question {&lt;br /&gt;   String title&lt;br /&gt;   String details&lt;br /&gt;   Boolean closed&lt;br /&gt;   Date dateCreated&lt;br /&gt;   Date lastUpdated&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;First, let's see the simple part of the question: How to filter data and return the result. It's really simple, and any grails developer will finish it in no time:&lt;br /&gt;&lt;pre&gt;def load = {&lt;br /&gt;    params.max = Math.min( params.max ? params.max.toInteger() : 10,  100)&lt;br /&gt;    def fields=[]&lt;br /&gt;    def fieldsCount= 0&lt;br /&gt;&lt;br /&gt;    def questionList=[]&lt;br /&gt;    if(!params.title &amp;amp;&amp;amp; !params.closed){&lt;br /&gt;        fieldsCount= Question.count()&lt;br /&gt;        questionList= Question.list(params)&lt;br /&gt;    }else{&lt;br /&gt;        def c= Question.createCriteria()&lt;br /&gt;        fieldsCount= c.get{&lt;br /&gt;            projections{&lt;br /&gt;                count('id')&lt;br /&gt;            }&lt;br /&gt;            if(!params.closed  params.closed=='all'){&lt;br /&gt;                like("title", "%${params.title?:''}%")&lt;br /&gt;            }else{&lt;br /&gt;                def closed= params.closed=='y'? true:false&lt;br /&gt;                and{&lt;br /&gt;                    like("title", "%${params.title?:''}%")&lt;br /&gt;                    eq("closed", closed)&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        questionList= Question.withCriteria{&lt;br /&gt;            if(!params.closed  params.closed=='all'){&lt;br /&gt;                like("title", "%${params.title?:''}%")&lt;br /&gt;            }else{&lt;br /&gt;                def closed= params.closed=='y'? true:false&lt;br /&gt;                and{&lt;br /&gt;                    like("title", "%${params.title?:''}%")&lt;br /&gt;                    eq("closed", closed)&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            maxResults(params.max)&lt;br /&gt;            firstResult(params.offset? params.offset.toInteger():0)&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    questionList.each{ question -&amp;gt;&lt;br /&gt;        fields &amp;lt;&amp;lt; [&lt;br /&gt;                    id: question.id&lt;br /&gt;                   ,title: question.title&lt;br /&gt;                   ,dateCreated: g.formatDate(date:question.dateCreated, format:'yyyy-MM-hh HH:mm')&lt;br /&gt;                   ,lastUpdated: g.formatDate(date:question.lastUpdated, format:'yyyy-MM-hh HH:mm')&lt;br /&gt;                   ,closed: question.closed&lt;br /&gt;                   ,action: """&amp;lt;A href='/codeline/question/edit/${question.id}'&amp;gt;update&amp;lt;/A&amp;gt;&lt;br /&gt;                               ${ if(!question.closed){&lt;br /&gt;                                    "&amp;lt;br/&amp;gt;&amp;lt;A href='/codeline/question/delete/${question.id}'&amp;gt;delete&amp;lt;/A&amp;gt;"&lt;br /&gt;                                  }else{&lt;br /&gt;                                    ""&lt;br /&gt;                                  }&lt;br /&gt;                                }&lt;br /&gt;                             """&lt;br /&gt;                  ]&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    response.setHeader("Cache-Control", "no-store")&lt;br /&gt;    def result= [&lt;br /&gt;        totalRecords: fieldsCount&lt;br /&gt;      , results: fields]&lt;br /&gt;    render result as JSON&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Then I will show you the markup of page: a query zone and a datatable:&lt;br /&gt;&lt;pre&gt;&amp;lt;div class=&amp;quot;roundbg roundspace&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;div class=&amp;quot;roundtl&amp;quot;&amp;gt;&amp;lt;div class=&amp;quot;roundbr&amp;quot;&amp;gt;&amp;lt;div class=&amp;quot;roundtrc&amp;quot;&amp;gt;&amp;lt;div class=&amp;quot;roundblc roundpad&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;h5 class=&amp;quot;title_ico&amp;quot;&amp;gt;Question List&amp;lt;/h5&amp;gt;&lt;br /&gt;    &amp;lt;div class=&amp;quot;list_toolbar&amp;quot;&amp;gt;&amp;lt;form&amp;gt;&lt;br /&gt;    &amp;lt;label for=&amp;quot;title&amp;quot;&amp;gt;Title:&amp;lt;/label&amp;gt;&lt;br /&gt;    &amp;lt;input type=&amp;quot;text&amp;quot; maxlength=&amp;quot;30&amp;quot; id=&amp;quot;title&amp;quot; name=&amp;quot;title&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;label for=&amp;quot;closed&amp;quot;&amp;gt;Closed?:&amp;lt;/label&amp;gt;&lt;br /&gt;    &amp;lt;input type=&amp;quot;radio&amp;quot; id=&amp;quot;closed&amp;quot; name=&amp;quot;closed&amp;quot; value=&amp;quot;all&amp;quot; class=&amp;quot;cssform_radiobutton&amp;quot; checked/&amp;gt;All&lt;br /&gt;    &amp;lt;input type=&amp;quot;radio&amp;quot; id=&amp;quot;closed&amp;quot; name=&amp;quot;closed&amp;quot; value=&amp;quot;y&amp;quot; class=&amp;quot;cssform_radiobutton&amp;quot;/&amp;gt;Y&lt;br /&gt;    &amp;lt;input type=&amp;quot;radio&amp;quot; id=&amp;quot;closed&amp;quot; name=&amp;quot;closed&amp;quot; value=&amp;quot;n&amp;quot; class=&amp;quot;cssform_radiobutton&amp;quot;/&amp;gt;N&lt;br /&gt;    &amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;submit&amp;quot; class=&amp;quot;button&amp;quot; onclick=&amp;quot;query&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;/form&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;gui:dataTable id=&amp;quot;questions&amp;quot; draggableColumns=&amp;quot;true&amp;quot; &lt;br /&gt;            columnDefs=&amp;quot;[&lt;br /&gt;                   [key:'id', sortable:true, resizeable: true, label:'ID']&lt;br /&gt;                  ,[key:'title', sortable:true, resizeable: true, label:'Title']&lt;br /&gt;                  ,[key:'dateCreated', sortable:true, resizeable:true, label:'Created Time']&lt;br /&gt;                  ,[key:'lastUpdated', sortable:true, resizeable:true, label:'Updated Time']&lt;br /&gt;                  ,[key:'closed', label:'Is closed?']&lt;br /&gt;                  ,[key:'action', label:'Action']&lt;br /&gt;                    ]&amp;quot;&lt;br /&gt;            controller=&amp;quot;question&amp;quot; action=&amp;quot;load&amp;quot; rowsPerPage=&amp;quot;1&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;&lt;br /&gt;You may notice the submit button has a handler named "query", which will build a query string and send it to the action given above. Its code is here:&lt;br /&gt;&lt;pre&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;    var query = function(){&lt;br /&gt;        var str= '';&lt;br /&gt;        if(YAHOO.util.Dom.get('title').value.length&amp;gt;0){&lt;br /&gt;            str+= "title=" + YAHOO.util.Dom.get('title').value;&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        if(str.length&amp;gt;0){&lt;br /&gt;            str+= '&amp;amp;'&lt;br /&gt;        }&lt;br /&gt;        str+= "closed=" + YAHOO.util.Selector.query('#closed:checked')[0].value;&lt;br /&gt;        GRAILSUI.questions.customQueryString = str;&lt;br /&gt;       &lt;br /&gt;        GRAILSUI.questions.loadingDialog.show();&lt;br /&gt;        GRAILSUI.questions.cleanup();&lt;br /&gt;        var sortedBy  = GRAILSUI.questions.get('sortedBy');&lt;br /&gt;        var newState = {&lt;br /&gt;            startIndex: 0,&lt;br /&gt;            sorting: {&lt;br /&gt;                key: sortedBy.key,&lt;br /&gt;                dir: ((sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? YAHOO.widget.DataTable.CLASS_DESC : YAHOO.widget.DataTable.CLASS_ASC)&lt;br /&gt;            },&lt;br /&gt;            pagination : {&lt;br /&gt;                recordOffset: 0,&lt;br /&gt;                rowsPerPage: GRAILSUI.questions.get("paginator").getRowsPerPage()&lt;br /&gt;            }&lt;br /&gt;        };&lt;br /&gt;        var oCallback = {&lt;br /&gt;            success: GRAILSUI.questions.onDataReturnInitializeTable,&lt;br /&gt;            failure: GRAILSUI.questions.onDataReturnInitializeTable,&lt;br /&gt;            scope: GRAILSUI.questions,&lt;br /&gt;            argument: newState&lt;br /&gt;        };&lt;br /&gt;        GRAILSUI.questions.getDataSource().sendRequest(GRAILSUI.questions.buildQueryString(newState), oCallback);&lt;br /&gt;    }&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;&lt;br /&gt;This code needs to be explained carefully:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;In the example I did not use GrailsUI DataTable taglib's attribute "params", but used "customQueryString" instead. The reason is that the value of "params" will be passed to this attribute. Its purpose is to give you a way to define your own query string, its value will be a part of url that the DataSource owned by DataTable request. And this url will be built by buildQueryString()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The remains of query fucntion was mainly copied from GRAILSUI.DataTable.onPaginatorChangeRequest, which is in "%grailsui-home%/web-app/js/grailsui/DataTable.js". And there are two differences:&lt;/li&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;startIndex and recordOffser is zero&lt;/li&gt;&lt;br /&gt;&lt;li&gt;success handler and failure handler is not onDataReturnSetRows but onDataReturnInitializeTable&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;br /&gt;Hope you enjoy it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-6077289170865485936?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/6077289170865485936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=6077289170865485936' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/6077289170865485936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/6077289170865485936'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/04/how-to-change-result-of-grailsui.html' title='How to change result of GrailsUI DataTable in an ajax way'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12369778.post-6495794402876741801</id><published>2009-04-15T02:04:00.000-07:00</published><updated>2009-04-27T18:58:32.483-07:00</updated><title type='text'>A simple way to customize format for GrailsUI DataTable Cells</title><content type='html'>I was playing around with grails-ui these days.I really like this plugin, it makes my life easy.&lt;br /&gt;&lt;br /&gt;After somedays digging, I find a simple way to custom formatting for DataTable cells. It is so simple that I think you will understand it quickly after reading the following controller action code. There is nothing different in GSP, so its code is omited.&lt;br /&gt;&lt;pre&gt;def load = {&lt;br /&gt;    params.max = Math.min( params.max ? params.max.toInteger() : 10, 100)&lt;br /&gt;    def fields=[]&lt;br /&gt;    Question.list(params).each{&lt;br /&gt;        fields &amp;lt;&amp;lt; [&lt;br /&gt;            id: it.id&lt;br /&gt;            ,title: it.title&lt;br /&gt;            ,dateCreated: it.dateCreated&lt;br /&gt;            ,lastUpdated: it.lastUpdated&lt;br /&gt;            ,closed: it.closed&lt;br /&gt;            ,updateAction: &amp;quot;&amp;lt;A href='/codeline/question/edit/${it.id}'&amp;gt;update&amp;lt;/A&amp;gt;&amp;quot;&lt;br /&gt;            ,deleteAction: &amp;quot;&amp;lt;A href='/codeline/question/delete/${it.id}'&amp;gt;delete&amp;lt;/A&amp;gt;&amp;quot;&lt;br /&gt;        ]&lt;br /&gt;    }&lt;br /&gt;    response.setHeader(&amp;quot;Cache-Control&amp;quot;, &amp;quot;no-store&amp;quot;)&lt;br /&gt;    def result= [ totalRecords: Question.count(), results: fields]&lt;br /&gt;    render result as JSON&lt;br /&gt;}&lt;/pre&gt;Do you follow me? That's right! It is "&lt;strong&gt;returning html in the result&lt;/strong&gt;". Be careful, if the html is complex, the best way is to check the &lt;a href="http://developer.yahoo.com/yui/docs/"&gt;YUI Document&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12369778-6495794402876741801?l=foxgem.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://foxgem.blogspot.com/feeds/6495794402876741801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12369778&amp;postID=6495794402876741801' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/6495794402876741801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12369778/posts/default/6495794402876741801'/><link rel='alternate' type='text/html' href='http://foxgem.blogspot.com/2009/04/simple-way-to-customize-format-for.html' title='A simple way to customize format for GrailsUI DataTable Cells'/><author><name>foxgem</name><uri>http://www.blogger.com/profile/12938727581809086766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://2.bp.blogspot.com/_m-t84QncF24/SuRWUcMa0YI/AAAAAAAAAAM/VEbqTcpEgUk/S220/foxgem80_100.jpg'/></author><thr:total>0</thr:total></entry></feed>
