Migrating applications that use concurrency and asynchronous programming models to Liberty
You can migrate applications that use Concurrency Utilities for Java™ EE, Asynchronous beans, and CommonJ Timer and Work Manager from WebSphere® Application Server traditional to WebSphere Application Server Liberty.
If you are migrating to Liberty 21.0.0.12 or earlier, the Asynchronous beans, CommonJ Timer, and Work Manager APIs are not available. You must update your applications to use the Concurrency Utilities for Java EE programming model instead. For more information, see Example API tasks that implement Concurrency Utilities for Java EE.
Differences in configuration and threading
WebSphere Application Server traditional provides a number of configuration options for Concurrency Utilities for Java EE related to thread pooling, which are configured differently or not available in Liberty. Liberty has a single common thread pool across all managed executors and managed scheduled executors that are also shared by Liberty components. This allows Liberty to optimize thread management. In WebSphere Application Server traditional, each work manager can be configured with two thread pools. One thread pool is for tasks that are submitted to run as soon as possible (submit/execute/invoke methods of managed executor) and has a configurable work request queue and action to take when the queue capacity is exceeded. The other thread pool is for scheduled tasks (schedule methods of managed scheduled executor).
If you plan to use Concurrency Utilities for Java EE with separate thread pools in Liberty, various approaches can be taken to achieve behavior similar to the configuration options that are available in WebSphere Application Server traditional.
Use concurrency policy configuration
Use the concurrencyPolicy
element to replicate some of the behavior from WebSphere Application Server traditional in Liberty. The following table lists the available
configuration options for the concurrencyPolicy
element and how this element
compares to WebSphere Application Server traditional. The
concurrencyPolicy
configuration does not apply to scheduled tasks.
Liberty managed executors
concurrencyPolicy |
WebSphere Application Server traditional work manager and managed executors |
---|---|
expedite | Minimum threads |
max | Maximum threads |
maxQueueSize | Work Request Queue Size |
maxWaitForEnqueue | Work Request Queue Full Action = WAIT |
runIfQueueFull = false | Work Request Queue Full Action = FAIL |
startTimeout | Start timeout |
longRunningPolicyRef + ManagedTask.LONGRUNNING_HINT | Daemon work |
Constructing a Java SE executor around a managed thread factory
The java.util.concurrent
package of Java
SE provides several ways to construct thread pools as executors and scheduled executors around a
specific thread factory. A managed thread factory can be supplied in place of an unmanaged thread
factory, producing a thread pool that runs tasks on managed threads. Unlike the managed executor and
managed scheduled executors, the managed threads that are pooled by these executors run with the
thread context from when the managed thread factory was looked up, rather than from when the task
was submitted or scheduled. Thread context also remains on the managed thread for the life of
thread, which reduces thread context switching overhead.
Example replacement for maximum work threads and work queue configuration
<managedExecutorService jndiName="concurrent/execSvc">
<concurrencyPolicy max="2" maxQueueSize="3" runIfQueueFull="false" maxWaitForEnqueue="0"/>
</managedExecutorService>
Example replacement for maximum alarm threads
int maxAlarms = 2;
ManagedThreadFactory threadFactory =
(ManagedThreadFactory) new InitialContext().lookup(
"java:comp/DefaultManagedThreadFactory");
ScheduledExecutorService executor =
Executors.newScheduledThreadPool(maxAlarms, threadFactory);
Callable<Integer> task = new MyTask();
ScheduledFuture<Integer> future = executor.schedule(
task, 50, TimeUnit.SECONDS);
int result = future.get();
Applying a start timeout
<managedExecutorService jndiName="concurrent/execSvc">
<concurrencyPolicy startTimeout="1m"/>
</managedExecutorService>
Applying a work timeout
Use WebSphere Application Server traditional to configure a work timeout that
applies to tasks that you submit to run as soon as possible (submit/execute/invoke methods). If a
task runs for longer than the allotted work timeout, the program tries to cancel the task by
interrupting it. Liberty does not have work
timeout as a configuration option, but you can implement a similar behavior by submitting the task
with a managed task listener. In response to the taskStarting
notification, the
managed task listener schedules a task to cancel the Future
method. Alternately,
when you use a thread pool executor where a managed task listener is not available, you can override
the thread pool executor beforeExecute
method to schedule a task that interrupts
the thread of execution.
Example replacement for work timeout
ManagedExecutorService executor =
(ManagedExecutorService) new InitialContext().lookup(
"java:comp/DefaultManagedExecutorService");
Callable<Long> slowTask = new SlowTask();
slowTask = ManagedExecutors.managedTask(
slowTask, new WorkTimeout(5, TimeUnit.SECONDS));
Future<Long> future = executor.submit(slowTask);
try {
long result = future.get(1, TimeUnit.MINUTES);
// task successful...
} catch (CancellationException x) {
// task was canceled, possibly due to timeout
}
public class WorkTimeout implements ManagedTaskListener {
private final long timeout;
private final TimeUnit unit;
public WorkTimeout(long timeout, TimeUnit unit) {
this.timeout = timeout;
this.unit = unit;
}
public void taskSubmitted(Future<?> future,
ManagedExecutorService executor, Object task) {}
public void taskStarting(final Future<?> future,
ManagedExecutorService executor, Object task) {
try {
ScheduledExecutorService scheduledExecutor =
(ScheduledExecutorService) new InitialContext().lookup(
"java:comp/DefaultManagedScheduledExecutorService");
scheduledExecutor.schedule(new Runnable() {
@Override
public void run() {
if (!future.isDone())
future.cancel(true);
}
}, timeout, unit);
} catch (NamingException x) {
x.printStackTrace(System.out);
}
}
public void taskAborted(Future<?> future, ManagedExecutorService executor, Object task, Throwable x) {}
public void taskDone(Future<?> future, ManagedExecutorService executor, Object task, Throwable x) {}
}
Example of daemon work
Configuration:
<concurrencyPolicy id="longRunning" max="2"/>
<managedExecutorService jndiName="concurrent/execSvc" longRunningPolicyRef="longRunning" />
Application code:
ManagedExecutorService executor =
(ManagedExecutorService) new InitialContext().lookup(
"concurrent/execSvc");
Map<String, String> props = Collections.singletonMap(ManagedTask.LONGRUNNING_HINT,
Boolean.TRUE.toString());
Callable<Long> slowTask = ManagedExecutors.managedTask(new SlowTask(), props, null);
Future<Long> future = executor.submit(slowTask);