JCA 1.5, Part 2: Work management and transaction inflow

Executing work synchronously, asynchronously, periodically, and transactionally

David Currie continues his three-part series on enhancements and changes in the latest version of the Java™ 2 Enterprise Edition (J2EE) Connector Architecture (JCA). In this article, he introduces the new JCA 1.5 work-management contract, which lets a resource adapter utilize an application server's capabilities for scheduling and processing work. With the help of another JCA enhancement -- transaction-inflow support -- an enterprise information system can perform that work under its own transaction.

David Currie (david_currie@uk.ibm.com), Staff Software Engineer, IBM UK Ltd

Photo of David CurrieDavid Currie recently joined the IBM Software Services for WebSphere organization based in Hursley, UK. Prior to that, he worked on the development of WebSphere Application Server, initially in the area of transactions and, most recently, designing and implementing JCA resource adapters for the messaging support. You can reach David at david_currie@uk.ibm.com.


developerWorks Contributing author
        level

04 May 2005

Also available in Russian Japanese

JCA 1.5, the latest version of the J2EE Connector Architecture, includes many important enhancements and some major additions. This article, the second in a three-part series covering these changes, builds on the lifecycle-management features introduced in Part 1 by taking a look at JCA's new work-management contract. This contract lets a resource adapter create timers for delayed or periodic execution of work and lets it perform processing -- either synchronously or asynchronously -- using application-server threads. This article describes how transaction-inflow support lets this processing take place in a transaction that the resource adapter imports into the server, and how the resource adapter can then control the transaction's completion.

This article is essential reading if you want to make use of this functionality in an existing resource adapter or are considering writing a new JCA 1.5 resource adapter. It might also interest you if you write applications that use resource adapters and want to know more about what goes on behind the scenes.

Getting work done

Part 1 in this series introduced the ResourceAdapter interface as a mechanism for giving a resource adapter a lifecycle of its own within the application server. You might remember that the start method was passed an object called the BootstrapContext. We glossed over this object at the time, but the three methods on the BootstrapContext interface, shown in Listing 1, are the key to the work-management and transaction-inflow contracts:

Listing 1. The BootstrapContext interface used to obtain three utility objects
public interface BootstrapContext {

    WorkManager getWorkManager();
    XATerminator getXATerminator();
    Timer createTimer() throws UnavailableException;

}

The WorkManager lets a resource adapter schedule work for synchronous or asynchronous execution on an application-server thread. This work can be performed in a transaction the resource adapter imports, in which case the XATerminator lets the work be completed. A Timer provides for delayed or periodic execution of work. This article will look at each of these classes in more detail and describe how you can use them.

The WorkManager interface provides three sets of methods (doWork, startWork, and scheduleWork) for processing work, as Listing 2 shows:

Listing 2. The WorkManager interface for submitting work items
public interface WorkManager {
    
    void doWork(Work work) throws WorkException;
    void doWork(Work work, long startTimeout, ExecutionContext execContext,
            WorkListener workListener) throws WorkException;

    long startWork(Work work) throws WorkException;
    long startWork(Work work, long startTimeout, ExecutionContext execContext,
            WorkListener workListener) throws WorkException;

    void scheduleWork(Work work) throws WorkException;
    void scheduleWork(Work work, long startTimeout,
            ExecutionContext execContext, WorkListener workListener)
            throws WorkException;
    
}

Each of these methods takes as its first parameter an instance of an object implementing the Work interface, shown in Listing 3:

Listing 3. The Work interface implemented by the resource adapter
public interface Work extends Runnable {

    void release();
    
}

The Work interface extends Runnable and you should implement the work to be performed in the run method, as you do when you program with Java threads directly. You'll see where the release method comes into play in a moment.

The doWork methods on WorkManager let a piece of work be executed synchronously, blocking until that work has completed. This might not seem like a particularly useful thing to do -- isn't it exactly what would happen if you invoked the run method directly? Well, not quite. First, it lets the application server say that now is not a good time to do the work. For example, if you call doWork during the scope of a ResourceAdapterstart method, then you might find that it throws a WorkRejectedException to indicate that you should be returning from this method as quickly as possible, scheduling work for asynchronous processing if necessary.

