2

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.

iankits
  • 2,414
  • 5
  • 30
  • 43
  • Yes, this looks awkward but I need this for my own logic and project requirement. This is just an example. That is why I am interested in knowing whether I can do this way or not. Similar to me: http://stackoverflow.com/questions/21284191/cdi-multithreading – iankits Aug 19 '14 at 14:55

3 Answers3

6

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?

Community
  • 1
  • 1
Ken Y-N
  • 12,690
  • 21
  • 62
  • 98
1

the container must be cleaning up the servlet object after the request is over

The thread should create its own resources

by the way, if you use container (tomcat/weblogic/websphere) it does some extra things to set up transactional context, which is NOT available to your own threads

its best not to mix thread api with java ee

Java EE specification and multi threading

Community
  • 1
  • 1
Kalpesh Soni
  • 5,577
  • 2
  • 44
  • 51
0

I would like to add a solution to the problem I asked in this question. The question was solved with the help of answers given here. I think, this answer would help other developers in future. My main aim was to get a timeout of a function or

I used @Asyncronous on method to run in separate thread which also contains the CDI context. By using .get(timeout, TimeUnit), I set timeout parameter for the task which runs 'Future' task.

Following is the code of the Helloworld servlet which runs the task and gets the timeout if it runs more than the expected time to finish the work.

@WebServlet(value = "/HelloWorld", asyncSupported = true)
public class HelloWorld extends HttpServlet {
    private static final long serialVersionUID = 1L;

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

    @Inject doItWithAsync doAsync;
    @Inject doItWithAsync doAsyncTimeout;

    /**
     * @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>New Thread with Context Experiment</h1>");
        try {
            String ret1 = doAsync.doItAsync().get(10, TimeUnit.SECONDS);
            writer.println("<h2>Result1: "+ ret1+"</h2>");
            String ret2 = doAsyncTimeout.doItAsync().get(2, TimeUnit.SECONDS);
            writer.println("<h2>Result2: "+ ret2+"</h2>");
        } catch (TimeoutException e) {
            writer.println("<h2>Timeout Exception: "+ e.getMessage()+"</h2>");
            e.printStackTrace();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        writer.println("<body>");
        writer.println("</html>");
        writer.close();         
    }

    public static BeanManager getBeanManagerFromInitialContext() {
        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;
        }
    }
}

The separate function or task which should be running in separate thread for timeout check is implemented in different class as @stateless session beans.

@Stateless
public class doItWithAsync {

    @Asynchronous
    public Future<String> doItAsync() throws Exception {
        Thread.sleep(5000);
        String result;
        if(HelloWorld.getBeanManagerFromInitialContext() == null) { 
            throw new Exception( "BEAN MANAGER IS NULL !"); 
        } else {
            result = "OK Async!!";
        }

        return new AsyncResult<String>(result);
    }
}

The output would look like this:

New Thread with Context
Result1: OK Async!!
Timeout Exception: JBAS014334: Task did not complete in 2 SECONDS
iankits
  • 2,414
  • 5
  • 30
  • 43