过滤器链的GlassFish问题:java.lang.IllegalStateException:已为此响应调用了PWC3990:getWriter()

Tho*_*sen 6 java tomcat servlets glassfish java-ee

我们需要升级老式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)
....
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

从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);
    }
}
Run Code Online (Sandbox Code Playgroud)

可能的原因是我们仍然带着我们自己的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,这似乎解决了这个问题(没有太阳 - 网络应用程序.

Bal*_*usC 5

这可能有两个原因:

  1. Filter链中还有另一个ExtensionsFilter(间接)调用getWriter().
  2. 此请求是从JSP文件而不是Servlet类转发的.

在这种特殊情况下,它看起来就像两个 sendRedirect(),并doFilter()在同一个请求-响应链被称为(因为sendRedirect()可以隐式调用getWriter()).在Filter打电话时sendRedirect(),应该doFilter()事后做.发布的代码不能证明这一点,但也许有一些行已被删除以进行清理,或者在链之前还有另一个过滤器就是这样做的.

更新:在再次考虑这个问题并查看其ExtensionsFilter来源后,ExtensionsFilter实际上获得了过滤OutputStream 的请求/响应.因此,有问题的URL调用/执行的页面,servlet或任何其他Javacode(隐式)调用了getWriter().

更新2: Glassfish v3默认使用Sun Mojarra JSF 2.0参考实现.它可能与Webproject中提供的MyFaces 1.x实现发生了某种程度的冲突.您可以通过将useMyFaces(或更新的)useBundledJsf属性设置为truein 来指示您更喜欢使用MyFaces的Glassfish v3 /WEB-INF/sun-web.xml.你用过吗?试试看.

<sun-web-app>
    <class-loader delegate="false"/>
    <property name="useBundledJsf" value="true"/>
</sun-web-app>
Run Code Online (Sandbox Code Playgroud)

另请参阅GlassFish上的替代JSF实现 - MyFaces和Tomahawk.


Pas*_*ent 5

我没有完整的解释(即我不知道在哪里getWriter被调用)但这可能是Tomahawk 1.1.3/MyFaces 1.1.4中的一个错误,如JOM问题中所报道的那样,如TOMAHAWK-579MYFACES-1310(与与IllegalStateExceptionServlet规范相同).请注意,正如您所遇到的那样,此错误似乎与容器有关.

所以,无论是与较新版本的战斧尝试/ MyFaces的(参见兼容性列表),或者获得补丁对应于固定r442340并将其应用到战斧的分支1.1.3.后一个选项可能是最简单的选项.至少,这是我会尝试的.