Second, if the application server is particularly busy, it might defer the start of this piece of work. You can use the second startTimeout parameter to specify how long the resource adapter is prepared to wait for the work to start. If the application server fails to start the work within this time then, again, a WorkRejectedException is thrown. The WorkManager interface defines the constants IMMEDIATE and INDEFINITE to let the resource adapter indicate that it is not prepared to wait at all or that it is prepared to wait forever.

Third, as the next section explains, it is possible to have the piece of work executed in the context of a transaction imported by the resource adapter, rather than the context associated with the current thread. This is the purpose of the third parameter -- an optional ExecutionContext.

Finally, using the doWork method gives the application server more control over the work that a resource adapter performs. If the resource adapter is in the middle of a long, complicated operation when the application server is shutting down, then the server doesn't need to wait for the resource adapter to finish or pull the rug out from under its feet. Instead, the server can signal to the resource adapter by calling the release method on the Work object. The resource adapter should then complete processing as quickly as possible. Listing 4 shows an example of how the release method might be used:

Listing 4. An example Work object
public class ExampleWork implements Work {

    private volatile boolean _released;

    void run() {
        for (int i = 0; i < 100; i++) {
            if (_released) break;
            // Do something
        }
    }

    void release() {
        _released = true;
    }
    
}

Note the use of the volatile keyword in Listing 4. The release method will be called on a separate thread while the run method is processing. The volatile modifier ensures that the run method sees the updated field.

The doWork method can also fail with the rather oddly named WorkCompletedException. This is thrown when the run method is allocated a thread, but either the setup of context failed or the method exited by throwing a runtime exception. An error code is provided to indicate which of these failure paths occurred, and the cause of the problem is provided as a linked exception.


Why wait?

You've seen how the doWork methods let you perform work while the calling thread is blocked. But what if you don't want to wait -- that is, what if you don't want to perform the work synchronously? In a Java 2 Platform, Standard Edition (J2SE) environment, you'd use multithreading to achieve this objective. However, the J2EE specification lets an application server use the Java 2 security manager to prevent applications spinning off their own threads. This is reasonable, given that part of the application server's function is to provide concurrency through pools of EJBs and servlets. The server might also want to associate various forms of context with a thread that would be lost when a new thread is spun off. Finally, as I discussed earlier, threads out of the server's control make it difficult to achieve an orderly shutdown.

Although you can often override this restriction on a case-by-case basis through the use of security policies, the startWork and scheduleWork methods on WorkManager let a resource adapter process work asynchronously while still keeping the application server in control. The startWork method waits until the piece of work has started to execute but not until it completes. This method can therefore be used by a caller that needs to know that the work will be performed but does not need to wait until it has finished. In contrast, the scheduleWork method returns as soon as the work has been accepted for processing. In this case, there are no guarantees that work will actually be performed.

It is with these asynchronous methods that the fourth parameter on the WorkManager methods in Listing 2 -- a WorkListener -- is at its most useful. Listing 5 shows this interface:

Listing 5. The WorkListener interface for receiving event notification
public interface WorkListener {

    void workAccepted(WorkEvent e);
    void workRejected(WorkEvent e);
    void workStarted(WorkEvent e);
    void workCompleted(WorkEvent e);
    
}

The resource adapter can optionally pass a listener that will be notified as the item of work passes through the states of accepted, started, and completed or, in the failure case, rejected. This listener could be used to provide notification back to the originator of a piece of work once it has completed, or to reschedule an item of work when it fails. A WorkAdapter class is also included that provides default implementations of all the methods so that a subclass need only override those it's interested in.

Each of the methods on the WorkListener takes a WorkEvent object, shown in Listing 6, as a parameter:

Listing 6. Additional methods on the WorkEvent class
public class WorkEvent extends EventObject {

    ...

    public int getType() { ... }
    public Work getWork() { ... }
    public long getStartDuration() { ... }
    public WorkException getException() { ... }
 
}

In addition to the usual event methods, the WorkEvent class provides accessors for the type of event (accepted, rejected, started, or completed) and the item of work in question. This lets you use a single (multithreaded) listener for multiple work submissions. There are also methods to return the time that it took to start processing the work and, in the case of workRejected or workCompleted, to return any corresponding WorkRejectedException or WorkCompletedException that might have occurred.

