Apache Geronimo on Grails

Create a Grails application to deploy on Apache Geronimo

Do you want to build your Web sites faster and cheaper, but still leverage industrial-strength technology? You can do just that using Grails and Apache Geronimo. Grails leverages the power of the dynamic language Groovy to accelerate your development. However, it runs on the Java® Virtual Machine and leverages proven Java technologies. This makes it easy to take your Grails application to the next level by deploying it to Apache Geronimo, the premiere open source Java EE V5-certified application server. In this article, you will see how easy Grails can make Web development and how easy Geronimo can make Grails deployment. You will also see how a Grails application can leverage the resources and services provided by Geronimo.

Michael Galpin (mike.sr@gmail.com), Developer, eBay

Michael Galpin's photoMichael Galpin has been developing Java software professionally since 1998. He currently works for eBay. He holds a degree in mathematics from the California Institute of Technology.



08 July 2008

Getting started

In this article, you will build a Web application using Grails, and deploy it on Apache Geronimo. Following is the software you need installed to follow along.

Java Development Kit
Both Grails and Geronimo require a Java Development Kit. Grails needs only Java V1.4, but Geronimo needs V1.5. That is because it is a Java EE V5-certified application server, and Java EE V5 uses Java V1.5 features such as annotations and generics. In this article, we use Java SE V1.6_05.
Groovy programming language
Grails uses the Groovy programming language, but this is included with Grails, so there are no additional downloads. Grails also uses many best-of-breed products, such as Hibernate and Spring, but these are also included with Grails.
Grails
This article uses Grails V1.0.2.
Apache Geronimo
This article uses Apache Geronimo V2.1.1. You can use either Geronimo with Tomcat or Jetty, but this article used the Jetty distribution.
MySQL
This article uses MySQL V5.0.41, but you should be able to use any database supported by Hibernate.

This article is not an introduction to Grails. You should already be familiar with Grails, though you could probably get by if you are familiar with Ruby on Rails and Java. The Resources section has some good articles to get you up to speed on Grails.


The Grails ad network

To demonstrate how you can use Grails and Geronimo together, you will build a simple application with Grails, then deploy and enhance it with Geronimo. For your application, you will build a simple ad network. Here is a quick breakdown of the use cases of the application:

  • An advertiser can register on the network. All that's needed is a name and password.
  • An advertiser can log in to the network.
  • An advertiser can create an ad. The ad will have a title, text, and a URL for an image. It will also have a start and end date, a bid, and keywords. The keywords determine when the ad gets displayed, and the bid is used for priority when multiple ads have the same keyword.
  • An affiliate can call a Web service to get a list of ads. The affiliate will provide a keyword and a maximum number of ads.

This is a pretty simple application. The Grails ad network is unlikely to supplant Google or Yahoo!, but it does let us touch many of the attractive features of Grails. Let's take a look at some of those features now and see how they enable us to rapidly develop the ad-network application using Grails.

Grails leverages the principles of convention over configuration and don't repeat yourself to greatly reduce the amount of code you need to write for a typical Web application. You will kick-start your development using some of the Grails code-generation scripts.

Creating an application

Let's call the application adserver. You will start by using the Grails command generate-app. You will not spend too much time with how this command works (see Resources if you are unfamiliar with it). What is important is that your application is set up according to the Grails conventions. Not only is it crucial for the Grails framework to know where to find classes and configuration metadata but it will also make it much easier to package and deploy your application. Figure 1 shows a snapshot of what this directory structure should look like.

Figure 1. The adserver application structure
The adserver application structure

Grails encourages a bottom-up development process, where you usually start by defining your domain models. You can use the Grails command create-domain-class to help with this. The first model you will create is your Advertiser model, as shown in Listing 1.

Listing 1. The Advertiser class
class Advertiser {
    static hasMany = [ads:Ad]
    String name
    String password
}

This class is pretty simple, as you would expect if you were familiar with Grails or Groovy in general. The most complicated thing here is the hasMany line. This indicates that an Advertiser can have multiple Ads. Let's take a look at the Ad class.

Listing 2. The Ad class
class Ad {
    static belongsTo = [advertiser:Advertiser]
    String title
    String imageUrl
    String text
    String keywords
    Date startDate
    Date endDate
    Integer bid
}

This class is also pretty simple, with the other end of the one-to-many relationship between it and the Advertiser class. You could now use the Grails script generate-all to create scaffolding code (controller classes and GNU Server Pages (GSP) views for all CRUD actions) for each of these classes, then run the application using the run-app command. This is optional, but especially useful if you are new to Grails. It gives you good examples of how to use the domain classes, as well as how to write a controller and a view. We need this knowledge to create more customized controllers and views for your application.

