orporations encounter various scenarios in which they need parallel processing to achieve high throughput and better response times. For example, various financial institutions perform reconciliation as batch jobs at the end of each day. In these cases, a company may need to process millions of units of work to reconcile its portfolio. These units of work typically are processed in parallel. This article demonstrates how to accomplish parallel processing within a J2EE container.
The J2EE design revolves around the request/response paradigm. For a login request, a user typically provides a user name and password to the server and waits for a response to get access to the site. A J2EE container can serve multiple users at the same time (in parallel) by managing a pool of threads, but for various reasons opening independent threads within a single J2EE container is not recommended. Some containers’ security managers will not allow user programs to open threads. Moreover, if some containers allowed opening threads, then they would not manage those threads and therefore no container-managed services would be available for the threads. That would leave you to implement these services manually, which is tedious and liable to add complexity to your code.
Until recently, performing parallel processing directly within a managed environment (J2EE container) was not advisable. Thankfully, IBM and BEA came up with a joint specification that resolves this problem. This JSR is named “JSR-237: Work Manager for Application Servers“. JSR-237 specifies the Work Manager API, which provides abstraction from the lower-level APIs that enable an application to access a container-managed thread. Work Manager API also provides a mechanism to join various concurrent work items, so an application can programmatically add the dependency of work completion as a condition for starting other tasks. This can be useful for implementing workflow types of application. These features were difficult to implement prior to Work Manager.
This article discusses this specification and its design, and presents some code snippets for implementing a concurrent application for a managed environment. First, it discusses the design of the Work Manager API and it’s key interfaces.
Work Manager Design
The Work Manager API defines Work as a unit of work, which you want to execute asynchronously. Work is an abstraction of the lower-level java.lang.Runnable interface. Implementation of the Work interface requires you to define a
run() method and implement business logic for performing tasks/work in this method. The Work Manager API has six key interfaces for implementation:
Figure 1 shows a class diagram for these interfaces.
|Figure 1: Work Manager Class Diagram
J2EE container providers such as BEA and IBM must implement the WorkManager interface, and server administrators can create WorkManager by defining it in their J2EE container configurations. Most leading J2EE containers provide user interface (UI) tools for defining WorkManager. WorkManager encapsulates a pool of threads. By invoking the
schedule(Work work) method of the WorkManager interface, a client can schedule work for asynchronous execution. Behind the scenes, the Work Manager implementation gets a container-managed thread for executing the Work object. So work is executed in parallel with the current thread. A previously mentioned, the Work interface implements a run method of the java.lang.Runnable interface, so a thread can execute an object of type Work.
Work has states in its lifecycle, as illustrated in Figure 2.
|Figure 2: Work Lifecycle
A Work lifecycle starts after the schedule method of Work Manager is invocated. At any given time, Work can be in any one of the following states:
- Accepted: Work Manager has accepted work for further processing.
- Started: Work just started execution on a thread. You can assume this state just prior to entering the run method of Work.
- Completed: Work just completed execution. You can assume this state to be just after exiting from run method.
- Rejected: Work could be rejected at various stages. Work Manager can reject work in case it’s unable to process a request.
Work Manager provides a listener interface (WorkListener) that defines four callback methods associated with the Work lifecycle:
Work Manager invokes these methods at corresponding states of the Work lifecycle. The user can implement the WorkListener interface and pass implementation of WorkListener to Work Manager while scheduling work. Work Manager would invoke callback methods corresponding to each state.
Remote Processing of Work
A Work Manager implementation can execute work in a local JVM or transport work for execution by a remote JVM. To process work on a remote JVM implementation, the Work interface needs to implement the java.io.Serializable interface. A Work Manager implementation can check whether Work has implemented the Serializable interface, and then send it to a remote JVM for execution. Processing work in a remote JVM is implementation specific, and some implementations can execute work locally even if Work has implemented Serializable. Work Manager’s design encapsulates the complexities of sending work to remote JVMs from a client.
App server providers could implement a scalable Work Manager framework by implementing a work manager capable of executing work on remote JVMs.
Adding Dependency to Work Completion
Work Manager provides a simple mechanism for adding dependencies of work completion on to other tasks. Work Manager provides a simple API for the common join operation. The current specification provides two methods for accomplishing a simple join. Both take a collection of WorkItem objects to check the status of Work objects and a timeout parameter to timeout after a specified interval, even if the condition is not met:
waitForAll(): An application waits for all tasks to complete before moving on to further steps. This is a blocking call with configurable timeout value.
waitForAny(): An application waits for any one of the work items to complete before moving on to the next step.
Work Manager defines two constants that correspond to timeouts:
- WorManager.INDEFINITE: Wait indefinitely for any/all work to complete (Use this option with care, because it could lead your application thread to wait forever.)
- WorManager.IMMEDIATE: Check status of any/all work completion and return immediately
Configuring Work Manager for J2EE Container
J2EE container providers that support the Work Manager API normally provide a UI-based tool for defining Work Manager. WebLogic 9.0, for example, provides such a tool for configuring Work Manager (Figure 3 shows a WebLogic 9.0 administrative console showing a Work Manager configuration). The user needs to provide a work manager name and point Work Manager to a target server or cluster. As an example, I have created TestWorkManager.
|Figure 3: WebLogic Administrative Console Showing TestWorkManager
An implementation also can provide features that are not part of a specification. For example, WebLogic allows users to configure Work Manager with the following optional parameters:
- Minimum thread constraint: The minimum number of threads allocated to resolve deadlock
- Maximum thread constraint: The maximum number of threads that can be allocated to execute requests
- Capacity constraint: Total number of requests that can queue or execute before WebLogic starts rejecting requests
Implementation of Work Manager-Based Application
Take the following steps to implement an application for parallel processing with WebLogic 9.0:
- Implement the Work interface and define your business logic in the run() method. The current version of WebLogic 9.0 does not support execution of work in remote JVMs, but it may in the future. So it’s a good practice to implement java.io.Serializable as well for future enhancements to Work Manager.
- Implement the WorkListener interface for listening to work lifecycle events. Although not necessary, this step is a good practice.
- JNDI lookup is the primary way of accessing Work Manager. Servlets and EJB can access Work Manager via JNDI lookup within the local JVM. You can associate the Work Manager resource to an application by defining resource-ref in the appropriate deployment descriptor. For a servlet to access Work Manager, you define the resource-ref in web.xml. For EJB, you can define the resource-ref in ejb-jar.xml:
wm/TestWorkManager commonj.work.WorkManager Container Shareable
- Now, look at a code snippet that invokes Work Manager and schedules work for execution on a container-managed thread. The following snippet shows the code for using the Work Manager API:
//#1. Get Work Manager from local JNDIWorkManager workManager;InitialContext ctx = new InitialContext();this.workManager = (WorkManager)ctx.lookup("java:comp/env/wm/TestWorkManager");//#2. Create instance of Work and WorkListener implementationWork work = new WorkImpl("HelloWorld");WorkListener workListener=new WorkListenerImpl();//#3. Schedule work for execution, which would start in parallel to current threadWorkItem workItem = workManager.schedule(work, workListener);//#4. Create list of WorkItem you want to wait for completionList workItemList=new ArrayList();workItemList.add(workItem);//#5. Wait for completion of workthis.workManager.waitForAll(workItemList, WorkManager.INDEFINITE);//#6. You can get results from Work implementationWorkImpl workImpl= (WorkImpl)workItem.getResult();
In this implementation, you assume WorkImpl and WorkListenerImpl are implementations of the Work and WorkListener interfaces.
This article discussed a simple and powerful API for parallel execution of tasks within a managed environment. It explained the high-level design of the Work Manager API and explored WebLogic 9.0’s working implementation of the Work Manager API.
With the Work Manager API, developers can design robust applications for executing tasks in parallel, listen to work lifecycle events, and add the dependency of task completion to other tasks. Work Manager implementations also could be highly scalable if app server providers implemented a work manager capable of executing work on remote JVMs.