Figure 1 shows the states through which a Work object passes.

Figure 1. State diagram for a Work object
State diagram for Work object

Along the bottom of Figure 1 are the three types of submission methods, with the vertical dashed lines indicating the point in the lifecycle at which that particular method will return.


Why do now what you can put off until tomorrow?

The doWork, startWork, and scheduleWork methods all submit work to the WorkManager immediately. A delay might occur before WorkManager accepts the submission, but the caller can control only the maximum delay (using the start timeout). What if you want the work to be processed later rather than right now? The scheduleWork method only allows work to be scheduled on another thread, not scheduled for some future point in time.

This is where the third method on the BootstrapContext interface comes to the rescue. The createTimer method lets a resource adapter obtain an instance of the java.util.Timer class. This class, shown in Listing 7, has been a part of the standard Java libraries since version 1.3:

Listing 7. Methods on the Timer class
public class Timer  {
 
    public void schedule(TimerTask task, long delay) { ... }
    public void schedule(TimerTask task, Date time) { ... }

    public void schedule(TimerTask task, Date firstTime, long period) { ... }
    public void schedule(TimerTask task, long delay, long period) { ... }
    public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) { ... }
    public void scheduleAtFixedRate(TimerTask task, long delay, long period) { ... }

    public void cancel() { ... }
 
}

You can use the first two methods on the java.util.Timer class to schedule a task to occur once after a specified delay or at a specified date and time. The remaining four schedule methods have an additional period parameter. You can use them to schedule events that should occur at regular intervals after the initial run, with the period specifying the interval. The schedule and scheduleAtFixedRate methods are different because the timings of these operations are not guaranteed; operations such as garbage collection might cause a task to be executed late. If a task is delayed, then the schedule methods still wait the full period before running the next task. However, the scheduleAtFixedRate methods run the next task the fixed period after the previous task should have run. So, if the time between tasks is important to you, then use schedule. If absolute or cumulative time is important, then use scheduleAtFixedRate.

Each of the schedule methods takes an object extending the TimerTask class as its first parameter. As with the Work interface, this class extends Runnable, and Timer invokes the run method at the appropriate times. The TimerTask class has a cancel method that you can use to cancel subsequent invocations of the task. Alternatively, you can call the cancel method on Timer to cancel all currently scheduled tasks. A scheduledExecutionTime method lets a run method compare the actual current time with the time at which it should have been called.

Although the TimerTask's run method is invoked on a new thread, this thread has been allocated by the JVM and is out of the application server's control. If you intend the resource adapter to do any serious processing, then it should use the WorkManager at this point to switch to an application-server thread. Listing 8 shows an example of using this good practice to schedule a piece of work for execution every minute from the current time onwards:

Listing 8. Combining the use of Timer and WorkManager
final WorkManager workManager = context.getWorkManager();
final Timer timer = context.createTimer();

timer.scheduleAtFixedRate(new TimerTask() {
    public void run() {
        workManager.scheduleWork(new ExampleWork());
    }
}, 0, 60 * 1000);

Alternatives to JCA WorkManager and Timer

As the preceding section mentioned, there's an issue with the use of Timer in JCA: The thread on which the TimerTask is executed is not under the application server's control. Because Timer is a class rather than an interface and the threads are actually spun off in that class's constructors, an application server cannot modify this behavior. An alternative option that does permit the intervention of the application server is to use the interfaces from the Timer and Work Manager for Application Servers specification (see Resources) developed jointly by IBM and BEA.

When you use this specification's interfaces, an implementation of TimerManager is obtained from the Java Naming and Directory Interface (JNDI) namespace. An implementation of TimerListener is scheduled with the manager in return for a Timer (a commonj.timers.Timer rather than a java.util.Timer). The TimerListener is invoked at the scheduled times, and you can use the Timer to perform operations such as canceling the scheduled operations.