Customizing the application

The first thing you will want to do is let your users (advertisers) register. Create a RegisterController. Its code is shown in Listing 3.

Listing 3. The RegisterController
class RegisterController {

    def index = { }

    def save = {
      def exists = Advertiser.findWhere(name:params.name)
      if (exists){
        flash.message = "The name " + params.name + " is already taken"
        redirect(action:index)
      }
        def advertiser = new Advertiser()
        advertiser.properties = params
        advertiser.save(flush:true)
      session.advertiser=advertiser
      redirect(controller:"ad",action:"adsFor")
    }
}

The main thing this does is check to see if the name the advertiser picked has already been taken. If it has, an error message is created and you redirect to the index page. If the name is not taken, the advertiser is created. I put the Advertiser instance in the session, so I do not have to look it up again, and I redirect to the Ad controller. You will look at that class soon, but first you need to let the advertiser log in. The Login controller is shown in Listing 4.

Listing 4. Login controller
class LoginController {

    def index = { }
    def login = {
    def advertiser = Advertiser.findWhere( 
                          name:params.name , password:params.password )
    if (!advertiser){
        flash.message = "The password does not match the name of the advertiser"
        redirect(action:index)
    }
    session.advertiser=advertiser
    redirect(controller:"ad",action:"adsFor")
   }
}

This is another simple Groovy class. It simply checks if the name and password match up to the database. If it does, it sets the advertiser in the session and forwards to the same action in the Ad controller you first saw in the Register controller. Before moving on to the Ad controller, you might have noticed that the controller methods make direct use of domain-object data-access code. An alternative would be to use a service layer.

Grails service layer

The best practice here would be to factor out the Advertiser.findWhere code seen in the RegisterController and LoginController to a service layer. To do so, you can use the Grails command create-service and refactor your code to move business logic to the service class. For the register/login scenario shown above, it might look something like Listing 5.

Listing 5. Sample AdvertiserService
class AdvertiserService {

    boolean transactional = true

    def advertiserExists(String name){
    def exists = Advertiser.findWhere(name:name)
        exists != null
    }
    
    def login(String name, String password){
        Advertiser.findWhere(name:name, password:password)
    }
}

Remember, Grails is built on proven Java technology such as the Spring framework. A Grails service class becomes a Spring bean. Your service will be a singleton by default, just like it would be if you were using Spring directly. It can also be injected to other Spring beans, and all controllers happen to be Spring beans as well. Convention over configuration kicks in again here and makes it easy to reference the service in a controller.

Listing 6. Using a service in a controller
class LoginController {
    def advertiserSerivce
    def index = { }
    def login = {
    def advertiser = advertiserService.login(params.name, params,password)
    if (!advertiser){
        flash.message = "The password does not match the name of the advertiser"
        redirect(action:index)
    }
    session.advertiser=advertiser
    redirect(controller:"ad",action:"adsFor")
   }
}

By simply having a member variable following the xyzService convention, Grails knows to inject the xyzService singleton. In this sample, you will keep things simple and will not worry too much about a service layer. However, it is important to recognize this as a feature of Grails that really sets it apart from many other rapid-development/convention-over-configuration frameworks. Let's get back to the application code and look at how ads are created and served.

Creating and serving ads

So far, we have two controllers — one for registering and one for logging into the ad network. Both controllers forward control to another controller: the AdsController.

Listing 7. The AdsController
import grails.converters.XML
            
class AdController {
    
    def index = { redirect(action:list,params:params) }

    // the delete, save and update actions only accept POST requests
    def allowedMethods = [delete:'POST', save:'POST', update:'POST', placement:'GET']

    def adsFor = {
        render(view:"list", model:[adList: Ad.findAllWhere(advertiser : 
session.advertiser )]);
    }
    def list = {
        if(!params.max) params.max = 10
        [ adList: Ad.list( params ) ]
    }

