JSR 236, the concurrency utilities API, is a new edition to the Java EE 7 platform that provides asynchronous processing to applications using container-managed threads. In Java EE standards prior to 7, it was expressly forbidden or undefined for service endpoints, like servlets or EJBs, to spawn their own threads, because those Java threads were outside the control of the container. Although it was not portable, many applications broke this rule and found ways for the application to manage threads for the lifecycle of its deployment, sometimes using vendor specific APIs.
In Java EE 7, concurrency utilities provide managed versions of the Java SE concurrency facilities. The new concurrency utilities facilities live inside the Java package javax.enterprise.concurrent
. The following table describes the interfaces:
In Chapter 1, Java EE 7 HTML 5 Productivity, we briefly showed some code for ManagedExecutorService
. It was injected into the component with the following code:
@Resource(name="concurrent/LongRunningTasksExecutor") ManagedExecutorService executor;
The application server does not automatically provision the
ManagedExecutorService
instances. Unfortunately, they must be configured in an XML deployment descriptor. Here is a web.xml
file that illustrates the additional configuration:
<?xml version="1.0" encoding="UTF-8"?> <web-app ...> <display-name>Xen Tracker</display-name> <!-- ... --> <resource-env-ref> <description> A Executor for RESTful operations. </description> <resource-env-ref-name> concurrent/LongRunningTasksExecutor </resource-env-ref-name> <resource-env-ref-type> javax.enterprise.concurrent.ManagedExecutorService </resource-env-ref-type> </resource-env-ref> </web-app>
A similar configuration is required for the other two types:
ManagedThreadFactory
and ManagerScheduledExectorService
. All Java EE 7 products are required to supply a default ManagedThreadFactory
under the JNDI lookup name java:comp/DefaultManagedThreadFactory
. The resource-env-ref-name
reference specifies the reference name of the environment resource and the resource-env-ref-type
reference specifies the Java data type of the resource.
The application server is responsible for transferring a context, an application container context, to the non-container thread of context. This allows the application thread to interact with the application server in a responsible way. However, the container must manage the application thread from its internal thread pool: it is the most important stipulation. In particular, the application server does the following:
Saves the existing context of the current application thread
Identifies the correct application container context
Applies the identified application container context to the application thread and then allows the task to proceed
After the task completes it restores the previous context to the thread
The application server will apply an application container context to the following tasks: java.lang.Runnable
and java.util.concurrent.Callable
. This procedure is known as applying contextual services to tasks.
The concurrency utilities specification describes optional contextual support for the following classes: ManagedTaskListener
and Trigger
.
A contextual task is essentially the bundling of a concurrent task (runnable and callable) with the application context container. A task becomes a contextual task whenever it is submitted to a managed execution service or thread factory.
In order to access additional information, a contextual task can optionally implement the Java interface
javax.enterprise.concurrency.ManagedTask
. The task can invoke getExecutionProperties()
, which returns a map of properties. The execution properties permit the task to set an identity with the property name IDENTITY_NAME
.
The task can communicate hints back to the container. For example, property LONGRUNNING_HINT
(or javax.enterprise.concurrent.LONGRUNNING_HINT
can be set to true
or false
, which informs the container that this task is going to be long running or not. The other execution property TRANSACTION
may be set to SUSPEND
or USE_TRANSACTION_OF_EXECUTION_THREAD
in order to communicate a hint about the transaction context in the contextual task.
In 2005 whilst working for an investment banking client in the city of London, I developed an asynchronous task with a proprietary J2EE asynchronous API. It was part of the CommonJ IBM/BEA specification. The project was to read client valuation data from an external system by FTP, parse these valuation files into domain object, and store them inside the bank's client trading portal. Given that we now have Java EE 7 technology, how would I have refactored my 2005 code into modern use?
Here is my initial stab at the code:
public class ClientValuationsProcessTask implements Callable<ClientValuation>, ManagedTask { Map<String,String> properties = new HashMap<>(); TaskListener listener = new TaskListener(); public ClientValuationProcessTask( String fileId) { properties = new HashMap<>(); properties.put(ManagedTask.IDENTITY_NAME, "ClientValuationsProcessTask_"+fileId); properties.put(ManagedTask.LONGRUNNING_HINT,"true"); } public ClientValuation call() { // Parse the XML file referenced from id // Create a value object ClientValuation valuation = foo(); return valuation; } public Map<String, String> getExecutionProperties() { return properties; } public ManagedTaskListener getManagedTaskListener() { return listener; } class TaskListener implements ManagedTaskListener { ... } }
Because I can work with Java SE 7 now rather than J2EE 1.4, the first refactoring is to use Callable
to return the domain object, ClientValuation
. This allows another contextual task to reclaim the domain object in reduce
operation (or fold) the results. I could have several ClientValuationProcessTask
instances all running concurrently in a map
operation. Furthermore, I can hint to the application server that, actually, my ClientValuationProcessTask
is going to take a long time, since this task will parse a XML file and create some value object, by supplying LONGRUNNING_HINT
key setting the value to true
.
ManagedTask
must also implement the getManagedTaskListener()
call, which can return a null
or an instance ManagedTaskListener
. The
ManagedTaskListener
allows the task to listen to life cycle events about the associated contextual task.
Here is the exposition of the TaskListener
inner class:
class TaskListener implements ManagedTaskListener { public void taskSubmitted( Future<?> future, ManagedExecutorService executor, Object task) { /* ... */ } public void taskAborted(Future<?> future,ManagedExecutorService executor, Object task,Throwable exception) { /* ... */ } public void taskDone(Future<?> future, ManagedExecutorService executor, Object task,Throwable exception) { /* ... */ } public void taskStarting(Future<?> future, ManagedExecutorService executor, Object task) { /* ... */ } }
The ManagedTaskListener
provides a way to take some action when the contextual task is aborted. For example, the ManagedExecutorService
is about to shut down when the Future
is cancelled from the user side.
ContextService
is a way for a Java EE application to create contextual tasks without using the standard ManagedExecutor
facilities. This is an advanced facility for developers and designers who know concurrency programming very well. Teams who provide libraries in enterprise integration and workflow management and open source frameworks should get involved here and take advantage of porting their products over to standard Java EE 7.
Here is some sample code of a singleton EJB that encapsulates ExecutorService
with ContextService
:
@Singleton ClientValuationTaskManager { @Resource(name="concurrent/ThreadFactory") ThreadFactory threadFactory; @Resource(name="concurrent/ContextService") ContextService contextService; ExecutorService executor; @PostConstruct public void setup() { executor = Executors.newThreadPool(10, threadFactory); } public Future submitTask( ClientValuationProcessTask callable ) { Runnable proxyRunnber = contextService.createContextualProxy(callable, Callable.class); Future future = executor.submit(proxyRunner); return future; } /* ... */ }
This completes the short discussion on concurrency utilities.