包含CDI的Servlet因ContextNotActiveException失败:WELD-001303:

时间:2015-06-03 13:52:42

标签: java tomcat servlets cdi weld

我正在编写一个在Tomcat上运行的CDI应用程序。我正在使用Tomcat 7.0.62 焊接2.2.12.Final作为CDI实施。我正在使用没有JSF的CDI。

该应用程序由一个未启用CDI的调度程序servlet组成。该 dispatcher包括启用CDI的servlet的输出以创建页面。

当调度程序servlet和CDI servlet都在同一个Web应用程序中时, 它工作正常。但是,我需要将CDI servlet放在不同的Web应用程序中,所以 我做了一个跨上下文包括。当我执行交叉上下文包括CDI servlet时 生成输出,直到它试图访问@RequestScoped bean。豆 访问失败,但出现以下异常:

org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
    at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:708)
    at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:90)
    at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:165)
    at org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63)
    at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:83)
...

我也试过为调度程序servlet激活CDI,但它没有 似乎有所作为。

在我看来,没有设置CDI servlet的请求上下文 适当的时候包括CDI servlet而不是接收请求 直。

我搜索过此网站并通过Google搜索,但未找到匹配项 问题方案。我发现了一个tomcat上下文设置&fireverquestListenersOnForwards =" true"' 我应用于调度程序servlet,但这没有什么区别。

这是配置问题吗?任何人都可以提供如何解决这个问题的线索吗?

我将非常感激!

背景资料:

有问题的实际应用程序很大,所以我把它压缩得很好 到了破碎的本质。结果,我有两个战争文件。第一个战争档案 包含CDI servlet和调度程序(在代码中我称之为包容器)servlet。第二个war文件只包含 调度员servlet。

CDI Servlet

CDI Servlet在其META_INF目录中有一个context.xml文件,其中包含以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Resource name="BeanManager" 
        auth="Container"
        type="javax.enterprise.inject.spi.BeanManager"
        factory="org.jboss.weld.resources.ManagerObjectFactory" />
</Context>

CDI servlet web.xml文件包含以下行:

<listener>
  <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener>

<resource-env-ref>
    <resource-env-ref-name>BeanManager</resource-env-ref-name>
    <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
</resource-env-ref>

CDI Servlet WEB-INF目录包含beans.xml文件。

CDI servlet通过使用获得的BeanManager来引导bean执行 通过JNDI查找(这是有效的):

BeanManager bm = null;
try {
    InitialContext context = new InitialContext();

    try {
       // "regular" naming
       bm = (BeanManager) context.lookup("java:comp/BeanManager");
    } catch(NameNotFoundException e) {
       // try again with Tomcat naming
       bm = (BeanManager) context.lookup("java:comp/env/BeanManager");
    }
} catch (Exception e) {}

if (bm == null) {
   writer.write("Couldn't look up the bean manager");
} else {
   Set<Bean<?>> beans = bm.getBeans(EnclosingBean.class);
   @SuppressWarnings("unchecked")
   Bean<EnclosingBean> bean = (Bean<EnclosingBean>) bm.resolve(beans);
   if (bean == null) {
      writer.write("Couldn't get the bean");
   } else {
      EnclosingBean eb = (EnclosingBean) bm.getReference(bean, bean.getBeanClass(), bm.createCreationalContext(bean));
      writer.write("finally here we are. Name is: ");
      writer.write(eb.getName());
   }
}

Includer(Dispatcher)Servlet

includer servlet在META-INF目录中有一个context.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<Context 
   path="/ExternalIncluderServlet" 
   docBase="ExternalIncluderServlet.war" 
   crossContext="true" 
   fireRequestListenersOnForwards="true">

    <Resource name="BeanManager" 
        auth="Container"
        type="javax.enterprise.inject.spi.BeanManager"
        factory="org.jboss.weld.resources.ManagerObjectFactory" />
</Context>

includer servlet查找CDI servlet的上下文并获取一个 RequestDispatcher如下(这可行):

ServletContext sc = request.getServletContext();
ServletContext extsc = sc.getContext("/SimpleCDIServlet");
if (extsc == null) {
   writer.println("<p>Couldn't get the external context.</p>");
} else {

   RequestDispatcher rd = extsc.getRequestDispatcher("/CDIServlet");
   if (rd == null) {
        writer.println("<p>RequestDispatcher is null.</p>");
   } else {
       writer.println("<p>Got the RequestDispatcher.</p>");
       rd.include(req, resp);
   }
}

结果:

当我使用浏览器直接通过URI访问CDI servlet时:localhost:8080 / SimpleCDIServlet / CDIServlet我得到了预期的输出:

Simple CDI Servlet
finally here we are. Name is: InjectedBean

如果我通过位于与CDI servlet相同的Web应用程序中的调度程序servlet访问CDI servlet,它也可以正常工作。 URI:/ SimpleCDIServlet / IncluderServlet,输出:

Simple CDI Servlet Including Servlet
Will now include the CDI servlet ...
Got the RequestDispatcher.
Simple CDI Servlet
finally here we are. Name is: InjectedBean

但是如果我从不同的上下文中包含CDI servlet,我不会在输出中获取注入的bean名称,并且上面提到的异常在日志中。 URI:/ ExternalIncluderServlet / IncluderServlet,输出:

CDI Servlet Includer

This servlet includes a CDI servlet in a different web app. It is not CDI enabled.

Got the RequestDispatcher.
Simple CDI Servlet
finally here we are. Name is: 

请注意,我无法使用请求调度程序转发而不是包含原始请求 应用程序包括来自其他几个servlet的输出,而不仅仅是一个。并开放 由于请求的数量,每个包含的新HTTP请求效率低下 将乘以包含的servlet的数量,这将是相当丑陋的 最重要的是。

更新 我在Tomee 1.7.2以及WebSphere Application Server v8.5上尝试过这个。结果总结如下。

                           WAS 8.5           Tomcat 7.0.62           Tomee 1.7.2
                           =======           =============           ===========
CDI servlet direct access   works               works                   works

Included by servlet         works               works                   works          
   in same web app

Included by servlet         works               broken                  broken 
   in different web app

我越想到这一点,我就越觉得它真的应该起作用。你应该能够 成功使用请求调度程序在Tomcat上包含来自启用CDI的servlet的输出。 我希望这里有人可以帮我弄清楚如何使它发挥作用。

1 个答案:

答案 0 :(得分:4)

WOW!在一位好朋友的帮助下,我明白了!

要使其工作,您需要配置一个特殊的Weld跨上下文过滤器。您可以通过将以下行添加到web.xml文件来配置筛选器。

<filter>
   <filter-name>WeldCrossContextFilter</filter-name>
   <filter-class>org.jboss.weld.servlet.WeldCrossContextFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>WeldCrossContextFilter</filter-name>
   <url-pattern>/*</url-pattern>
   <dispatcher>INCLUDE</dispatcher>
   <dispatcher>FORWARD</dispatcher>
   <dispatcher>ERROR</dispatcher>
</filter-mapping>

当我配置它时,CDI servlet的交叉上下文包含按预期工作。 我在Tomcat 7.0.42和8.0.23上尝试过,到目前为止,非常好。

另见:

http://javadox.com/org.jboss.weld/weld-core-impl/2.2.4.Final/org/jboss/weld/servlet/WeldCrossContextFilter.html

我在Weld官方文档中查找了对过滤器的引用,但无法使用 找到任何东西。无论如何,我希望这可以帮助别人。