12. Accessing enterprise services – Spring in Action, Second Edition

Chapter 12. Accessing enterprise services

This chapter covers

  • Wiring JNDI resources in Spring

  • Sending email messages

  • Scheduling tasks

  • Exporting and using MBeans

Contrary to what you may have heard, Spring’s manifesto does not include a wholesale ousting of JEE technologies. Spring recognizes that the JEE specification is a collection of several effective subspecifications that still have their place in enterprise Java development. However, JEE does have a few rough edges that encourage less-than-ideal programming practices. Therefore, rather than subvert and replace JEE, Spring aims to complement JEE with a set of abstractions that round off the rough edges.

As it stands, a motorist can log into the RoadRantz application and view any rants that have been posted against their vehicles. That’s great, but it requires the motorist to play an active role, logging in to check for new rants. More often than not, there won’t be any new rants posted for them (unless they’re an exceptionally bad driver). Suppose that instead of making the motorist come to RoadRantz to check for rants, we take RoadRantz to the motorist whenever they receive new rants.

In this chapter, we’re going to add an email feature to RoadRantz to alert motorists that they have new rants. Along the way, we’ll explore some of Spring’s useful abstraction APIs, including Spring’s support for Java Naming and Directory Interface (JNDI), JavaMail, scheduling, and Java Management Extensions (JMX).

To get started, let’s see how to wire JNDI-managed objects into a Spring application context.

Wiring objects from JNDI

Throughout this book, you’ve seen how to use Spring to configure and wire your application objects. But what if you need to wire in objects that aren’t configured in Spring? What if you need to wire in objects that are stored in JNDI?

What? Objects that aren’t configured in Spring? How can that be? After all, this is a book about Spring. Why would I even suggest that some objects wouldn’t be configured in Spring?

Before you chastise me for putting forward such deplorable thoughts, consider the following dataSource bean (taken from chapter 5):

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.
          DriverManagerDataSource">
  <property name="driverClassName"
      value="org.hsqldb.jdbcDriver" />
  <property name="url"
      value="jdbc:hsqldb:hsql://localhost/roadrantz/roadrantz" />
  <property name="username" value="sa" />
  <property name="password" value="" />
</bean>

This bean configures a JDBC DataSource in Spring, perfectly suitable for wiring into a JdbcTemplate, HibernateTemplate, or some other data access object. It will certainly work anywhere access to the RoadRantz database is required, but it presents a couple of problems:

  • All of the database information is configured directly in the Spring application context. For purposes of change control and security, system administrators may prefer to configure and control the data source when the application is deployed, rather than allowing the developers to configure it themselves in the Spring context.

  • Changing the database connection information can be inconvenient. Should the database connection need to change (perhaps the database is moved to a different server or the password must be changed), the application will likely need to be rebuilt and redeployed.

To address these concerns, you can configure the data source external to Spring, perhaps in a JNDI-accessible directory.

JNDI is a Java API that enables lookup of objects by name in a directory (often, but not necessarily, an LDAP directory). JNDI provides Java applications with access to a central repository for storing and retrieving application objects. JNDI is typically used in JEE applications to store and retrieve JDBC data sources and JTA transaction managers.

But if some of our application objects are configured in JNDI, external to Spring, how can we inject them into the Spring-managed objects that need them?

In this section, we’ll be looking at how Spring supports JNDI by providing a simplified abstraction layer above the standard JNDI API. Spring’s JNDI abstraction makes it possible to declare JNDI lookup information in your Spring context definition file. Then you can wire a JNDI-managed object into the properties of other Spring beans as though the JNDI object were just another POJO.

To gain a deeper appreciation of what Spring’s JNDI abstraction provides, let’s start by looking at how to look up an object from JNDI without Spring.

Working with conventional JNDI

Looking up objects in JNDI can be a tedious chore. For example, suppose you need to perform the very common task of retrieving a javax.sql.DataSource from JNDI. Using the conventional JNDI APIs, your might write some code that looks like this:

InitialContext ctx = null;
try {
  ctx = new InitialContext();

  DataSource ds =
      (DataSource)ctx.lookup("java:comp/env/jdbc/RantzDatasource");
} catch (NamingException ne) {
  // handle naming exception
  ...
} finally {
  if(ctx != null) {
    try {
      ctx.close();
    } catch (NamingException ne) {}
  }
}

If you’ve ever written JNDI lookup code before, you’re probably very familiar with what’s going on in this code snippet. You may have written a similar incantation dozens of times before to raise an object out of JNDI. Before you repeat it again, however, take a closer look at what is going on:

  • You must create and close an initial context for no other reason than to look up a DataSource. This may not seem like a lot of extra code, but it is extra plumbing code that is not directly in line with the goal of retrieving a data source.

  • You must catch or, at the very least, rethrow a javax.naming.NamingException. If you choose to catch it, you must deal with it appropriately. If you choose to rethrow it, the calling code will be forced to deal with it. Ultimately, someone somewhere will have to deal with the exception.

  • Your code is tightly coupled with a JNDI lookup. All your code needs is a DataSource. It doesn’t matter whether it comes from JNDI. But if your code contains code like that shown earlier, you’re stuck retrieving the DataSource from JNDI.

  • Your code is tightly coupled with a specific JNDI name—in this case java:comp/env/jdbc/RantzDatasource. Sure, you could extract that name into a properties file, but then you’ll have to add even more plumbing code to look up the JNDI name from the properties file.

Upon closer inspection we find that most of the code is boilerplate JNDI lookup that looks pretty much the same for all JNDI lookups. The actual JNDI lookup happens in just one line:

DataSource ds =
    (DataSource)ctx.lookup("java:comp/env/jdbc/RantzDatasource");

Even more disquieting than boilerplate JNDI code is the fact that the application knows where the data source comes from. It is coded to always retrieve a data source from JNDI. As illustrated in figure 12.1, the DAO that uses the data source will be coupled to JNDI. This makes it almost impossible to use this code in a setting where JNDI isn’t available or desirable.

Figure 12.1. Using conventional JNDI to get dependencies means that an object is coupled to JNDI, making it difficult to use the object anywhere that JNDI is not available.

For instance, imagine that the data source lookup code is embedded in a class that is being unit tested. In an ideal unit test, we’re testing an object in isolation without any direct dependence on specific objects. Although the class is decoupled from the data source through JNDI, it is coupled to JNDI itself. Therefore, our unit test has a direct dependence on JNDI and a JNDI server must be available for the unit test to run.

Regardless, this doesn’t change the fact that sometimes you need to be able to look up objects in JNDI. DataSources are often configured in an application server to take advantage of the application server’s connection pooling and then retrieved by the application code to access the database. How can your code get an object from JNDI without being dependent on JNDI?

The answer is found in dependency injection (DI). Instead of asking for a data source from JNDI, you should write your code to accept a data source from anywhere. That is, your code should have a DataSource property that is injected either through a setter method or through a constructor. Where the object comes from is of no concern to the class that needs it.

The data source object still lives in JNDI, however. So how can we configure Spring to inject an object that is stored in JNDI?

Injecting JNDI objects

Spring’s JndiObjectFactoryBean gives you the best of both JNDI and DI. It is a factory bean, which means that when it is wired into a property, it will actually create some other type of object that will wire into that property. In the case of JndiObjectFactoryBean, it will wire an object retrieved from JNDI.

To illustrate how this works, let’s revisit an example from chapter 5 (section 5.2.1). There I used JndiObjectFactoryBean to retrieve a DataSource from JNDI:

<bean id="dataSource"
    class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/RantzDatasource" />
</bean>

The jndiName property specifies the name of the object in JNDI. By default, the name is used as is to look up the object in JNDI. But if the lookup is occurring in a JEE container then a java:comp/env/ prefix needs to be added. You can manually add the prefix to the value specified in jndiName. Or you can set the resourceRef property to true to have JndiObjectFactoryBean automatically prepend jndiName with java:comp/env/:

<bean id="dataSource"
    class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/RantzDatasource" />
  <property name="resourceRef" value="true" />
</bean>

With the dataSource bean declared, you may now inject it into a dataSource property. For instance, you may use it to configure a Hibernate session factory as follows:

<bean id="sessionFactory" class="org.springframework.orm.
        hibernate.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
...
</bean>

As shown in figure 12.2, when Spring wires the sessionFactory bean, it will inject the DataSource object retrieved from JNDI into the session factory’s dataSource property.

Figure 12.2. JndiObjectFactoryBean looks up an object from JNDI on behalf of a Spring object that it is wired into.

