Chapter 21. Asynchronicity and messaging

Seam makes it easy to perform work asynchronously from a web request. Asynchronicity in Java EE is usually linked with JMS, and where your quality of service requirements are strict and well-defined, this is logical. It is easy to send JMS messages through Seam components.
However, for many use cases, JMS is more powerful than necessary. Seam layers a simple, asynchronous method and event facility over your choice of dispatchers:
  • java.util.concurrent.ScheduledThreadPoolExecutor (by default)
  • the EJB timer service (for EJB 3.0 environments)
  • Quartz

21.1. Asynchronicity

Asynchronous events and method calls have the same quality of service expectations as the underlying dispatcher mechanism. The default dispatcher, based upon a ScheduledThreadPoolExecutor performs efficiently but provides no support for persistent asynchronous tasks, and hence no guarantee that a task will ever actually be executed. If you are working in an environment that supports EJB 3.0, add the following line to components.xml to ensure that your asynchronous tasks are processed by the container's EJB timer service:
<async:timer-service-dispatcher/>
If you want to use asynchronous methods in Seam, you do not need to interact directly with the Timer service. However, it is important that your EJB3 implementation has the option of using persistent timers, which give some guarantee that the task will eventually be processed.
Alternatively, you can use the open source Quartz library to manage asynchronous method. To do so, bundle the Quartz library JAR (found in the lib directory) in your EAR, and declare it as a Java module in application.xml. You can configure the Quartz dispatcher by adding a Quartz property file to the classpath —this file must be named seam.quartz.properties. To install the Quartz dispatcher, you will also need to add the following line to components.xml:
<async:quartz-dispatcher/>
Since the Seam API for the default ScheduledThreadPoolExecutor, the EJB3 Timer, and the Quartz Scheduler are very similar, you can "plug and play" by adding a line to components.xml.

21.1.1. Asynchronous methods

An asynchronous call allows a method call to be processed asynchronously (in a different thread) to the caller. Usually, asynchronous calls are used when we want to send an immediate response to the client, and simultaneously process expensive work in the background. This pattern works well in AJAX applications, where the client can automatically poll the server for the result of the work.
For EJB components, annotate the implementation of the bean to specify that a method be processed asynchronously. For JavaBean components, annotate the component implementation class:
@Stateless
@Name("paymentHandler")
public class PaymentHandlerBean implements PaymentHandler
{
  @Asynchronous
  public void processPayment(Payment payment) {
    //do some work!
  }
}
Asynchronicity is transparent to the bean class. It is also transparent to the client:
@Stateful
@Name("paymentAction")
public class CreatePaymentAction
{
  @In(create=true) PaymentHandler paymentHandler;
  @In Bill bill;
    
  public String pay() {
    paymentHandler.processPayment( new Payment(bill) );
    return "success";
  }
}
The asynchronous method is processed in a fresh event context, and has no access to the session or conversation context state of the caller. However, the business process context is propagated.
You can schedule asynchronous method calls for delayed execution with the @Duration, @Expiration and @IntervalDuration annotations.
@Local
public interface PaymentHandler {
  @Asynchronous
  public void processScheduledPayment(Payment payment, 
                                      @Expiration Date date);

  @Asynchronous
  public void processRecurringPayment(Payment payment, 
                                      @Expiration Date date, 
                                      @IntervalDuration Long interval);
}
@Stateful
@Name("paymentAction")
public class CreatePaymentAction
{
  @In(create=true) PaymentHandler paymentHandler;
  @In Bill bill;
    
  public String schedulePayment() {
    paymentHandler.processScheduledPayment(new Payment(bill), 
                                           bill.getDueDate() );
    return "success";
  }

  public String scheduleRecurringPayment() {
    paymentHandler.processRecurringPayment(new Payment(bill), 
                                           bill.getDueDate(), ONE_MONTH );
    return "success";
  }
}
Both client and server can access the Timer object associated with the invocation. The Timer shown below is the EJB3 timer used with the EJB3 dispatcher. For the default ScheduledThreadPoolExecutor, the timer returns Future from the JDK. For the Quartz dispatcher, it returns QuartzTriggerHandle, which will be discussed in the next section.
@Local
public interface PaymentHandler
{
  @Asynchronous
  public Timer processScheduledPayment(Payment payment, 
                                       @Expiration Date date);
}
@Stateless
@Name("paymentHandler")
public class PaymentHandlerBean implements PaymentHandler {
  @In Timer timer;
    
  public Timer processScheduledPayment(Payment payment, 
                                       @Expiration Date date) {
    //do some work!    
    return timer; //note that return value is completely ignored
  }
}
@Stateful
@Name("paymentAction")
public class CreatePaymentAction
{
  @In(create=true) PaymentHandler paymentHandler;
  @In Bill bill;
    
  public String schedulePayment() {
    Timer timer = 
      paymentHandler.processScheduledPayment(new Payment(bill), 
                                             bill.getDueDate());
    return "success";
  }
}
Asynchronous methods cannot return any other value to the caller.

21.1.2. Asynchronous methods with the Quartz Dispatcher

