<?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:blogger='http://schemas.google.com/blogger/2008' 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-822152031423180590</id><updated>2025-12-22T00:01:27.898-08:00</updated><category term="grails"/><category term="Custom Tags"/><category term="GQuick"/><category term="Quartz"/><category term="TagLibs"/><category term="Testing"/><category term="deployment"/><category term="gsp"/><category term="images"/><category term="jndi"/><category term="mysql"/><category term="upgrade"/><title type='text'>Grails: A Quick-Start Guide</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>8</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-822152031423180590.post-54828360241220928</id><published>2010-05-12T03:48:00.000-07:00</published><updated>2010-05-12T03:52:05.638-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="GQuick"/><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Quartz"/><title type='text'>Misplaced Blog Post</title><content type='html'>I was intending to post here about adding Quartz scheduling to the TekDays application, but I accidentally posted it on &lt;a href=&quot;http://dave-klein.blogspot.com/2010/05/quartz-and-grails.html&quot;&gt;http://dave-klein.blogspot.com&lt;/a&gt; instead.  I apologize for the confusion, but please feel free to check it out.  &lt;br /&gt;&lt;br /&gt;Thanks!&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;
This is a post from &lt;a href=&quot;http://gquick.blogspot.com/&quot;&gt;http://gquick.blogspot.com/&lt;/a&gt;, the blog home of the book &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/54828360241220928/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gquick.blogspot.com/2010/05/misplaced-blog-post.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/54828360241220928'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/54828360241220928'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/2010/05/misplaced-blog-post.html' title='Misplaced Blog Post'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-822152031423180590.post-3554835551071820266</id><published>2010-03-27T09:51:00.000-07:00</published><updated>2010-03-27T17:42:02.837-07:00</updated><title type='text'>Clustering A Grails App with Terracotta</title><content type='html'>&lt;p style=&quot;margin-bottom:0in;&quot;&gt;When I was writing &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;, I thought that scalability issues were too complicated and “out of scope” for a “getting started” book.  Well, the folks at &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://www.terracotta.org/&quot;&gt;Terracotta&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt; have been busy at work making me wrong.  Integrating Terracotta&#39;s &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://www.terracotta.org/web-sessions/&quot;&gt;Web Sessions&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt; clustering product is so easy that I could have covered it in a page or two.  So, we&#39;ll do that now.&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;In this post, we&#39;ll install and configure Web Sessions Express.  In subsequent posts we&#39;ll look at some of Terracotta&#39;s other products:  &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://www.terracotta.org/ehcache/&quot;&gt;EhCache&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;, &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://www.terracotta.org/quartz/&quot;&gt;Quartz&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;, and &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://www.terracotta.org/ehcache/distributed-cache/ehcache-ex-hibernate&quot;&gt;EhCache for Hibernate&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;We&#39;ll start with installing Terracotta, which can be downloaded at &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://www.terracotta.org/dl/oss-download-catalog&quot;&gt;http://www.terracotta.org/dl/oss-download-catalog&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;.  We&#39;ll grab the tar/zip file (&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;terracotta-3.2.1.tar.gz&lt;/span&gt;&lt;/span&gt;) and expand it to an appropriate location (like &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;/opt/terracotta-3.2.1/&lt;/span&gt;&lt;/span&gt;).  Then we&#39;ll set that directory as our &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;TERRACOTTA_HOME&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;Included with the install is a JAR file that will be used by our server.  The exact location where we copy this file varies by server.  We&#39;ll be using Tomcat 6.0.24, so we&#39;ll follow the instructions for Tomcat 6.0x. Let&#39;s copy  &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;TERRACOTTA_HOME/sessions/terracotta-session-1.0.0.jar&lt;/span&gt;&lt;/span&gt; to &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;CATALINA_HOME/lib&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size:100%;&quot;&gt;. (For the correct location for other servers, see &lt;/span&gt;&lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://bit.ly/apiizT&quot;&gt;&lt;span style=&quot;font-size:100%;&quot;&gt;the Terracotta documentation&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size:100%;&quot;&gt;.)&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;Now for our application, we&#39;ll use the version of TekDays from the final chapter (Deployment and Beyond) of GQuick.  If you worked your way through the book, you know that this application is going to be so popular that we&#39;re going to have to deploy it on a whole bunch of servers. :-)  If you didn&#39;t build the application, you can download it from &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://pragpub.com/titles/dkgrails/code&quot;&gt;http://pragpub.com/titles/dkgrails/code&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;. Also, check out this &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://gquick.blogspot.com/2010/03/upgrading-and-deploying-gquick-code.html&quot;&gt;post on upgrading and deploying TekDays&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;u&gt;&lt;a href=&quot;http://gquick.blogspot.com/2010/03/upgrading-and-deploying-gquick-code.html&quot;&gt;&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;u&gt;&lt;a href=&quot;http://gquick.blogspot.com/2010/03/upgrading-and-deploying-gquick-code.html&quot;&gt;&lt;/a&gt;&lt;/u&gt; for some helpful tips.&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;Next we need to add a very simple configuration file to our application.  We&#39;ll create the file &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;web-app/META-INF/context.xml&lt;/span&gt;&lt;/span&gt; and include the following bit of XML:&lt;/p&gt;&lt;pre class=&quot;brush:xml&quot;&gt;&amp;lt;Context&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;Valve className=&quot;&lt;br /&gt;org.terracotta.session.TerracottaTomcat60x&lt;br /&gt;SessionValve&lt;br /&gt;&quot;  &lt;br /&gt;     tcConfigUrl=&quot;localhost:9510&quot;/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/Context&amp;gt;&lt;/pre&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;I&#39;ve suggested that the Terracotta team come up with a Groovy DSL for configuring their tools in Grails applications, and it seems that they&#39;re considering it.  But in the meantime, take the necessary precautions when you&#39;re adding the XML, and be sure to take a break and bandage any wounds you received through handling those sharp edges.&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;Note that these instructions are for deploying to a Tomcat server in the 6.0x family.  If you are using an older version of Tomcat or if for some strange reason &amp;lt;grin&amp;gt; you are using some other server, you can find the appropriate configuration code at the &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://terracotta.org/documentation/&quot;&gt;terracotta.org documentation page&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;We&#39;re almost done.  Right now we could start up the Terracotta server, start up our Tomcat server, and deploy a &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;TekDays.war&lt;/span&gt;&lt;/span&gt;. And it would &lt;i&gt;almost&lt;/i&gt; work.  But in order for Terracotta to cluster an application, all of the artifacts that we want to be clustered need to implement &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;serializable&lt;/span&gt;&lt;/span&gt;.  So, we&#39;ll just open up some of our classes and add &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;implements serializable&lt;/span&gt;&lt;/span&gt; to their class declaration.  Here&#39;s a list of the files that we need to modify:&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;grails-app/domain/TekEvent.groovy&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style=&quot;margin-bottom: 0in&quot;&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;grails-app/domain/TekUser.groovy&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style=&quot;margin-bottom: 0in&quot;&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;grails-app/domain/Task.groovy&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style=&quot;margin-bottom: 0in&quot;&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;grails-app/domain/Message.groovy&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style=&quot;margin-bottom: 0in&quot;&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;grails-app/domain/Sponsor.groovy&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style=&quot;margin-bottom: 0in&quot;&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;grails-app/domain/Sponsorship.groovy&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style=&quot;margin-bottom: 0in&quot;&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;grails-app/conf/SecurityFilters.groovy&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-bottom: 0in&quot;&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;For example, we&#39;ll modify the &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;TekEvent&lt;/span&gt;&lt;/span&gt; class like so:&lt;/p&gt;&lt;pre class=&quot;brush:groovy&quot;&gt;&lt;br /&gt;class TekEvent implements Serializable {&lt;br /&gt;//...&lt;/pre&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;We&#39;ll do the same for each of those other files, and then we&#39;ll be ready to try it out.  To run our newly &lt;i&gt;terracottafied&lt;/i&gt; TekDays application, take the following steps:&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;1. Build a WAR file: from the TekDays application directory, run &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;grails war tekdays.war&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;2. Copy &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;tekdays.war&lt;/span&gt;&lt;/span&gt; to Tomcat&#39;s &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;webapps&lt;/span&gt;&lt;/span&gt; folder: &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;&lt;catalina_home&gt;/webapps&lt;/catalina_home&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;3. Start the Terracotta server:  &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;&lt;terracotta_home&gt;/bin/start-tc-server.sh&lt;/terracotta_home&gt;&lt;/span&gt;&lt;/span&gt; (or &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;.bat&lt;/span&gt;&lt;/span&gt; if you&#39;re on Windows)&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;4. Start Tomcat: &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;&lt;catalina_home&gt;/bin/startup.sh &lt;/catalina_home&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size:100%;&quot;&gt;(or &lt;/span&gt;&lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;.bat&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size:100%;&quot;&gt; if you&#39;re on Windows)&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;5. Quick sanity check: navigate to &lt;span style=&quot;color:#000080;&quot;&gt;&lt;span lang=&quot;zxx&quot;&gt;&lt;u&gt;&lt;a href=&quot;http://localhost:8080/tekdays&quot;&gt;http://localhost:8080/tekdays&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt; (or the port that your Tomcat instance is running on.)&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;6. Start the Terracotta Developer Console (this step isn&#39;t necessary to run the app, but it gives us a nice way to see that it&#39;s working): &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;&lt;terracotta_home&gt;/bin/dev-console.sh&lt;/terracotta_home&gt;&lt;/span&gt;&lt;/span&gt; (again &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;.bat&lt;/span&gt;&lt;/span&gt; if you&#39;re on Windows)&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;The Developer Console will give us a screen like this:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrLQvk_OC4xqQ9tuW89OH4ZyOX9joIcsuNj1_31xTJ8rn4GLe28iosqiHhRavfsGJbQQHLnR8A-oju3X_gdNLyH_j_uUPW1onghigjUHizn5BPEAz6xRdbvXiCKY8CWPyCVq1qkBShgho/s1600/tc_console1.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 154px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrLQvk_OC4xqQ9tuW89OH4ZyOX9joIcsuNj1_31xTJ8rn4GLe28iosqiHhRavfsGJbQQHLnR8A-oju3X_gdNLyH_j_uUPW1onghigjUHizn5BPEAz6xRdbvXiCKY8CWPyCVq1qkBShgho/s200/tc_console1.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5453172657938714930&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;When we click &quot;Connect&quot;, we should get something like this:&lt;/p&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-gtlufc9KxdNGqQYwOKnqnOvCEW8lyvxIlugL08VCWeKxu2WE4hGVDmV4j4R0VpsHjmB6cyZQdwRqBDsZekH3q1BcUZNyzeK2dNsesaG7LcLya6Hx9yi6eYnxfTVuSTEesahfLnYRLTo/s1600/tc_console2.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 144px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-gtlufc9KxdNGqQYwOKnqnOvCEW8lyvxIlugL08VCWeKxu2WE4hGVDmV4j4R0VpsHjmB6cyZQdwRqBDsZekH3q1BcUZNyzeK2dNsesaG7LcLya6Hx9yi6eYnxfTVuSTEesahfLnYRLTo/s200/tc_console2.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5453172815567905810&quot; /&gt;&lt;/a&gt;&lt;div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-gtlufc9KxdNGqQYwOKnqnOvCEW8lyvxIlugL08VCWeKxu2WE4hGVDmV4j4R0VpsHjmB6cyZQdwRqBDsZekH3q1BcUZNyzeK2dNsesaG7LcLya6Hx9yi6eYnxfTVuSTEesahfLnYRLTo/s1600/tc_console2.png&quot;&gt;&lt;/a&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;Now this only shows one connected client right now, since that&#39;s all that we&#39;re running.  To really see the magic, we need to fire up another Tomcat instance on a different port.   But we&#39;ll leave that as an exercise for the reader.&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;There is one simple thing we can do to catch a glimpse of what Terracotta is doing for us.  With TekDays running, navigate to a page – maybe create a new event and start adding some tasks.  OK, now leave that page up, go to the console, and shut down the Tomcat server: &lt;span style=&quot;font-family:Courier New, monospace;&quot;&gt;&lt;span style=&quot;font-size: 10pt;font-size:85%;&quot;&gt;&lt;catalina_home&gt;/bin/shutdown.sh&lt;/catalina_home&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;Let the shutdown finish, and then restart Tomcat.  Give that a minute to wake up, then go back to the page and continue what you were doing.  Everything is just as you left it.  The logged in user is even in the session!  Pretty slick!&lt;/p&gt;&lt;p style=&quot;margin-bottom:0in;&quot;&gt;There are other steps for setting up multiple Terracotta servers and such, but as far as our application goes, that&#39;s all there is to it.   If I ever get a chance to write a second edition of Grails: A Quick-Start Guide, there will definitely be a section on scalability.  Because, as we all know, &lt;b&gt;Grails Scales! &lt;/b&gt; &lt;/p&gt;&lt;/div&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;
This is a post from &lt;a href=&quot;http://gquick.blogspot.com/&quot;&gt;http://gquick.blogspot.com/&lt;/a&gt;, the blog home of the book &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/3554835551071820266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gquick.blogspot.com/2010/03/clustering-grails-app-with-terracotta.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/3554835551071820266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/3554835551071820266'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/2010/03/clustering-grails-app-with-terracotta.html' title='Clustering A Grails App with Terracotta'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrLQvk_OC4xqQ9tuW89OH4ZyOX9joIcsuNj1_31xTJ8rn4GLe28iosqiHhRavfsGJbQQHLnR8A-oju3X_gdNLyH_j_uUPW1onghigjUHizn5BPEAz6xRdbvXiCKY8CWPyCVq1qkBShgho/s72-c/tc_console1.png" height="72" width="72"/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-822152031423180590.post-5389152370008435057</id><published>2010-03-25T16:25:00.000-07:00</published><updated>2010-03-25T18:36:58.491-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="deployment"/><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="jndi"/><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><category scheme="http://www.blogger.com/atom/ns#" term="upgrade"/><title type='text'>Upgrading and Deploying GQuick Code</title><content type='html'>Several people have gone through the sample application from GQuick using more recent versions of Grails (1.2 and 1.2.1), and it all seems to work, with one exception.  If you use packages, as Grails 1.2 and above encourage you to do, one of the plugins (the Blurb plugin) will fail.  I&#39;ve been in contact with the plugin&#39;s author and he promises to fix that soon.&lt;br /&gt;&lt;br /&gt;On the other hand, if you didn&#39;t build the TekDays application but would like to work with the code downloaded from the &lt;a href=&quot;http://pragprog.com/titles/dkgrails/source_code&quot;&gt;Pragmatic Programmer&#39;s website&lt;/a&gt;, there is a different issue.  I recently downloaded the final code base, which is found in the download bundle at &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;code/deploy/TekDays/&lt;/span&gt;, and upgraded it to Grails 1.2.1.  I ran into a conflict with two versions of the Bubbling plugin.  It appears that the upgrade process pulls down the newer version of the Bubbling plugin (2.1.1), but the old version (1.5.1) was still installed.  The solution (or at least &lt;i&gt;a&lt;/i&gt; solution) was to go to &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;home/.grails/1.2.1/projects/TekDays/plugins/&lt;/span&gt;and remove the 1.5.1 version of the plugin.  After that it all worked fine.&lt;br /&gt;&lt;br /&gt;It worked fine, that is, until I tried to deploy the app to Tomcat.  When I built the &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;.war&lt;/span&gt; file and copied it to my &lt;catalina_home&gt;&lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;/webapps/&lt;/span&gt; directory, it wouldn&#39;t start. I tried forcing it to start from the Tomcat manager. No dice.  So I checked in &lt;catalina_home&gt;&lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;/logs/catalina.out&lt;/span&gt; (if you&#39;re using Tomcat, you gotta love that file), and found a datasource error that made me smack my forehead.&lt;br /&gt;&lt;br /&gt;In the last chapter of GQuick, we look at how to configure a Grails application to use a JNDI datasource, which is a good idea for a real production deployment.  But you may not &lt;i&gt;have&lt;/i&gt; JDNI configured on your local Tomcat instance; I sure didn&#39;t.  So, I changed my &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;grails-app/conf/DataSource.groovy&lt;/span&gt; file to make the production datasource the same as development.&lt;br /&gt;&lt;br /&gt;If you are doing this for the first time and haven&#39;t worked through the book, you will need to create a MySQL database called &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;tekdays&lt;/span&gt; and set the user and password to match that in the &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;DataSource.groovy&lt;/span&gt; file (or vice versa).  Alternatively, you can use any database with a JDBC driver.  Just include your JDBC driver in the &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;TekDays/grails-app/lib/&lt;/span&gt; directory and modify your &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;DataSource.groovy&lt;/span&gt; accordingly.  I prefer MySQL because it&#39;s so easy to get and easy to use. (More info on MySQL can be found at &lt;a href=&quot;http://dev.mysql.com/&quot;&gt;http://dev.mysql.com&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;One final note: Some people have mentioned getting &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;OutOfMemoryError&lt;/span&gt;s when deploying the app to Tomcat.  This can happen with any Grails app if the JVM memory settings are too low.  To prevent this, you can add (or modify) a &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;JAVA_OPTS&lt;/span&gt; environment variable.  In Linux/Unix or Mac OSX, you could do something like this:&lt;br /&gt;&lt;pre class=&quot;brush:bash&quot;&gt;&lt;br /&gt;export JAVA_OPTS=&#39;-Xmx1G -XX:MaxPermSize=256m&#39;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In Windows, you can use the wizard in the control panel under System.  Or maybe you can edit your &lt;span class=&quot;Apple-style-span&quot;  style=&quot;font-family:&#39;courier new&#39;;&quot;&gt;autoexec.bat&lt;/span&gt; file. (Do they still use that?)&lt;br /&gt;&lt;br /&gt;For the best learning experience, it&#39;s probably worth the time to go through the book and build the app as you go, but if you want to just &lt;a href=&quot;http://pragprog.com/titles/dkgrails/source_code&quot;&gt;download it&lt;/a&gt; and take it for a spin first, these tips should help you get going.&lt;br /&gt;&lt;br /&gt;Have fun!&lt;/catalina_home&gt;&lt;/catalina_home&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;
This is a post from &lt;a href=&quot;http://gquick.blogspot.com/&quot;&gt;http://gquick.blogspot.com/&lt;/a&gt;, the blog home of the book &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/5389152370008435057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gquick.blogspot.com/2010/03/upgrading-and-deploying-gquick-code.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/5389152370008435057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/5389152370008435057'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/2010/03/upgrading-and-deploying-gquick-code.html' title='Upgrading and Deploying GQuick Code'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-822152031423180590.post-4641375625542968865</id><published>2010-02-02T09:04:00.000-08:00</published><updated>2010-02-03T07:42:22.945-08:00</updated><title type='text'>Using Groovy&#39;s MarkupBuilder In Grails&#39; GSP Tags</title><content type='html'>&lt;p&gt;One of the cool features of Groovy that we were not able to cover in GQuick is the &lt;code&gt;MarkupBuilder&lt;/code&gt;.  &lt;code&gt;MarkupBuilder&lt;/code&gt; turns your markup language into a Groovy DSL.  The markup elements become methods.  If they have body content, it goes in a closure passed to the element / method.  For example, an anchor tag looks something like this:&lt;/p&gt;&lt;br /&gt;&lt;pre class=&quot;brush:groovy&quot;&gt; &lt;br /&gt;a(href:&#39;http://somehost.com/somepath/someresource&#39;){&lt;br /&gt;    //anchor text goes here&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;So, how can we use this in the GQuick example app, Tekdays.com?  We could use it to render some HTML to a view from a controller action, but that would be kind of lame.  If you read the book, you know that one of my favorite Grails features is custom GSP tags.  We created several of them for TekDays, and they all spit out some HTML.  So let&#39;s see how we can transform one of our custom tags using Groovy&#39;s &lt;code&gt;MarkupBuilder&lt;/code&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;We&#39;ll do our conversion on the &lt;code&gt;loginToggle&lt;/code&gt; tag.  This tag shows a Login link at the top of the page if the user is not logged in.  If the user is logged in, the tag shows a welcome message on the left side of the page and a Logout link on the right.  It&#39;s pretty handy.  We can just drop it on any page and avoid using a bunch of messy &lt;code&gt;&lt;&amp;lt;g:if&amp;gt;&lt;/code&gt; logic in our page.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This tag is introduced in Chapter 8, “Knock, Knock: Who&#39;s There? Grails Security”.  Here is how it looks now:&lt;/p&gt;&lt;br /&gt;&lt;pre class=&quot;brush:groovy&quot;&gt;&lt;br /&gt;def loginToggle = {&lt;br /&gt;    out &lt;&lt; &quot;&amp;lt;div&amp;gt;&quot;&lt;br /&gt;    if (session.user){&lt;br /&gt;        out &lt;&lt; &quot;&amp;lt;span style=&#39;float:left;&lt;br /&gt;                margin:5px 0px 0px 10px&#39;&amp;gt;&quot;&lt;br /&gt;        out &lt;&lt; &quot;Welcome ${session.user}.&quot;&lt;br /&gt;        out &lt;&lt; &quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&#39;float:right;&lt;br /&gt;                margin:5px 10px 0px 0px&#39;&amp;gt;&quot;&lt;br /&gt;        out &lt;&lt; &quot;&amp;lt;a href=&#39;${createLink(controller:&lt;br /&gt;        &#39;tekUser&#39;, action:&#39;logout&#39;)}&#39;&amp;gt;&quot;&lt;br /&gt;        out &lt;&lt; &quot;Logout &amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&quot;&lt;br /&gt;    }&lt;br /&gt;    else{&lt;br /&gt;        out &lt;&lt; &quot;&amp;lt;span style=&#39;float:right;&lt;br /&gt;                margin:5px 10px 0px 0px&#39;&amp;gt;&quot;&lt;br /&gt;        out &lt;&lt; &quot;&amp;lt;a href=&#39;${createLink(controller:&#39;tekUser&#39;,&lt;br /&gt;                action:&#39;login&#39;)}&#39;&amp;gt;&quot;&lt;br /&gt;        out &lt;&lt; &quot;Login &amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&quot;&lt;br /&gt;    }&lt;br /&gt;    out &lt;&lt; &quot;&amp;lt;/div&amp;gt;&amp;lt;br/&amp;gt;&quot;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To use the tag, we just placed this in our &lt;code&gt;main.gsp&lt;/code&gt;:&lt;/p&gt;&lt;br /&gt;&lt;pre class=&quot;brush:xml&quot;&gt;&lt;br /&gt;&amp;lt;g:loginToggle /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;That gave us something like this:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ0WO5QQs-agaIe9bydDnAInFCjkymfa8x21YGF1LFuAMui6JGqAyFEOa5GLG_Dg-v-PGfLlvUQwAMfoKjVul-WLvYzW5eqRAHphCFVBSSU4LWpVI3zs3WuHIOi88fwPLxC2xyqa-DDEE/s1600-h/pic.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 94px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ0WO5QQs-agaIe9bydDnAInFCjkymfa8x21YGF1LFuAMui6JGqAyFEOa5GLG_Dg-v-PGfLlvUQwAMfoKjVul-WLvYzW5eqRAHphCFVBSSU4LWpVI3zs3WuHIOi88fwPLxC2xyqa-DDEE/s200/pic.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5433704504298054498&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Well, it doesn&#39;t give us all that, but it does give us the “Welcome John Doe.” message and the Logout link.  Now let&#39;s replace all those &lt;code&gt;out &lt;&lt; ...&lt;/code&gt; statements with the magic of &lt;code&gt;MarkupBuilder&lt;/code&gt;.&lt;/p&gt;&lt;br /&gt;&lt;pre class=&quot;brush:groovy&quot;&gt;&lt;br /&gt;def loginToggle = {&lt;br /&gt;    def mb = new groovy.xml.MarkupBuilder(out)&lt;br /&gt;        mb.div{&lt;br /&gt;        if (session.user){&lt;br /&gt;            span(style:&#39;float:left;margin:5px 0px 0px 10px&#39;){&lt;br /&gt;                mb.yield &quot;Welcome ${session.user}.&quot;&lt;br /&gt;            }&lt;br /&gt;            span(style:&#39;float:right;&lt;br /&gt;             margin:5px 10px 0px 0px&#39;){&lt;br /&gt;                a(href:createLink(&lt;br /&gt;                  controller:&#39;tekUser&#39;,&lt;br /&gt;                  action:&#39;logout&#39;)){&lt;br /&gt;                    mb.yield &#39;Logout&#39;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        else{&lt;br /&gt;            span(style:&#39;float:right;&lt;br /&gt;                 margin:5px 10px 0px 0px&#39;){&lt;br /&gt;                a(href:createLink(controller:&lt;br /&gt;                 &#39;tekUser&#39;, action:&#39;login&#39;)){&lt;br /&gt;                    mb.yield &#39;Login&#39;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In the first line, we create an instance of &lt;code&gt;MarkupBuilder&lt;/code&gt; using the constructor that takes a &lt;code&gt;Writer&lt;/code&gt;.  The &lt;code&gt;out&lt;/code&gt; property of all &lt;code&gt;TagLib&lt;/code&gt;s is an instance of &lt;code&gt;Writer&lt;/code&gt;, so we&#39;ll pass it that.  Now whatever our &lt;code&gt;MarkupBuilder&lt;/code&gt; produces will be automatically sent to &lt;code&gt;out&lt;/code&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Next we&#39;ll start creating elements.  The outer element for this tag is a &lt;code&gt;div&lt;/code&gt;, so we&#39;ll start with that.  The &lt;code&gt;div&lt;/code&gt; call will just take a closure.  If we were setting any attributes on the &lt;code&gt;div&lt;/code&gt;, they would be method arguments:&lt;/p&gt;&lt;br /&gt;&lt;pre class=&quot;brush:groovy&quot;&gt;&lt;br /&gt;div(class:&quot;someCSSclass&quot;){&lt;br /&gt;    //...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Note that we don&#39;t need a closing &lt;code&gt;div&lt;/code&gt; call --  just the closing brace.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;One of the nice things about Builders is that they are just Groovy code, so you can mix in any other Groovy code along with it.  You might wonder “Why doesn&#39;t it try to turn it into an HTML element?” Well, it&#39;s because &lt;strong&gt;Groovy is smart&lt;/strong&gt;.  It will only try to turn unrecognized symbols into elements.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;So, inside of our &lt;code&gt;if&lt;/code&gt; block, we are calling the &lt;code&gt;span()&lt;/code&gt; method and passing a &lt;code&gt;style&lt;/code&gt; argument, followed by a closure.  The closure will be the body of the generated &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; tag.  Since the body of the &lt;code&gt;span&lt;/code&gt; tag in this case is only text, we will use a method of &lt;code&gt;MarkupBuilder&lt;/code&gt; called &lt;code&gt;yield()&lt;/code&gt;.  This method is necessary for text output in a tag that can contain other tags.  Without it, the Builder would have no way to know that we want text printed literally and not turned into an element.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In the next &lt;code&gt;span&lt;/code&gt; the styling is a bit different to position it on the right side, and inside the closure we have another tag.  This time it is an anchor tag with an &lt;code&gt;href&lt;/code&gt; argument.  For the value of the &lt;code&gt;href&lt;/code&gt;, we call the &lt;code&gt;createLink&lt;/code&gt; tag as a method similar to the way we did before, but this time we don&#39;t have to put it in a Groovy &lt;code&gt;${}&lt;/code&gt; expression. This is because we&#39;re not outputting &lt;code&gt;Strings&lt;/code&gt;; instead, we&#39;re calling Groovy methods.  For the body of the anchor tag, we use the &lt;/code&gt;yield()&lt;/code&gt; method again.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The rest of the code does the same thing for the &lt;code&gt;else&lt;/code&gt; clause, so there&#39;s not much more to talk about, except to note that we don&#39;t do anything else to get all this to be rendered to our view.  Just because we&#39;re binding &lt;code&gt;out&lt;/code&gt; to the &lt;code&gt;MarkupBuilder&lt;/code&gt; in the constructor, the text generated by the Builder will automatically be rendered at the completion of the method.  Pretty cool.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You may have noticed that the &lt;code&gt;MarkupBuilder&lt;/code&gt; version of this tag is actually a tad longer than the previous version, but you&#39;ll have to admit that the readability and flexibility are improved. We also no longer have to worry about leaving out a closing tag when we are doing a bunch of nesting.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I think I&#39;ll be using &lt;code&gt;MarkupBuilder&lt;/code&gt; in most of my tags from now on.  A special shout-out and thanks to Jerome Jahnke for suggesting this topic on the &lt;a href=&quot;http://forums.pragprog.com/forums/116&quot;&gt;pragprog.com Grails forum&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;If you&#39;ve got a suggestion for a blog post related to GQuick, please leave a comment here or on &lt;a href=&quot;http://forums.pragprog.com/forums/116&quot;&gt;the pragprog forum&lt;/a&gt;.  Thanks!&lt;/p&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;
This is a post from &lt;a href=&quot;http://gquick.blogspot.com/&quot;&gt;http://gquick.blogspot.com/&lt;/a&gt;, the blog home of the book &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/4641375625542968865/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gquick.blogspot.com/2010/02/markup-builder-in-gsp.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/4641375625542968865'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/4641375625542968865'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/2010/02/markup-builder-in-gsp.html' title='Using Groovy&#39;s MarkupBuilder In Grails&#39; GSP Tags'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ0WO5QQs-agaIe9bydDnAInFCjkymfa8x21YGF1LFuAMui6JGqAyFEOa5GLG_Dg-v-PGfLlvUQwAMfoKjVul-WLvYzW5eqRAHphCFVBSSU4LWpVI3zs3WuHIOi88fwPLxC2xyqa-DDEE/s72-c/pic.png" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-822152031423180590.post-2359294930029746525</id><published>2009-11-10T04:40:00.000-08:00</published><updated>2009-11-24T15:58:09.418-08:00</updated><title type='text'>Ajax-Enabled Checkboxes in Grails</title><content type='html'>In &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;, we have a task list to help TekEvent organizers and volunteers keep track of what&#39;s left to be done for their event.  It&#39;s pretty handy, but it could be a bit easier to use.&lt;br /&gt;&lt;br /&gt;Specifically, it would be great if we didn&#39;t have to click on a task to open the &lt;span style=&quot;font-style: italic;&quot;&gt;show&lt;/span&gt; view and then click again to &lt;span style=&quot;font-style: italic;&quot;&gt;edit&lt;/span&gt; it, just to mark a task as completed.  What would be really nifty is if we could mark a task as completed by clicking on a simple checkbox. And it turns out that Grails provides an Ajax tag - &lt;code&gt;&amp;lt;g:remotefunction&amp;gt;&lt;/code&gt; - that&#39;s just what we need.&lt;br /&gt;&lt;br /&gt;Here&#39;s a picture of our current list view.  We&#39;ll put our checkbox in the last column, since we don&#39;t really need to repeat the Event name.&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5vCf92AMc2bjNGMo0FQzsnvlyGrlaEThyHToTvFcsMG6FR3PdZKYIHgYBiRq-zAIOrh-nfz9IzuryoN48aWJfCry2y1Z6X7c3fs6TivEpgdt4gD58gkmhIaaEsjZ08hSUnvCwwuy_OWQ/s1600/old_tasklist.jpg&quot;&gt;&lt;img style=&quot;margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 276px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5vCf92AMc2bjNGMo0FQzsnvlyGrlaEThyHToTvFcsMG6FR3PdZKYIHgYBiRq-zAIOrh-nfz9IzuryoN48aWJfCry2y1Z6X7c3fs6TivEpgdt4gD58gkmhIaaEsjZ08hSUnvCwwuy_OWQ/s320/old_tasklist.jpg&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5406029673137313954&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We&#39;ll modify the list view in two places. First, in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section, we&#39;ll replace this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;g:sortableColumn property=&quot;id&quot; title=&quot;Id&quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;g:sortableColumn property=&quot;title&quot; title=&quot;Title&quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;g:sortableColumn property=&quot;notes&quot; title=&quot;Notes&quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;th&amp;gt;Assigned To&amp;lt;/th&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;g:sortableColumn property=&quot;dueDate&quot; title=&quot;Due Date&quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;th&amp;gt;Event&amp;lt;/th&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;With this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&lt;br /&gt;     &amp;lt;g:sortableColumn property=&quot;id&quot; title=&quot;Id&quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;     &amp;lt;g:sortableColumn property=&quot;title&quot; title=&quot;Title&quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;     &amp;lt;g:sortableColumn property=&quot;notes&quot; title=&quot;Notes&quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;th&amp;gt;Assigned To&amp;lt;/th&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;g:sortableColumn property=&quot;dueDate&quot; title=&quot;Due Date&quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;th&amp;gt;Completed&amp;lt;/th&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;All we did here is change the text of the column heading. (Trivial, yet necessary.)&lt;br /&gt;&lt;br /&gt;Next, we will modify the table row that currently displays the Event name.  Let&#39;s replace this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;td&amp;gt;${fieldValue(bean:taskInstance, field:&#39;event&#39;)}&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;with this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;td&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;g:checkBox name=&#39;completed&#39; value=&quot;${taskInstance.completed}&quot;&lt;br /&gt;   onclick=&quot;${remoteFunction(action:&#39;toggleCompleted&#39;, id:taskInstance.id,&lt;br /&gt;   params:&#39;\&#39;completed=\&#39; + this.checked&#39;)}&quot; /&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Here we are using a &lt;code&gt;&amp;lt;g:checkBox&amp;gt;&lt;/code&gt; tag to show whether a Task is completed or not.  We will also use it to toggle the completed value.  For that, we use a &lt;g:remotefunction&gt; tag, but we call it as a method and assign it to the &lt;code&gt;onclick&lt;/code&gt; event of the &lt;code&gt;checkBox&lt;/code&gt;.  We&#39;ll talk a bit more about the &lt;code&gt;remoteFunction&lt;/code&gt;, but first, there&#39;s one more thing we need.&lt;br /&gt;&lt;br /&gt;In order to use any of Grails&#39; JavaScript tags, we need to set a JavaScript library to be used.  We do this with the &lt;code&gt;&amp;lt;g:javascript&amp;gt;&lt;/code&gt; tag, which can be placed anywhere in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section of our page.  Like so:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;g:javascript library=”prototype” /&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The &lt;code&gt;remoteFunction&lt;/code&gt; tag can take a &lt;code&gt;controller&lt;/code&gt;, &lt;code&gt;action&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;params&lt;/code&gt;.  We will use all except the &lt;code&gt;controller&lt;/code&gt;, since we are on a page produced by the &lt;code&gt;TaskController&lt;/code&gt;, which is the controller we&#39;ll be using.  This tag can also take an &lt;code&gt;update&lt;/code&gt; attribute, but we don&#39;t want to update anything on the page, so we&#39;ll skip that one too.  For the &lt;code&gt;action&lt;/code&gt; we&#39;ll use &lt;code&gt;&#39;toggleCompleted&#39;&lt;/code&gt; (which we will write shortly).  The &lt;code&gt;id&lt;/code&gt; will be the id of the &lt;code&gt;taskInstance&lt;/code&gt; being displayed. Then comes the &lt;code&gt;params&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;params&lt;/code&gt; property can be a &lt;code&gt;Map&lt;/code&gt; just as in other GSP tags, but it can also be a &lt;code&gt;String&lt;/code&gt; formatted like request parameters.  That&#39;s the way we use it here: &lt;code&gt;params:&#39;\&#39;completed=\&#39; + this.checked&#39;&lt;/code&gt;.  Since we are already inside a double quoted &lt;code&gt;GString&lt;/code&gt;, we have to use single quotes for the &lt;code&gt;params&lt;/code&gt; value, and that means escaping the single quotes that need to appear in the final value.   The phrase “&lt;code&gt;this.checked&lt;/code&gt;” is a literal that will be passed into the resulting JavaScript function, where it will be evaluated to the &lt;code&gt;checked&lt;/code&gt; value of the checkbox.  This value will end up in our controller as either &#39;&lt;code&gt;true&lt;/code&gt;&#39; or &#39;&lt;code&gt;false&lt;/code&gt;&#39;.&lt;br /&gt;&lt;br /&gt;If we were to look at the page with &lt;a href=&quot;http://getfirebug.com/&quot;&gt;Firebug&lt;/a&gt; (or a similar tool) we would see that the value being assigned to the &lt;code&gt;onclick&lt;/code&gt; event is something like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;new Ajax.Request(&#39;/TekDays/task/toggleCompleted/1&#39;,{asynchronous:true,evalScripts:true,parameters:&#39;completed=&#39; + this.checked});&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Here&#39;s what our page looks like now:&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOI67j9jG_6hVBcOFgqmUXhxXnT45m1Indioa8scvEcbEIGHfx8zhTW3euk4VMLLuPbBEWRoNgl-B8ZdtZ401-_GYxDl2RGl8q3R6AKphGL6ySuuhLZtrbh0GKrXVY4CFFgwPINmm386w/s1600/Screenshot.png&quot;&gt;&lt;img style=&quot;margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 249px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOI67j9jG_6hVBcOFgqmUXhxXnT45m1Indioa8scvEcbEIGHfx8zhTW3euk4VMLLuPbBEWRoNgl-B8ZdtZ401-_GYxDl2RGl8q3R6AKphGL6ySuuhLZtrbh0GKrXVY4CFFgwPINmm386w/s320/Screenshot.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5407818837277735138&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;img src=&quot;file:///home/ben/Desktop/Screenshot.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;As I mentioned earlier, we don&#39;t need to update anything on the page when the user clicks the checkbox.  The checkbox already gives the visual cue we are looking for.  What we want is to set the &lt;code&gt;completed&lt;/code&gt; property on the corresponding &lt;code&gt;Task&lt;/code&gt; instance and then save it.  So, let&#39;s implement the &lt;code&gt;toggleCompleted&lt;/code&gt; action in our &lt;code&gt;TaskController&lt;/code&gt;.  Open &lt;code&gt;/grails-app/controllers/TaskController.groovy&lt;/code&gt; and add the following action:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;def toggleCompleted = {&lt;br /&gt;  def task = Task.get(params.id)&lt;br /&gt;  if (task){&lt;br /&gt;      task.completed = params.completed == &#39;true&#39;&lt;br /&gt;      task.save()&lt;br /&gt;  }&lt;br /&gt;  render &#39;&#39;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The first step in this action is to get the &lt;code&gt;Task&lt;/code&gt; based on the &lt;code&gt;id&lt;/code&gt; passed in.  Then if we get that successfully, we&#39;ll set its &lt;code&gt;completed&lt;/code&gt; property based on the value of the &lt;code&gt;completed&lt;/code&gt; parameter.  You might be tempted to try &lt;code&gt;task.completed = params.completed&lt;/code&gt;, but &lt;code&gt;params.completed&lt;/code&gt; is a &lt;code&gt;String&lt;/code&gt; (either &#39;&lt;code&gt;true&lt;/code&gt;&#39; or &#39;&lt;code&gt;false&lt;/code&gt;&#39;) and not a &lt;code&gt;Boolean&lt;/code&gt; value.  So we have to compare against a &lt;code&gt;String&lt;/code&gt; literal.&lt;br /&gt;&lt;br /&gt;Next we save the Task instance, and finally render a blank &lt;code&gt;String&lt;/code&gt; to prevent any attempt to render something back to the browser.&lt;br /&gt;&lt;br /&gt;That&#39;s all there is to it.  We can now mark a task complete or incomplete right from the list view - no form needed.  Pretty handy.&lt;br /&gt;&lt;br /&gt;(Now we could also clean up the page in other ways, as we did on our &lt;code&gt;TekEvent&lt;/code&gt; list view in section 6.2, “Modifying the Scaffolded Views,” on page 92.)&lt;/g:remotefunction&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;
This is a post from &lt;a href=&quot;http://gquick.blogspot.com/&quot;&gt;http://gquick.blogspot.com/&lt;/a&gt;, the blog home of the book &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/2359294930029746525/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gquick.blogspot.com/2009/11/ajax-enabled-checkboxes-in-grails.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/2359294930029746525'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/2359294930029746525'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/2009/11/ajax-enabled-checkboxes-in-grails.html' title='Ajax-Enabled Checkboxes in Grails'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5vCf92AMc2bjNGMo0FQzsnvlyGrlaEThyHToTvFcsMG6FR3PdZKYIHgYBiRq-zAIOrh-nfz9IzuryoN48aWJfCry2y1Z6X7c3fs6TivEpgdt4gD58gkmhIaaEsjZ08hSUnvCwwuy_OWQ/s72-c/old_tasklist.jpg" height="72" width="72"/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-822152031423180590.post-6929881617232710659</id><published>2009-10-28T18:44:00.000-07:00</published><updated>2009-10-28T19:05:21.412-07:00</updated><title type='text'>Grails: A Quick-Start Guide is shipping!</title><content type='html'>Last week, during the &lt;a href=&quot;http://dave-klein.blogspot.com/2009/10/yet-another-2gx-wrap-up.html&quot;&gt;SpringOne 2GX conference&lt;/a&gt;, I received word that &lt;a href=&quot;http://pragprog.com/titles/dkgrails&quot;&gt;pragprog.com&lt;/a&gt; was shipping pre-orders of GQuick.  Then just today, I began hearing from visionary individuals that their &lt;a href=&quot;http://www.amazon.com/Grails-Quick-Start-Guide-Dave-Klein/dp/1934356468/ref=sr_1_10?ie=UTF8&amp;amp;s=books&amp;amp;qid=1254509146&amp;amp;sr=1-10&quot;&gt;Amazon.com&lt;/a&gt; pre-orders had arrived! &lt;br /&gt;&lt;br /&gt;A huge &lt;span style=&quot;font-size:130%;&quot;&gt;Thank You&lt;/span&gt; to all of those who pre-ordered or who got the beta ebook.  Feedback is always welcome and if you have questions or would like to see something expanded on here on the GQuick blog, &lt;a href=&quot;mailto:daveklein@usa.net&quot;&gt;drop me a line&lt;/a&gt; or post to the PragProg forum at &lt;a href=&quot;http://forums.pragprog.com/forums/116&quot;&gt;http://forums.pragprog.com/forums/116&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Next up, we will see how we can add some helpful features to the Task List in the &quot;Getting Things Done&quot; chapter.  Thanks to beta reader, Johannes Deutschland for that suggestion.&lt;br /&gt;&lt;br /&gt;Then, since Grails 1.2 is destined to be out soon, I will be working through the code to see if anything breaks in 1.2.  If so, I&#39;ll post the fixes here.&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;
This is a post from &lt;a href=&quot;http://gquick.blogspot.com/&quot;&gt;http://gquick.blogspot.com/&lt;/a&gt;, the blog home of the book &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/6929881617232710659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gquick.blogspot.com/2009/10/grails-quick-start-guide-is-shipping.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/6929881617232710659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/6929881617232710659'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/2009/10/grails-quick-start-guide-is-shipping.html' title='Grails: A Quick-Start Guide is shipping!'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-822152031423180590.post-4635692000095508331</id><published>2009-08-26T19:47:00.000-07:00</published><updated>2009-09-06T22:59:32.815-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Custom Tags"/><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="TagLibs"/><category scheme="http://www.blogger.com/atom/ns#" term="Testing"/><title type='text'>Testing GSP Custom Tags</title><content type='html'>&lt;div&gt;In Grails: A Quick-Start Guide we used several custom tags.  Unfortunately we didn&#39;t get a chance to write any tests for these tags.  So let&#39;s do that now.&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In GQuick we have a loginToggle tag which will display a &quot;login&quot; link if no user is logged in and a &quot;logout&quot; link if a user is logged in.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;#160;&amp;#160;def loginToggle = {&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;&amp;lt;div&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;if (session.user){&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;&amp;lt;span&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;Welcome ${session.user}.&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;&amp;lt;a href=&#39;${createLink(controller:&#39;tekUser&#39;,&amp;#160;&amp;#160; &lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; action:&#39;logout&#39;)}&#39;&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;Logout &amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;}&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;else{&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;&amp;lt;span&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;&amp;lt;a href=&#39;${createLink(controller:&#39;tekUser&#39;, &lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; action:&#39;login&#39;)}&#39;&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;Login &amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;}&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;out &amp;lt;&amp;lt; &quot;&amp;lt;/div&amp;gt;&amp;lt;br/&amp;gt;&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;As you can see from the tag code, we are storing a reference to our user object in the session.  If we find a user we display a welcome message and a Logout link.  If there is no user we just display the Login link.  Let see how we can write a unit test for this tag. &lt;br/&gt;&lt;br /&gt;When we create a taglib with the grails create-tag-lib command, a corresponding test is created for us which extends the TagLibUnitTestCase class.  This class makes testing taglibs a piece of cake.  Take a look at the following test:&lt;br/&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;void testLoginToggleWithUser() {&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;mockSession.user = &quot;user&quot;&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;def output = tagLib.loginToggle().toString()&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;assert output.contains(&#39;Logout&#39;)&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;assert output.contains(&#39;action=logout&#39;)&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br/&gt;&lt;br /&gt;The first thing you&#39;ll notice is that we are setting the value of user on the mockSession object.  This is one of the mock objects that are included in the TagLibUnitTestCase.  Also included are mockRequest, mockResponse, mockParams, and mockFlash.  Any calls to session in our tag code will be directed to this mockSession.  So, when we look for the existence of a user to determine which text to display, we will find one.  &lt;br/&gt;&lt;br /&gt;In the next line we call the tag as a method of the implicit tagLib object.  This object is loaded for us by the TagLibUnitTestCase.  We don&#39;t need to declare or create it.  It&#39;s just there and ready for us to use.  Every tag in the tagLib can be called as a method.  All of these tag methods return an instance of java.io.StringWriter.  We call toString() on this to get a String representation of what the tag would have sent to the page.&lt;br/&gt;&lt;br /&gt;Next we just makes some assertions on that output.  We know that, if a user is in the session, we should have the word &#39;Logout&#39; in the tag&#39;s output, so we can check for that with the contains() method.  We&#39;ll talk about the second assertion in a minute.  But first, if we were to run this test it would fail with an error because out tag calls the createLink tag that comes with Grails and none of the built-in Grails tags are available to unit tests. &lt;br/&gt;&lt;br /&gt;Groovy makes this easy to handle.  Let&#39;s just add the following line to our test class&#39; setUp() method.&lt;br/&gt;&lt;br /&gt;&lt;code&gt;tagLib.metaClass.createLink = {map -&amp;gt; &quot;action=${map.action}&quot;}&lt;/code&gt;&lt;br/&gt;&lt;br /&gt;Make sure to add this after the call to super.setUp() so that the tagLib object will be there.  Now when a call to createLink is made within our tagLib it will call the method on the tagLib.metaClass which will return a string with the name of the action that the createLink would have linked to.  This allows us to verify that we are passing the correct action, in this case &#39;logout&#39;.  &lt;br/&gt;&lt;br /&gt;To be thorough, we&#39;ll also write a test to verify the behavior of our tag when there is no user logged in.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;void testLoginToggleNoUser() {&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;def output = tagLib.loginToggle().toString()&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;assert output.contains(&#39;Login&#39;)&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;assert output.contains(&#39;action=login&#39;)&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;Now that we see how easy it is to write unit tests for Grails tagLibs we have no excuse for leaving those auto-generated test classes empty.&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;
This is a post from &lt;a href=&quot;http://gquick.blogspot.com/&quot;&gt;http://gquick.blogspot.com/&lt;/a&gt;, the blog home of the book &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/4635692000095508331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gquick.blogspot.com/2009/08/testing-gsp-custom-tags.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/4635692000095508331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/4635692000095508331'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/2009/08/testing-gsp-custom-tags.html' title='Testing GSP Custom Tags'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-822152031423180590.post-4668654700385829181</id><published>2009-08-15T20:04:00.000-07:00</published><updated>2009-08-15T21:25:31.035-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="gsp"/><category scheme="http://www.blogger.com/atom/ns#" term="images"/><title type='text'>Display An Image Property In a GSP</title><content type='html'>This is the first post for the GQuick blog.  In this blog I plan to include helpful tips about Grails that we weren&#39;t able to get to in the book as well as address questions that come up from readers.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To kick this off we&#39;ll look at something that a couple readers of the beta ebook have asked about:  Displaying the Sponsor logo in a GSP.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For those who haven&#39;t read the book, a bit of background.  We have a Sponsor class which represents organizations that might sponsor a technical event.  Here&#39;s an abbreviated listing of the Sponsor class:&lt;/div&gt;&lt;div style=&quot;border: 1px solid black; overflow: auto; font-family: courier,monospace;&quot;&gt;&lt;pre&gt;class Sponsor {&lt;br /&gt;...&lt;br /&gt;byte[] logo&lt;br /&gt;&lt;br /&gt;static constraints = {&lt;br /&gt;    ...&lt;br /&gt;    logo(nullable:true, maxSize:1000000)&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;This will give us, in most databases, a BLOB type in which we can store an image of our sponsor&#39;s logo.  Grails&#39; scaffolded views provide the file upload mechanism for us but they don&#39;t provide a way to display the stored image in our pages.  But don&#39;t worry, it&#39;s a piece of cake, this is Grails after all. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first thing we&#39;ll do is add an action to our SponsorController called &lt;i&gt;displayLogo.&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-style: normal; &quot;&gt;   &lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid black; overflow: auto; font-family: courier,monospace;&quot;&gt;&lt;pre&gt;class SponsorController {&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;def displayLogo = {&lt;br /&gt;    def sponsor = Sponsor.get(params.id)&lt;br /&gt;    response.contentType = &quot;image/jpeg&quot;&lt;br /&gt;    response.contentLength = sponsor?.logo.length&lt;br /&gt;    response.outputStream.write(sponsor?.logo)&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;In this action we are retrieving a Sponsor with the id being passed in the &lt;i&gt;params&lt;/i&gt;.  Then we set a couple properties on the &lt;i&gt;response&lt;/i&gt; object that is automatically injected into all Grails controllers.  We set the &lt;i&gt;contentType&lt;/i&gt; to &quot;image&quot; and the &lt;i&gt;contentLength&lt;/i&gt; to the length of the logo property of the Sponsor instance.  Finally we write the sponsor.logo to the outputStream of the response.  Pretty simple so far.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The next step is to call this action in our GSP.  To do this we&#39;ll use the &lt;i&gt;createLink&lt;/i&gt; tag within an &lt;i&gt;img&lt;/i&gt; tag, like so:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid black; overflow:auto; font-family: courier,monospace;&quot;&gt;&lt;pre&gt;&amp;#60;img src=&quot;http://www.blogger.com/${createLink(action:&#39;displayLogo&#39;, id:sponsorInstance?.id)}&quot; /&amp;#62;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Now if we drop this in our sponsor/show.gsp in place of the current ${sponsorInstance?.logo} (which spits out a bunch of binary goodness) we will get... an x or a ? depending on which browser we&#39;re using.  Don&#39;t panic, this is just our SecurityFilter trying to help us out.  ( For those who haven&#39;t read GQuick, we have a SecurityFilter that checks for a logged in user for all actions but those listed in the &lt;i&gt;allowedActions&lt;/i&gt; list).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, let&#39;s add displayLogo to the allowedActions list, like this:&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;border: 1px solid black; overflow: auto; font-family: courier,monospace;&quot;&gt;&lt;pre&gt;def allowedActions = [&#39;show&#39;, &#39;index&#39;, &#39;list&#39;, &#39;login&#39;,&lt;/pre&gt;&lt;pre&gt;                      &#39;validate&#39;, &#39;search&#39;, &#39;displayLogo&#39;]&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;Note: this step is only necessary if you&#39;ve been following along with the TekDays application in Grails: A Quick-Start Guide.  If you are just using this technique in your own application then you&#39;re already done.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now when we view one of our sponsors it will look more like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA0hrcudj0K0tM5e9ceiRBnTXnS1zt0MnquNOS5G5YajCXuujJpWa7uk-pIqhuoDJS059Pej0qmTh4xQCThv7jKP9_eb-uSp5-Mtroc3YqAB43rt_ezYxt7d1c0UrOkEMVt285wRILgKM/s1600-h/SponsorLogo.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 254px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA0hrcudj0K0tM5e9ceiRBnTXnS1zt0MnquNOS5G5YajCXuujJpWa7uk-pIqhuoDJS059Pej0qmTh4xQCThv7jKP9_eb-uSp5-Mtroc3YqAB43rt_ezYxt7d1c0UrOkEMVt285wRILgKM/s320/SponsorLogo.png&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5370409483658390082&quot; /&gt;&lt;/a&gt;&lt;div class=&quot;blogger-post-footer&quot;&gt;&lt;p&gt;
This is a post from &lt;a href=&quot;http://gquick.blogspot.com/&quot;&gt;http://gquick.blogspot.com/&lt;/a&gt;, the blog home of the book &lt;a href=&quot;http://pragprog.com/titles/dkgrails/grails&quot;&gt;Grails: A Quick-Start Guide&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gquick.blogspot.com/feeds/4668654700385829181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gquick.blogspot.com/2009/08/display-image-property-in-gsp.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/4668654700385829181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/822152031423180590/posts/default/4668654700385829181'/><link rel='alternate' type='text/html' href='http://gquick.blogspot.com/2009/08/display-image-property-in-gsp.html' title='Display An Image Property In a GSP'/><author><name>Dave Klein</name><uri>http://www.blogger.com/profile/05996044798024907436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgY6nRiSeylzslkIqob9uvanc-hRG6UvurLGWyc9hACk6BwDcllYf4F_HZZFBMXu1W6Ns0d5xu5zpuEk-tL08VtM5wlHa8fjicOOB-R9esMwHe9tMBssMTpzUk8VB3_k/s220/dklein_avatar.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA0hrcudj0K0tM5e9ceiRBnTXnS1zt0MnquNOS5G5YajCXuujJpWa7uk-pIqhuoDJS059Pej0qmTh4xQCThv7jKP9_eb-uSp5-Mtroc3YqAB43rt_ezYxt7d1c0UrOkEMVt285wRILgKM/s72-c/SponsorLogo.png" height="72" width="72"/><thr:total>2</thr:total></entry></feed>