The great thing about using JndiObjectFactoryBean to look up an object in JNDI is that the only part of the code that knows that the DataSource is retrieved from JNDI is the XML declaration of the dataSource bean. The sessionFactory bean doesn’t know (or care) where the DataSource came from. This means that if you decide that you would rather get your DataSource from a JDBC driver manager, all you need to do is redefine the dataSource bean to be a DriverManagerDataSource.

Now our data source is retrieved from JNDI and then injected into the session factory. No more explicit JNDI lookup code! Whenever we need it, the data source is always handy in the Spring application context as the dataSource bean.

As you have seen, wiring a JNDI-managed bean in Spring is fairly simple. Now let’s explore a few ways that we can influence when and how the object is retrieved from JNDI, starting with caching.

Caching JNDI objects

Oftentimes, the objects retrieved from JNDI will be used more than once. A data source, for example, will be needed every time you access the database. It would be inefficient to repeatedly retrieve the data source from JNDI every time that it is needed. For that reason, JndiObjectFactoryBean caches the object that it retrieves from JNDI by default.

Caching is good in most circumstances. However, it precludes hot redeployment of objects in JNDI. If you were to change the object in JNDI, the Spring application would need to be restarted so that the new object can be retrieved.

If your application is retrieving an object from JNDI that will change frequently, you’ll want to turn caching off for JndiObjectFactoryBean. To turn caching off, you’ll need to set the cache property to false when declaring the bean:

<bean id="dataSource"
    class="org.springframework.jndi.JndiObjectFactoryBean"
  <property name="jndiName" value="jdbc/RantzDatasource" />
  <property name="cache" value="false" />
  <property name="proxyInterface" value="javax.sql.DataSource" />
</bean>

Setting the cache property to false tells JndiObjectFactoryBean to always fetch the object from JNDI. Notice that the proxyInterface property has also been set. Since the JNDI object can be changed at any time, there’s no way for JndiObjectFactoryBean to know the actual type of the object. The proxyInterface property specifies a type that is expected for the object retrieved from JNDI.

Lazily loading JNDI objects

Sometimes your application won’t need to retrieve the JNDI object right away. For instance, suppose that a JNDI object is only used in an obscure branch of your application’s code. In that situation, it may not be desirable to load the object until it is actually needed.

By default, JndiObjectFactoryBean fetches objects from JNDI when the application context is started. Nevertheless, you can configure it to wait to retrieve the object until it’s needed by setting the lookupOnStartup property to false:

<bean id="dataSource"
    class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/RantzDatasource" />
  <property name="lookupOnStartup" value="false" />
  <property name="proxyInterface" value="javax.sql.DataSource" />
</bean>

As with the cache property, you’ll need to set the proxyInterface property when setting lookupOnStartup to false. That’s because JndiObjectFactoryBean won’t know the type of the object being retrieved until it is actually retrieved. The proxyInterface property tells it what type to expect from the fetched object.

Fallback objects

You now know how to wire JNDI objects in Spring and have a JNDI-loaded data source to show for it. Life is good. But what if the object can’t be found in JNDI?

For instance, maybe your application can count on a data source being available in JNDI when running in a production environment. But that arrangement may not be practical in a development environment. If Spring is configured to retrieve its data source from JNDI for production, the lookup will fail in development. How can we make sure that a data source bean is always available from JNDI in production and explicitly configured in development?

As you’ve seen, JndiObjectFactoryBean is great for retrieving objects from JNDI and wiring them in a Spring application context. But it also has a fallback mechanism that can account for situations where the requested object can’t be found in JNDI. All you must do is configure its defaultObject property.

For example, suppose that you’ve declared a data source in Spring using DriverManagerDataSource as follows:

<bean id="devDataSource"
    class="org.springframework.jdbc.datasource.
         DriverManagerDataSource">
  <property name="driverClassName"
      value="org.hsqldb.jdbcDriver" />
  <property name="url"
      value="jdbc:hsqldb:hsql://localhost/roadrantz/roadrantz" />
  <property name="username" value="sa" />
  <property name="password" value="" />
</bean>

This is the data source that you’ll use in development. But in production, you’d rather use a data source configured in JNDI by the system administrators. If that’s the case, you’ll configure the JndiObjectFactoryBean like this:

<bean id="dataSource"
    class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/RantzDatasource" />
  <property name="defaultObject" ref="devDataSource" />
</bean>

Here I’ve wired the defaultObject property with a reference to the devDataSource bean. If JndiObjectFactoryBean can’t find an object in JNDI at jdbc/RantzDatasource, it will use the devDataSource bean as its object.

As you can see, it’s reasonably simple to use JndiObjectFactoryBean to wire JNDI-managed objects into a Spring application context. What we’ve covered so far works in all versions of Spring. But if you’re using Spring 2, there’s an even easier way. Let’s see how Spring 2’s namespace support makes JNDI wiring even simpler.

Wiring JNDI objects in Spring 2

As easy as it is to wire JNDI objects into your Spring context using JndiObjectFactoryBean, it’s even easier if you’re using Spring 2. Spring 2’s jee namespace provides the <jee:jndi-lookup> configuration element for retrieving objects from JNDI. To use the <jee:jndi-lookup> element, you must declare the jee namespace in your Spring XML using the following preamble:

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jee="http://www.springframework.org/schema/jee"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/
                spring-beans-2.0.xsd
       http://www.springframework.org/schema/jee
       http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">

Now you can look up JNDI objects using <jee:jndi-lookup>. For example, the following XML snippet retrieves a data source from JNDI:

<jee:jndi-lookup id="dataSource"
    jndi-name="jdbc/RantzDatasource"
    resource-ref="true" />

Under the covers, <jee:jndi-lookup> configures a JndiObjectFactoryBean in the Spring context. This makes <jee:jndi-lookup> a shortcut for referencing objects in JNDI. The jndi-name attribute maps to the jndiName property of JndiObjectFactoryBean to identify the name of the object to look up. Likewise, the resource-ref attribute maps to the resourceRef property and is used to indicate that the object should be looked up as a JEE resource by prepending the jndi-name with java:comp/env/.

Looking up objects in JNDI comes in handy when you need access to objects that are configured external to Spring. As you’ve seen, data sources may be configured through an application server and accessed through JNDI. And as you’ll see next, Spring’s JNDI lookup capability can be useful when sending email. Let’s take a look at Spring’s email abstraction layer next.

Sending email

As a registered RoadRantz user, you might want to know when new rants are posted for your vehicles. Although you could visit the RoadRantz site over and over again, it may get a bit old to check every day only to find out that there’s nothing new most of the time. Wouldn’t it be nice if, instead of having to check the site for new rants, the site would contact you if there were new rants?

In this section, we’ll add email functionality to the RoadRantz application so that an email is generated when a new rant is posted. In doing so, we’re going to take advantage of the email support provided by Spring.

Configuring a mail sender

Spring comes with an email abstraction API that makes simple work of sending emails. At the heart of Spring’s email abstraction is the MailSender interface.

As its name implies and as illustrated in figure 12.3, a MailSender implementation sends email.

Figure 12.3. Spring’s MailSender interface is primary component of Spring’s email abstraction API. It simply sends an email to a mail server for delivery.

Spring comes with two implementations of this interface, as described in table 12.1.

Table 12.1. Mail senders handle the intricacies of sending email. Spring comes with two mail sender implementations.

Mail sender implementation

Description

CosMailSenderImpl

Simple implementation of an SMTP mail sender based on Jason Hunter’s COS (com.oreilly.servlet) implementation from his Java Servlet Programming book (O’Reilly, 1998).

JavaMailSenderImpl

A mail sender based on the JavaMail API. Allows for sending of MIME messages as well as non-SMTP mail (such as Lotus Notes).

Either mail sender is capable of meeting the needs of the RoadRantz email. But we’ll choose JavaMailSenderImpl since it is the more versatile and more standard of the two options. We’ll declare it as a <bean> in roadrantz-services.xml as follows:

<bean id="mailSender"
    class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="host" value="mail.roadrantz.com" />
</bean>

The host property specifies the hostname of the mail server that we’ll use to send the email. By default, JavaMailSenderImpl assumes that the mail server is listening on port 25 (the standard SMTP port). However, if your mail server is listening on a different port, specify the correct port number using the port property:

<bean id="mailSender"
    class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="host" value="mail.roadrantz.com" />
  <property name="port" value="1025" />
</bean>

If the mail server requires authentication, also set values for the username and password properties:

<bean id="mailSender"
    class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="host" value="mail.roadrantz.com" />
  <property name="username" value="rantzuser" />
  <property name="password" value="changeme" />
</bean>

As declared, this mailSender bean spells out the details of accessing the mail server. The mail server’s hostname and the username/password pair are explicitly configured in Spring. However, this setup may raise red flags for you with regard to security. Maybe you don’t want to hard-code this information in the Spring configuration.