    def placement = {
        def ads = Ad.findAll("from Ad as ad where ad.keywords like 
'%"+params.keyword+"%' order by ad.bid desc")
        render ads as XML
    }

    def show = {
        def ad = Ad.get( params.id )

        if(!ad) {
            flash.message = "Ad not found with id ${params.id}"
            redirect(action:list)
        }
        else { return [ ad : ad ] }
    }

    def delete = {
        def ad = Ad.get( params.id )
        if(ad) {
            ad.delete()
            flash.message = "Ad ${params.id} deleted"
            redirect(action:list)
        }
        else {
            flash.message = "Ad not found with id ${params.id}"
            redirect(action:list)
        }
    }

    def edit = {
        def ad = Ad.get( params.id )

        if(!ad) {
            flash.message = "Ad not found with id ${params.id}"
            redirect(action:list)
        }
        else {
            return [ ad : ad ]
        }
    }

    def update = {
        def ad = Ad.get( params.id )
        if(ad) {
            ad.properties = params
            if(!ad.hasErrors() && ad.save()) {
                flash.message = "Ad ${params.id} updated"
                redirect(action:show,id:ad.id)
            }
            else {
                render(view:'edit',model:[ad:ad])
            }
        }
        else {
            flash.message = "Ad not found with id ${params.id}"
            redirect(action:edit,id:params.id)
        }
    }

    def create = {
        def ad = new Ad()
        ad.properties = params
        return ['ad':ad]
    }

    def save = {
        def ad = new Ad(params)
        if (session.advertiser){
            ad.advertiser = session.advertiser
        }
        if(!ad.hasErrors() && ad.save()) {
            flash.message = "Ad ${ad.id} created"
            redirect(action:show,id:ad.id)
        }
        else {
            render(view:'create',model:[ad:ad])
        }
    }
}

If you have used the scaffolding command (generate-all ad), you will recognize much of this code. It has been customized in several ways though. You added the adsFor method, as this is what we have been forwarding to from the register and login controllers. This uses the advertiser from the session to get all the ads owned by the advertiser, then renders this with the "list" view. The save method has also been modified to use the advertiser from the session. Finally, the placement method has been added. This is the method that will be used by affiliates to retrieve ads. It takes a keyword parameter and gets all the ads with that keyword. It uses a custom query that uses the Hibernate Query Language, which is a REST Web service that uses XML to serialize the data. Grails makes this easy by simply returning render ads as XML. You have not had to add much code to customize your application to handle all your use cases. You can quickly find out just how little code you have actually written using the Grails stats command.

Listing 8. Ad-server application stats
$ grails stats

Welcome to Grails 1.0.2 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /Users/michael/lib/grails-1.0.2        
        
Base Directory: /Users/michael/code/grails/adserver
Note: No plugin scripts found
Running script /Users/michael/lib/grails-1.0.2/scripts/Stats.groovy
Environment set to development

