将Context(CDI / servlet)注入新的FutureTask Thread

时间:2014-08-19 14:18:41

标签: java multithreading servlets cdi futuretask

我发现在servlet donot中创建的新线程包含servlet / CDI上下文。我创建了一个HelloWorld servlet(如下所示)来试验这个问题。在下面的示例中,您将看到我在新的Thread(FutureTask)中运行'doIt()'函数。但是它返回NULL但是当我直接调用'doIt()'方法时,BeanManager不是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;
        }
    }
}

我在网上搜索但找不到一个好的参考,而不是说它是可能的。如果有人可以帮助我或者为我提供一些好的指针来注入/传递上下文到新线程中,那就太棒了。

3 个答案:

答案 0 :(得分:5)

如果您使用的是EJB 3.1和Java EE 6或更高版本,方法上的符号@Asynchronous会使其在单独的线程中运行并负责管理资源等。

如果你有Java EE 7或更高版本,如需要更多控制,如Kalpesh Soni所指出,this answer describes如何使用ManagedExecutorService

此外,我不确定您是否真的需要getBeanManager()静态方法 - 不仅仅@EJB BeanManager beanManager;(或@Inject)就足够了吗?

答案 1 :(得分:1)

请求结束后,容器必须清理servlet对象

线程应该创建自己的资源

顺便说一下,如果你使用容器(tomcat / weblogic / websphere),它会做一些额外的事情来设置事务上下文,这对你自己的线程是不可用的

最好不要将线程api与java ee混合

Java EE specification and multi threading

答案 2 :(得分:0)

我想为我在这个问题中提出的问题添加一个解决方案。这个问题在这里给出的答案的帮助下解决了。我想,这个答案将来会有助于其他开发者。我的主要目的是获得一个函数的超时或

我在方法上使用 @Asyncronous 在单独的线程中运行,该线程也包含CDI上下文。通过使用.get(timeout, TimeUnit),我为运行&#39; Future &#39;的任务设置了超时参数。任务。

以下是运行任务的Helloworld servlet的代码,如果运行超过预期的完成工作时间,则会获得超时。

@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;
        }
    }
}

应该在单独的线程中运行以进行超时检查的单独函数或任务在不同的类中实现为@stateless会话bean。

@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);
    }
}

输出如下所示:

New Thread with Context
Result1: OK Async!!
Timeout Exception: JBAS014334: Task did not complete in 2 SECONDS