Search code examples
javamultithreadingservletscdifuturetask

Inject Context (CDI/servlet) into new FutureTask Thread


I found that a new thread created in the servlet donot contain the servlet/CDI context. I created a HelloWorld servlet (given below) to experiment with this problem. In the below example, you'll see that I am running 'doIt()' function in a new Thread (FutureTask). But it returns NULL but when I call 'doIt()' method directly, BeanManager is not NULL.

/**
 * Servlet implementation class HelloWorld
 */
@WebServlet("/HelloWorld")
public class HelloWorld extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private static Logger logger = Logger.getLogger(HelloWorld.class
            .getName());

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();

        writer.println("<html>");
        writer.println("<head><title>Hello World Servlet</title></head>");
        writer.println("<body>");
        writer.println("<h1>Context injection into Thread Experiment</h1>");
        try {
            // 1. This is NOT working
            new Thread(testTask).start();
            testTask.get(5000, TimeUnit.SECONDS);

            // 2. This is working
                //doIt();

        } catch (Exception e) {
            e.printStackTrace();
        }

        writer.println("<body>");
        writer.println("</html>");
        writer.close();         
    }

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                doIt();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    FutureTask<Object> testTask = new FutureTask<Object>(runnable, null);

    protected void doIt() throws Exception {
        if(getBeanManager() == null) { 
            throw new Exception( "BEAN MANAGER IS NULL"); 
        }
    }

    public static BeanManager getBeanManager() {
        try {
            InitialContext initialContext = new InitialContext();
            return (BeanManager) initialContext.lookup("java:comp/BeanManager");
        } catch (NamingException e) {
            logger.info("Couldn't get BeanManager through JNDI");
            return null;
        }
    }
}

I searched online but could not find a good reference than saying it is possible. It would be great, if anybody can help me or provide me with some good pointers to inject/pass context into the new thread.


Solution

  • If you are using EJB 3.1 and Java EE 6 or greater, the notation @Asynchronous on a method makes it run in a separate thread and takes care of managing resources, etc.

    If you need more control, as pointed out by Kalpesh Soni, this answer describes how to use ManagedExecutorService, if you have Java EE 7 or greater.

    Additionally, I'm not sure if you really need that getBeanManager() static method - wouldn't just @EJB BeanManager beanManager; (or @Inject) suffice?