You may already have a javax.mail.MailSession configured in JNDI (or perhaps one was placed there by your application server). If so then Spring’s JavaMailSenderImpl offers you an option to use the MailSender in JNDI.

Using a JNDI mail session

You’ve already seen how to retrieve objects from JNDI in section 12.1. To use a mail session from JNDI, you can retrieve it using JndiObjectFactoryBean:

<bean id="mailSession"
    class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="mail/Session" />
  <property name="resourceRef" value="true" />
</bean>

Or you can use the <jee:jndi-lookup> configuration element:

<jee:jndi-lookup id="mailSession"
    jndi-name="mail/Session"
    resource-ref="true" />

In either event, the mail session object retrieved from JNDI can be wired into the mailSender bean as follows:

<bean id="mailSender"
    class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="session" ref="mailSession" />
</bean>

The mail session wired into the session property of JavaMailSenderImpl completely replaces the explicit server (and username/password) configuration from before. Now the mail session is completely configured in JNDI.

Wiring the mail sender into a service bean

Now that the mail sender has been configured, it’s time to wire it into the bean that will use it. In the RoadRantz application, the RantServiceImpl class is the most appropriate place to send the email from. To make the mail sender available to the service bean, add a mailSender property (and its setter method) to RantServiceImpl:

private MailSender mailSender;
public void setMailSender(MailSender mailSender) {
  this.mailSender = mailSender;
}

Now we can use Spring DI to wire the mailSender bean into the mailSender property:

<bean id="rantService"
    class="com.roadrantz.service.RantServiceImpl">
  <property name="rantDao" ref="rantDao" />
  <property name="mailSender" ref="mailSender" />
</bean>

With the mailSender bean wired into the rantService bean, we’re ready to construct and send the email.

Constructing the email

Since we want to send an email to a motorist to alert the motorist of new rants for their vehicle, it seems that we’ll need a method that sends an email for a particular vehicle. The sendEmailForVehicle() method in listing 12.1 uses the mail sender to send the email.

Example 12.1. Sending an email to tell a motorist that they have new rants

The first thing that sendEmailForVehicle() does is verify that the vehicle has a motorist associated with it. If the motorist of the vehicle hasn’t registered with RoadRantz, the motorist will be null and we won’t be able to send the email.

Next, the details of the message are set. The motorist’s email address is given to the setTo() method to specify the recipient of the email. And the message’s text is set through the setText() method.

Finally, sendEmailForVehicle() uses the mail sender to send the email.

Most of the code in listing 12.1 is straightforward. But where does the email message come from? And what is going on with those calls to StringUtils.replace()?

Although we could explicitly define the entire email message within the scope of the sendEmailForVehicle() method, it would involve a lot of hard-coded values. It would be better to extract that message definition to a Spring-configured bean. The following bean declaration captures the common properties of the mail message:

<bean id="mailMessage"
    class="org.springframework.mail.SimpleMailMessage">
  <property name="from">
    <value><![CDATA[RoadRantz <notify@roadrantz.com>]]></value>
  </property>
  <property name="subject" value="You've got new Rantz!" />
  <property name="text">
    <value>
      <![CDATA[
Someone's been ranting about you! Log in to RoadRantz.com or
click on the link below to see what they had to say.

http://www.roadrantz.com/rantsForVehicle.htm?
         state=%STATE%&plateNumber=%PLATE%
      ]]>
    </value>
  </property>
</bean>

The mailMessage bean serves as a template for all of the beans sent from the RoadRantz application. The from and subject values will be the same for all emails sent from RoadRantz, so there’s no reason why we shouldn’t configure them in this bean. The contents of the message will be mostly the same for all emails sent, so we can configure it here using the text property. On the other hand, the recipient will vary from email to email, so it doesn’t make much sense to set the to property in the mailMessage bean.

To make the mailMessage bean available to the sendEmailForVehicle() method, we’ll need to wire it into the RantServiceImpl class. First add a property to hold the bean and a setter that will be used to inject it into RantServiceImpl:

private SimpleMailMessage mailMessage;
public void setMailMessage(SimpleMailMessage mailMessage) {
  this.mailMessage = mailMessage;
}

Then configure the rantService bean to wire the mailMessage bean into the mailMessage property:

<bean id="rantService"
    class="com.roadrantz.service.RantServiceImpl">
  <property name="rantDao" ref="rantDao" />
  <property name="mailSender" ref="mailSender" />
  <property name="mailMessage" ref="mailMessage" />
</bean>

The only thing left to discuss is what is going on in sendEmailForVehicle() where the StringUtils.replace() methods are used. Although the text of the email is mostly the same for all emails that are sent, it will vary slightly. That’s because the link that is included in the email has parameters that are specific to the vehicle in question.

While we could have constructed the email text in the sendEmailForVehicle() method, we’d prefer to configure it externally. By configuring it externally, we are afforded the opportunity to tweak the message without having to change the method’s source code.

So, the message configured in the mailMessage bean has two placeholders—%STATE% and %PLATE%—that are replaced in sendEmailForVehicle() with the vehicle’s state and license plate number. This makes it possible for the message to be somewhat dynamic and still be configured in Spring.

Now that we have a sendEmailForVehicle() method, we should figure out how to best use it. Since we’d like to send an email alerting registered motorists of new rants for their vehicles, it would seem best to make the call to sendEmailForVehicle() from within RantServiceImpl’s addRant() method:

public void addRant(Rant rant) {
  rant.setPostedDate(new Date());

  Vehicle rantVehicle = rant.getVehicle();

  // check for existing vehicle with same plates
  Vehicle existingVehicle =
      rantDao.findVehicleByPlate(rantVehicle.getState(),
      rantVehicle.getPlateNumber());
  if(existingVehicle != null) {
    rant.setVehicle(existingVehicle);
  } else {
    rantDao.saveVehicle(rantVehicle);
  }

  rantDao.saveRant(rant);

  sendEmailForVehicle(existingVehicle);
}

Used this way, sendEmailForVehicle() will send an email to the motorist of the vehicle within moments of a new rant being added. This is good, but it could result in the motorist being bombarded with emails from RoadRantz. Maybe there is some justice in filling a bad motorist’s inbox with frequent emails, but it probably will only result in the motorist discontinuing their relationship with RoadRantz—something we’d rather not have happen.

Rather than email the user after every rant, maybe it would be better to send only one email per motorist per day. For that we’ll need a way to schedule the sending of emails. As it so happens, Spring provides support for task scheduling, as you’ll see in the next section.

Scheduling tasks

Much of the code we’ve written thus far is triggered as the result of some user action. However, even though much of an application’s functionality is triggered through user activity, sometimes it’s necessary to kick off some activity based on a schedule.

For example, in the RoadRantz application we’d like to send a single email to every motorist who has received a rant within a given day. Even if the motorist has been ranted about multiple times, only one email should be sent per day. The sendDailyRantEmails() method in listing 12.2 pulls together a unique list of vehicles that have been ranted about and sends an email to their motorist.

Example 12.2. Sending a daily email to motorists who have been ranted about

The first thing sendDailyRantEmails() does is call getRantsForDay() to retrieve a list of rants for the current day. Because a motorist may have received several rants on a given day, sendDailyRantEmails() then goes through the rants, placing each vehicle into a java.util.Set. By putting them into a Set, we know that there won’t be any duplicates (and thus we won’t send duplicate emails). Finally, sendDailyRantEmails() iterates over the set of vehicles and uses sendEmailForVehicle() to send an email to each vehicle’s motorist.

Now we have a method that can be used to send a daily email to all the motorists who have been ranted about. What we need now is to set up a schedule for when that method will be called.

Scheduling with Java’s Timer

Starting with Java 1.3, the Java SDK has included rudimentary scheduling functionality through its java.util.Timer class. This class lets you schedule a task (defined by a subclass java.util.TimerTask) to occur every so often.

Spring provides application context support for Java’s Timer through TimerFactoryBean. TimerFactoryBean is a Spring factory bean that produces a Java Timer in the application context that kicks off a TimerTask. Figure 12.4 illustrates how TimerFactoryBean works.

Figure 12.4. Spring’s TimerFactoryBean produces a Java Timer, scheduled to kick off a TimerTask after a specified time has passed.

I’ll show you how to configure a TimerFactoryBean in a moment. But first, we need a TimerTask to send the email.

Creating a timer task

The first step in scheduling the daily rant email using Java’s Timer is to create the email task by subclassing java.util.TimerTask, as shown in listing 12.3.

Example 12.3. A timer task for sending the daily rant emails

