2010-02-10 52 views
7

我們需要升級老年web應用程序運行在GlassFish 3而不是Tomcat爲了獲得EAR部署( Glassfish被選爲參考JEE 6實現)GlassFish問題與過濾器鏈:java.lang.IllegalStateException:PWC3990:getWriter()已被調用此響應

不幸的是,它很快證明,確保用戶登錄的機制無法正常工作,並且抱怨getWriter()已經被調用(這是最有可能是正確的),我不明白爲什麼。

該方法是我們對整套JSP文件進行篩選,檢查用戶是否已登錄,如果沒有,則使用filterChain.doFilter(servletRequest, servletResponse);重定向到登錄頁面。用戶狀態(包括憑證)存儲在會話範圍內所謂的控制器對象中,該範圍由登錄驗證Java代碼設置。從Glassfish的


堆棧跟蹤:從LoggedInToXXXFilter.java

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> 

過濾代碼:

(堆棧跟蹤發生在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 with Tomahawk)?


編輯:用完整(但匿名)web.xml更新問題。請注意,有很多註釋掉的東西。我把它放在不小心刪除了重要的信息


編輯:嘗試用陽光web應用程序的配置文件,並發現它並沒有發揮作用。有趣的是,登錄後登錄頁面拋出異常,但我可以手動導航到主頁面(也是JSF),並看到兩個功能正常的頁面。除了引發異常的登錄頁面外,還有三個頁面。

我最初的想法是分離功能是t-taglib(對於Tomahawk),但經過快速調查似乎並非如此,因爲一些工作頁面使用了Tomahawk,有些則沒有。


編輯:比較兩個JSP的頁面,其中一個失敗了,另外一個它不沒有發現任何明顯的差別這應該引起這個。正如有人指出,有報道稱Tomahawk 1.1有這個bug,而且我們使用的是1.1.3,現在我已經升級到了最新的Apache Myfaces Tomahawk 1.1.9,這似乎解決了這個問題(沒有太陽 - 網絡應用程序)。

+0

任何進展/更新? – Bozho 2010-02-16 20:50:47

+0

您看到我對sun-web-app條目的編輯了嗎?今天我還沒有時間進行更多的實驗,但我希望明天能明確「_THIS_是不同之處」。 – 2010-02-16 22:04:26

回答

5

我沒有一個完整的解釋(即我不知道在哪裏getWriter被調用),但這可能是戰斧錯誤1.1.3/1.1.4的MyFaces報道JIRA中的問題,如TOMAHAWK-579MYFACES-1310(與根據Servlet規範的IllegalStateException相同)。請注意,該錯誤似乎與容器相關,正如您所遇到的。

所以,無論是與較新版本戰斧/ MyFaces的嘗試的(見compatibility matrix)或獲得patch對應的修復程序r442340並將其應用到戰斧的分支1.1.3。後面的選項可能是最簡單的一個。至少,這是我會嘗試的。

+0

就是這樣。升級tomahawk庫解決了這個問題。謝謝! – 2010-02-18 18:38:56

2

一個最初的問題 - 如果您在GlassFish上運行此項目,爲什麼堆棧跟蹤具有對Catalina的引用?我可能會誤會,但Catalina是Tomcat的核心,Grizzly是GlassFish的核心。

您可能已經知道這個問題,但問題是getWriter()和getOutputStream()不能同時在一個響應中調用。如果你把這種東西留給容器,它應該是正確的。

所以一個問題是,你的任何代碼調用getWriter()?這段代碼不是。我沒有看到任何關於這個看起來可疑的東西,所以我想深入瞭解這個過濾器的任何代碼,如果有的話?

+0

這是直接從Glassfish 3新鮮安裝 - 底線(我刪除)提到灰熊 - 卡塔琳娜引用我也困惑。 我會看看在哪裏,如果有的話,我實際上調用getWriter和getOutputStream(),但我不這麼認爲。這不是一個火箭科學應用。但是,如果卡塔麗娜拋出異常,也許我應該在最新的Tomcat中看到它? – 2010-02-10 12:09:28

+0

Glassfish建立在Tomcat的基礎之上,基本上只有HTTP連接器改爲Grizzly。灰熊不是一個servletcontainer。這是一個HTTP連接器。 – BalusC 2010-02-12 14:48:30

+0

快速瀏覽了Grizzly主頁,他們根本沒有解釋任何東西。什麼是HTTP連接器? – 2010-02-17 12:23:34

4

這可以有兩種原因:

  1. 還有另一個Filter在鏈ExtensionsFilter之前,它是(間接地)調用getWriter()
  2. 此請求已從JSP文件而不是Servlet類轉發。

在此特定情況下,它sendRedirect()doFilter()已經在相同的請求 - 響應鏈被稱爲(因爲sendRedirect()可以隱含地調用getWriter())。當一個Filter調用sendRedirect()時,它應該而不是事後要做doFilter()。發佈的代碼並不能證明這一點,但也許有一些行被刪除,以消毒,或者有另一個過濾器之前,在鏈中完成。

更新:思維再次瞭解這個和尋找在ExtensionsFilter的源之後,ExtensionsFilter實際取得的OutputStream過濾請求/響應。因此,由該URL調用/執行的頁面,servlet或任何其他Java代碼已經(隱含地)稱爲getWriter()

更新2: Glassfish v3默認發佈Sun Mojarra JSF 2.0參考實現。它可能與Web項目中發佈的MyFaces 1.x實現相沖突。您可以通過將useMyFaces或(較新的)useBundledJsf屬性設置爲true來指示您更喜歡使用MyFaces的Glassfish v3,您可以在/WEB-INF/sun-web.xml中指定。你用過嗎?試一試。

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

另請參閱Alternative JSF implementations on GlassFish - MyFaces and Tomahawk

+0

我已經添加了完整的web.xml。我只能將MyFaces的東西看作過濾器,所以它可能是MyFaces不是100%正確的。該版本仍然是1.1.3,但我希望在遷移到JSF 2.0之前讓應用程序可以正常工作。 – 2010-02-12 17:26:13

+0

發佈的方法是完整的副本 - 僅匿名。 – 2010-02-12 19:36:02

+0

你檢查我的更新嗎? 'ExtensionsFilter'在處理請求和響應之後獲得'OutputStream'。即'response.getOutputStream()'是在'chain.doFilter(request,response)'''後面完成的。因此,'Writer'必須在已經由特定請求執行的「普通」Servlet/Java/JSP代碼中的某處被調用。 – BalusC 2010-02-12 19:39:02

2

嘗試定義您的過濾器之前所有其他過濾器web.xml

如果還是不行,這裏就是我會進行下去調試這樣的:

選項1:

  1. 下載Glassfish的來源(或Tomcat的相應版本的源代碼,實際上)
  2. 包含在你的源代碼查找目錄中的這些源(調試)
  3. 將斷點在getWriter()org.apache.catalina.connector.Response
  4. 檢查堆棧,以瞭解是誰打來的getWriter()

選項2:

  1. 定義一個新的過濾器ontop的所有其他過濾器的web.xml
  2. 創建圍繞供應HttpServletResponse的包裝,並把斷點放在getWriter()new Exception().printStackTrace();

這兩個選項基本上都有相同的想法。在這兩種情況下給予反饋,以便我們可以進行頭腦風暴。

1

如果使用JSP,請仔細檢查原始請求是否未被JSP頁面重定向。 JSP已經在幕後調用了getWriter,所以這將與使用Glassfish v.3打包的Catalina默認版本的自定義過濾器衝突。 也參考post

+1

問題是通過升級Tomahawk解決的。 – 2012-09-25 09:42:15