Asynchronous tasks in JSF applications

This is an example on how to implement concurrency in JSF applications using the Executor framework.

We used, in our example below, java.util.concurrent.ThreadPoolExecutor as a static instance that is created and destroyed from the init and destroy events listened to by javax.servlet.ServletContextListener of our application. ThreadPoolExecutor execute threads from a queue of Runnable or java.util.concurrent.Callable using an unbounded java.util.concurrent.LinkedBlockingQueue that holds the tasks submitted by clients for execution.

Here is the syntax of java.util.concurrent.ThreadPoolExecutor. Please see the Javadoc for more.

public ThreadPoolExecutor(int corePoolSize,
                  int maximumPoolSize,
                  long keepAliveTime,
                  TimeUnit unit,
                  BlockingQueue<Runnable> workQueue)

Our example

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class UpdateListener implements ServletContextListener {

    private static ExecutorService executor;

    @Override
    public void contextInitialized(ServletContextEvent cs) {
         createExecutor();
         cs.getServletContext().log("Executor service started !");
    }

    @Override
    public void contextDestroyed(ServletContextEvent cs) {

     executor.shutdown();

    cs.getServletContext().log("Executor service shutdown !");
    }

    public static synchronized void submitTask(Runnable runnable) {
        if (executor == null) {
            createExecutor();
        }
        executor.submit(runnable);
    }

    public static synchronized Future<String> submitTask(Callable callable) {
        if (executor == null) {
            createExecutor();
        }
        return executor.submit(callable);
    }

    static void  createExecutor() {
        executor = new ThreadPoolExecutor(
                1,
                3,
                100L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());

    }
}

For our ServletContextListener to listen to init and destroy (line 15 and line 21), our class must be registered in the deployment descriptor (web.xml) of the application.

<listener>
    <listener-class>info.kahimyang.TaskExecutorListener</listener-class>
</listener>

If you are using EE6, it can also be annotated with WebListener instead of updating your web.xml.

@WebListener
public class TaskExecutorListener implements ServletContextListener {
	// our ServletListener class above
}

Client code

Using our class takes implementing Runnable or Callable interface. The inlined code fragment that follows should accomplish submiting a task for execution by our Executor from any of your class or beans. Note that our thread pool is independent of any JSF session.

TaskExecutorListener.submitTask (new Runnable () {
    @Override
    public void run() {
	// the actual job
    }

});

The example above, does not return any value or status of the task. Using Callable enables client code to return a value from the task. See below.

Future future = TaskExecutorListener.submitTask (new Callable() {
    @Override
    public String call () {
        String result;
	// the actual task
	return result;
    }

});

try {
	String status = future.get ();  // blocks
}
catch (Exception e) {}

For more other options about using the Java Executor framework please see java.util.Concurrent package.

That's it. Good luck.

If you like the article, please share.
(Site URL pattern has changed as a result social actions counter was reset.)



Comment icon   Comments (Newest first)