As its name suggests, this specification also provides an alternative to JCA work management. The interfaces here are very similar to those for JCA except that the initial commmonj.work.WorkManager is obtained from JNDI. Additional behavior this contract provides includes the ability to block until one or all pieces of scheduled work have completed and the possibility of performing serializable pieces of work on a remote JVM. As with the commonj Timer, the use of the commonj WorkManager isn't restricted to resource adapters. Any server-side J2EE component can use this functionality.

Listing 9 shows how the example from Listing 8 looks when it's rewritten to use the commonj classes:

Listing 9. Using the commonj interfaces
final InitialContext context = new InitialContext();
final WorkManager workManager = (WorkManager) 
        context.lookup("java:comp/env/wm/MyWorkManager");
final TimerManager timerManager = (TimerManager)
	context.lookup("java:comp/env/timer/MyTimer");

timerManager.scheduleAtFixedRate(new TimerListener() {
    public void timerExpired(final Timer timer) {
        try {
            workManager.schedule(new ExampleCommonjWork());
        } catch (final WorkException exception) {
        }
    }
}, 0, 60 * 1000);

The ExampleCommonjWork class is identical to the ExampleWork class from Listing 4, except that it implements the additional isDaemon method that the commonj Work interface requires. This method should return true if the work is long-lived.


Importing transactions and completing them

Prior to JCA 1.5, the application server always acted as the transaction coordinator. The application server was responsible for starting the transaction and, after each resource manager had enlisted its XAResource via the JCA connection manager, for coordinating the completion of the transaction by committing or rolling back all of the resources together. JCA 1.5's transaction-inflow contract lets an enterprise information system (EIS) act as the transaction coordinator by starting and completing the transaction. The EIS can then import the transaction into an application server via a resource adapter and perform work in the server under that transaction. For example, it might invoke a message-driven bean (MDB) with a container manager transaction attribute of Supports. The work that the MDB method does would then be part of that transaction, including calls to other EJBs.

The resource adapter imports the transaction via that third ExecutionContext parameter that you saw passed to the WorkManager in Listing 2. As you can see in Listing 10, this class has methods for setting a transaction ID (XID) and a transaction timeout:

Listing 10. The ExecutionContext class passed to the WorkManager
public class ExecutionContext {

    public ExecutionContext() { ... }
    public void setXid(Xid xid) { ... }
    public Xid getXid() { ... }
    public void setTransactionTimeout(long timeout)
        throws NotSupportedException { ... }
    public long getTransactionTimeout() { ... }    
    
}

An XID uniquely identifies the transaction and is made up of three parts: a format identifier, a transaction qualifier, and a branch qualifier. If the EIS doesn't already have an XID for the transaction, then one must be constructed in accordance with the XA specification (see Resources). The application server then associates this transaction with the thread of execution before calling the work object's run method.

Having imported the transaction into the application server, the resource adapter is then responsible for notifying the server of events pertaining to that transaction. In particular, it must notify the application server of transaction completion. It does this via the XATerminator interface shown in Listing 11, an implementation of which can be obtained from the BootstrapContext:

Listing 11. The XATerminator class used for transaction completion
public interface XATerminator {

    void commit(Xid xid, boolean onePhase) throws XAException;
    void forget(Xid xid) throws XAException;
    int prepare(Xid xid) throws XAException;
    Xid[] recover(int flag) throws XAException;
    void rollback(Xid xid) throws XAException;
    
}

The methods on the XATerminator interface match those on an XAResource, only in this case the resource adapter invokes the application server. Typically, if more than one resource is involved in the transaction, the resource adapter will call prepare on the XATerminator, passing the same Xid that was passed in the ExecutionContext. Provided all the resources return XA_OK (or XA_RDONLY), the resource adapter will proceed to call commit; otherwise, it will call rollback.


Conclusion

This article examined how you can use the WorkManager interface to schedule work for processing on a thread under the application server's control, either synchronously or asynchronously. You've seen how to use Timer instances to execute work in the future and periodically and learned the commonj alternatives to using the objects provided by JCA. You've seen how the resource adapter can execute work in a transaction it has imported into the application server and then use the XATerminator interface to control the transaction's completion. In the third and final article of this series, I'll cover the JCA 1.5 message-inflow contract, better known as message-driven bean support.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=82454
ArticleTitle=JCA 1.5, Part 2: Work management and transaction inflow
publish-date=05042005