The Quartz dispatcher lets you use the @Asynchronous, @Duration, @Expiration, and @IntervalDuration annotations, as above, but it also supports several additional annotations.
The @FinalExpiration annotation specifies an end date for a recurring task. Note that you can inject the QuartzTriggerHandle.
@In QuartzTriggerHandle timer;
        
// Defines the method in the "processor" component
@Asynchronous
public QuartzTriggerHandle schedulePayment(@Expiration Date when, 
                                           @IntervalDuration Long interval,
                                           @FinalExpiration Date endDate, 
                                           Payment payment) { 
  // do the repeating or long running task until endDate
}
    
... ...
    
// Schedule the task in the business logic processing code
// Starts now, repeats every hour, and ends on May 10th, 2010
Calendar cal = Calendar.getInstance ();
cal.set (2010, Calendar.MAY, 10);
processor.schedulePayment(new Date(), 60*60*1000, cal.getTime(), payment);
Note that this method returns the QuartzTriggerHandle object, which can be used to stop, pause, and resume the scheduler. The QuartzTriggerHandle object is serializable, so it can be saved into the database if required for an extended period of time.
QuartzTriggerHandle handle=
    processor.schedulePayment(payment.getPaymentDate(), 
                              payment.getPaymentCron(), 
                              payment);
payment.setQuartzTriggerHandle( handle );
// Save payment to DB
        
// later ...
        
// Retrieve payment from DB
// Cancel the remaining scheduled tasks
payment.getQuartzTriggerHandle().cancel();
The @IntervalCron annotation supports Unix cron job syntax for task scheduling. For example, the following asynchronous method runs at 2:10pm and at 2:44pm every Wednesday in the month of March.
// Define the method
@Asynchronous
public QuartzTriggerHandle schedulePayment(@Expiration Date when, 
                                           @IntervalCron String cron, 
                                           Payment payment) { 
  // do the repeating or long running task
}
    
... ...
    
// Schedule the task in the business logic processing code
QuartzTriggerHandle handle = 
     processor.schedulePayment(new Date(), "0 10,44 14 ? 3 WED", payment);
The @IntervalBusinessDay annotation supports invocation in the "nth Business Day" scenario. For instance, the following asynchronous method runs at 14:00 on the 2nd business day of each month. All weekends and US Federal holidays are excluded from the business days by default.
// Define the method
@Asynchronous
public QuartzTriggerHandle schedulePayment(@Expiration Date when, 
                                 @IntervalBusinessDay NthBusinessDay nth, 
                                 Payment payment) { 
  // do the repeating or long running task
}
    
... ...
    
// Schedule the task in the business logic processing code
QuartzTriggerHandle handle = 
    processor.schedulePayment(new Date(), 
                              new NthBusinessDay(2, "14:00", WEEKLY), 
                              payment);
The NthBusinessDay object contains the configuration of the invocation trigger. You can specify more holidays (company holidays and non-US holidays, for example) in the additionalHolidays property.
public class NthBusinessDay implements Serializable {
  int n;
  String fireAtTime;
  List<Date> additionalHolidays;
  BusinessDayIntervalType interval;
  boolean excludeWeekends;
  boolean excludeUsFederalHolidays;

  public enum BusinessDayIntervalType { WEEKLY, MONTHLY, YEARLY }

  public NthBusinessDay () {
    n = 1;
    fireAtTime = "12:00";
    additionalHolidays = new ArrayList<Date> ();
    interval = BusinessDayIntervalType.WEEKLY;
    excludeWeekends = true;
    excludeUsFederalHolidays = true;
  }     
      ... ...
}
The @IntervalDuration, @IntervalCron, and @IntervalNthBusinessDay annotations are mutually exclusive. Attempting to use them in the same method will cause a RuntimeException error.

21.1.3. Asynchronous events

Component-driven events can also be asynchronous. To raise an event for asynchronous processing, call the raiseAsynchronousEvent() method of the Events class. To schedule a timed event, call the raisedTimedEvent() method and pass a schedule object. (For the default dispatcher or timer service dispatcher, use TimerSchedule.) Components can observe asynchronous events as usual, but only business process context is propagated to the asynchronous thread.

21.1.4. Handling exceptions from asynchronous calls

Each asynchronous dispatcher behaves differently when an exception propagates through it. For example, the java.util.concurrent suspends further executions of a repeating call, and the EJB3 timer service swallows the exception, so Seam catches any exception that propagates from the asynchronous call before it reaches the dispatcher.
By default, any exception that propagates from an asynchronous execution will be caught and logged at error level. You can customize this behavior globally by overriding the org.jboss.seam.async.asynchronousExceptionHandler component:
@Scope(ScopeType.STATELESS)
@Name("org.jboss.seam.async.asynchronousExceptionHandler")
public class MyAsynchronousExceptionHandler 
             extends AsynchronousExceptionHandler { 
  @Logger Log log;
   
  @In Future timer;
   
  @Override
  public void handleException(Exception exception) {
    log.debug(exception);
    timer.cancel(false);
  }
   
}
Here, with java.util.concurrent dispatcher, we inject its control object and cancel all future invocations when an exception is encountered.
You can alter this behavior for an individual component by implementing the public void handleAsynchronousException(Exception exception); method on that component, like so:
   
public void handleAsynchronousException(Exception exception) { 
  log.fatal(exception); 
}