Jboss Java EE container and an ExecutorService
Asked Answered
E

5

29

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?

Enfeoff answered 18/12, 2012 at 11:35 Comment(0)
L
34

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);
    }
}
Limey answered 16/10, 2013 at 13:20 Comment(5)
That's the preferred way in EE 7, but what about EE 6?Groupie
There is no safe way to do this is Java EE6. I guess this is why it won the vote to be included in Java EE7.Limey
Thanks... Using the Managed Executor is definitely the way to go.Carpus
If you manage properly the initialization/shutdown of your ExecutorService, what is so “dangerous”? A ManagedExecutorService actually extends ExecutorService. The only issue I am aware of is that you need to shutdown the backing thread pool when the executor service is no longer needed, so that the threads do not continue running longer than warranted.Philip
@BasilBourque One of the main objectives of JSR 236 was to integrate Java SE threads with EE containers. The main shortcomings of SE threads in containers are: • Threads not controlled by Java EE containers • Thread context like class loader, security, naming are not propagated • Lack of manageability and transaction isolation semantics. See the specification for more details. jcp.org/aboutJava/communityprocess/final/jsr236/index.htmlLimey
S
29

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");
Silurian answered 19/12, 2012 at 4:33 Comment(8)
Slightly O/T: This is a very good pattern (we use it a lot too), however it falls short when your algorithm requires separate thread-pools to prevent deadlocks (see EJB Spec issue discussion about this as well)Meaningful
We use the same design in our application but off lately we are facing a lot of data overwrites in the database when we are running bulk actions in the background using the executors. Any ideas if that might be a common/know issue. If yes, then might there be a fix availableStationery
The correct way to do this is using a ManagedExecutorService, my answer belowLimey
Take care: an asynchronous method call on an EJB will be executed in the scope of the transaction of the caller (and will extend the lifetime of @RequestScope entities). If it is not what you need, you can explicitly disable transaction support on this EJB using @TransactionAttribute(value= TransactionAttributeType.NEVER) Hematuria
@Hematuria Which server did you encounter that in? Per spec the EJB asynchronous method should execute in its own transaction and security context.Silurian
Following docs.jboss.org/cdi/spec/1.0/html/contexts.html section 6.7.1, RequestScope is active in scope of an Asynchronous EJB call. I didn't look in the specs regarding transactions. I had the issue with Wildfly 9.Hematuria
When this is used with JBOSS AS 7, the default thread pool is taken. Any idea what is needed to let it use another (custom named and configured) thread pool?Evieevil
This example illustrates Runnable, what about Callable?Gaseous
L
5

Well... David's solution did not work for me for the following reasons:

  1. Compiler was bitching around that java.util.concurrent is not allowed... which kinda makes sense in JBOSS scope.
  2. 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!

Lepto answered 3/5, 2013 at 13:24 Comment(2)
I have just devised the most elegant solution to my testmethod: Just let the main test thread sleep for some time so that the other threads that are under the management of the app server have time to complete. OK... not so elegant, but working.Lepto
Yanked static -- code I copied from was a static inner class. Removed as the pasted code was intended to be a top level class.Silurian
G
3

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.

Groupie answered 28/11, 2013 at 9:18 Comment(0)
R
0

If you are using JBoss, you can use org.jboss.seam.async.ThreadPoolDispatcher.

ThreadPoolDispatcher is completely managed.

For others useful managed classes, see the package: org.jboss.seam.async.

Radman answered 27/4, 2017 at 15:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.