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 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 9001mySessionFactory
: 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.
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.
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.
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.
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.
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.
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.
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=""
}
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.
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.
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
.
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
}
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.
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.
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.
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.
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.
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.
Figure 16-3. The ActiveMQ web console
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.
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.
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.
Figure 16-5. The available JMS queues
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).
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.
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.
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")
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
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.
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.
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).