Проблема GlassFish с цепочкой фильтров: java.lang.IllegalStateException: PWC3990: getWriter () уже был вызван для этого ответа - PullRequest
6 голосов
/ 10 февраля 2010

Нам нужно обновить устаревшее веб-приложение для запуска под 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 (MyFaces 1.1.4 с Tomahawk)?


РЕДАКТИРОВАТЬ: обновленный вопрос с полным (но анонимным) web.xml.много закомментированных вещей. Я оставил это, чтобы случайно не удалить важную информацию


РЕДАКТИРОВАТЬ: Экспериментировал с файлом конфигурации sun-web-app и обнаружил, что это не имеет значенияИнтересно то, что после входа на страницу входа выдается исключение, но я могу вручную перейти на главную страницу (также JSF) и увидеть еще две страницы с функциональностью.в дополнение к странице входа в систему, которая выдает исключение.

Первоначально я думал, что разделительной функцией будет t-taglib (для томагавка), но что после быстрого расследования это не так, как некоторыеиз рабочих страниц использовать Томагавк, а некоторые нет.


РЕДАКТИРОВАТЬ: Сравнение двух jsp-страниц, одна из которых потерпела неудачу, другая, которая не обнаружила каких-либо очевидных различий, которые должны вызывать это.Как было отмечено, об этой самой ошибке сообщалось в Tomahawk 1.1, и мы использовали 1.1.3, теперь я обновился до последней версии Apache Myfaces Tomahawk 1.1.9, которая, по-видимому, разрешила проблему (безвеб-приложение вообще).

Ответы [ 5 ]

5 голосов
/ 12 февраля 2010

Это может иметь две причины:

  1. Есть еще один Filter в цепочке до ExtensionsFilter, который (косвенно) вызывает getWriter().
  2. Этот запрос был перенаправлен из файла JSP вместо класса Servlet.

В данном конкретном случае похоже на , что оба sendRedirect() и doFilter() были вызваны в одной и той же цепочке запрос-ответ (поскольку sendRedirect() может неявно вызывать getWriter()). Когда Filter звонит sendRedirect(), он должен не делать doFilter() впоследствии. Размещенный код не доказывает этого, но, возможно, некоторые строки были удалены из него для очистки, или есть другой фильтр в цепочке, который делает именно это.

Обновление: , подумав еще раз и посмотрев источник ExtensionsFilter, ExtensionsFilter фактически получает OutputStream после фильтрации запроса / ответа , Таким образом, страница, сервлет или любой другой Javacode, который был вызван / выполнен по указанному URL, (неявно) называется getWriter().

Обновление 2: Glassfish v3 поставляется по умолчанию с эталонной реализацией Sun Mojarra JSF 2.0. Это могло как-то противоречить реализации MyFaces 1.x, поставляемой в веб-проекте. Вы можете указать Glassfish v3, что предпочитаете использовать MyFaces, задав для свойства useMyFaces или (более нового) useBundledJsf значение true в /WEB-INF/sun-web.xml. Вы использовали это? Попробуйте.

<sun-web-app>
    <class-loader delegate="false"/>
    <property name="useBundledJsf" value="true"/>
</sun-web-app>

Также см. Альтернативные реализации JSF на GlassFish - MyFaces и Tomahawk .

5 голосов
/ 14 февраля 2010

У меня нет полного объяснения (т.е. я не знаю, где вызывается getWriter), но это может быть ошибкой в ​​Tomahawk 1.1.3 / MyFaces 1.1.4, о чем сообщалось в таких проблемах Jira, как TOMAHAWK-579 или MYFACES-1310 (с таким же значением IllegalStateException в соответствии со спецификацией сервлета).Обратите внимание, что эта ошибка, по-видимому, зависит от контейнера.

Итак, либо попробуйте использовать более свежие версии Tomahawk / MyFaces (см. матрицу совместимости ), либо получите patch , соответствующий исправлению в r442340 , и примените его к ветви 1.1.3 Tomahawk.Последний вариант, возможно, самый простой.По крайней мере, это то, что я бы попробовал.

2 голосов
/ 14 февраля 2010

Попробуйте определить свой фильтр перед всеми другими фильтрами в web.xml.

Если это не сработает, вот как я продолжу отлаживать это:

Вариант 1:

  1. Скачать исходники Glassfish (или, собственно, источники соответствующей версии Tomcat)
  2. Включить эти источники в каталог поиска источников (для отладчика)
  3. Поместите точку останова в getWriter() из org.apache.catalina.connector.Response
  4. Проверьте стек, чтобы увидеть, кто звонит getWriter()

Вариант 2:

  1. Определите новый фильтр поверх всех остальных фильтров в web.xml
  2. Создайте обертку вокруг поставляемого HttpServletResponse и установите точку останова в getWriter() или new Exception().printStackTrace();

Оба варианта по сути одинаковы. В обоих случаях дайте обратную связь, чтобы мы могли приступить к мозговому штурму.

2 голосов
/ 10 февраля 2010

Один начальный вопрос - если вы используете это на GlassFish, почему трассировка стека имеет ссылки на Catalina? Я могу ошибаться, но Каталина - ядро ​​Tomcat, Grizzly - ядро ​​GlassFish.

Возможно, вы уже знаете это, но проблема в том, что getWriter () и getOutputStream () не могут быть вызваны одновременно для одного ответа Если вы оставите такую ​​вещь в контейнере, она должна сделать все правильно.

Итак, один из вопросов заключается в том, вызывает ли ваш код вызов getWriter ()? Этот код не Я не вижу в этом ничего подозрительного, поэтому я бы покопался в любом коде выше по потоку от этого фильтра, если он есть?

1 голос
/ 25 сентября 2012

При использовании JSP дважды проверьте, что исходный запрос не перенаправляется страницей JSP. JSP уже вызывает getWriter за кулисами, поэтому это будет конфликтовать с вашим собственным настраиваемым фильтром, использующим версию Catalina по умолчанию, поставляемую в комплекте с Glassfish v.3. Также см. Этот пост :

...