28 December 2009

Spring 3.0's out (you didn't get the memo?), and there's a lot of interesting stuff. This release has been long in coming due in no small part to the specifications it tracks and to the intense furvor and emphasis placed on quality assurance and testing. One target platform - JEE6 (and with it the final versions of JSR330) - also just recently went final, and you'll find support for these technologies, as well, where appropriate. Now that it's out, you can start certifying applications in production on it. I've found that - aside from dealing with the new Maven imports - the upgrade process has been flawless. I've taken complex applications and merely fixed the jars and they continue to work. You will find, however, that there are a lot of reasons to go through and start selectively enabling new features, and namespaces.

In our book Spring Enterprise Recipes, my co-author Gary Mak and I discuss a lot of the new, exciting Spring 3.0 features. Spring 3.0 debuts slightly more streamlined scheduling / TaskExecutor / thread pool support. The idea is that you can more readily model asynchronous, repeating (at a scheduled time, rate, or both), and concurrent programming problems using these facilities and - in some cases - do so while leveraging more advanced facilities underlying a given target platform, like the WorkerJ implementations, the Java5 task executors, and thread pools and more.

I won't go into much of that here as the book ablely covers most of it, but one thing I did want to cover (which we couldn't cover in time in the book as the feature was not ready as the book went to press) was the very elegant task namespace.

Background

