Presentation about the newly released JSR236 spec that Anthony Lai (Oracle) and Fred Rowe (IBM) did for session CON7948 at JavaOne SF 2013.
JSR 236 is part of EE7 platform and defines extensions to the SE concurrency APIs to allow them to be used in an app server environment.
Today we will start with an overview of what JSR 236 Concurrency Utilities for Java EE is about. What its goals are and what features does it add to the Java EE platform.Then we will walk through the different types of managed objects that are defined in the spec. Then we will go through some of the feature areas that may be of interest to the developers.
First of all, lets go through the goals of Concurrency Utilities as listed in the spec…
Lets briefly go through the history of JSR 236. The JSR has a long history.It started way back in 2003 as 2 JSRs - JSR 236 and 237 were filed in 2003. JSR 236 defines the CommonJ Timers API for app servers while JSR 237 defines the CommonJ Work Manager API for application servers. Some of you may have used the CommonJ API before as it is supported by a few application servers.Then in 2006, after the introduction of the Concurrency Utilities API in Java SE 5, the API were rewritten to align with the API in the concurrent package in Java SE.In 2008, JSR 237 was officially withdrawn and the two JSRs were merged into JSR 236.However, no real progress were made in the next few years on the JSR. The JSR was left in an inactive state until last year when the JSR was restarted and work was resumed on the JSR. The spec was finally released this year as part of Java EE 7.
What are the main features provided by the Concurrency Utilities for Java EE? On a high level, it provides- A Simple, standardized API for using concurrency from application components- Provides API for Java EE application developers that is extension of Java SE Concurrency Utilities APIs (JSR-166)- Provide low-level asynchronous processing capabilities to Java EE application components in a safe, reliable, consistent manner- Provides API for Managing and monitoring of the lifecycle of asynchronous operations
More specifically, concurrency Utilities for Java EE defines 4 types of managed objects that implement the following 4 new interfaces:ManagedExecutorServiceManagedScheduledExecutorServiceManagedThreadFactory, andContextServiceThese interfaces are found in the javax.enterprise.concurrent package.The spec also defines how container context propagation and transaction management should behave for asynchronous tasks using the API. It also provides a mechanism to allow Java EE application components to receive notifications to keep track of the progress of asynchronous tasks.
A managed object is an object provided by Java EE product providers that implements one of the 4 new interfaces. The managed objects are in the package javax.enterprise.concurrent.They are for use by application components. Application code can get reference to them using JNDI lookup or by using resource injection.Configurable – each application server would provide some tools, such as GUI tools or command line interface, for configuration of these managed objects.There can be multiple configured managed objects of each type, each with different configuration properties.Configuration properties not defined by the spec. It is up to each Java EE container to decide what those properties are. However, the spec requires that application server must provide a preconfigured default instance of a managed object for each of the 4 types. They can be looked up using some standard JNDI names. For example, “java:comp/DefaultManagedThreadFactory” etc.A default instance for the specified resource type is injected if “lookup” is not specified in @Resource
ManagedThreadFactory provides a standard mechanism for Java EE applications to obtain a managed thread from the Java EE container. Most Java EE container discourage applications from creating plain java Threads. Those threads are not maintained by the Java EE container and may interfere with the proper operations of the container. Also, application context are not propagated to these threads. We will talk more about context propagation in a couple slides.Again, instead of reinventing the wheel, we simply extend from the ThreadFactory interface from the Java SE concurrent package, inheriting the same API for obtaining a thread. The ManagedThreadFactory is useful for obtaining threads to work with asynchronous servlets that was added to the Servlets 3.0 spec in Java EE 6. it is also useful in advanced use cases such as when the Java EE application needs to create custom executors with its own pool of threads obtained from the container.
The ManagableThread interface provides a means for the application to check whether a thread created by the thread factory is in the process of shutting down.For example, a ManagedThreadFactory may be used with the utility method Executors.newCachedThreadPool from SE API
ManagedExecutorService.isShutdown() throws IllegalStateException ??There is a utility method on the class ManagedExecutors.isCurrentThreadShutdown() to check whether the ManageableThread.isShutdown()
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)In this example, the default instance of ManagedThreadFactory will be injected and is then used to create a ThreadPoolExecutor with the desired configuration.
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)In this example, the default instance of ManagedThreadFactory will be injected and is then used to create a new thread on which to run the work of the async servlet. That work will run with the context of the servlet.
Tasks executed by the ManagedExecutorService are run in container managed threads with the context of the application that submitted the task.
It contains various forms of the execute, submit, invokeAll, and invokeAny APIs. Execute is used for submitting asynchronous tasks .Submit() methods are similar to execute(), except they return a handle, called Future, where the code can use to check for the execution status of the task, to cancel the task, or to wait for and retrieve the result of the task. InvokeAll and invokeAny methods allow submission of a collection of tasks, and provide a way to wait for and retrieve the results of either all tasks, or only the first task that completes.All of the APIs are inherited from the ExecutorService interface in the Java SE concurrent package. In fact, we did not introduce any new methods in ManagedExecutorService. This allows application developers to use APIs from Java SE concurrency package that they are already familiar with in their Java EE applications. The lifecycle of a ManagedExecutorService is managed by an application server. All lifecycle operations (like shutdown()) on the ManagedExecutorService interface will throw a java.lang.IllegalStateException exception.
In this example, the default instance of ManagedExecutorService will be injected and is then used to submit a task which will execute with the context of the application. The Future returned by submit() may be used to determine the status of the task and to access the results.
In this example, the default instance of ManagedExecutorService will be injected and is then used to submit an async servlet task which will execute with the context of the application. The Future returned by submit() may be used to determine the status of the task and to access the results.
A ManagedExecutorService instance is intended to be used by multiple components and applications. When the executor runs a task, the context of the thread is changed to match the component instance that submitted the task. The context is then restored when the task is complete.In the figure, a single ManagedExecutorService instance is used to run tasks (in blue) from multiple application components (each denoted in a different color). Each task, when submitted to the ManagedExecutorService automatically retains the context of the submitting component and it becomes a Contextual Task. When the ManagedExecutorService runs the task, the task would be run in the context of the submitting component (as noted by different colored boxes in the figure).ManagedExecutorService instances may be terminated or suspended by the application server when applications or components are stopped or the application server itself is shutting down.
The next interface that is provided by the managed objects is ManagedScheduledExecutorService.It is used for scheduling tasks to run after a given delay, to run periodically, or at some custom schedule that is controlled programmatically. Similar to ManagedExecutorService, these tasks are also run on threads that are provided by the Java EE container.
The ManagedScheduledExecutorService implements both ManagedExecutorService and ScheduledExecutorService APIs.
The first and second schedule() methods provide a means to execute a task once after a fixed delay.The scheduleAtFixedFate() method provides a means to schedule a task to run periodically either at specified period or at a specified delay after completion of the task
These schedule() methods provides a means to execute a task with a custom schedule as specified by the Trigger.
Instances of Triggers are not contextualized by default, but may be contextualized by using the ContextService.
skipRun: Return true if this run instance should be skipped.This is useful if the task shouldn't run because it is late or if the task is paused or suspended.Once this task is skipped, the state of it's Future's result will throw a SkippedException. Unchecked exceptions will be wrapped in a SkippedException.-------------------------getNextRunTime: Retrieve the next time that the task should run after.
This is an example of a trigger which executes a task at a specific time.The getNextRunTime() method calculates whether the specified time for the execution has already passed, and if so, will return null indicating that, otherwsie, it will return the specifc time at which to execute the task.The skipRun() method similarly simply calculates whether it is already past the time at which the task was scheduled to run. The LastExecution will either be null indicating the task hasn’t run or can be used to retrieve either the results or other info about the task.
Can be used to create dynamic proxy objects (as defined by java.lang.reflect.Proxy)
The only execution properties required by the spec are the IDENTITY_NAME, LONGRUNNING_HINT and TRANSACTION
More details on context propagation. It is important for tasks and methods from contextual proxy objects to be run under the same context supplied for the application by the container which is the same as that in the application where they are submitted from or created.The types of container context listed in the spec includes classloading, JNDI namespace, and security identity.Note that transactional context is not one of them.Configurable – Each MO can be configured to propagate different types of container context by an administrator. Extensible - The types of container context do not need to be restricted to the 3 types listed in this slides. The concurrency EE spec allows Java EE containers to support propagation of additional context types. However, the 3 types of container context listed here are required to be supported and propagated in the default managed objects provided by all Java EE containers.
When does context propagation happen?When using ManagedExecutorService or ManagedScheduledExecutorService to submit a task to be run asynchronously on a container managed thread, the Java EE container would capture such context information at the time of task submission, and reapply the context information on the container managed thread that is used for running the task just before the task is run. The result is that the task, even though it is most likely being run on a thread that is different from the thread where it is submitted from, would be able to load classes from the application that submitted the task, to perform JNDI lookup as if it is running in the same application as the one that submit the task, and to run under the same user identity that was logged in to the application when the task was submitted.In the case of ManagedThreadFactory, the container context would be captured when its newThread method is called to retrieve a managed thread. When the managed thread is started, the captured context would be applied to the thread before calling the run() method of the Runnable task that is passed into the newThread method.For ContextService, the container context are captured when a contextual proxy object is created, ie, when the createContextualProxy API is called. Later, when the proxy method is invoked, the captured context is imposed onto the thread even if the method is invoked on a thread that has a different container context. The context of that application component will be restored after the proxy method is completed.
The next feature that we would like to talk about is how transaction management is supported in the managed objects.When a task is run on a different thread from the one that submitted the task, as in the case in ManagedExecutorService, and ManagedSchduledExecutorService, or when a thread is obtained from ManagedThreadFactory, transaction is not propagated from the thread where the task is submitted or where the thread is obtained, to the managed thread where the task is run. However, transaction demarcation is still possible from within the task with the use of the UserTransaction interface from the Java Transaction API specification, or JTA. Java EE container are required to provide UserTransaction to the tasks so a task implementation can begin, commit, or rollback a transaction using the UserTransaction if it wanted to.For contextual proxy objects, the spec provides two choices for the application developers regarding transaction management. One option is to run the proxy method under the same transaction as the thread where the proxy method is invoked from. For example, if the proxy method is invoked from an application component with a transaction already started, any resources used by the contextual proxy object method will be enlisted into the same transaction, and will be committed or rolled back together along with the resources enlisted by the application component. (note: A UserTransaction will only be available if it is also available in the execution thread (for example, when the proxy method is invoked from a Servlet or Bean Managed Transaction EJB).Another option to run the contextual proxy object method under a different transaction. In this case, the transaction of the application component will be suspended, and the proxy method can use UserTransaction for transaction demarcation, and any work done within the proxy method will be committed or rolled back before the method returns. At the end of the method call, the transaction of the invoking application component will be restored. The choice of option can be specified programmatically when the contextual proxy object is created.
Spec supports notifications of lifecycle events for tasks submitted to MES or MSES with the use of MTL interfaceTask lifecycle events:- SubmittedAborted such as unable to start, perhaps disallowed by application server due to server resource constraint policy, or perhaps the application component that submitted the task has endedOr Cancelled – cancelled by applicationFor logging and monitoring of tasks progressRemedial actions – for example, a task that tries to retrieve some data from a remote service has timed out and failed. Upon getting a notification that the task has failed, the application can resubmit the task or perhaps send an email to the administrator about the failure.
Unspecified context by default for performance reasons - we don’t want application servers to incur the cost of setting up container context before calling the listener methods if it is not needed by the methods.
Note that each of the methods are passed with A Future object that was returned when the task was submitted. This can be used for checking the execution status of the task, whether it has been cancelled or completed. The Future can also be used for cancelling the taskAn executor which the task was submitted to. This can be used for resubmitting the task if necessaryThe original task that was submitted. Again this can be useful when the an application wants to resubmit the taskIn the taskAborted and taskDone methods, an exception object is also passed in to the methods to provide details about what has caused the task to be aborted or what caused the task to fail. For example, an instance of CancellationException would be passed if the task was cancelled by the application.
Examples showing how to associate a ManagedTaskListener with a Runnable task.One way is to have the task implement an interface called ManagedTask, in addition to implementing Runnable or Callable.The spec also provide a helper API to return a task that associates a ManagedTaskListener to a given task. Then you would submit the task that is returned by that helper method to the executor. This is useful for example, when the task implementation comes from a different library than the ManagedTaskListener.
Any task submitted to MES or MSES can optionally implement the ManagedTask interface.It allows applications to provide identifying information about a task, to provide an instance of ManagedTaskListener to get notification of lifecycle events of the task as we just discussed, or to provide additional execution properties.
Contains 2 APIs. One for returning the ManagedTaskListener, the other for returning the execution properties associated with the task.Execution properties allow application to provide additional information or hints to the application server about the task that is being submitted.Standard execution properties defined in the spec are located in the ManagedTask interface, which includes:LONGRUNNING_HINT – A hint telling whether the task could take a long time to complete. Java™ EE Containers may make use of this hint value to decide how to allocate thread resource for running this task.IDENTITY_NAME – provides a name or ID for the task so that the task can be easily identifiable through any management or monitoring utilities provided by the application serverTRANSACTION – tells the Java™ EE Container under which transaction should the proxy methods of a contextual proxy object be executed in, as we have discussed a few slides earlier.
isCurrentThreadShutdown() - Utility method for checking the isShutdown() value of the current thread if it is a ManageableThread created from ManagedThreadFactory.newThread() .
There are only 5 APIs in this utility class.First one for detecting whether the current thread is marked for shutdownThe remaining 4 are for associating a given Runnable or Callable with a ManangedTaskListener and execution properties. The first 2 of these methods are for use when the original task is a Runnable, one of which only associates a ManagedTaskListener to the Runnable whereas the other one also associates a Map containing execution properties to the Runnable task.The other 2 methods are similar but for use when the original task is a Callable.
Container context – propagation of context supplied for the application by the container so that tasks or proxy methods will be run under the proper container context.
JIRA issues from project website.Errata can be found thereNew features request welcome for next release – go to project website and file jira issue.Emphasize feedback email