    +----------------------+-------+-------+
    | Name                 | Files |  LOC  |
    +----------------------+-------+-------+
    | Controllers          |     4 |   179 | 
    | Domain Classes       |     2 |    15 | 
    | Services             |     1 |    10 | 
    | Integration Tests    |     5 |    20 | 
    +----------------------+-------+-------+
    | Totals               |    12 |   224 | 
    +----------------------+-------+-------+

That is not a whole lot of code. Actually, most of it is code you generated using the generate-all scaffolding for advertiser and ad. Grails makes it easy to develop by not making you write much code. It also makes it easy to develop by making it easy to test.

Testing the application

To run the application, simply issue the Grails command run-app. This invokes a script that uses an embedded in-memory database and an embedded Jetty Web container, so you do not need to set up a stand-alone server or database. You should be able to immediately go to http://localhost:8080/adserver and access your controllers, etc. Once you have tested and debugged your application, you are ready to start using it with Geronimo.


Leveraging Geronimo

So now you have developed a Web application that satisfies all of your use cases. You were able to develop it rapidly and had to write less code because you took advantage of Grails. Of course, less code is obviously a relative term — less code compared to what? The obvious answer is a typical Java Web application. There is no question that Grails lets you develop faster and write less code than a typical Java Web application, but is there a cost to this? The good news is that a Grails application is a Java Web application. Grails lets you package it up as a standard Java Web application, a WAR, and deploy it to any Java Web container — including Apache Geronimo. Let's take a look at how you deploy your Grails ad-server application to Geronimo.

Deploying a Grails WAR

The first step in deploying our Grails application to Geronimo is to create a WAR. Looking at the Grails directory structure, it would not be too hard to write an Ant script to do this, but luckily, Grails make it even easier by providing a simple Grails command: war. The script compiles code from the grails-app tree and combines it with code from the base Grails ($GRAILS_HOME) directory. The result is added to the /web-app directory. Since you are using Geronimo, you need to add a Geronimo deployment plan. You can simply create a geronimo-web.xml file in /web-app/WEB-INF.

Listing 9. Geronimo deployment plan
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-1.1">
    <environment xmlns="http://geronimo.apache.org/xml/ns/deployment-1.1">
        <moduleId>
            <groupId>grailsApps</groupId>
            <artifactId>AdServer</artifactId>
            <version>0.1</version>
            <type>war</type>
        </moduleId>
        <hidden-classes>
            <filter>org.springframework</filter>
            <filter>org.apache.cxf</filter>
            <filter>org.apache.commons</filter>
        </hidden-classes>        
    </environment>
    <context-root>/adserver</context-root>
</web-app>

There is one very important thing to notice here, and that is the hidden-classes section. These are packages that are included by default with Geronimo, but are also included with Grails. This tells the class loader that will load our Grails app, to ignore any classes from these packages available to the parent class loader (i.e., the container's class loader). This will guarantee that the Grails versions of these classes are loaded and we will not have any nasty class-loader conflicts.

Now you are ready to run the Grails war command. Listing 10 shows the command being run and the output of the command.

Listing 10. Using the war command
$ grails war

Welcome to Grails 1.0.2 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /Users/michael/lib/grails-1.0.2        
        
Base Directory: /Users/michael/code/grails/adserver
Note: No plugin scripts found
Running script /Users/michael/lib/grails-1.0.2/scripts/War.groovy
Environment set to production
   [delete] Deleting: /Users/michael/.grails/1.0.2/projects/
adserver/resources/web.xml
   [delete] Deleting directory /Users/michael/.grails/1.0.2/projects/adserver/classes
   [delete] Deleting directory /Users/michael/.grails/1.0.2/projects/adserver/resources
    [mkdir] Created dir: /Users/michael/.grails/1.0.2/projects/adserver/classes
  [groovyc] Compiling 13 source files to /Users/michael/.grails/1.0.2/
projects/adserver/classes
    [mkdir] Created dir: /Users/michael/.grails/1.0.2/projects/adserver/
resources/grails-app/i18n
[native2ascii] Converting 10 files from /Users/michael/code/grails/adserver/
grails-app/i18n to /Users/michael/.grails/1.0.2/projects/adserver/resources/
grails-app/i18n
     [copy] Copying 1 file to /Users/michael/.grails/1.0.2/projects/adserver/classes
     [copy] Copying 1 file to /Users/michael/.grails/1.0.2/projects/adserver/resources
    [mkdir] Created dir: /Users/michael/code/grails/adserver/staging
     [copy] Copying 93 files to /Users/michael/code/grails/adserver/staging
     [copy] Copied 19 empty directories to 1 empty directory under /Users/michael/
code/grails/adserver/staging
     [copy] Copying 23 files to /Users/michael/code/grails/adserver/staging/
WEB-INF/grails-app
     [copy] Copying 55 files to /Users/michael/code/grails/adserver/staging/
WEB-INF/classes
    [mkdir] Created dir: /Users/michael/code/grails/adserver/staging/
WEB-INF/spring
     [copy] Copying 1 file to /Users/michael/code/grails/adserver/staging/
WEB-INF/classes
    [mkdir] Created dir: /Users/michael/code/grails/adserver/staging/
WEB-INF/templates/scaffolding
     [copy] Copying 6 files to /Users/michael/code/grails/adserver/staging/
WEB-INF/templates/scaffolding
     [copy] Copying 50 files to /Users/michael/code/grails/adserver/staging/
WEB-INF/lib
     [copy] Copying 1 file to /Users/michael/code/grails/adserver/staging/
WEB-INF
   [delete] Deleting: /Users/michael/.grails/1.0.2/projects/adserver/resources/web.xml
     [copy] Warning: /Users/michael/code/grails/adserver/plugins not found.
[propertyfile] Updating property file: /Users/michael/code/grails/adserver/staging/
WEB-INF/classes/application.properties
    [mkdir] Created dir: /Users/michael/code/grails/adserver/staging/WEB-INF/plugins
     [copy] Warning: /Users/michael/code/grails/adserver/plugins not found.
      [jar] Building jar: /Users/michael/code/grails/adserver/adserver-0.1.war
   [delete] Deleting directory /Users/michael/code/grails/adserver/staging
Done creating WAR /Users/michael/code/grails/adserver/adserver-0.1.war

To deploy the WAR, you can simply add it to the Geronimo deployment directory ($GERONIMO_HOME/deploy, where $GERONIMO_HOME is the location of your Geronimo installation) or you can use the Geronimo Console.

Figure 2. Using the Geronimo console to deploy Grails WAR
Using the Geronimo console to deploy Grails WAR

In the Geronimo deployment plan in Listing 9, we set the context path of the application to /adserver, so http://localhost:8080/adserver will bring up our deployed application. Now that our Grails application is running on Geronimo, let's look at how Geronimo can be used to enhance the application.

Creating a database pool

It is common knowledge that you can gain a lot of performance benefits by using database connection pools. You save the significant overhead of creating a database connection on every request to your Web application. Thus, it is very common to set up a database connection pool in Java Web applications. An application server like Geronimo makes it even easier to create such a pool that can be reused by any application deployed on Geronimo. You can create a pool easily from the Geronimo console, as shown in Figure 3.

Figure 3. Creating a database pool in Geronimo
Creating a database pool in Geronimo

Now that the database pool has been created, we just need to access it from our Grails application. There are a couple of steps we need to do for this.

Accessing a database pool from Grails

The database pool is represented as a Java DataSource object and it is bound in JNDI, so any application can use it. For this to exist in the context of a Web application, it needs to be referenced in our web.xml file. Wait a minute — what web.xml? Grails creates one for us that it bases off of $GRAILS_HOME/conf/webdefault.xml. You could edit that, or you could generate the WAR, unzip it, and edit the generated one. Either way, you will need to add the section shown in Listing 11 to it.

Listing 11. Referencing a DataSource in web.xml
<resource-ref>
    <res-ref-name>jdbc/MyDataSource</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

This can usually be added just before the end of the web.xml file. Next, we need to add a reference to our Geronimo deployment plan.

Listing 12. New Geronimo deployment plan
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-1.1">
    <environment xmlns="http://geronimo.apache.org/xml/ns/deployment-1.1">
        <moduleId>
            <groupId>grailsApps</groupId>
            <artifactId>AdServer</artifactId>
            <version>0.1</version>
            <type>war</type>
        </moduleId>
        <hidden-classes>
            <filter>org.springframework</filter>
            <filter>org.apache.cxf</filter>
            <filter>org.apache.commons</filter>
        </hidden-classes>
        <dependencies>
            <dependency>
                <groupId>console.dbpool</groupId>
                <artifactId>adserver</artifactId>
            </dependency>
        </dependencies>        
    </environment>
    <context-root>/adserver</context-root>
    <resource-ref>
        <ref-name>jdbc/MyDataSource</ref-name>
        <resource-link>adserver</resource-link>
    </resource-ref>    
</web-app>

Note that we called our database pool adserver and that is the resource-link you see in the resource-ref and the dependency. The ref-name in the deployment plan must match the res-ref-name in the web.xml snippet in Listing 11. This connects the dots between Geronimo and your Web application.

Now the connection pool is available to the application. So how do you access it? Turns out that Grails makes this part easy. All we need to do is edit /grails-app/conf/DataSource.groovy.

Listing 13. DataSource.groovy
dataSource {
    jndiName = "java:comp/env/jdbc/MyDataSource"
}
hibernate {
    cache.use_second_level_cache=true
    cache.use_query_cache=true
    cache.provider_class='org.hibernate.cache.EhCacheProvider'
}
// environment specific settings
environments {
    development {
        dataSource {
            dbCreate = "update"
        }
    }
    test {
        dataSource {
            dbCreate = "create-drop"
        }
    }
    production {
        dataSource {
            dbCreate = "create"
        }
    }
}

Normally, the first dataSource block is where we define JDBC properties, like driver class, username, password, etc. Now we simply provide the JNDI name. This name must be "java:comp/env/" plus whatever you put for the res-ref-name in the web.xml file. You can override all of these settings for any of your environments, just like you normally would.


Summary

You have seen how Grails makes it possible to rapidly build a Web application. Its use of convention over configuration makes a lot of popular technologies fuse together with no effort. You follow the conventions and Grails makes your life easy. You still have to write code for your business logic, but even that code is minimal, courtesy of the expressiveness of the Groovy programming language. Grails with Geronimo just gives this happy story an even happier ending. You are able to leverage all the power of Geronimo as our Grails application runs just like any other Java Web application and can access Geronimo resources in a similar fashion. From here, you can make your Grails application more sophisticated by using more Geronimo features like messaging, or you could make it more scalable by deploying it to a Geronimo cluster.


Download

DescriptionNameSize
Sample codeos-ag-grails.adserver.example.zip829KB

Resources

Learn

Get products and technologies

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=318030
ArticleTitle=Apache Geronimo on Grails
publish-date=07082008