It should be noted that these features are not, exactly, novel. Spring has shipped with a Quartz scheduling framework integration for years. Among many other niceties included therein was (and still is, for what it's worth) a MethodInvokingJobDetailFactoryBean that allowed you to schedule future executions of a method on a Java bean using Quartz.

A few years later, EJB 3 debuted support for a limited form of scheduling using the Timer mechanism. One major limitation was the lack of support for CRON-like expressions and for asynchronous methods. Naturally, you could use JMS to acheive the same effect, in a way...

The JBoss gang debuted JBoss Seam support for scheduled and asynchronous method execution on a Seam component as well as a proprietary mechanism to do the same with EJB 3. In Seam, using these features was simply a matter of adding the appropriate configuration (as we do in Spring) to enable the executor (they have a few, including one based on the EJB3 Timer and one based Quartz.) The usage here is familiar to what has recently been debuted in Spring 3.0.

Clearly, the need for such features is common enough that they've both been fully incorporated into JEE6 and EJB3.1. There, you can specify CRON expressions as well as defer the invocation of a method using the @Asynchronous or @Schedule annotations. A simple example looks like:

package com.joshlong.ejb.timer ; 
import java.util.Date; 
import java.util.logging.Logger; 
import javax.annotation.Resource; 
import javax.ejb.Schedule; 
import javax.ejb.Asynchronous; 
import javax.ejb.Stateless; 
import javax.ejb.Timeout; 
import javax.ejb.Timer; 
import javax.ejb.TimerService; 
@Singleton 
public class PeriodicGreeter { 
 // you could use this to schedule things manually 
 @Resource TimerService timerService; 
 // or use the annotations to do so automatically 
 @Schedule(minute="*/3", hour="*") 
 public void sayHelloPeriodically() { 
   System.out.println( String.format( "Hello, world, at %s" , new 
Date()) ) ; 
 } 
 @Asynchronous 
 public Future<String> sayHelloAsynchronously() { 
   System.out.println( String.format( "Hello, world, at %s" , new 
Date()) ) ; 
   // ... 
 } 
}

Spring 3.0 Implementation

The task namespace let's you declaratively configure a TaskScheduler and a TaskExecutor instance using XML. From here, you can configure beans that have scheduled, or asynchronous, executions using the XML or - my personal favorite if you can get access to the code - via Java annotations.

Here's an example Spring application context featuring this namespace and configuring a scheduler and executor with default-ish settings:

 
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:task="http://www.springframework.org/schema/task" 
   xmlns:context="http://www.springframework.org/schema/context" 
   xsi:schemaLocation=" 
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
       http://www.springframework.org/schema/lang 
       http://www.springframework.org/schema/lang/spring-lang-3.0.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-3.0.xsd 
       http://www.springframework.org/schema/task 
       http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 
 <context:annotation-config/> 
 <context:component-scan annotation-config="true" 
       base-package="com.yourbasepackage.scheduling" /> 
 
 <task:scheduler id="scheduler" pool-size="10"/> 
 <task:executor id="executor" pool-size="10"/> 
 <task:annotation-driven scheduler="scheduler" executor = "executor" /> 
 
</beans> 

What you do from here is up to you. You could simply start defining tasks inline with your XML. That approach certainly has its redeeming qualities, not the least of which is that your code is more readily "documented" and conceivably adjustable at runtime with some refresh trickery... sure.. you could.

 
<task:scheduled-tasks scheduler="myScheduler"> 
 <task:scheduled ref="greeter" method="sayHello" fixed-delay="5000"/> 
 <task:scheduled ref="greeter" method="sayHello" fixed-rate="5000"/> 
 <task:scheduled ref="greeter" method="sayHello" cron="*/5 * * * 
* MON-FRI"/> 
<task:scheduled-tasks/> 

...In practice, however, the annotation approach just FEELS soo much better! So, here is how you might write a scheduled or asynchronous bean in Spring.

package com.joshlong.spring.timer ; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.scheduling.annotation.Scheduled; 
import org.springframework.stereotype.Component; 
import javax.annotation.PostConstruct; 
import javax.annotation.Resource; 
import java.util.Date; 
@Component 
public class SpringPeriodicGreeter { 
 // you can do everything you normally would using Spring, obviously 
 @Autowired CustomerService customerService ; 
 // this runs once every 5 minutes 
 @Scheduled(fixedRate = 1000 * 60 * 5) 
 public void sayHelloEveryFiveMinutes() { 
   System.out.println( String.format( "Hello, world, at %s" , new 
Date()) ) ; 
 } 
 @Scheduled(cron="*/5 * * * * MON-FRI") 
 public void sayHelloOnlyOneWeekdays() { 
   System.out.println( String.format( "Hello, world, at %s" , 
new Date()) ) ; 
 } 
 @Async 
 public void doSomething(String s) { 
  // this will be executed asynchronously 
 } 
 @Async 
 public Future<String> returnSomething(int i) { 
   // this will be executed asynchronously, but you can get the 
   // result by blocking on Future.get 
 } 
} 

Thus, this is nothing too strange - very simialar in fact to what's in JEE6. Obviously, I've not mentioned every permutation of the features from the various technologies, but hopefully this gets you past the initial cognitive "hump" of adapting a new technology. It helps that - at least in the Spring case - it's dead simple to start using it if you're already using Spring. I wonder if there will be support for working with the JEE6 annotations that describe the same things? The major takeaways are that you can get this simply by updating your version of Spring, which should be painless if you're not tied to Java 1.4. You can get the JEE6 functionality by updating your version of the platform and server, if the platform/server are ready (Glassfish is!).

Use

Now, as to where this stuff might be used, well, I can only think of a few thousand things...

  1. Quartz is probably still more powerful, but the implementation and usage are hackneyed - this new approach will feel much more elegant. You could probably get away with removing the old Quartz code and using this for most of your implementation needs.
  2. Because Spring's implementations are swappable, you could back this functionality with varying TaskExecutors/TaskSchedulers of your own implementation, if you wanted.
  3. The obvious use case is that you can now remove CRON, Autosys, Flux and any number of other third-party, dedicated middleware schedulers from your architecture (if you're only using them to run Java services.)
  4. As with Spring itself, this abstraction is useful because you can deploy it inside of a web applicatior or any other place you can imagine Spring running, so you don't need to install a scheduler if you just want something run periodically inside your web container
  5. If you have a Spring Batch job, this might be an ideal way to kick the jobs off periodically. You need to start processing the billing batch every night at 2am, but only on weekdays? This is a match made in heaven! How you get a Batch job to start running is left mainly as an exercise to the user. I recommend using an ESB (like Spring Integration) to react to events, or using a scheduler like this task namespace (or Quartz) to kick the jobs off.
  6. Spring Integration has a gateway mechanism that lets you front what is essentially a send (and/or) recieve operation on a channel (think: JMS queue/topic) with a method on an interface. I love this feature and use it a lot because I don't want to surface JMS queues / topics to the client (that's a little too loose and decoupled an API!). I also use it because it allows me to model fire-n-forget messaging using Java interfaces, which is exactly what the @Async annotation does. The other benefit of the Spring Integration feature is that the processing leaves the VM and goes somewhere to finish (ostensibly, wherever the consumer for the topic/queue lives) the job and then return the result. This provides scalability benefits to both the client and the server, whereas the @Async annotation would only increase the thoroughput of the client, in this case: processing still takes place on the node with the @Async annotation, it's simply deferred... Deferment is a valid way to increase capacity while decreasing thoroughput.