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.
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 objectspublic 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 itemspublic 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 adapterpublic 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 ResourceAdapter start 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 objectpublic 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.
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 notificationpublic 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 classpublic 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
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 classpublic 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 WorkManagerfinal 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 WorkManagerpublic 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 completionpublic 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.
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.
- Don't miss the other articles in this series, which cover optimizations and life-cycle management and message-inflow contract.
- You can download the J2EE Platform, EJB 2.1, J2EE Connector 1.5, and Java Message Service specifications from Sun's J2EE v1.4 Documentation page.
- Download the trial version of WebSphere Application Server to experiment with JCA 1.5 for yourself.
- Check out the growing list of J2EE 1.4 compatible application servers.
- The XA specification for distributed transaction processing defines the format of a transaction ID (XID) and is available from The Open Group.
- "Introduction to the J2EE Connector Architecture" (developerWorks, November 2002) is an excellent tutorial by Willy Farrell covering the original JCA specification on which version 1.5 builds.
- See the November 23, 2004 edition of Sun's Enterprise Java Technologies Tech Tips for a summary of JCA 1.5 enhancements.
- "Specifications: Service Data Objects, WorkManager, and Timers" (developerWorks, November 2003, updated August 2004) contains the source, Javadoc, and specifications for these APIs, which were produced by IBM in collaboration with BEA.
- Read "Introduction to Service Data Objects" (developerWorks, September 2004) for a foray into next-generation data programming with Service Data Objects (SDO).
- Check out "Understanding JCA transactions" (developerWorks, October 2004) to learn how the various levels of transaction support provided by different EISs and resource adapters can affect application design.
- "Developing applications with JCA-based tools" (developerWorks, January 2002) introduces the practical aspects of the J2EE Connector Architecture by using JCA-based tools from IBM to build, test, deploy, and run a J2EE EJB application. (Excerpted from J2EE Connector Architecture and Enterprise Application Integration by Rahul Sharma, Beth Stearns, and Tony Ng; Addison-Wesley, 2001.)
- "Build JCA-compliant resource adapters with WebSphere Studio Application Developer" (developerWorks, August 2003) shows you how to write your own JCA-compliant resource adapter.
- "Getting started with EJB technology" (developerWorks, April 2003) is a comprehensive tutorial that introduces the basics of EJB programming and the J2EE environment.
- Get involved in the developerWorks community by participating in developerWorks
blogs.
- You'll find articles about every aspect of Java programming in the developerWorks Java technology zone.
- Browse for books on these and other technical topics.
- See the Java technology zone tutorials page for a complete listing of free Java-focused tutorials from developerWorks.

David 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.





