2

我发现在 servlet 中创建的新线程不包含 servlet/CDI 上下文。我创建了一个 HelloWorld servlet(如下所示)来试验这个问题。在下面的示例中,您将看到我在一个新线程 (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;
        }
    }
}

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

4

3 回答 3

6

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

如果您需要更多控制,正如 Kalpesh Soni 所指出的,如果您有 Java EE 7 或更高版本,此答案描述了如何使用。ManagedExecutorService

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

于 2014-08-19T16:06:33.997 回答
1

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

线程应该创建自己的资源

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

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

Java EE 规范和多线程

于 2014-08-19T15:13:53.487 回答
0

我想为我在这个问题中提出的问题添加一个解决方案。借助此处给出的答案解决了这个问题。我认为,这个答案将来会帮助其他开发人员。我的主要目的是让函数超时或

我使用@Asyncronous on 方法在单独的线程中运行,该线程还包含 CDI 上下文。通过使用.get(timeout, TimeUnit),我为运行“未来”任务的任务设置超时参数。

以下是 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 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);
    }
}

输出将如下所示:

New Thread with Context
Result1: OK Async!!
Timeout Exception: JBAS014334: Task did not complete in 2 SECONDS
于 2014-08-30T19:37:59.820 回答