SeamFramework.orgCommunity Documentation

Seam Cron Module

Reference Guide


1. Introduction
1.1. Mission statement
1.2. Introductory Examples
1.2.1. Scheduling
1.2.2. Asynchronous Method Invocation
2. Configuration
2.1. Building Seam Cron From Source
2.1.1. Prerequisites:
2.1.2. Checkout and Build
2.2. Application Configuration
2.2.1. Maven Dependencies
2.2.2. Scheduling
2.2.3. Asynchronous Method Invocation

Seam Cron is a CDI portable extension for scheduled and asynchronous method invocation. It wraps common scheduling and backgrounding tasks in an intuitive, type-safe, event driven API.

Seam Cron uses standard CDI @Observer methods for both configuration and execution of scheduled methods.

public void generateDailyReport(@Observes @Scheduled("01:00") Trigger trigger) { ... }

The method above will be executed at 1:00 AM daily. For more detailed schedule definitions use a cron-style expression. In the following example, the method will execute at 1:00 AM on the 3rd Wednesday of every month.

public void deliverNewsletters(@Observes @Scheduled("0 0 1 ? * 4") Trigger trigger) { .. }

Alternatively, use any valid property name instead of a schedule definition, and then specify the definition for that name in the file at the root of your classpath. Here is an example.

# 0 1 ? * 4
public void deliverNewsletters(@Observes @Scheduled("") Trigger trigger) { ... }

To replace the @Scheduled annotation with something more type-safe create a custom qualifier with the @Scheduled annotation applied to it like so:

@Retention( RUNTIME )
@Target( { PARAMETER })
public @interface NewsletterDelivery {}

Now your scheduled observer methods are fully type-safe and higly readable:

public void deliverNewsletters(@Observes @NewsletterDelivery Trigger trigger) { ... }

If your requirements are fairly simple, for example running a task repeatedly at a specific Interval, then you can use the @Every qualifier as in the example below. Valid interval values are SECOND, MINUTE and HOUR. @Every also takes an optional "nth" parameter which defaults to 1.

public void clockChimes(@Observes @Every(HOUR) Trigger t) { 
    int chimes = t.getValue() % 12;
    if (chimes == 0) { chimes = 12; }
    for (int i=0; i<chimes; i++) {
public void workBreak(@Observes @Every(nth=30, value=MINUTE) Trigger t) { 

Seam Cron provides the @Asynchronous annotation which when applied to a method causes that method to be invoked asynchronously. When applied to a type, all methods in that type will be invoked asynchronously.

@Inject DocumentIndex index;

public DocumentIndexStats reindexDocuments() {
    return index.reindex();

To obtain a reference to the result once the invocation is finished simply observe the return type of the method as follows:

public void reportIndexStats(@Observes DocumentIndexStats stats) {"Index Updated: " + stats.toString());


The rules concerning return types of @Asynchronous methods are as follows:

You would typically want one dedicated return type per asynchronous method invocation for a one-to-one mapping between methods and their observers, but you may also have multiple asynchronous methods all reporting their results to a single observer. Alternatively you might wish to introduce some additional qualifiers. Below is an example covering these concepts.

@Asynchronous @Credit
public Balance addCredit(int dollars) {
    return new Ballance();

@Asynchronous @Debit
public Balance addDebit(int dollars) {
    return new Ballance();

public void reportNewBalance(@Observes Balance balance) {;

public void trackSpending(@Observes @Debit Balance balance) {

Finally, if you prefer a more traditional, EJB-esque approach then you can specify a return type of Future and use the AsyncResult helper to return the result of your method call. Seam Cron will automatically wrap this in a legit Future which the calling code can use as expected immediately.

public Future<Box> doSomeHeavyLiftingInTheBackground() {
    return new AsyncResult(new Box());

The calling code would look like this:

@Inject LiftingBean liftingBean;

public void someMethod() {
    Future<Box> future = liftingBean.doSomeHeavyLiftingInTheBackground();
    // blocks until asynch method returns or gives up
    Box result = future.get(10, SECONDS);