包含的启用 CDI 的 Servlet 失败并出现 ContextNotActiveException:WELD-001303:
Included CDI-enabled Servlet Fails with ContextNotActiveException: WELD-001303:
我正在编写一个在 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 上下文设置 'fireRequestListenersOnForwards="true"'
我将其应用于调度程序 servlet,但这并没有什么不同。
这是配置问题吗?谁能提供有关如何解决此问题的线索?
我将不胜感激!
背景资料:
有问题的实际应用程序很大,所以我将其压缩为
到破碎的本质。结果,我有两个 war 文件。第一个war个文件
包含 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(调度程序)Servlet
包含器 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 位于同一网络应用程序中的调度程序 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:
请注意,我不能使用 ward 的请求调度程序来代替原始的 include
应用程序包括来自其他几个 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 的输出。
我希望这里有人可以帮助我弄清楚如何让它工作。
哇!在好朋友的帮助下,搞定了!
要实现此功能,您需要配置一个特殊的 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 上试用过,到目前为止,一切顺利。
另请参阅:
我在官方 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 上下文设置 'fireRequestListenersOnForwards="true"' 我将其应用于调度程序 servlet,但这并没有什么不同。
这是配置问题吗?谁能提供有关如何解决此问题的线索?
我将不胜感激!
背景资料:
有问题的实际应用程序很大,所以我将其压缩为 到破碎的本质。结果,我有两个 war 文件。第一个war个文件 包含 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(调度程序)Servlet
包含器 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 位于同一网络应用程序中的调度程序 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:
请注意,我不能使用 ward 的请求调度程序来代替原始的 include 应用程序包括来自其他几个 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 的输出。 我希望这里有人可以帮助我弄清楚如何让它工作。
哇!在好朋友的帮助下,搞定了!
要实现此功能,您需要配置一个特殊的 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 上试用过,到目前为止,一切顺利。
另请参阅:
我在官方 Weld 文档中查找了对过滤器的参考,但找不到 找到任何东西。无论如何,我希望这可能对某人有所帮助。