The run() method defines what the task is to do when it is run. In this case, it calls the sendDailyRantEmails() method on the injected RantService object.

The DailyRantEmailTask can be configured in Spring like this:

<bean id="dailyRantEmailTask"
    class="com.roadrantz.service.DailyRantEmailTask">
  <property name="rantService" ref="rantService" />
</bean>

By itself, this <bean> declaration only places an instance of DailyRantEmailTask into the Spring application context and wires the rantService bean into its rantService property. But this bean is only a task—it won’t do anything interesting until you schedule it.

Scheduling the timer task

Spring’s ScheduledTimerTask defines how often a timer task is to be run. Since the rant email is daily, we should schedule it to run every 24 hours. The following ScheduledTimerTask bean should do the trick:

<bean id="scheduledEmailTask"
    class="org.springframework.scheduling.timer.ScheduledTimerTask">
  <property name="timerTask" ref="dailyRantEmailTask" />
  <property name="period" value="86400000" />
</bean>

The timerTask property tells the ScheduledTimerTask which TimerTask to run. Here it is wired with a reference to the dailyRantEmailTask bean, which is the DailyRantEmailTask. The period property is what tells the ScheduledTimerTask how often the TimerTask’s run() method should be called. This property, specified in milliseconds, has been set to 86400000 to indicate that the task should be kicked off every 24 hours.

Starting the timer

The last thing you’ll need to do is to start the timer. Spring’s TimerFactoryBean is responsible for starting timer tasks. You can declare the TimerFactoryBean in Spring like this:

<bean class="org.springframework.scheduling.timer.
                 TimerFactoryBean">
  <property name="scheduledTimerTasks">
    <list>
      <ref bean="scheduledEmailTask"/>
    </list>
  </property>
</bean>

The scheduledTimerTasks property takes an array of timer tasks that it should start. By default, TimerFactoryBean will start these tasks immediately upon application startup. Since we only have one timer task right now, the list contains a single reference to the scheduledEmailTask bean.

Delaying the start of the timer

Unfortunately, even though the task will be run every 24 hours, there is no way to specify what time of the day it should be run. ScheduledTimerTask does have a delay property that lets you specify how long to wait before the task is first run.

For example, to delay the first run of DailyRantEmailTask by an hour, you’d use this:

<bean id="scheduledEmailTask"
    class="org.springframework.scheduling.timer.
             ScheduledTimerTask">
  <property name="timerTask" ref="reportTimerTask"/>
  <property name="period" value="86400000" />
  <property name="delay" value="3600000" />
</bean>

Even with the delay, however, the time that the DailyRantEmailTask will run will be relative to when the application starts. And each successive run will be relative to the end time of the previous run. How can you have it sent at midnight every night (aside from starting the application at 11:00 p.m.)?

This highlights a limitation of using Java’s Timer. Although it’s great for running tasks at a regular interval, it’s difficult to schedule tasks to run at a specific time. In order to specify precisely when the email is sent, you’ll need to use the Quartz scheduler instead.

Using the Quartz scheduler

Suppose that we want the daily rant email to be sent at 11:59 p.m. every night. Unfortunately, Java’s Timer is limited to scheduling how often the task is performed, not when it is performed.

The Quartz scheduler provides richer support for scheduling jobs. Just as with Java’s Timer, you can use Quartz to run a job every so many milliseconds. But Quartz goes beyond Java’s Timer by enabling you to schedule a job to run at a particular time and/or day. This makes Quartz more suitable for sending the daily rant email than Java’s Timer.

For more information about Quartz, visit the Quartz home page at http://www.opensymphony.com/quartz.

Creating a Quartz job

The first step in defining a Quartz job is to create the class that defines the job. For that, we’ll subclass Spring’s QuartzJobBean, as shown in listing 12.4.

Example 12.4. Defining a Quartz job for sending a daily rant email

A QuartzJobBean is the Quartz equivalent of Java’s TimerTask. It is an implementation of Quartz’s org.quartz.Job interface. The executeInternal() method defines the actions that the job will do when its time comes. Here, just as with DailyRantEmailTask, the task simply makes a call to the sendDailyRantEmails() method of the injected RantService bean.

Declare the job in the Spring configuration file as follows:

<bean id="dailyRantEmailJob"
    class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass"
      value="com.roadrantz.service.DailyRantEmailJob" />
  <property name="jobDataAsMap">
    <map>
      <entry key="rantService" value-ref="rantService" />
    </map>
  </property>
</bean>

Notice that although the job is defined in the DailyRantEmailJob class, it’s not this class that is declared in the Spring context. Instead, a JobDetailBean is declared. This is an idiosyncrasy of working with Quartz. JobDetailBean is a subclass of Quartz’s org.quartz.JobDetail, which requires that the Job implementation be set through the jobClass property.

Another quirk of working with Quartz’s JobDetail is that the rantService property of DailyRantEmailJob isn’t set directly. Instead, JobDetail’s jobDataAsMap property takes a java.util.Map that contains properties that are to be set on the object specified by jobClass. Here, the map contains a reference to the rantService bean with a key of rantService. When JobDetailBean is instantiated, it will inject the rantService bean into the rantService property of DailyRantEmailJob.

Scheduling the job

Now that the job is defined, you’ll need to schedule the job. As shown in figure 12.5, Quartz’s org.quartz.Trigger class decides when and how often a Quartz job should run.

Figure 12.5. A Quartz trigger determines the exact time that a job will be kicked off.

Spring comes with two subclasses of Trigger: SimpleTriggerBean and CronTriggerBean. Which one should you use? Let’s have a look at each of them, starting with SimpleTriggerBean.

SimpleTriggerBean is very similar to ScheduledTimerTask, which we discussed in the last section. Using it, you can specify how often a job should run and (optionally) how long to wait before running the job for the first time. For example, to schedule the report job to run every 24 hours, with the first run starting one hour after the application starts, declare the following bean:

<bean id="simpleReportTrigger"
    class="org.springframework.scheduling.quartz.
         SimpleTriggerBean">
  <property name="jobDetail" ref="dailyRantEmailJob"/>
  <property name="startDelay" value="3600000" />
  <property name="repeatInterval" value="86400000" />
</bean>

The jobDetail property is wired with the job that is to be scheduled. Here it is the dailyRantEmailJob bean, which we declared earlier. The repeatInterval property tells the trigger how often to run the job (in milliseconds). Here we’ve specified that the job should run every 86,400,000 milliseconds—or every 24 hours. Finally, the optional startDelay property has been set to delay the first run of the job to one hour (or 3,600,000 milliseconds) after the application is started.

Although you can probably think of many applications for which SimpleTriggerBean is perfectly suitable, it isn’t sufficient for emailing the daily rant email. Just as with the DailyRantEmailTask (which is based on Java’s Timer), we can only specify how often the job is run—not exactly when it’s run. Therefore, we can’t rely on SimpleTriggerBean to send out the daily emails at midnight as we want. For more precise control over scheduling, Quartz provides cron jobs.

Scheduling a cron job

CronTriggerBean is another Spring subclass of Quartz’s Trigger class. This trigger class, however, lets you specify exact times and days when the job will run. If you’re familiar with the Unix cron tool, you’ll feel right at home with CronTriggerBean. Instead of declaring how often a job is run, CronTriggerBean lets you use a cron expression to specify exact times (and days) that a job will run.

For example, to declare that DailyRantEmailJob be run every day at 11:59 p.m., declare a CronTriggerBean in Spring as follows:

<bean id="cronEmailTrigger"
    class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail" ref="dailyRantEmailJob"/>
  <property name="cronExpression" value="0 59 23 * * ?" />
</bean>

As with SimpleTriggerBean, the jobDetail property tells the trigger which job to schedule. Again, we’ve wired it with a reference to the dailyRantEmailJob bean. The cronExpression property tells the trigger when to fire. If you’re a cron fanatic, you will have no trouble deciphering this property’s value (and we’re guessing that you have little trouble setting the timer on your VCR).

But for those of you who aren’t as well versed in cron expressions, let’s break down the cronExpression property a bit. It is made up of six (or possibly seven) time elements, separated by spaces. In order from left to right, the elements are defined as follows:

  1. Seconds (0–59)

  2. Minutes (0–59)

  3. Hours (0–23)

  4. Day of month (1–31)

  5. Month (1–12 or JAN–DEC)

  6. Day of week (1–7 or SUN–SAT)

  7. Year (1970–2099)

Each of these elements can be specified with an explicit value (e.g., 6), a range (e.g., 9–12), a list (e.g., 9,11,13), or a wildcard (e.g., *). The day of the month and day of the week elements are mutually exclusive, so you should also indicate which one of these fields you don’t want to set by specifying it with a question mark (?). Table 12.2 shows some example cron expressions and what they mean.

