Wednesday, May 12, 2010

Misplaced Blog Post

I was intending to post here about adding Quartz scheduling to the TekDays application, but I accidentally posted it on http://dave-klein.blogspot.com instead. I apologize for the confusion, but please feel free to check it out.

Thanks!

Saturday, March 27, 2010

Clustering A Grails App with Terracotta

When I was writing Grails: A Quick-Start Guide, I thought that scalability issues were too complicated and “out of scope” for a “getting started” book. Well, the folks at Terracotta have been busy at work making me wrong. Integrating Terracotta's Web Sessions clustering product is so easy that I could have covered it in a page or two. So, we'll do that now.

In this post, we'll install and configure Web Sessions Express. In subsequent posts we'll look at some of Terracotta's other products: EhCache, Quartz, and EhCache for Hibernate.

We'll start with installing Terracotta, which can be downloaded at http://www.terracotta.org/dl/oss-download-catalog. We'll grab the tar/zip file (terracotta-3.2.1.tar.gz) and expand it to an appropriate location (like /opt/terracotta-3.2.1/). Then we'll set that directory as our TERRACOTTA_HOME.

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'll be using Tomcat 6.0.24, so we'll follow the instructions for Tomcat 6.0x. Let's copy TERRACOTTA_HOME/sessions/terracotta-session-1.0.0.jar to CATALINA_HOME/lib. (For the correct location for other servers, see the Terracotta documentation.)

Now for our application, we'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're going to have to deploy it on a whole bunch of servers. :-) If you didn't build the application, you can download it from http://pragpub.com/titles/dkgrails/code. Also, check out this post on upgrading and deploying TekDays for some helpful tips.

Next we need to add a very simple configuration file to our application. We'll create the file web-app/META-INF/context.xml and include the following bit of XML:

<Context>

<Valve className="
org.terracotta.session.TerracottaTomcat60x
SessionValve
"
tcConfigUrl="localhost:9510"/>

</Context>

I've suggested that the Terracotta team come up with a Groovy DSL for configuring their tools in Grails applications, and it seems that they're considering it. But in the meantime, take the necessary precautions when you're adding the XML, and be sure to take a break and bandage any wounds you received through handling those sharp edges.

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 <grin> you are using some other server, you can find the appropriate configuration code at the terracotta.org documentation page.

We're almost done. Right now we could start up the Terracotta server, start up our Tomcat server, and deploy a TekDays.war. And it would almost work. But in order for Terracotta to cluster an application, all of the artifacts that we want to be clustered need to implement serializable. So, we'll just open up some of our classes and add implements serializable to their class declaration. Here's a list of the files that we need to modify:

grails-app/domain/TekEvent.groovy

grails-app/domain/TekUser.groovy

grails-app/domain/Task.groovy

grails-app/domain/Message.groovy

grails-app/domain/Sponsor.groovy

grails-app/domain/Sponsorship.groovy

grails-app/conf/SecurityFilters.groovy


For example, we'll modify the TekEvent class like so:


