Jboss Java EE container and an ExecutorService
I have a standalone java app which used the ExecutorService to process a number of jobs in parallel
ExecutorService es = Executors.newFixedThreadPool(10);
I now want to re-use the same solution within an EJB bean but am unsure how to correctly initialize the ThreadPool, since I'd normally leave the Java EE container to control all thread resources. Can I just use the same code or is there an alternative correct way to get a Jboss managed thread pool?
The correct way to do this in your EJB is to use the ManagedExecutorService, which is part of the Concurrency Utils API (Java EE7). You shouldn't be using any ExecutorService that is part of the java.util.concurrent in your enterprise code.
By using the ManagedExecutorService your new thread will be created and managed by the container.
The following example is taken from my site here.
To create a new thread using a ManagedExecutorService, first create a task object that implements Callable. Within the call() method we will define the work that we want carried out in a separate thread.
public class ReportTask implements Callable<Report> {
Logger logger = Logger.getLogger(getClass().getSimpleName());
public Report call() {
try {
Thread.sleep(3000);
catch (InterruptedException e) {
logger.log(Level.SEVERE, "Thread interrupted", e);
}
return new Report();
}
}
Then we need to invoke the task by passing it though to the submit() method of the ManagedExecutorService.
@Stateless
public class ReportBean {
@Resource
private ManagedExecutorService executorService;
public void runReports() {
ReportTask reportTask = new ReportTask();
Future<Report> future = executorService.submit(reportTask);
}
}
Obligatory warning: Creating your own threads in a Java EE app server (even Tomcat) is discouraged as it can be a huge performance issue and in most cases will prevent container functionality, such as JNDI, from working. The new threads won't know which application they belong to, the Thread context classloader will not be set and many other hidden issues.
Fortunately there is a way to get the Java EE server to manage the thread pool via the Java EE 6 @Asynchronous
and this clever design pattern. Portable to any Java EE 6 certified server.
Create this EJB in your application.
package org.superbiz;
import javax.ejb.Asynchronous;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
@Stateless(name="Executor")
public class ExecutorBean implements Executor {
@Asynchronous
@Override
public void execute(Runnable command) {
command.run();
}
}
Then you can refer to this bean elsewhere in your application via plain dependency injection (if the referring component is a Servlet, Listener, Filter, other EJB, JSF Managed bean).
@EJB
private Executor executor;
Then use the Executor
as normal.
If the component is not another Java EE component, you can lookup the bean via:
InitialContext initialContext = new InitialContext();
Executor executor = (Executor) initialContext.lookup("java:module/Executor");
Well... David's solution did not work for me for the following reasons:
- Compiler was bitching around that java.util.concurrent is not allowed... which kinda makes sense in JBOSS scope.
- Also: public STATIC class...? Read this up: Why are you not able to declare a class as static in Java?
Here's what I did:
My installation:
- JBOSS AS 7.1.1
- Java 1.6
- RHEL
- Running the example with Gradle and Arquillian:
@Stateless
public class ExecutorBean {
@Asynchronous
public void execute(Runnable command) {
command.run();
}
}
Then your client looks like this:
@EJB ExecutorBean eb;
@Test
public void testExecutorBean() {
eb.execute(new YourCustomizedRunnableWhichDoesALotOfUsefulStuff());
assertFalse(!true);
}
Beware, though: In my standalone.xml (or generally speaking my config file for JBOSS I have a section 'thread-pools'. Have a look at it (if you happen to use JBOSSAS) and tinker with the values there. Find out how it behaves. When I use threads with arquillian tests I get threads that are killed though my keepalive-time is very high. I think this has to do with how arquillian microdeploys. When arquillian finishes all non-finished threads are killed which were running whilst the tests are being run... at least that is what I think I observe. On the other hand all finished threads actually behaved well in that sense that they completed their tasks/operations.
Hope this post helps!
Prior to EE7, you might want to use WorkManager from JSR 237
http://docs.oracle.com/javaee/1.4/api/javax/resource/spi/work/WorkManager.html
This spec is currently withdrawn, still some app servers implement it. I use ibm implementation in WebSphere 8.5 - IBM WorkManager. It is fully managed resource, available in administration console. Please note that it is not interface-compatible with Oracle.
Here is an example for IBM version:
@Resource(lookup = "wm/default")
WorkManager workManager;
public void process() {
try {
ArrayList<WorkItem> workItems = new ArrayList<WorkItem>();
for (int i = 0; i < 100; i++) {
// submit 100 jobs
workItems.add(workManager.startWork(new Work() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " Running");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void release() {
System.out.println(Thread.currentThread().getName() + " Released");
}
}));
}
// wait for all jobs to be done.
workManager.join(workItems, WorkManager.JOIN_AND, 100000);
} catch (WorkException e) {
e.printStackTrace();
}
}
Also I am aware of Commonj Workmanager.