Table 12.2. Some sample cron expressions.

Expression

What it means

0 0 10,14,16 * * ?

Every day at 10 a.m., 2 p.m., and 4 p.m.

0 0,15,30,45 * 1-30 * ?

Every 15 minutes on the first 30 days of the month

30 0 0 1 1 ? 2012

30 seconds after midnight on January 1, 2012

0 0 8-17 ? * MON-FRI

Every working hour of every business day

In the case of the cronEmailTrigger bean, we’ve set the cronExpression property to 0 59 23 * * ?. You can read this as the zero second of the 59th minute of the 23rd hour of any day of the month of any month (regardless of the day of the week). In other words, the trigger is fired at a minute before midnight every night.

With this kind of precision in timing, it’s clear that CronTriggerBean is better suited for our daily email than SimpleTriggerBean. Now all that’s left is to start the job.

Starting the job

To start a Quartz job, we’ll use Spring’s SchedulerFactoryBean. SchedulerFactoryBean is the Quartz equivalent to TimerFactoryBean. It is declared in the Spring configuration as follows:

 <bean class="org.springframework.scheduling.
        quartz.SchedulerFactoryBean">
  <property name="triggers">
    <list>
      <ref bean="cronEmailTrigger"/>
    </list>
  </property>
</bean>

The triggers property takes an array of references to trigger beans. Since we only have a single trigger at the moment, we simply need to wire it with a list containing a single reference to the cronEmailTrigger bean.

At this point, we should have a nightly email generated at just before midnight every night. But in doing so, perhaps we’ve done a bit too much work. Before we let this go, let’s take a look at a slightly easier way to schedule the nightly email.

Invoking methods on a schedule

In scheduling the nightly rant email we wrote a DailyRantEmailJob bean (or the DailyRantEmailTask bean in the case of the timer tasks). But this bean doesn’t do much more than make a simple call to the sendDailyRantEmails() method of the RantService. In this light, both DailyRantEmailJob and DailyRantEmailTask seem a bit superfluous. Wouldn’t it be great if we could just ask Spring to call sendDailyRantEmails() without having to write the extra task or job class?

Good news! If all you want to do is schedule a single method call, you can do that without writing a separate TimerTask or QuartzJobBean class. To accomplish this, Spring has provided MethodInvokingTimerTaskFactoryBean and MethodInvokingJobDetailFactoryBean to schedule method calls with Java’s timer support and the Quartz scheduler, respectively.

For example, to schedule a call to sendDailyRantEmails() using Java’s timer service, redeclare the scheduledEmailTask bean as follows:

<bean id="scheduledEmailTask"
    class="org.springframework.scheduling.timer.
            MethodInvokingTimerTaskFactoryBean">
  <property name="targetObject" ref="rantService"/>
  <property name="targetMethod" value="sendDailyRantEmails" />
</bean>

Behind the scenes, MethodInvokingTimerTaskFactoryBean will create a TimerTask that calls the method specified by the targetMethod property on the object that is referenced by the targetObject property (as shown in figure 12.6). This is effectively the same as the DailyRantEmailTask. Now you can eliminate the DailyRantEmailTask class and its declaration in the dailyRantEmailTask bean.

Figure 12.6. MethodInvokingTimerTaskFactoryBean produces a Java TimerTask that is configured to invoke a specific method on a specific bean in the application context.

MethodInvokingTimerTaskFactoryBean is good when scheduling simple onemethod calls using a ScheduledTimerTask. But ScheduledTimerTask didn’t provide us with the precision needed to schedule the email at just before midnight every night. So instead of using MethodInvokingTimerTaskFactoryBean, let’s redeclare the dailyRantEmailJob bean as follows:

<bean id="dailyRantEmailJob"
     class="org.springframework.scheduling.quartz.
            MethodInvokingJobDetailFactoryBean">
  <property name="targetObject" ref="rantService"/>
  <property name="targetMethod" value="sendDailyRantEmails" />
</bean>

As you may have guessed, MethodInvokingJobDetailFactoryBean is the Quartz equivalent of MethodInvokingTimerTaskFactoryBean. Under the covers it will create a Quartz JobDetail object that makes a single method call to the object and method specified by the targetObject and targetMethod properties (see figure 12.7).

Figure 12.7. MethodInvokingJobDetailFactoryBean produces a Quartz JobDetail that invokes a single method on a specified bean.

Now that we’ve scheduled our email, we can sit back and enjoy the fact that our registered motorists are receiving their rant notification emails. But wait... what if the mail server is down at the time when the scheduler goes off? Is there a way that we can manually trigger the email? Or what if we decide to change the scheduler’s time? Do we need to redeploy the application to enact changes to the scheduler?

To address these concerns, let’s now look at how Spring’s support for JMX enables us to create a management interface for our application’s beans, letting us change and invoke them on the fly.

Managing Spring beans with JMX

Spring’s support for DI is a great way to configure bean properties in an application. But once the application has been deployed and is running, there’s not much that DI alone can do to help you change that configuration. Suppose that you want to dig into a running application and change its configuration on the fly. That’s where Java Management Extensions (JMX) comes in.

JMX is a technology that enables you to instrument applications for management, monitoring, and configuration. Originally available as a separate extension to Java, JMX is now a standard part of the Java 5 distribution.

In this section we’ll focus on how JMX is supported in Spring. If you want to learn more about JMX, we recommend that you have a look at JMX in Action (Manning, 2002).

The key component of an application that is instrumented for management with JMX is the MBean (managed bean). An MBean is a JavaBean that exposes certain methods that define the management interface. The JMX specification defines four types of MBeans:

  • Standard MBeans—Standard MBeans are MBeans whose management interface is determined by reflection on a fixed Java interface that is implemented by the bean class.

  • Dynamic MBeans—Dynamic MBeans are MBeans whose management interface is determined at runtime by invoking methods of the DynamicMBean interface. Because the management interface isn’t defined by a static interface, it can vary at runtime.

  • Open MBeans—Open MBeans are a special kind of dynamic MBean whose attributes and operations are limited to primitive types, class wrappers for primitive types, and any type that can be decomposed into primitives or primitive wrappers.

  • Model MBeans—A model MBean is a special kind of dynamic MBean that bridges a management interface to the managed resource. Model MBeans aren’t written as much as they are declared. Model MBeans are typically produced by a factory that uses some meta-information to assemble the management interface.

Spring’s JMX module enables you to export Spring beans as Model MBeans so that you can see inside your application and tweak the configuration—even while the application is running. Let’s see how to use Spring’s JMX support to manage the beans within a Spring application.

Exporting Spring beans as MBeans

In the previous section, we used Spring’s scheduling support to schedule the generation of emails at 11:59 p.m. every night. Although just before midnight is usually best for sending the emails, it would also be nice to be able to adjust the timing of the email without having to redeploy the RoadRantz application. To accommodate reconfiguration of the scheduler, we’re going to use Spring’s JMX support to export the timer bean as an MBean.