class TekEvent implements Serializable {
//...

We'll do the same for each of those other files, and then we'll be ready to try it out. To run our newly terracottafied TekDays application, take the following steps:

1. Build a WAR file: from the TekDays application directory, run grails war tekdays.war

2. Copy tekdays.war to Tomcat's webapps folder: /webapps

3. Start the Terracotta server: /bin/start-tc-server.sh (or .bat if you're on Windows)

4. Start Tomcat: /bin/startup.sh (or .bat if you're on Windows)

5. Quick sanity check: navigate to http://localhost:8080/tekdays (or the port that your Tomcat instance is running on.)

6. Start the Terracotta Developer Console (this step isn't necessary to run the app, but it gives us a nice way to see that it's working): /bin/dev-console.sh (again .bat if you're on Windows)

The Developer Console will give us a screen like this:




When we click "Connect", we should get something like this:



Now this only shows one connected client right now, since that's all that we're running. To really see the magic, we need to fire up another Tomcat instance on a different port. But we'll leave that as an exercise for the reader.

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: /bin/shutdown.sh

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!

There are other steps for setting up multiple Terracotta servers and such, but as far as our application goes, that'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, Grails Scales!

Thursday, March 25, 2010

Upgrading and Deploying GQuick Code

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've been in contact with the plugin's author and he promises to fix that soon.

On the other hand, if you didn't build the TekDays application but would like to work with the code downloaded from the Pragmatic Programmer's website, there is a different issue. I recently downloaded the final code base, which is found in the download bundle at code/deploy/TekDays/, 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 a solution) was to go to home/.grails/1.2.1/projects/TekDays/plugins/and remove the 1.5.1 version of the plugin. After that it all worked fine.

It worked fine, that is, until I tried to deploy the app to Tomcat. When I built the .war file and copied it to my /webapps/ directory, it wouldn't start. I tried forcing it to start from the Tomcat manager. No dice. So I checked in /logs/catalina.out (if you're using Tomcat, you gotta love that file), and found a datasource error that made me smack my forehead.

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 have JDNI configured on your local Tomcat instance; I sure didn't. So, I changed my grails-app/conf/DataSource.groovy file to make the production datasource the same as development.

If you are doing this for the first time and haven't worked through the book, you will need to create a MySQL database called tekdays and set the user and password to match that in the DataSource.groovy file (or vice versa). Alternatively, you can use any database with a JDBC driver. Just include your JDBC driver in the TekDays/grails-app/lib/ directory and modify your DataSource.groovy accordingly. I prefer MySQL because it's so easy to get and easy to use. (More info on MySQL can be found at http://dev.mysql.com.)

One final note: Some people have mentioned getting OutOfMemoryErrors 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 JAVA_OPTS environment variable. In Linux/Unix or Mac OSX, you could do something like this:

export JAVA_OPTS='-Xmx1G -XX:MaxPermSize=256m'

In Windows, you can use the wizard in the control panel under System. Or maybe you can edit your autoexec.bat file. (Do they still use that?)

For the best learning experience, it's probably worth the time to go through the book and build the app as you go, but if you want to just download it and take it for a spin first, these tips should help you get going.

Have fun!

Tuesday, February 2, 2010

Using Groovy's MarkupBuilder In Grails' GSP Tags

One of the cool features of Groovy that we were not able to cover in GQuick is the MarkupBuilder. MarkupBuilder 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:


 
a(href:'http://somehost.com/somepath/someresource'){
//anchor text goes here
}


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's see how we can transform one of our custom tags using Groovy's MarkupBuilder.



We'll do our conversion on the loginToggle 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's pretty handy. We can just drop it on any page and avoid using a bunch of messy <<g:if> logic in our page.



This tag is introduced in Chapter 8, “Knock, Knock: Who's There? Grails Security”. Here is how it looks now:



def loginToggle = {
out << "<div>"
if (session.user){
out << "<span style='float:left;
margin:5px 0px 0px 10px'>"
out << "Welcome ${session.user}."
out << "</span><span style='float:right;
margin:5px 10px 0px 0px'>"
out << "<a href='${createLink(controller:
'tekUser', action:'logout')}'>"
out << "Logout </a></span>"
}
else{
out << "<span style='float:right;
margin:5px 10px 0px 0px'>"
out << "<a href='${createLink(controller:'tekUser',
action:'login')}'>"
out << "Login </a></span>"
}
out << "</div><br/>"
}


To use the tag, we just placed this in our main.gsp:



<g:loginToggle />


That gave us something like this:





Well, it doesn't give us all that, but it does give us the “Welcome John Doe.” message and the Logout link. Now let's replace all those out << ... statements with the magic of MarkupBuilder.



def loginToggle = {
def mb = new groovy.xml.MarkupBuilder(out)
mb.div{
if (session.user){
span(style:'float:left;margin:5px 0px 0px 10px'){
mb.yield "Welcome ${session.user}."
}
span(style:'float:right;
margin:5px 10px 0px 0px'){
a(href:createLink(
controller:'tekUser',
action:'logout')){
mb.yield 'Logout'
}
}
}
else{
span(style:'float:right;
margin:5px 10px 0px 0px'){
a(href:createLink(controller:
'tekUser', action:'login')){
mb.yield 'Login'
}
}
}
}
}


In the first line, we create an instance of MarkupBuilder using the constructor that takes a Writer. The out property of all TagLibs is an instance of Writer, so we'll pass it that. Now whatever our MarkupBuilder produces will be automatically sent to out.



Next we'll start creating elements. The outer element for this tag is a div, so we'll start with that. The div call will just take a closure. If we were setting any attributes on the div, they would be method arguments:



div(class:"someCSSclass"){
//...
}


Note that we don't need a closing div call -- just the closing brace.



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't it try to turn it into an HTML element?” Well, it's because Groovy is smart. It will only try to turn unrecognized symbols into elements.



So, inside of our if block, we are calling the span() method and passing a style argument, followed by a closure. The closure will be the body of the generated <span> tag. Since the body of the span tag in this case is only text, we will use a method of MarkupBuilder called yield(). 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.



In the next span 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 href argument. For the value of the href, we call the createLink tag as a method similar to the way we did before, but this time we don't have to put it in a Groovy ${} expression. This is because we're not outputting Strings; instead, we're calling Groovy methods. For the body of the anchor tag, we use the yield() method again.



The rest of the code does the same thing for the else clause, so there's not much more to talk about, except to note that we don't do anything else to get all this to be rendered to our view. Just because we're binding out to the MarkupBuilder in the constructor, the text generated by the Builder will automatically be rendered at the completion of the method. Pretty cool.



You may have noticed that the MarkupBuilder version of this tag is actually a tad longer than the previous version, but you'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.



I think I'll be using MarkupBuilder in most of my tags from now on. A special shout-out and thanks to Jerome Jahnke for suggesting this topic on the pragprog.com Grails forum.



If you've got a suggestion for a blog post related to GQuick, please leave a comment here or on the pragprog forum. Thanks!