我们需要将旧的Web应用程序升级为在GlassFish 3而不是Tomcat下运行才能获得EAR部署(Glassfish被选中,因为它是参考JEE 6实现)
不幸的是,很快发现确保用户登录的机制无法正常工作并抱怨已经调用了getWriter()(这很可能是正确的)并且我无法弄清楚原因。
方法是我们在完整的JSP文件集上有一个过滤器,用于检查用户是否已登录,如果没有,则使用filterChain.doFilter(servletRequest, servletResponse);
重定向到登录页面。用户状态(包括凭证)存储在会话范围内的所谓控制器对象中,该对象范围是从登录验证java代码设置的。
来自Glassfish的堆栈跟踪:
java.lang.IllegalStateException: PWC3990: getWriter() has already been called for this response
at org.apache.catalina.connector.Response.getOutputStream(Response.java:676)
at org.apache.catalina.connector.ResponseFacade.getOutputStream(ResponseFacade.java:205)
at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:176)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
at com.XXX.LoggedInToXXXFilter.doFilter(LoggedInToXXXFilter.java:61)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:277)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
....
web.xml片段
<?xml version="1.0"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<description>
XXX provides a web interface for a given user.
</description>
<display-name>
XXX
</display-name>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/online-faces-config.xml</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.ALLOW_JAVASCRIPT</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<listener-class>
org.apache.myfaces.webapp.StartupServletContextListener
</listener-class>
</listener>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>
javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<session-config>
<!-- idle time in minutes before user is automatically logged out by the container -->
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>MyFacesExtensionsFilter</filter-name>
<filter-class>
org.apache.myfaces.webapp.filter.ExtensionsFilter
</filter-class>
<init-param>
<param-name>maxFileSize</param-name>
<param-value>1m</param-value>
<!-- description>Set the size limit for uploaded files.
Format: 10 - 10 bytes
10k - 10 KB
10m - 10 MB
1g - 1 GB
</description-->
</init-param>
</filter>
<!-- extension mapping for adding <script/>, <link/>, and other resource tags to JSF-pages -->
<filter-mapping>
<filter-name>MyFacesExtensionsFilter</filter-name>
<!-- servlet-name must match the name of your javax.faces.webapp.FacesServlet entry -->
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
<!-- extension mapping for serving page-independent resources (javascript, stylesheets, images, etc.) -->
<filter-mapping>
<filter-name>MyFacesExtensionsFilter</filter-name>
<url-pattern>/faces/myFacesExtensionResource/*</url-pattern>
</filter-mapping>
<filter>
<description>Ensure user is logged in</description>
<filter-name>LoggedInToXXXFilter</filter-name>
<filter-class>
com.XXX.servlet.filters.LoggedInToXXXFilter
</filter-class>
<init-param>
<param-name>signon_page</param-name>
<param-value>/login.jsf</param-value>
</init-param>
<init-param>
<param-name>autologout_page</param-name>
<param-value>/autologout.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LoggedInToXXXFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- filter>
<filter-name>extensionsFilter</filter-name>
<filter-class>org.apache.myfaces.component.html.util.ExtensionsFilter</filter-class>
<init-param>
<param-name>uploadMaxFileSize</param-name>
<param-value>100m</param-value>
</init-param>
<init-param>
<param-name>uploadThresholdSize</param-name>
<param-value>100k</param-value>
</init-param>
</filter-->
<!-- filter-mapping>
<filter-name>extensionsFilter</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>extensionsFilter</filter-name>
<url-pattern>/faces/*</url-pattern>
</filter-mapping-->
<!-- error-page>
<exception-type>java.lang.IllegalArgumentException</exception-type>
<location>/WEB-INF/jsp/IllegalArgumentException.jsp</location>
</error-page-->
<error-page>
<exception-type>java.lang.RuntimeException</exception-type>
<location>/WEB-INF/jsp/RuntimeException.jsp</location>
</error-page>
<!-- error-page>
<exception-type>com.transaxiom.axsWHSweb.struts.action.UserIsNotLoggedInException</exception-type>
<location>/WEB-INF/jsp/UserIsNotLoggedInException.jsp</location>
</error-page-->
<error-page>
<exception-type>
com.XXX.struts.action.SecurityViolationException
</exception-type>
<location>/WEB-INF/jsp/SecurityViolationException.jsp</location>
</error-page>
<error-page>
<exception-type>
com.XXX.logic.UncheckedCommunicationException
</exception-type>
<location>/WEB-INF/jsp/CommunicationException.jsp</location>
</error-page>
<error-page>
<exception-type>
com.XXX.logic.ConnectionNotCreatedException
</exception-type>
<location>
/WEB-INF/jsp/ConnectionNotCreatedException.jsp
</location>
</error-page>
<!-- error-page>
<exception-type>com.XXX.logic.UncheckedConnectionNotCreatedException</exception-type>
<location>/WEB-INF/jsp/ConnectionNotCreatedException.jsp</location>
</error-page-->
<!-- filter>
<filter-name>MyFacesExtensionsFilter</filter-name>
<filter-class>org.apache.myfaces.component.html.util.ExtensionsFilter</filter-class>
<init-param>
<param-name>maxFileSize</param-name>
<param-value>20m</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MyFacesExtensionsFilter</filter-name>
<url-pattern>*.faces</url-pattern>
</filter-mapping-->
</web-app>
从LoggedInToXXXFilter.java过滤代码:
(堆栈跟踪发生在filterChain.doFilter(servletRequest, servletResponse)
行。
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain filterChain) throws IOException, ServletException {
boolean ok = false;
if (servletRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String servletPath = request.getServletPath();
if ((servletPath.equals(signOnPage) == true) || servletPath.endsWith(".css") || servletPath.equals(autologoutPage)) {
ok = true;
} else {
Controller controller = Controller.getControllerFromSession(request.getSession(false));
if ((controller != null) && controller.isSignedOn()) {
ok = true;
}
}
if (ok) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
// Hop to the sign on page.
// http://forum.java.sun.com/thread.jspa?threadID=548967&messageID=2676856
ServletContext servletContext = filterConfig.getServletContext();
URL url = new URL(new URL(request.getRequestURL().toString()), (request.getContextPath() + signOnPage));
((HttpServletResponse) servletResponse).sendRedirect(url.toString());
}
} else {
// Only for http requests
filterChain.doFilter(servletRequest, servletResponse);
}
}
可能的原因是我们仍然带着我们自己的JSF库(带有Tomahawk的MyFaces 1.1.4)?
编辑:使用完整(但匿名)的web.xml更新了问题。请注意,有很多注释掉的东西。我把它留在了不小心删除重要信息
编辑:使用sun-web-app配置文件进行实验,发现它没有什么区别。有趣的是,在登录后,登录页面抛出了异常,但我可以手动导航到主页面(也是JSF)并看到另外两个功能正常的页面。除了登录页面之外还有三个页面会引发异常。
我最初的想法是分离特征是t-taglib(对于Tomahawk),但经过快速调查似乎并非如此,因为一些工作页面使用Tomahawk而有些则没有。
编辑:比较两个jsp页面,一个失败,另一个没有显示任何明显的差异,这应该导致这一点。正如有人指出的那样,据报道Tomahawk 1.1的这个错误,我们使用的是1.1.3,我现在升级到了最新的Apache Myfaces Tomahawk 1.1.9,这似乎解决了这个问题(没有太阳 - 网络应用程序。
答案 0 :(得分:5)
这可能有两个原因:
Filter
之前的链中有另一个ExtensionsFilter
,它(间接)调用getWriter()
。在这种特殊情况下,看起来像 sendRedirect()
和doFilter()
已在同一请求 - 响应链中被调用(因为sendRedirect()
可能会隐式调用getWriter()
)。当Filter
拨打sendRedirect()
时,不之后应该doFilter()
。发布的代码不能证明这一点,但也许有一些行已被删除以进行清理,或者在链之前还有另一个过滤器就是这样做了。
更新:在再次考虑这个问题并查看ExtensionsFilter
的来源后,ExtensionsFilter
实际上获得了OutputStream
之后的<{1}} / strong>过滤请求/响应。因此,由相关URL调用/执行的页面,servlet或任何其他Javacode(隐式)调用getWriter()
。
更新2: Glassfish v3默认提供Sun Mojarra JSF 2.0参考实现。它可能与Webproject中提供的MyFaces 1.x实现发生了某种程度的冲突。您可以通过在useMyFaces
中将useBundledJsf
或(较新的)true
属性设置为/WEB-INF/sun-web.xml
来指示您更喜欢使用MyFaces的Glassfish v3。你用过吗?试一试。
<sun-web-app>
<class-loader delegate="false"/>
<property name="useBundledJsf" value="true"/>
</sun-web-app>
另见Alternative JSF implementations on GlassFish - MyFaces and Tomahawk。
答案 1 :(得分:5)
我没有完整的解释(即我不知道getWriter
被调用的地方)但这可能是Tomahawk 1.1.3 / MyFaces 1.1.4中的一个错误,如Jira问题中报道的那样{ {3}}或TOMAHAWK-579(根据Servlet规范使用相同的IllegalStateException
)。请注意,正如您所遇到的那样,此错误似乎与容器有关。
因此,请尝试使用更新版本的Tomahawk / MyFaces(请参阅MYFACES-1310)或获取与compatibility matrix中的修复相对应的patch并将其应用于分支1.1。战斧3。后一个选项可能是最简单的选项。至少,这是我会尝试的。
答案 2 :(得分:2)
一个初始问题 - 如果您在GlassFish上运行此问题,为什么堆栈跟踪会引用Catalina?我可能会弄错,但Catalina是Tomcat的核心,Grizzly是GlassFish的核心。
您可能已经知道这一点,但问题是getWriter()和getOutputStream()不能同时在一个响应上调用。如果你将这种东西留给容器,它应该是正确的。
所以有一个问题是,您的任何代码是否都在调用getWriter()?这段代码不是。我没有看到任何看起来很可疑的内容,所以我会深入研究这个过滤器上游的任何代码,如果有的话?
答案 3 :(得分:2)
尝试在web.xml
中的所有其他过滤器之前定义过滤器。
如果这不起作用,请按以下步骤进行调试:
选项1:
getWriter()
org.apache.catalina.connector.Response
中放置一个断点
getWriter()
选项2:
web.xml
HttpServletResponse
创建一个包装器,并在getWriter()
或new Exception().printStackTrace();
这两个选项基本上都有相同的想法。在这两种情况下都会提供反馈,以便我们可以进行头脑风暴。
答案 4 :(得分:1)
如果使用JSP,请仔细检查JSP页面是否未重定向原始请求。 JSP已经在后台调用了getWriter,因此这将使用与Glassfish v.3打包的默认版本Catalina与您自己的自定义过滤器冲突。 另请参阅此post: