CHAPTER 16
Leveraging Spring

The ever-popular Spring Framework (http://www.springframework.org) was one of the first frameworks to pioneer simplified development in the Java space. Promoting the use of Plain Old Java Objects (POJOs), rather than objects tied to specific Java APIs such as EJB and JMS, Spring shot to prominence and is now integrated into pretty much every popular Java open source framework. However, it's not just the open source software advocates who have been quick to integrate Spring; major software vendors such as BEA (now part of Oracle) have chosen to base their software on the Spring Framework.

In this chapter, we'll cover what Spring is, how it relates to Grails, and how you can leverage it to do some pretty interesting things. Spring is a huge framework in itself that provides wrappers and utility classes for pretty much every common problem found using Java technology; hence, full coverage of Spring is beyond the scope of this book. Nevertheless, we'll go through a quick overview so you can garner a basic understanding of what Spring is.

Spring Basics

Spring is the engine that underpins Grails. At its core, it is a dependency injection container that allows you to configure and wire together dependencies. When using raw Spring, this is typically done in an XML format, as shown in Listing 16-1.

Listing 16-1. Spring's XML Format

<beans>
  <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
               destroy-method="close">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>
  </bean>
    <bean id="mySessionFactory"
                 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
      <property name="dataSource" ref="myDataSource"/>
      <property name="mappingResources">
        <list>
          <value>product.hbm.xml</value>
        </list>
      </property>
      <property name="hibernateProperties">
        <value>
         hibernate.dialect=org.hibernate.dialect.HSQLDialect
       </value>
     </property>
   </bean>
 </beans>

Note In recent times, Spring has been extended to allow this type of configuration to be done using Java 5 annotations instead of XML. See http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config for further information.


The example in Listing 16-1 defines two Spring "beans" using the <bean> element:

  • myDataSource: An instance of the org.apache.commons.dbcp.BasicDataSource class that uses an HSQLDB database running on port 9001
  • mySessionFactory: A Hibernate SessionFactory using the HSQLDialect and a single Hibernate XML mapping file called product.hbm.xml

You set the properties of each bean using the <property> element. Notice how you can reference beans you have defined using the ref attribute:

<property name="dataSource" ref="myDataSource"/>

As an alternative to explicitly managing dependencies as in the previous example, you can configure beans for "autowiring," in which case Spring will try to wire your dependencies together automatically. Autowiring can be configured using the type or the name on the <bean> element:

<bean autowire="byType" ...>
    ...
</bean>

In this case, the type of class is used to calculate how beans are wired together; however, you can also specify autowiring to happen by bean name using a value of byName for the autowire attribute. See http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-factory-autowire for more information.

Once you have configured a bunch of beans, you can construct an org.springframework.context.ApplicationContext instance. You can do this in a number of different ways. With XML, you could use a ClassPathXmlApplicationContext instance that searches the classpath for the given XML file name:

ApplicationContext applicationContext =
                                new ClassPathXmlApplicationContext("beans.xml")

Once you have an ApplicationContext, you can query it for any configured beans, as shown in Listing 16-2.

Listing 16-2. Using the ApplicationContext

SessionFactory sessionFactory = applicationContext.getBean("mySessionFactory")

With that rather simplistic introduction out the way, let's look at the Spring and Grails combo.

Spring and Grails

You may well be wondering at this point how all this relates to Grails. Essentially, the way Grails works is that it does what Spring's XML does for you at runtime. Instead of you defining an XML file with all the dependencies configured, Grails makes some decisions based on the conventions in the project and automatically configures Spring using sensible defaults. However, all Grails objects are essentially Spring beans that have been configured for autowiring by name.

Dependency Injection and Grails

The way Grails allows you to inject services into other Grails classes is powered by Spring. Grails takes the class name and infers a bean name from it. For example, the com.g2one.gtunes.StoreService becomes the bean called storeService. Then you can simply define a property with the appropriate name and have it injected by Spring:

def storeService

If you prefer to look up beans explicitly, then you can always use the ApplicationContext directly. All you have to do is implement the interface org.springframework.context. ApplicationContextAware, and Grails will inject the ApplicationContext instance into your class. Listing 16-3 shows the updates to the StoreController class needed to achieve this.

Listing 16-3. Implementing ApplicationContextAware

import org.springframework.context.*

class StoreController implements ApplicationContextAware {
    ApplicationContext applicationContext

    StoreService getStoreService() { applicationContext.getBean("storeService") }
    ...
s}

A concern about Grails often raised by Spring users is that they will lose some control without the fine-grained ability to micromanage each bean definition. This concern is unfounded because although Grails does configure beans for you, you retain the ability to override any bean definition and add your own bean definitions.

You can achieve this in a couple of ways. If you are used to Spring's XML format and prefer it, then you can create a file called grails-app/conf/spring/resources.xml and add your XML bean definitions in the regular Spring way. However, the preferred way in Grails is to use the Spring DSL provided by Grails. In the next section, you'll learn about the Spring DSL and how you can use it to manipulate Grails' Spring configuration.

The BeanBuilder DSL

In early versions of Grails, a Grails application was configured by generating the necessary Spring XML at runtime. The generated XML would then be parsed into bean definitions. All in all, it was a rather clunky solution to the problem of creating Spring bean definitions at runtime. Later, the Grails developers came up with a new way of configuring Spring encapsulated by the grails.spring.BeanBuilder class.

Essentially, Grails searches the directory grails-app/conf/spring for any Groovy scripts. By default, Grails creates a single script called resources.groovy in the grails-app/conf/spring directory when you create a project. Figure 16-1 shows the resources.groovy script nested snugly within the gTunes project.

image

Figure 16-1. The resources.groovy script

The resources.groovy script itself contains a single property called beans that is assigned a block of code, as shown in Listing 16-4.

Listing 16-4. The beans Property

beans = {
    // Place your Spring DSL code here
}

The beans property is equivalent to the root <beans> element in Spring XML. Now let's find out how to define a Spring bean using the BeanBuilder DSL.

Defining Spring Beans

To define a bean with BeanBuilder, you need to invoke a method with the bean class as the first argument. For example, the myDataSource bean from Listing 16-1 can be defined using BeanBuilder, as shown in Listing 16-5.

Listing 16-5. Defining a Bean

myDataSource(org.apache.commons.dbcp.BasicDataSource) { bean ->
    bean.destroyMethod = "close"
    driverClassName"org.hsqldb.jdbcDriver"
    url="jdbc:hsqldb:hsql://localhost:9001"
    username="sa"
    password=""
}

The Spring bean identifier is taken from the method name you invoke, while the class of the bean is the first argument. The final argument to the method is a Groovy closure that lets you define the properties of the bean.

The example in Listing 16-5 sets the driverClassName, url, username, and password properties of the myDataSource bean within the closure. Notice also that the closure's first argument allows you to set any property on the Spring org.springframework.beans.factory.support.AbstractBeanDefinition class. The example in Listing 16-5 sets the destroyMethod of the bean to close using this technique:

bean.destroyMethod = "close"

This is equivalent to the destroy-method attribute used in the <bean> element of Spring XML. You may want to set a number of other useful properties in the bean argument, including the following:

  • autowire: Allows you to control whether a bean is a candidate for autowiring. This can be set to true (defaults to autowiring by name), byName, byType, or byConstructor.
  • abstract: Whether this bean is an abstract bean (see the section "Abstract Beans").
  • dependsOn: Specify the names of the beans that the bean depends on as a List. Spring will ensure dependent beans are initialized first.
  • destroyMethod: The method to call when the bean is destroyed, because of container shutdown.
  • factoryBean: The bean that is used to construct instances of this bean (see the section "Factory Beans").
  • initMethod: The method to call when the bean is initialized.
  • parent: The parent of this bean definition (see the section "Abstract Beans").
  • scope: The scope of the bean (see the section "Bean Scopes").

Each of these properties allows you to control the manner in which a bean is constructed and disposed of. One thing to note is that the code in Listing 16-5 works because the BasicDataSource class has a default constructor. If there is no default constructor, then you can pass arguments to the bean's constructor by simply appending them after the class name:

helloWorldString(String, "hello world!")

In this case, a new java.lang.String will be created as a bean called helloWorldString using the String(String) constructor. Also, BeanBuilder supports Groovy's additional default constructor that takes a Map that allows you to set properties on the instance after it is constructed. This is quite common in GORM; for example, to create a new Album instance, you can do the following:

new Album(title:"The Bends", genre:"Alternative")

To do the same thing with BeanBuilder, you can do the following:

theBendsAlbum(Album, title:"The Bends", genre:"Alternative")

As you can see from the previous example, by specifying a sequence of named arguments after the class, you can set properties on the bean.

Overriding Spring Beans

Any Spring bean that is automatically defined by Grails can be overridden by creating a bean of the same name in the grails-app/conf/spring/resources.groovy file or the equivalent XML version. You can find a complete list of names of the beans that Grails configures automatically in the "Plugins" section of the reference documentation for Grails at http://grails.org/doc/1.1.x.

As you learned in Chapter 13, Grails itself is configured by plugins, and each plugin typically defines a doWithSpring closure that uses BeanBuilder syntax to configure Spring in some way. The following are some of the more critical beans that Grails configures that you may want to consider overriding:

  • dataSource: The javax.sql.DataSource instance that represents the connection to the database.
  • jspViewResolver: The org.springframework.web.servlet.ViewResolver instance that Grails uses to resolve GSP or JSP views. You can override this bean to integrate custom view technologies, such as FreeMarker (http://freemarker.org/), into Grails.
  • localeResolver: The org.springframework.web.servlet.LocaleResolver instance that Grails uses to resolve the Locale of an incoming request.
  • messageSource: The org.springframework.context.MessageSource instance that Grails uses to resolve i18n messages (discussed in Chapter 7).
  • multipartResolver: The org.springframework.web.multipart.MultipartResolver instance that Grails uses to handle file uploads (discussed in Chapter 4).
  • sessionFactory: The org.hibernate.SessionFactory instance used to configure Hibernate.
  • transactionManager: The org.springframework.transaction.PlatformTransactionManager instance Grails uses to manage transactions using Spring's transaction abstraction (discussed in Chapter 11).

As an example, say you wanted to override the dataSource bean to use a C3PO (http://sourceforge.net/projects/c3p0) connection pool. All you have to do is provide a bean in the grails-app/conf/spring/resources.groovy file with the corresponding name, as shown in Listing 16-6.

Listing 16-6. Overriding the dataSource Bean

dataSource(com.mchange.v2.c3p0.ComboPooledDataSource) { bean ->
    bean.destroyMethod = "close"
    driverClass"org.hsqldb.jdbcDriver"
    jdbcUrl="jdbc:hsqldb:hsql://localhost:9001"
    user="sa"
    password=""
}
Factory Beans

A common pattern in Spring is the factory bean, essentially a bean that constructs another bean. In Spring, factory beans are encapsulated by the org.springframework.beans.factory.FactoryBean interface, as shown in Listing 16-7.

Listing 16-7. The FactoryBean Interface

public interface FactoryBean {
    Object getObject() throws Exception;
    Class getObjectType();
    boolean isSingleton();
}

You've already seen a factory bean being used in the Spring XML in Listing 16-1. The org.springframework.orm.hibernate3.LocalSessionFactoryBean class is a FactoryBean instance that constructs a Hibernate org.hibernate.SessionFactory instance. The LocalSessionFactoryBean class implements the FactoryBean interface so that the getObject() method returns a fully constructed SessionFactory instance.

In other words, although the type of the factory bean is LocalSessionFactoryBean, the finally constructed bean is actually an instance of the SessionFactory class. Listing 16-8 shows an example of using the LocalSessionFactoryBean instance with BeanBuilder.

Listing 16-8. Using LocalSessionFactoryBean with BeanBuilder

mySessionFactory(org.springframework.orm.hibernate3.LocalSessionFactoryBean) {
    dataSource = myDataSource
    mappingResources = ['product.hbm.xml']
    hibernateProperties = ['hibernate.dialect':'org.hibernate.dialect.HSQLDialect']
}

There are a few interesting things to note about the example in Listing 16-8. First, as you can see, you can reference other beans simply by referencing the name of the bean:

dataSource = myDataSource

Here, the myDataSource bean that was defined in Listing 16-5 is referenced. You can even reference beans that haven't been defined yet or that are defined by Grails. For example, to reference the dataSource bean constructed by Grails instead, you can use the ref method:

dataSource = ref("dataSource")

Second, note the usage of Groovy Map and List literals in Listing 16-8 when defining bean properties:

mappingResources = ['product.hbm.xml']
hibernateProperties = ['hibernate.dialect':'org.hibernate.dialect.HSQLDialect']

Compared to the Spring XML, the Groovy syntax is far more concise and readable. Now, typically factory beans do implement the FactoryBean interface defined earlier, but it is important to note that you don't have to implement this interface to use factory beans. By using the factoryMethod property, you can implement similar logic. For example, Listing 16-9 constructs a java.util.Calendar instance that is prototyped. This means that a new bean is constructed every time you call the getBean(String) method of the ApplicationContext class or the bean is wired into another bean (you'll learn more in the "Bean Scopes" section).

Listing 16-9. Using the factoryMethod Property

calendarBean(java.util.Calendar) { bean ->
    bean.factoryMethod = "getInstance"
    bean.scope = "prototype"
}

Notice how in Listing 16-9 the factoryMethod property of the bean argument is used to specify that in order to construct a Calendar instance the static getInstance() method must be called. You can even use other beans as factories. For example, to create another bean from the calendarBean in Listing 16-9, you can simply pass the name of the bean as an argument to the bean-defining method:

USCalendar(calendarBean) {
    firstDayOfWeek = Calendar.SUNDAY
}
frenchCalendar(calendarBean) {
    firstDayOfWeek = Calendar.MONDAY
}

In this example, two new beans are defined, called USCalendar and frenchCalendar, that both use the calendarBean as their factory and set a different firstDayOfWeek property for each bean. The fun with factory beans doesn't end there. You can also define a method that needs to be called to construct the bean:

timeZoneBean(calendarBean:"getTimeZone")

In this example, a new bean called timeZoneBean will be constructed by invoking the get-TimeZone() method of the calendarBean instance.

Inner Beans

Occasionally you may need to define a bean that you don't want to expose to clients because it relates to the internal workings of another bean. To achieve this, you can use inner beans. To define an inner bean, you can assign a closure to a property where the first argument to the closure is the bean type. For example, if you wanted to define the myDataSource bean as an inner bean of the mySessionFactory bean, you could do so, as shown in Listing 16-10.

Listing 16-10. Using Inner Beans

mySessionFactory(org.springframework.orm.hibernate3.LocalSessionFactoryBean) {
    dataSource = { org.apache.commons.dbcp.BasicDataSource bd ->
        driverClassName"org.hsqldb.jdbcDriver"
        url="jdbc:hsqldb:hsql://localhost:9001"
        username="sa"
        password=""
    }
    mappingResources = ['product.hbm.xml']
    hibernateProperties = ['hibernate.dialect':'org.hibernate.dialect.HSQLDialect']
}

With the example in Listing 16-10, only the mySessionFactory bean will be exposed because the dataSource property has been defined using an inner bean.

Abstract Beans

An abstract bean in Spring is more akin to the template pattern than an abstract class in Java. Essentially, you can define an incomplete, or abstract, bean that provides a set of common properties but that is not itself instantiated. Other beans can then extend from the abstract bean and inherit any properties defined on said abstract bean.

For example, consider the case where you are defining multiple data sources. Each data source uses the same driver, so it would be painful and would waste time to have to repeat this information over and over. Instead, you could define an abstract bean that sets the driverClassName property, and any other common properties, and then create individual beans that use the abstract bean as a parent. Listing 16-11 shows an example.

Listing 16-11. Using Abstract Beans

dataSourceCommons {
    driverClassName"org.hsqldb.jdbcDriver"
    username="sa"
    password=""
}
firstDataSource(org.apache.commons.dbcp.BasicDataSource) { bean ->
    bean.parent = dataSourceCommons
    url="jdbc:hsqldb:hsql://localhost:9001"
}
secondDataSource(org.apache.commons.dbcp.BasicDataSource) { bean ->
    bean.parent = dataSourceCommons
    url="jdbc:hsqldb:hsql://localhost:9002"
}

In Listing 16-11, the code defines an abstract bean called dataSourceCommons. BeanBuilder assumes that if you don't pass a class name as the first argument, then the bean is abstract. Alternatively, if you have a bean definition that accepts a class name and you want it to be abstract, then you can set the abstract property of the bean argument to true:

dataSourceCommons(org.apache.commons.dbcp.BasicDataSource) { bean ->
    bean.abstract = true
    driverClassName"org.hsqldb.jdbcDriver"
    username="sa"
    password=""
}

Returning to Listing 16-11, the remaining code then constructs a further two beans called firstDataSource and secondDataSource, each of which sets the parent property of the bean argument to dataSourceCommons. This allows these beans to inherit the properties set on the abstract bean dataSourceCommons.

Bean Scopes

By default, all Spring beans are singleton scoped. This means that there is only ever one instance of the bean within the Spring container. As you saw in the "Factory Beans" section, beans can also be prototype scoped. In this case, a new instance of the bean is created every time the bean is requested. Several other scopes are available, listed here in order of their longevity:

  • prototype: A new bean is created every time the getBean(name) method of the ApplicationContext is called or every time the bean is injected into another bean.
  • request: A new bean is created for each request.
  • flash: A new bean is created and stored in flash scope, making it available for the current and next requests only.
  • flow: When using Web Flows (see Chapter 9), a new bean is created and placed into flow scope. The bean is disposed of when the flow terminates.
  • conversation: When using Web Flows, a new bean is created and placed in conversation scope. The bean is disposed of when the conversation ends.
  • session: A new bean is created and stored in the client session. The bean is disposed of when the session is invalidated.
  • singleton: A single bean exists for the life of the Spring container.

Tip You can even create your own scopes; see the section on custom scopes in the Spring user guide at http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes-custom.


As you've already seen, to register a bean that utilizes one of the previously mentioned scopes, you need to set the scope property of the bean argument:

frenchCalendar(calendarBean) { bean ->
    bean.scope = "prototype"
    firstDayOfWeek = Calendar.MONDAY
}
Dynamically Creating Beans

The major benefit of BeanBuilder in comparison to Spring's static XML format is that because the BeanBuilder DSL is Groovy code, you can create beans dynamically, on the fly. For example, it is often useful to configure different beans for different environments. This is nontrivial to achieve in raw Spring. You often have to use a combination of FactoryBean instances and the org.springframework.beans.factory.config.PropertyPlaceholderConfigurer class to substitute different values for different environments in your build.

With BeanBuilder, this isn't really necessary. Take, for example, the mySessionFactory bean you saw earlier. You could configure the SessionFactory differently for development than for production. Listing 16-12 shows an example.

Listing 16-12. Dynamically Configuring Beans

def hibProps = ['hibernate.dialect':'org.hibernate.dialect.HSQLDialect']
if(grails.util.GrailsUtil.isDevelopmentEnv()) {
    hibProps."hibernate.show_sql" = "true"
    hibProps."hibernate.format_sql" = "true"
}
mySessionFactory(org.springframework.orm.hibernate3.LocalSessionFactoryBean) {
    dataSource = myDataSource
    mappingResources = ['product.hbm.xml']
    hibernateProperties = hibProps
}

As you can see from Listing 16-12, using regular Groovy code you can check whether you're in the development environment and, if so, set up some properties that are useful for development only. In the example in Listing 16-12, the code configures the hibernate.show_sql and hibernate.format_sql properties, which allow you to debug Hibernate SQL, to be enabled only in the development environment.

However, it is not just the properties of the beans you can configure dynamically. Thanks to Groovy's ability to invoke methods using strings, you can easily create the beans themselves dynamically (see Listing 16-13).

Listing 16-13. Dynamic Bean Creation

def dataSources = [firstDataSource: 9001, secondDataSource:9002]

dataSources.each { name, port ->
    "$name"(org.apache.commons.dbcp.BasicDataSource) { bean ->
        bean.destroyMethod = "close"
        driverClassName"org.hsqldb.jdbcDriver"
        url="jdbc:hsqldb:hsql://localhost:$port"
        username="sa"
        password=""
    }
}

As you can see from Listing 16-13, which uses a map of data source names and ports, you can create bean names dynamically by invoking the bean-defining method using a String:

"$name"(org.apache.commons.dbcp.BasicDataSource) { bean ->

This code will create two beans called firstDataSource and secondDataSource using the keys of the dataSources map.

Spring in Action

Now that you've learned what can be achieved with BeanBuilder, let's put some of that knowledge to work and have some fun with Spring. We'll take you through a couple of examples that will build on the gTunes application. The first involves taking advantage of Spring's excellent support for the JMS API.

Integrating JMS with Spring JMS

What many people fail to realize when evaluating Spring is that it is far more than just a dependency injection container. It is an entire abstraction layer over Java EE standards and popular open source software, which promotes POJO programming. The idea behind Spring is that you should rarely, if ever, have to refer to framework code in your own code. The Spring JMS support is one such an example, where Spring allows you to define Message-Driven POJOs (see http://static.springframework.org/spring/docs/2.5.x/reference/jms.html#jms-asynchronousMessageReception).

In the following sections, we'll show how you can use BeanBuilder to set up Spring's support for Message-Driven POJOs with JMS. The functionality you're going to implement will allow users to subscribe to a particular Artist. When the Artist releases a new Album, an e-mail notification needs to be sent to all subscribed users. The mechanics of this are going to be implemented using asynchronous, reliable messaging. But before you can do that, you're going need a JMS container. A popular open source solution is the Apache ActiveMQ project.

Setting up ActiveMQ

To get started using ActiveMQ, download the distribution from http://activemq.apache.org/, and extract it somewhere locally. Figure 16-2 shows an example of the ActiveMQ distribution once installed.

image

Figure 16-2. The ActiveMQ installation directory


Tip You'll find detailed installation instructions covering multiple platforms on the ActiveMQ web site at http://activemq.apache.org/getting-started.html.


Now to get started using ActiveMQ, you need to start up the ActiveMQ container. You can do this by running the bin/activemq command or one of the OS-specific commands such as bin/macosx/activemq, as shown in Listing 16-14.

Listing 16-14. Starting ActiveMQ


$ activemq start
Starting ActiveMQ Broker...

You can verify that ActiveMQ has started up correctly by opening the ActiveMQ web console, which you can typically find on port 8161 at the URL http://localhost:8161/admin/. Figure 16-3 shows an example of the ActiveMQ web console.

image

Figure 16-3. The ActiveMQ web console

Configuring ActiveMQ with BeanBuilder

With ActiveMQ running, it's now time to configure the gTunes application so that it can use the ActiveMQ instance. A prerequisite to using JMS is obtaining a reference to a javax.jms.ConnectionFactory instance. ActiveMQ provides an implementation of this interface through the org.apache.activemq.ActiveMQConnectionFactory class.

Before you can use it, you need to add the necessary JAR files to the lib directory of the gTunes application. Figure 16-4 shows the activemq-core-5.0.0.jar file and the necessary geronimo JARs, which you can obtain from the ACTIVEMQ_HOME/lib directory, in place.

image

Figure 16-4. The ActiveMQ JAR files


Tip There is actually an ActiveMQ plugin (http://grails.org/ActiveMQ+Plugin) for Grails that performs all the setup you can see in this section using a Grails plugin. But since you're learning about Spring, we'll continue to describe the manual way!


The next step is to open the grails-app/conf/spring/resources.groovy file and register a new Spring bean that points to the URL of the ActiveMQ container. Listing 16-15 shows the necessary BeanBuilder code.

Listing 16-15. Configuring the JMS ConnectionFactory for ActiveMQ

jmsFactory(org.apache.activemq.ActiveMQConnectionFactory) {
    brokerURL = "tcp://localhost:61616"
}

The example in Listing 16-15 sets up a single bean called jmsFactory that is an instance of the ActiveMQConnectionFactory class. It also sets the brokerURL property to the location of the ActiveMQ server, which by default runs on port 61616. One thing that is pretty common, though, is to use a pool of ConnectionFactory instances for sending messages, in the same way as you would pool a database java.sql.Connection. You can easily achieve this by using the org.apache.activemq.pool.PooledConnectionFactory class, as shown in Listing 16-16.

Listing 16-16. Using a ConnectionFactory Pool

jmsFactory(org.apache.activemq.pool.PooledConnectionFactory) { bean ->
    bean.destroyMethod = "stop"
    connectionFactory = { org.apache.activemq.ActiveMQConnectionFactory cf ->
        brokerURL = "tcp://localhost:61616"
    }
}

As you can see in Listing 16-16, the code defines the connectionFactory property of the PooledConnectionFactory class using an inner bean definition. You learned about inner beans in the section of that name earlier in the chapter.

Sending JMS Messages with Spring

To send JMS messages using the jmsFactory bean, you can take advantage of Spring's excellent org.springframework.jms.core.JmsTemplate class that provides helper methods for sending messages. You can define the JmsTemplate as another Spring bean, as shown in Listing 16-17.

Listing 16-17. Defining the jmsTemplate Bean

jmsTemplate(org.springframework.jms.core.JmsTemplate) {
    connectionFactory = jmsFactory
}

Notice how in Listing 16-17 the connectionFactory property is referencing the jmsFactory bean you defined earlier. Now you need to consider how to send a notification when a new Album is created. A good way to achieve this is to use a GORM event, as described in Chapter 10.

A useful thing about domain classes in Grails is that they too are Spring beans that can participate in autowiring. So, to get a reference to the jmsTemplate from the Album class, you simply need to define a property within the Album domain class that matches the bean name. Listing 16-18 shows the updates to the Album domain class.

Listing 16-18. Injecting the jmsTemplate into the Album Domain Class

class Album {
    ...
    transient jmsTemplate
}

You'll notice that in Listing 16-18 the jmsTemplate property is defined as transient. This is to avoid the JmsTemplate instance being serialized along with the Album class when it is sent in a JMS message or stored in a flow. Now you can define an afterInsert event, which is fired after an Album instance is inserted into the database that sends a JMS message containing the Album instance. Listing 16-19 shows the necessary code.

Listing 16-19. Sending a JMS Message

class Album {
    ...
    transient afterInsert = {
        try {
            jmsTemplate.convertAndSend("artistSubscriptions", this)
    }
    catch(e) {
        log.error "Error sending JMS message: ${e.message}",e
    }
  }
}

As you can see from Listing 16-19, the afterInsert event uses the JmsTemplate class' convertAndSend method to send the current Album instance to a JMS Queue called artistSubscriptions. Additionally, if an exception occurs, such as when the JMS container is down, the exception is logged at the error level.

If you now attempt to create a new Album using the administrative tools you created in Chapter 14, a JMS message will be sent when you do so. If you visit the ActiveMQ web console and click the Queues section, you'll notice that the artistSubscription queue has appeared in the list, as shown in Figure 16-5.

image

Figure 16-5. The available JMS queues

Consuming JMS Messages with Spring

At this point, you need to consider how to consume the JMS messages that have been sent. Luckily, Spring provides some excellent ways to achieve this too. A central concept is the notion of Message-Driven POJOs.16 A Message-Driven POJO is a normal Java class that has a method that is triggered by a JMS message. To implement a Message-Driven POJO, you can use the org.springframework.jms.listener.adapter.MessageListenerAdapter class, which is capable of listening for JMS messages and delegating the message to another bean.

A nice thing about Grails' Spring integration is that you can reuse the beans Grails defines automatically within your own bean definitions. So, for example, if you wanted to create a MessageListenerAdapter that delegates to a new method called onNewAlbum in the com.g2one.gtunes.StoreService, you can do so using the code shown in Listing 16-20.

Listing 16-20. Using MessageListenerAdapter to Delegate to a Grails Service

import org.springframework.jms.listener.adapter.*
...
jmsMessageListener(MessageListenerAdapter, ref("storeService")) {
    defaultListenerMethod = "onNewAlbum"
}

Notice how in Listing 16-20 the code uses the ref(beanName) method to create a reference to the storeService bean, which is one of the beans Grails automatically creates, and passes it to the constructor of the MessageListenerAdapter bean. If you don't specify the defaultListenerMethod property, then Spring will try to delegate to a method called handleMessage. That may be fine, but to increase the clarity of your code, you can change this as the example in Listing 16-20 does.

For the jmsMessageListener to receive messages, you need to set up an instance of the org.springframework.jms.listener.DefaultMessageListenerContainer class as a Spring bean. The DefaultMessageListenerContainer class works with the JMS ConnectionFactory to monitor for JMS messages. Listing 16-21 shows how to configure an instance of DefaultMessageListenerContainer with BeanBuilder.

Listing 16-21. Configuring a DefaultMessageListenerContainer Instance

jmsContainer(org.springframework.jms.listener.DefaultMessageListenerContainer) {
    connectionFactory = jmsFactory
    destinationName = "artistSubscriptions"
    messageListener = jmsMessageListener
    transactionManager = ref("transactionManager")
    autoStartup = false
}

As you can see from Listing 16-21, the destinationName is set to the same destination used by the jmsTemplate in Listing 16-19. The messageListener property is set to the jmsMessageListener bean you created earlier, and, interestingly, the transactionManager property is set to refer to Grails' built-in transactionManager instance.

The importance of this will become clear later; for now the last thing to note is that autoStartup is set to false. This is done to make sure Grails has sufficient time to load before the consumer starts receiving messages from the JMS container. The downside is that you have to take responsibility for starting the jmsContainer bean yourself. To do this, you can use the grails-app/conf/BootStrap.groovy class' init method. Listing 16-22 shows the changes to BootStrap.groovy to achieve this.

Listing 16-22. Starting the jmsContainer Instance

class BootStrap {

    def jmsContainer
    def init = { servletContext ->
        ...
        log.info "Starting JMS Container"
        jmsContainer.start()
   }
}

As with other Grails classes, the BootStrap class can obtain references to any Spring bean simply by defining a property that matches the bean name. In Listing 16-22, a jmsContainer property is defined, resulting in the DefaultMessageListenerContainer instance being injected, at which point the start() method can be called. To complete the picture, you need to implement the onNewAlbum method within the com.g2one.gtunes.StoreService class. For now, simply provide an implementation that prints the album title, as shown in Listing 16-23.

Listing 16-23. A Simple onNewAlbum Implementation

class StoreService {
    ...
    void onNewAlbum(Album album) {
        println "-- Got album $album.title"
    }
}

Now if you create a new Album instance, a JMS message will be sent to the ActiveMQ server and placed on the queue. The Spring jmsContainer bean is already listening for messages, and as soon as it gets one from the queue, the onNewAlbum message will be triggered, and a message such as the following will be printed:


-- Got album The Bends

All of this happens asynchronously and so doesn't interfere with the actual workflow of creating an Album instance. Spring is also doing a lot of magic under the surface to automatically translate what is in fact a javax.jms.ObjectMessage instance into the correct arguments to pass to the onNewAlbum method. The StoreService itself, you'll notice, has no direct references to the JMS APIs. It is a simple POJO (or POGO, as the case may be).

Enabling Artist Subscriptions

At the moment, the onNewAlbum method is not doing anything of great value. To rectify this situation, you first need to implement the capability for users to subscribe to a particular Artist. There is already a com.g2one.gtunes.Subscription domain class that models subscriptions. Of course, the Subscription needs to know what Artist it relates to. To do this, you can create a subclass of Subscription called ArtistSubscription by running the grails create-domain-class command:

$ grails create-domain-class com.g2one.gtunes.ArtistSubscription

After adding a new enum value called ARTIST to the SubscriptionType enum in the parent class, the ArtistSubscription implementation looks like Listing 16-24.

Listing 16-24. The ArtistSubscription Class

package com.g2one.gtunes

class ArtistSubscription extends Subscription {

    ArtistSubscription() {
        type = SubscriptionType.ARTIST
    }

    static belongsTo = [artist:Artist]
}

With the ArtistSubscription class in place, you need to consider how to allow users to subscribe. To do so, you can develop a GSP template that renders a "Subscribe" link if the user is not subscribed or an "Unsubscribe" link if they are. Listing 16-25 shows the code for the grails-app/views/artist/_subscribe.gsp template.

Listing 16-25. The subscribe Template

<jsec:isLoggedIn>
    <div id="subscription">
        <gtunes:isSubscribed artist="${artist}">
                <g:remoteLink update="subscription"
                      controller="artist"
                      action="unsubscribe"
                      id="${artist.id}">Unsubscribe</g:remoteLink> -
                      Click here to no longer receive e-mail updates when
                      <strong>${artist.name}</strong> release a new album.
        </gtunes:isSubscribed>
        <gtunes:notSubscribed artist="${artist}">
            <g:remoteLink update="subscription"
                  controller="artist"
                  action="subscribe"
                  id="${artist.id}">Subscribe</g:remoteLink>
                  Click here to receive e-mail updates when
                  <strong>${artist.name}</strong> release a new album.
        </gtunes:notSubscribed>
    </div>
</jsec:isLoggedIn>

The template in Listing 16-25 uses the <jsec:isLoggedIn> tag to ensure that the subscription controls are displayed only if the user is logged in. Then it uses two tags that haven't been created yet: <gtunes:isSubscribed> and <gtunes:notSubscribed>. The idea behind these two tags is to render different markup according to whether the user is subscribed. In Listing 16-25, the tags render either a "Subscribe" or "Unsubscribe" <g:remoteLink> tag that makes an Ajax call and updates the surrounding <div>.

To implement these tags, you need to create a new tag library. You can do so by running the grails create-tag-lib command:

$ grails create-tag-lib com.g2one.gtunes.Subscription

This command will create a new tag library at the location grails-app/taglib/com/g2one/gtunes/SubscriptionTagLib.groovy. Listing 16-26 shows the template for the SubscriptionTagLib class.

Listing 16-26. The SubscriptionTagLib Implementation

package com.g2one.gtunes

class SubscriptionTagLib {
      static namespace = "gtunes"
}

Notice that the SubscriptionTagLib in Listing 16-26 is using the gtunes namespace. To implement the <gtunes:isSubscribed> tag, you need a way to check whether a user is subscribed. You can do so by writing a utility method that uses GORM to query the database. Listing 16-27 shows a possible implementation.

Listing 16-27. Checking Whether a User Is Subscribed

boolean checkSubscribed(user, artist) {
       user && artist &&
            ArtistSubscription.findByUserAndArtist(user, artist, [cache:true])
}

Using the ArtistSubscription class you created earlier, the code in Listing 16-27 uses a dynamic finder called findByUserAndArtist to locate the Subscription instance. Note that the code uses the cache:true argument so that the query is cached, because it is likely to be used quite frequently. With the checkSubscribed method in place, writing a couple of tags to take advantage of it is pretty easy. Listing 16-28 shows the code for the <gtunes:isSubscribed> and <gtunes:notSubscribed> methods.

Listing 16-28. Implementing the SubscriptionTagLib Tags

def isSubscribed = { attrs, body ->
    if(checkSubscribed(request.user, attrs.artist)) {
        out << body()
    }
}
def notSubscribed = { attrs, body ->
    if(!checkSubscribed(request.user, attrs.artist)) {
        out << body()
    }
}

Of course, testing is crucial too. You could test the tag library using the GroovyPagesTestCase class you used to test the AlbumArtTagLib. However, since the SubscriptionTagLib is mainly about branching logic and not markup rendering, it is probably easier to take advantage of the grails.test.TagLibUnitTestCase class that lets you unit test tag libraries but not the markup they generate. Simply create a new a unit test in the test/unit/com/g2one/gtunes directory called SubscriptionTagLibTests that extends from the TagLibUnitTestCase class, as shown in Listing 16-29.

Listing 16-29. Using the TagLibUnitTestCase Class

package com.g2one.gtunes

class SubscriptionTagLibTests extends grails.test.TagLibUnitTestCase {
    ...
}

You can then write a couple of simple tests that check the behavior of the <gtunes: isSubscribed> and <gtunes:notSubscribed> tags. Listing 16-30 shows two tests called testIsSubscribed and testNotSubscribed.

Listing 16-30. Testing the SubscriptionTagLib Class

void testIsSubscribed() {
    mockDomain(ArtistSubscription)

    def artist = new Artist(name:"Kings of Leon")
    def user = new User(login:"testuser")
    new ArtistSubscription(artist:artist, user:user).save()

    tagLib.request.user = user
    tagLib.isSubscribed(artist:artist) {
        "subscribed"
    }
    tagLib.notSubscribed(artist:artist) {
        "notsubscribed"
    }

    assertEquals "subscribed", tagLib.out.toString()
}

void testNotSubscribed() {
    mockDomain(ArtistSubscription)

    def artist = new Artist(name:"Kings of Leon")
    def user = new User(login:"testuser")

    tagLib.request.user = user
    tagLib.isSubscribed(artist:artist) {
        "subscribed"
    }
    tagLib.notSubscribed(artist:artist) {
        "notsubscribed"
    }

    assertEquals "notsubscribed", tagLib.out.toString()
}

A closure can be passed as the body of the tag, as long as it returns a String representing the body contents. In Listing 16-30, either "subscribed" or "notsubscribed" will be written to the mock out variable. OK, with the tests out of the way, the next thing to do is to modify the grails-app/views/artist/_artist.gsp template to include the new _subscribe.gsp template. Listing 16-31 shows the necessary code changes highlighted in bold.

Listing 16-31. Updates to the _artist.gsp Template

<div id="artist${artist.id}" class="artistProfile" style="display:none;">
    <div class="artistDetails">
        ...
        <g:render template="subscribe" model="[artist:artist]"></g:render>
    </div>
</div>

Now when you visit one of the artist pages, you'll see a new "Subscribe" link, as shown in Figure 16-6.

image

Figure 16-6. The "Subscribe" link

Unfortunately, when you click the link, you'll receive a "Page not found" 404 error. To resolve this issue, you need to implement the server logic for the subscribe and unsubscribe actions that the <g:remoteLink> tags in Listing 16-25 refer to. Open the ArtistController class, and add a new action called subscribe that persists a new ArtistSubscription if one doesn't already exist. Listing 16-32 shows an example implementation.

Listing 16-32. Implementing the subscribe Action

def subscribe = {
    def artist = Artist.get(params.id)
    def user = request.user
    if(artist && user) {
        def subscription = ArtistSubscription.findByUserAndArtist(user, artist)
        if(!subscription) {
            new ArtistSubscription(artist:artist, user:user).save(flush:true)
        }
        render(template:"/artist/subscribe", model:[artist:artist])
    }
}

As you can see from the code in Listing 16-32, the subscribe action reuses the _subscribe.gsp template to render an Ajax response to the client. The logic in the SubscriptionTagLib deals with the rest. To add the unsubscribe logic, you simply need to delete the ArtistSubscription instance if it exists, as shown in Listing 16-33.

Listing 16-33. Implementing the unsubscribe Action

def unsubscribe = {
    def artist = Artist.get(params.id)
    def user = request.user
    if(artist && user) {
        def subscription = ArtistSubscription.findByUserAndArtist(user, artist)
        if(subscription) {
            subscription.delete(flush:true)
        }
        render(template:"/artist/subscribe", model:[artist:artist])
    }
}

Finally, you need to add a couple of URL mappings in order to expose the subscribe and unsubscribe actions, as shown in Listing 16-34.

Listing 16-34. The Subscriptions URL Mappings

"/artist/subscribe/$id"(controller:"artist", action:"subscribe")
"/artist/unsubscribe/$id"(controller:"artist", action:"unsubscribe")
Implementing Asynchronous E-mail Notifications

Now with users able to subscribe to their favorite artists, it is time to consider the onNewAlbum method of the StoreService class again. Whenever a JMS message is received, you're going to need to find all the subscribers for the Artist associated with the passed Album and send an e-mail to each one.

To do this, you first need a reference to the mailService bean, provided by the Mail plugin installed in Chapter 12, which can be obtained by defining a property of the same name:

def mailService

Next, you need to obtain a list of all the User instances subscribed to the Artist associated with the Album. To do this, you can get a reference to the Artist via the artist property:

def artist = album.artist

Then use a criteria query to obtain a list of users:

def users = ArtistSubscription.withCriteria {
    projections {
        property "user"
    }
    eq('artist', artist)
}

Notice the use of the projections block to specify that you want the result to contain the user property of each ArtistSubscription found. Once you have a list of users, you can now use the mailService to send an e-mail to each one:

for(user in users) {
    mailService.sendMail {
        from "[email protected]"
        to user.email
        title "${artist.name} has released a new album: ${album.title}!"
        body view:"/emails/artistSubscription", model:[album:album,
                                      artist:artist,
                                      user:user]
    }
}

As you can see, the body method is used to specify that the e-mail is to be rendered by a view called /emails/artistSubscription. We'll return to this view in a moment. For completeness, Listing 16-35 contains the full code listing for the onNewAlbum(Album) method.

Listing 16-35. The onNewAlbum(Album) Method

void onNewAlbum(Album album) {
    try {
        def artist = album.artist
        def users = ArtistSubscription.withCriteria {
            projections {
                property "user"
            }
            eq('artist', artist)
        }

        for(user in users) {
            mailService.sendMail {
                from "[email protected]"
                to user.email
                title "${artist.name} has released a new album: ${album.title}!"
                body view:"/emails/artistSubscription", model:[album:album,
                                               artist:artist,
                                               user:user]
            }
        }

    }
    catch(Exception e) {
        log.error "Error sending album $album notification message: $e.message", e
        throw e
    }
}

One addition that we didn't cover previously is the surrounding try/catch block in Listing 16-35. An exception could occur if there was an error sending a mail or communicating with the database. Notice how the exception is logged and rethrown within the catch block. So, why rethrow the exception?

Essentially, the StockService is a transactional service class. It is using Grails' transactionManager underneath the surface. If you recall, the jmsContainer bean was given a reference to the Grails transactionManager in Listing 16-21. As a reminder, here is the relevant snippet from grails-app/conf/spring/resources.groovy:

jmsContainer(org.springframework.jms.listener.DefaultMessageListenerContainer) {
    ...
    transactionManager = ref("transactionManager")
    autoStartup = false
}

If an exception is thrown, Grails will automatically roll back the transaction. Since the jmsContainer has a reference to the transactionManager, it will be made aware that the transaction was rolled back. The result is that the JMS transaction will be rolled back, effectively marking the message as undelivered. ActiveMQ will then try to deliver the message again later. Thanks to Spring's transaction abstraction layer, you get a reliable messaging system, with guarantees of message redelivery.

The last thing to do is to finish up the subscription implementation by providing the view that renders the e-mail. Listing 16-36 shows the grails-app/views/emails/artistSubscription. gsp view.

Listing 16-36. The artistSubscription View

<%@ page contentType="text/plain"%>
Dear ${user.firstName} ${user.lastName},

One of your favorite artists ${artist.name} has released
a new album called ${album.title}!

It is available now on gTunes at
<g:createLink controller="album"
              action="display"
              id="${album.id}" absolute="true" />

Kind Regards,

The gTunes Team

Mixing Groovy and Java with Spring

Although Grails already takes advantage of Groovy's joint compiler, allowing you to integrate Java code seamlessly into a Grails application, it is often nice to provide this integration via Spring.

As an example, currently the gTunes application is using some Groovy code to stream music to the user. You can find the relevant code in the stream action of the SongController, which is shown in Listing 16-37.

Listing 16-37. The Stream action of the SongController Class

def file = new File(song.file)
try {
    def type = file.name[-3..-1]
    response.contentType = "audio/x-${type}"
    def out = response.outputStream
    def bytes = new byte[BUFFER_SIZE]
    file.withInputStream { inp ->
        while( inp.read(bytes) != −1) {
            out.write(bytes)
            out.flush()
        }
    }
}
catch(Exception e) {
    log.error "Error streaming song $file: $e.message", e
    response.sendError 500
}

Performance-wise, Java undoubtedly has the edge on Groovy when writing low-level IO code like that in Listing 16-37. You may want to optimize the stream action of the SongController to use a Java class instead. To do so, create a new Java class called StreamingService in the src/ java/com/g2one/gtunes directory, as shown in Figure 16-7.

image

Figure 16-7. The StreamService.java file

Rather than reading each byte, you could take advantage of the java.nio.channels package that allows optimized file transfer. Of course, you could use the java.nio.channels package from Groovy, but we're currently shooting for maximum performance by writing the class in Java. Listing 16-38 shows the implementation of the StreamingService class, which provides a method called streamSong that can be used to transfer the bytes of a Song instance to the given OutputStream.

Listing 16-38. The StreamingService Class

package com.g2one.gtunes;

import java.io.*;
import java.nio.channels.*;
import org.apache.commons.logging.*;
public class StreamingService
{
    private static final int BUFFER_SIZE = 2048;
    private static final Log LOG = LogFactory.getLog(StreamingService.class);

    /**
     * Streams the given song to the given OutputStream
     */
    public void streamSong(Song song, OutputStream out) {
        if(song != null) {
            File file = new File(song.getFile());
            FileInputStream input = null;
            try {
                input = new FileInputStream(file);
                FileChannel in = input.getChannel();
                in.transferTo(0,in.size(), Channels.newChannel(out));
                out.flush();
            }
            catch(Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
            finally {
                try {
                    input.close();
                }
                catch(IOException e) {
                    // ignore
                }
            }
        }
    }
}

One important thing to note is that this Java class references the domain class com.g2one. gtunes.Song, which is written in Groovy. Groovy's joint compiler allows Java classes to resolve Groovy classes, something that, as of this writing, is not possible in any other dynamic language on the JVM. The remainder of the code simply obtains a FileChannel instance and then calls the transferTo method to transfer the file to the response OutputStream.

Now you could just use the new operator to create a new instance of the StreamingService class within the SongController. But a nicer way to do this is to use Spring. Simply register a new bean in the grails-app/conf/spring/resources.groovy file for the StreamingService class, as shown in Listing 16-39.

Listing 16-39. Creating a streamingService Bean

streamingService(com.g2one.gtunes.StreamingService)

Now to obtain a reference to this bean in SongController, just create the equivalent property:

def streamingService

The stream action can then be modified to take advantage of the streamingService instance, as shown in Listing 16-40.

Listing 16-40. Using the streamingService Bean

def stream = {
    ...
    if(song) {
        def albumPermission = new AlbumPermission(album:song.album)
        jsec.hasPermission(permission:albumPermission) {
            ...
                response.contentType = "audio/x-${song.file[-3..-1]}"
                streamingService.streamSong(song, response.outputStream)
            ...
        }
        ...
    }
    ...
}

As you can see from Listing 16-40, the streamSong method is called, passing in the Song instance and the response object's outputStream property. You now have a much better-performing implementation that uses the java.nio.channels package instead. Since it is a Spring bean, if you one day decided to change the StreamingService implementation—for example, to stream the music from Amazon S3 instead—then all you would need to do is alter the grails-app/conf/spring/resources.groovy file and register a different implementation.

The SongController would need no changes at all since it is using duck typing to invoke the streamSong method. If you prefer static typing, then you could introduce an interface that the StreamingService class can implement, exactly as you would do in Java.

Summary

This chapter gave you some revealing insight into the inner workings of Grails and its Spring underpinnings. Moreover, you have learned that just because Grails embraces Convention over Configuration, it does not mean that configuration is not possible. Quite the contrary— every aspect of Grails is customizable thanks to Spring.

Grails provides such a clean abstraction over Spring that often users of Grails simply don't know Spring is there. In this chapter, you saw how you can reach out to great Spring APIs, such as the JMS support, to help you solve commons problems. Having said that, Spring is an enormous framework that provides far more features and benefits than we could possibly cover in this chapter. There are Spring abstractions for pretty much every major Java standard and many of the popular open source projects too.

If you really want to get to grips with Spring, we recommend you invest some time reading the excellent reference documentation at http://static.springframework.org/spring/docs/2.5.x/reference/ or take a look at Apress' excellent array of Spring books. 17 Doing so will help improve your knowledge of Grails too, because fundamentally Grails is just Spring and Hibernate in disguise! In the next chapter, we'll look at one of the other major frameworks that Grails builds on for its persistence concerns: Hibernate.



16. There is an excellent introductory article on Message-Driven POJOs on the SpringSource Team blog at http://blog.springsource.com/2006/08/11/message-driven-pojos/.

17. Some recent Spring books published by Apress include Pro Spring 2.5 by Jan Machacek et al. (Apress, 2008), Spring Recipes: A Problem-Solution Approach by Gary Mak (Apress, 2008), and Pro Java EE Spring Patterns: Best Practices and Design Strategies Implementing Java EE Patterns with the Spring Framework by Dhrubojyoti Kayal (Apress, 2008).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset