我正在编写一个在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的输出。 我希望这里有人可以帮我弄清楚如何使它发挥作用。
答案 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上尝试过,到目前为止,非常好。
另见:
我在Weld官方文档中查找了对过滤器的引用,但无法使用 找到任何东西。无论如何,我希望这可以帮助别人。