Spring’s MBeanExporter is a bean that exports one or more Spring beans as Model MBeans in an MBean server. An MBean server (sometimes called an MBean agent) is a container where MBeans live and through which the MBeans are accessed. For MBeans to be of any use for management and configuration, they must be registered in an MBean server. As illustrated in figure 12.8, exporting Spring beans as JMX MBeans makes it possible for a JMX-based management tool such as MC4J (http://mc4j.org) to peer inside a running application to view the beans’ properties and invoke their methods.

Figure 12.8. Spring’s MBeanExporter exports the properties and methods of Spring beans as JMX attributes and operations in an MBean server. From there, a JMX management tool such as MC4J can look inside the running application.

The following <bean> declares an MBeanExporter bean in Spring to export the cronEmailTrigger bean as a Model MBean:

<bean class="org.springframework.jmx.export.MBeanExporter">
  <property name="beans">
    <map>
      <entry key="rantz:name=emailSchedule"
          value-ref="cronEmailTrigger"/>
    </map>
  </property>
</bean>

In its simplest form, MBeanExporter can be configured through its beans property with a <map> of one or more beans that you’d like to expose as a model MBean through JMX. The key of each <entry> is the name of the MBean. The value of the <entry> is a reference to the Spring-managed bean that is to be exported. Here we’re exporting the cronEmailTrigger bean so that the timer can be managed through JMX.

Note

As configured above, MBeanExporter assumes that it is running within an application server that provides an MBean server (such as Tomcat or JBoss). But if your Spring application will be running stand-alone or in a container that doesn’t provide an MBean server, you’ll want to configure an MBeanServerFactoryBean:

<bean id="jmxServer"
    class="org.springframework.jmx.support.
 MBeanServerFactoryBean">
  <property name="defaultDomain" value="rantz" />
</bean>

Then you’ll need to wire the MBeanServerFactoryBean into the MBeanExporter’s server property:

<bean id="mbeanExporter"
      class="org.springframework.jmx.export.MBeanExporter">
  <property name="beans">
    <map>
      <entry key="rantz:name=emailSchedule"
          value-ref="cronEmailTrigger"/>
    </map>
  </property>

  <property name="server" ref="jmxServer" />
</bean>

With the MBeanExporter in place, the cronEmailTrigger bean will be exported as a Model MBean to the MBean server for management under the name emailSchedule. Figure 12.9 shows how the cronEmailTrigger MBean appears when viewed through MC4J.

Figure 12.9. CronTriggerBean exported as an MBean and seen through the eyes of MC4J. Notice that all of CronTriggerBean's public methods and properties are exported as MBean operations and attributes.

As you can see from figure 12.9, all public members of the cronEmailTrigger bean are exported as Mbean operations and attributes. This is probably not what we want. All we really want to do is to be able to configure the timing of the daily email. The cronExpression property tells CronTriggerBean when to trigger jobs. But even if we change this property, its schedule won’t take effect until after the next job is fired. If we want to control when the next job is fired, we’ll also need to be able to configure the nextFireTime property.

All of the other attributes and operations, however, are superfluous and just get in the way. Furthermore, we may want to purposefully restrict those other attributes and operations from appearing in the management interface to avoid accidental changes to the bean. Thus, we need a way to select which attributes and operations end up in the management.

Recall that a model MBean is an MBean whose management interface is assembled using some form of meta-information. In Spring, when it comes to picking and choosing which methods and properties of a bean become operations and attributes of a model MBean, we must specify an MBean assembler:

<bean id="mbeanExporter"
      class="org.springframework.jmx.export.MBeanExporter">
  <property name="beans">
    <map>
      <entry key="rantz:name=emailSchedule"
           value-ref="cronEmailTrigger"/>
    </map>
  </property>

  <property name="assembler" ref="assembler" />
</bean>

An assembler is a bean whose job is to assemble the management interface for MBeans that are exported by MBeanExporter. We have three assemblers to choose from, each using a different source of meta-information to define the management interface.

  • MethodNameBasedMBeanInfoAssemblerLets you explicitly configure methods to expose by name

  • InterfaceBasedMBeanInfoAssemblerExposes bean methods based on what is contained in an interface

  • MetadataMBeanInfoAssemblerExposes bean methods and properties that are annotated with @ManagedOperation and @ManagedAttribute

Let’s look at how to use each of these MBean assemblers one by one starting with the MethodNameBasedMBeanInfoAssembler.

Exposing methods by name

MethodNameBasedMBeanInfoAssembler is an MBean assembler that decides which bean methods and properties to expose on the MBean’s management interface based on a list of method names. The following <bean> declaration shows an example of using MethodNameBasedMBeanInfoAssembler to expose the cronExpression and nextFireTime properties of the cronEmailTrigger bean:

<bean id="assembler"
    class="org.springframework.jmx.export.assembler.
                     MethodNameBasedMBeanInfoAssembler">
  <property name="managedMethods">
    <list>
       <value>setCronExpression</value>
       <value>getCronExpression</value>
       <value>setNextFireTime</value>
       <value>getNextFireTime</value>
    </list>
  </property>
</bean>

The managedMethods property takes a list of method names that are to be exposed as managed operations. Notice that to expose the cronExpression and nextFireTime properties as MBean attributes, we had to declare their setter and getter methods. Although managedMethods is meant to expose methods as managed operations, the corresponding properties are exposed as managed attributes if the methods are getter and setter methods.

Now when we look at the emailSchedule MBean in a JMX client, we see only the attributes and operations we specified to be exported. Figure 12.10 shows how the newly assembled emailSchedule MBean looks in MC4J.

Figure 12.10. Once we use an MBean assembler, only selected methods and properties are exposed through the exported MBean.

As you’ve seen, the MethodNameBasedMBeanInfoAssembler is the simplest of all of Spring’s MBean assemblers. It lets you succinctly list all the methods that you wish to expose in the management interfaces of the exported MBeans.

On the other hand, MethodNameBasedMBeanInfoAssembler is also the most cumbersome to use because it requires that you specify all of the methods that you wish to expose. If you are using MBeanExporter to export several beans, each with their own set of methods to be exposed, the list of method names given to managedMethods will likely grow very large. And because the method names are all listed together, it will be difficult to know which methods belong to which exported beans.

Using interfaces to define MBean operations and attributes

Another of Spring’s MBean assemblers is InterfaceBasedMBeanInfoAssembler. InterfaceBasedMBeanInfoAssembler is similar to MethodNameBasedMBeanInfoAssembler, except that instead of declaring which methods to expose on the management interface, you declare one or more Java interfaces that define the management methods.

To illustrate, suppose that we’ve defined the following interface:

package com.roadrantz.service.mbean;
import java.util.Date;

public interface ManagedCronTrigger {
  void setCronExpression(String ce);
  String getCronExpression();
  void setNextFireTime(Date date);
  Date getNextFireTime();
}

The ManagedCronTrigger interface contains declarations of the methods we’d like to expose from the Quartz CronTriggerBean. With this interface, we can declare an interface-based assembler as follows:

<bean id="assembler"
    class="org.springframework.jmx.export.assembler.
                      InterfaceBasedMBeanInfoAssembler">
  <property name="managedInterfaces">
    <list>
      <value>com.roadrantz.service.mbean.ManagedCronTrigger</value>
    </list>
  </property>
</bean>

With ManagedCronTrigger being the only managed interface specified, this assembler is effectively equivalent to the MethodNameBasedMBeanInfoAssembler we declared earlier. The difference is that we’re now able to use Java interfaces to define our MBean managed interfaces.

Before we move on to the next type of assembler, you may want to take note of the fact that although ManagedCronTrigger declares methods that we’d like to expose on the exported MBean, CronTriggerBean doesn’t directly implement this interface. Oftentimes, the interfaces specified in the managedInterfaces property will be interfaces that are actually implemented by the exported beans. But as you can see here in the case of CronTriggerBean and ManagedCronTrigger, they do not have to be.

Both MethodNameBasedMBeanInfoAssembler and InterfaceBasedMBeanInfoAssembler are suitable for assembling an MBean’s managed interface, especially when you do not have access to the source code for the beans that are to be exported. But there’s one more MBean information assembler that is great when you have access to the MBean’s source code.

Working with metadata-driven MBeans

If you are lucky enough to have access to the bean’s source code, you may want to consider exporting your beans using a metadata-driven MBean assembler.

For example, suppose that we’d like to export the rantService bean as an MBean so that we can invoke the sendDailyRantEmails() method as a managed operation. We could certainly use either MethodNameBasedMBeanInfoAssembler or InterfaceBasedMBeanInfoAssembler to assemble the MBean’s managed interface. But with either of those assemblers we’d have to write some interface or declaration separate from the rantService bean or the RantServiceImpl class. What if we could take advantage of Java 5 annotations instead?

Spring’s MetadataMBeanInfoAssembler is an MBean assembler that assembles an MBean’s managed interface based on source-level metadata placed on the methods and properties to be exposed. The following <bean> declaration sets up the assembler bean to use source-level metadata:

<bean id="assembler"
    class="org.springframework.jmx.export.assembler.
                    MetadataMBeanInfoAssembler">
  <property name="attributeSource" ref="attributeSource" />
</bean>

The attributeSource property is used to tell MetadataMBeanInfoAssembler what kind of metadata to look for. In theory, MetadataMBeanInfoAssembler can be configured to read MBean metadata from virtually any number of metadata sources, so long as the attributeSource property is configured with an implementation of org.springframework.jmx.export.metadata.JmxAttributeSource. Spring comes with two such implementations to choose from:

  • AttributesJmxAttributeSourceReads MBean metadata that is precompiled into source code using Jakarta Commons Attributes

  • AnnotationJmxAttributeSourceReads MBean metadata from JDK 1.5 annotations

Since we’re targeting Java 5, we’ll use AnnotationJmxAttributeSource so that we can use annotations. It is declared in Spring with the following <bean>:

<bean id="attributeSource"
    class="org.springframework.jmx.export.annotation.
                    AnnotationJmxAttributeSource" />

Meanwhile, MBeanExporter is wired with a reference to the MetadataMBeanInfoAssembler along with a couple of other useful properties:

<bean id="mbeanExporter"
    class="org.springframework.jmx.export.MBeanExporter">
  <property name="assembler" ref="assembler" />
  <property name="autodetectModeName"
      value="AUTODETECT_ASSEMBLER" />
  <property name="namingStrategy" ref="namingStrategy" />
</bean>

Rather than explicitly list all beans that are to be exposed as MBeans through the beans property, we’d like Spring to figure out which beans to expose as MBeans based on their annotations. Therefore, we’ve configured the autodetectModeName property with AUTODETECT_ASSEMBLER. This tells the MBeanExporter to use the MetadataMBeanInfoAssembler to look for all beans in the Spring application context that are annotated with the @ManagedResource annotation.

Moreover, MetadataMBeanInfoAssembler determines a bean’s managed attributes and operations by looking for properties and methods annotated with @ManagedAttribute and @ManagedOperation (respectively). For example, consider the annotated RantService interface in Listing 12.5.

Example 12.5. Using annotations to declaratively create MBeans

I’ve annotated the RantService interface with @ManagedResource to indicate that any class that implements it should be exposed as an MBean. I’ve also annotated the sendDailyRantEmails() method with @ManagedOperation to indicate that this method should be exposed as a managed operation.

However, the beans property did more than just list the beans to expose as MBeans; it also gave the MBeans their names. If we’re not explicitly listing the beans anymore, how can we make sure that the MBeans are named appropriately?

That’s what the namingStrategy property is for. By default, MBeanExporter uses KeyNamingStrategy, which draws the MBean name from the key value in the map that is wired into the beans property. Since we’re not using the beans map, KeyNamingStrategy won’t work. Instead, we’ll use MetadataNamingStrategy, which is declared as follows:

<bean id="namingStrategy"
    class="org.springframework.jmx.export.naming.
                  MetadataNamingStrategy">
  <property name="attributeSource" ref="attributeSource" />
</bean>

As you might guess, MetadataNamingStrategy determines MBean names from metadata placed in the bean class. In this case, we’ve wired the attributeSource with a reference to the AnnotationJmxAttributeSource bean we defined earlier. Thus, each MBean’s name will be specified by the objectName attribute of the @ManagedResource annotation.

Handling MBean object name collisions

So far you’ve seen how to publish an MBean into an MBean server using several approaches. In all cases, we’ve given the MBean an object name that is made up of a managed domain name and a key-value pair. Assuming that there’s not already an MBean published with the name we’ve given our MBean, we should have no trouble publishing our MBean. But what happens if there’s a name collision?

By default, MBeanExporter will throw an InstanceAlreadyExistsException should you try to export an MBean that is named the same as an MBean that’s already in the MBean server. But you can change that behavior by setting the registrationBehaviorName property on the MBeanExporter. For example, the following <bean> declares an MBeanExporter that replaces the existing MBean with the new MBean being registered:

<bean class="org.springframework.jmx.export.MBeanExporter">
  <property name="beans">
    <map>
      <entry key="rantz:name=emailSchedule"
          value-ref="cronEmailTrigger"/>
    </map>
  </property>

  <property name="registrationBehaviorName"
      value="REGISTRATION_REPLACE_EXISTING" />
</bean>

There are three options for the registrationBehaviorName property, as described in table 12.3.

Table 12.3. MBean registration behavior options.

Behavior name

Use it when...

REGISTRATION_FAIL_ON_EXISTING

You’d like to be notified (via an exception) when an MBean registration fails due to a name collision

REGISTRATION_IGNORE_EXISTING

You’d like to attempt to register an MBean but fail silently on a name collision

REGISTRATION_REPLACE_EXISTING

You’d like to replace an existing MBean with a new MBean

As we mentioned, MBeanExporter’s default behavior is to throw an InstanceAlreadyExistsException in the event of an MBean object name collision. Therefore, it’s unnecessary to explicitly set the registrationBehaviorName property with REGISTRATION_FAIL_ON_EXISTING.

Now that we’ve registered our MBeans using MBeanExporter, we’ll need a way to access them for management. As you’ve seen already, most JMX implementations support an HTML interface for the MBean server. But the HTML interface varies across all JMX implementations and doesn’t lend itself to programmatic management of MBeans. Fortunately, there’s another way to access MBeans as remote objects. Let’s explore how Spring’s support for remote MBeans will enable us to access our MBeans in a standard way.

Remoting MBeans

Although the original JMX specification referred to remote management of applications through MBeans, it didn’t define the actual remoting protocol or API. Consequently, it fell to JMX vendors to define their own, often proprietary, remoting solutions.

In response to the need for a standard for remote JMX, the Java Community Process produced JSR-160, the Java Management Extensions (JMX) Remote API Specification. This specification defines a standard for JMX remoting, which at minimum requires an RMI binding and optionally the JMX Messaging Protocol (JMXMP).

Exposing remote MBeans

The simplest thing we can do to make our MBeans available as remote objects is to configure Spring’s ConnectorServerFactoryBean:

<bean class="org.springframework.jmx.support.
     ConnectorServerFactoryBean" />

ConnectorServerFactoryBean creates and starts a JSR-160 JMXConnectorServer. By default, the server listens for the JMXMP protocol on port 9875—thus it is bound to service:jmx:jmxmp://localhost:9875. The problem with this is that most JMX implementations do not support JMXMP. Therefore, we’ll need to choose some other protocol for accessing our MBeans.

Depending on your JMX provider, you may have several remoting protocol options to choose from. MX4J’s (http://mx4j.sourceforge.net/) remoting support includes RMI, SOAP, Hessian/Burlap, and IIOP. RMI is sufficient for our needs, so let’s configure ConnectorServerFactoryBean to expose its beans remotely with RMI:

<bean class="org.springframework.jmx.support.
     ConnectorServerFactoryBean">
  <property name="serviceUrl"
      value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/
                 rantz" />
</bean>

The serviceUrl property is used to specify the remote binding for the JMXConnectorServer. In this case, we’re binding it to an RMI registry listening on port 1099 of the localhost. That means we’ll also need an RMI registry running. As you’ll recall from chapter 8, an RMI registry can be started through Spring with the following <bean> declaration:

<bean class="org.springframework.remoting.rmi.
     RmiRegistryFactoryBean">
  <property name="port" value="1099" />
</bean>

And that’s it! Now our MBeans are available through RMI. But there’s little point in doing this if nobody will ever access the MBeans over RMI. So, let’s now turn our attention to the client side of JMX remoting and see how to wire up a remote MBean in Spring.

Accessing remote MBeans

Accessing a remote MBean server involves configuring an MBeanServerConnectionFactoryBean in Spring. The following <bean> declares an MBeanServerConnectionFactoryBean that is used to access the remote server we created in the previous section:

<bean id="mBeanServerClient"
    class="org.springframework.jmx.support.
         MBeanServerConnectionFactoryBean">
  <property name="serviceUrl"
      value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/
                  rantz" />
</bean>

As its name implies, MBeanServerConnectionFactoryBean is a factory bean that creates an MBeanServerConnection. The MBeanServerConnection produced by MBeanServerConnectionFactoryBean acts as a local proxy to the remote MBean server. It can be wired into a property just like any other bean:

<bean id="jmxClient"
    class="com.springinaction.jmx.JmxClient">
  <property name="mbeanServerConnection"
      ref="mBeanServerClient" />
</bean>

MBeanServerConnection provides several methods that let you query the remote MBean server and invoke methods on the MBeans contained within it. For example, say that we’d like to know how many MBeans are registered in the remote MBean server. The following code snippet will print that information:

int mbeanCount = mbeanServerConnection.getMBeanCount();
print ("There are " + mbeanCount + " MBeans");

And you may also query the remote server for all of the MBean names using the queryNames() method:

java.util.Set mbeanNames =
    mbeanServerConnection.queryNames(null, null);

The two parameters passed to queryNames() are used to refine the results. By passing in null for both parameters, we’re asking for the names of all registered MBeans.

Querying the remote MBean server for bean counts and names is interesting, but doesn’t get much work done. The real value of accessing an MBean server remotely is found in accessing attributes and invoking operations on the MBeans that are registered in the remote server.

For accessing MBean attributes, you’ll want to use the getAttribute() and setAttribute() methods. For example, to retrieve the value of an MBean, you’d call the getAttribute() method like so:

String cronExpression = mbeanServerConnection.getAttribute(
    new ObjectName("rantz:name=emailSchedule"),
    "cronExpression");

Changing the value of an MBean’s property is similarly done using the setAttribute() method:

mbeanServerConnection.setAttribute(
    new ObjectName("rantz:name=emailSchedule"),
    new Attribute("cronExpression", "0 59 23 * * ?"));

If you’d like to invoke a method on a remote MBean, the invoke() method is what you’ll call. Here’s how you might invoke the sendDailyRantEmails() method on the RantService MBean:

mbeanServerConnection.invoke(
    new ObjectName("rantz:name=rantService"),
    "sendDailyRantEmails",
    new Object[] {},
    new String[] {""});

And there are dozens of other things you can do with remote MBeans using the MBeanServerConnection provided by MBeanServerConnectionFactoryBean. I’ll leave it to you to explore the possibilities.

However, invoking methods and setting attributes on remote MBeans is awkward through the API offered through MBeanServerConnection. Doing something as simple as calling the sendDailyRantEmails() method involves creating an ObjectName instance, and passing in several parameters to the invoke() method is not nearly as intuitive as simply calling the sendDailyRantEmails() method directly. For a more direct approach, we’ll need to proxy the remote MBean.

Proxying MBeans

Spring’s MBeanProxyFactoryBean is a proxy factory bean in the same vein as the remoting proxy factory beans we examined in chapter 8. But instead of providing proxy-based access to remote beans via RMI or Hessian/Burlap, MBeanProxyFactoryBean lets you access remote MBeans directly (as if they were any other locally configured bean). Figure 12.11 illustrates how this works.

Figure 12.11. MBeanProxyFactoryBean produces a proxy to a remote MBean. The proxy’s client can then interact with the remote MBean as if it were a locally configured POJO.

For example, consider the following declaration of MBeanProxyFactoryBean:

<bean id="remoteRantServiceMBean"
    class="org.springframework.jmx.access.MBeanProxyFactoryBean">
  <property name="objectName" value="rantz:name=RantService" />
  <property name="server" ref="mBeanServerClient" />
  <property name="proxyInterface"
      value="com.roadrantz.service.mbean.RantServiceRemoteMBean" />
</bean>

The objectName property specifies the object name of the remote MBean that is to be proxied locally. Here it’s referring to the MBean that is exported from the RantServiceImpl bean.

The server property refers to an MBeanServerConnection through which all communication with the MBean is routed. Here I’ve wired in the MBeanServerConnectionFactoryBean that we configured a page or so ago.

Finally, the proxyInterface property specifies the interface that will be implemented by the proxy. In this case, it is the RantServiceRemoteMBean interface:

public interface RantServiceRemoteMBean {
  void sendDailyRantEmails();
}

With the remoteRantServiceMBean bean declared, you can wire it into a RantServiceRemoteMBean property of any class that needs to access the remote MBean. Then you’ll be able to invoke the sendDailyRantEmails() method directly like this:

remoteRantServiceMBean.sendDailyRantEmails();

We’ve now seen several ways that we can communicate with MBeans and are now able to view and tweak our Spring bean configuration while the application is running. But thus far it’s been a one-sided conversation. We’ve talked to the MBeans, but the MBeans haven’t been able to get a word in edgewise. It’s now time for us to hear to what they have to say by listening for notifications.

Handling notifications

Querying an MBean for information is only one way of keeping an eye on the state of an application. It is not, however, the most efficient way to be informed of significant events within the application.

For example, suppose you want to know the precise moment that the one-millionth motorist registers to the RoadRantz applications. You could add an appropriate managed attribute to the RantService MBean and continually query the MBean, waiting for the one-millionth motorist. But you know what they say about a watched pot and when it boils—you could end up wasting a great deal of time querying the MBean only to have the one-millionth motorist arrive while you’re away at lunch.

Instead of asking the MBean if the one-millionth motorist has registered, a better approach would be to have the MBean notify you once the momentous milestone has been achieved. JMX notifications, as shown in figure 12.12, are a way that MBeans can communicate with the outside world proactively, instead of waiting for an external application to query them.

Figure 12.12. JMX notifications enable MBeans to communicate proactively with the outside world.

Spring’s support for sending notifications comes in the form of the NotificationPublisherAware interface. Any bean-turned-MBean that wishes to send notifications should implement this interface. Here are the pertinent changes required to enable the RantServiceImpl class to publish notifications:

public class RantServiceImpl implements RantService,
    NotificationPublisherAware {
...
  private NotificationPublisher notificationPublisher;
  public void setNotificationPublisher(
      NotificationPublisher notificationPublisher) {
    this.notificationPublisher = notificationPublisher;
  }
}

The NotificationPublisherAware interface only demands a single method to be implemented: setNotificationPublisher(). The setNotificationPublisher() method is used to inject a NotificationPublisher into the RantServiceImpl. Here we’ve wired ModelMBeanNotificationPublisher as an inner bean to the notificationPublisher property:

<bean id="rantService"
    class="com.roadrantz.service.RantServiceImpl">
...
  <property name="notificationPublisher">
    <bean class="org.springframework.jmx.export.notification.
            ModelMBeanNotificationPublisher" />
  </property>
</bean>

With a NotificationPublisher object at hand, we are now able to write the code that sends a notification when the one-millionth motorist registers. The following checkForOneMillionthMotorist() method should do the trick:

private void checkForOneMillionthMotorist() {
  if(rantDao.getMotoristCount() == 1000000) {
    notificationPublisher.sendNotification(
        new Notification(
            "RantService.OneMillionMotorists", this, 0));
  }
}

After determining that, in fact, the one-millionth motorist has just been added, the checkForOneMillionthMotorist() method constructs a new JMX Notification object and uses the NotificationPublisher’s sendNotification() method to publish the notification.

Once the sendNotification() method is called, the notification is on its way to... hmm... it seems that we haven’t decided who will receive the notification yet. Let’s set up a notification listener to listen to and react to the notification.

Listening for notifications

The standard way to receive MBean notifications is to implement the javax.management.NotificationListener interface. For example, consider PagingNotificationListener:

package com.roadrantz.service.mbean;
import javax.management.Notification;
import javax.management.NotificationListener;

public class PagingNotificationListener
    implements NotificationListener {
  public PagingNotificationListener() {}

  public void handleNotification(Notification notification,
      Object handback) {
     ... // send pager message
  }
}

PagingNotificationListener is a typical JMX notification listener. When a notification is received, the handleNotification() method will be invoked to react to the notification. Presumably, PagingNotificationListener’s handleNotification() method will send a message to a pager or cell phone about the one-millionth motorist. (I’ve left the actual implementation to the reader’s imagination.)

The only thing left to do is register PagingNotificationListener with the MBeanExporter:

<bean class="org.springframework.jmx.export.MBeanExporter">
...
  <property name="notificationListenerMappings">
    <map>
       <entry key="rantz:name=rantService">
         <bean class=
           "com.roadrantz.service.mbean.
                PagingNotificationListener" />
       </entry>
     </map>
   </property>
</bean>

MBeanExporter’s notificationListenerMappings property is used to map notification listeners to the MBeans that they’ll be listening to. In this case, we’ve set up PagingNotificationListener to listen to any notifications published by the rantService MBean.

Summary

In this chapter, we’ve added new email capabilities to the RoadRantz application. In doing so, we’ve explored a few of tidbits from among Spring’s enterprise abstraction APIs.

Although configuration through dependency injection is one of Spring’s strong points, sometimes it’s preferable to store certain configuration information or application objects outside of the Spring application context. JDBC data sources, for example, are often configured within an application server and made accessible through JNDI. Fortunately, as you’ve seen, Spring’s JNDI abstraction makes short work of injecting JNDI-managed objects into Spring-managed beans.

You’ve also seen how Spring’s JavaMail abstraction simplifies the sending of emails by enabling you to configure a mail sender bean in Spring. The mail sender can then be injected into any application object that needs to send email.

Oftentimes it is necessary for an application to perform specific tasks on a schedule. In the RoadRantz application, we used Spring’s scheduling support to periodically send emails to registered motorists. Keeping with the Spring theme of choice, Spring supports scheduling through a variety of scheduling APIs, including Java’s Timer object and OpenSymphony’s Quartz.

Finally, you saw how Spring enables management of beans through its JMX abstraction API. Using Spring JMX, we were able to expose Spring-configured beans as MBeans suitable for management through JMX.

Our journey through Spring’s enterprise APIs and abstractions is now at an end. We’ve covered a lot of ground, including database persistence, declarative transactions and security, remoting, web services, asynchronous messaging, and EJBs. These are the heart and mind of many applications, working behind the scenes to get the job done.

The enterprise technologies may keep an application humming, but it’s what’s on the screen that’s of the most concern to your application’s users. In the next part of this book, we’re going to look at several ways to build the user-facing portion of an application using Spring. We’ll start in the next chapter by looking at Spring’s own web framework, Spring MVC.