Search code examples
servletsjsf-2faceletsprettyfaces

JSF 2 + PrettyFaces -> IllegalStateException: PWC3990: getWriter() has already been called for this response


I have a Java EE App that has JSF2 + PrettyFaces + Facelets + EJB3 + EclipseLink.

I'm continually running into an IllegalStateException as stated above, due to some inconsistencies in the JSF2 + Faceletes rendering model to work along with PrettyFaces and h:link tag.

I removed all my old JSTL tags and also all the commandLink tags, according to good practices when using JSF2 + Faceletes.

In web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name>atlPortal</display-name>

    <context-param>
        <param-name>com.sun.faces.prefer.XHTML</param-name>
        <param-value>true</param-value>
    </context-param>

  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>

    <context-param>
        <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
        <param-value>/WEB-INF/facelets/customTags.taglib.xml</param-value>
    </context-param>

  <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>*.faces</url-pattern>
  </servlet-mapping>

    <servlet>
        <servlet-name>imageServlet</servlet-name>
        <servlet-class>com.mindvortex.atl.web.common.servlet.ImageServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>imageServlet</servlet-name>
        <url-pattern>/image/*</url-pattern>
        <url-pattern>/pages/image/*</url-pattern>
        <url-pattern>/pages/protected/image/*</url-pattern>
    </servlet-mapping>

    <filter>
       <filter-name>Pretty Filter</filter-name>
       <filter-class>com.ocpsoft.pretty.PrettyFilter</filter-class>    
       <async-supported>false</async-supported>    
    </filter>

    <filter-mapping> 
       <filter-name>Pretty Filter</filter-name>    
       <url-pattern>/*</url-pattern>
       <dispatcher>FORWARD</dispatcher> 
       <dispatcher>REQUEST</dispatcher>
       <dispatcher>ERROR</dispatcher>
    </filter-mapping>

  <listener>
    <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
  </listener>

  <!-- servlets and such would be above -->

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>userauth</realm-name>
        <form-login-config>
            <form-login-page>/login.faces</form-login-page>
            <form-error-page>/loginError.xhtml</form-error-page>
        </form-login-config>                
    </login-config>

    <security-constraint>
        <display-name>Block All XHTML</display-name>
        <web-resource-collection>
            <web-resource-name>blockXHTML</web-resource-name>
            <description></description>
            <url-pattern>*.xhtml</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
            <http-method>HEAD</http-method>
            <http-method>PUT</http-method>
            <http-method>OPTIONS</http-method>
            <http-method>TRACE</http-method>
            <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint />             
    </security-constraint>


    <security-constraint>   
        <display-name>ConstraintSSL</display-name>
        <web-resource-collection>
            <web-resource-name>protected</web-resource-name>
            <description/>
            <url-pattern>/pages/protected/*</url-pattern>
            <url-pattern>/login/*</url-pattern>
            <url-pattern>/login.*</url-pattern>
            <url-pattern>/account/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
            <http-method>HEAD</http-method>
            <http-method>PUT</http-method>
            <http-method>OPTIONS</http-method>
            <http-method>TRACE</http-method>
            <http-method>DELETE</http-method>
        </web-resource-collection>

        <user-data-constraint>
          <description>SSL not required for Development</description>
          <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>        
    </security-constraint>

    <security-constraint>   
        <display-name>ConstraintUser</display-name>
        <web-resource-collection>
            <web-resource-name>user</web-resource-name>
            <description/>
            <url-pattern>/account/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
            <http-method>HEAD</http-method>
            <http-method>PUT</http-method>
            <http-method>OPTIONS</http-method>
            <http-method>TRACE</http-method>
            <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint>       
            <description/>
            <role-name>ADMINISTRATORS</role-name>
            <role-name>USERS</role-name>
        </auth-constraint>

        <user-data-constraint>
          <description>SSL not required for Development</description>
          <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>        
    </security-constraint>

    <security-role>
        <description/>
        <role-name>USERS</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>ADMINISTRATORS</role-name>
    </security-role>

  <session-config>
    <session-timeout>30</session-timeout>
    <tracking-mode>COOKIE</tracking-mode>
  </session-config>

   <welcome-file-list>
    <welcome-file>init.faces</welcome-file>    
   </welcome-file-list>

</web-app>

This happens whenever I navigate from one XHTML page to another via

                        <h:link outcome="pretty:viewContactUs" styleClass="nav6">
                            #{msg['contactUs']}
                        </h:link>                                                                                                                                      

in pretty-faces.xml

  <url-mapping id="viewContactUs">
      <pattern value="/contactUs/" />
      <view-id value="contactUs.faces" />
      <action>#{commentsMB.openContactUs}</action>
  </url-mapping>            

in CommentsMB Managed Bean:

@ManagedBean
@RequestScoped
public class CommentsMB extends UserCrudMB<Comments, Integer> {

    private static final long serialVersionUID = 1L;

    @EJB(mappedName="ejb/CommentsService")
    private CommentsServiceBeanLocal commentsService;

// ... Code

    public String openContactUs() {
        this.entity = new Comments();
        return NavigationViews.VIEW_CONTACT_US;
    }

}

in faces-config.xml:

   <navigation-rule>
        <from-view-id>/*</from-view-id>
        <navigation-case>
            <from-outcome>contactUs</from-outcome>
            <to-view-id>/pages/contactUs.xhtml</to-view-id>         
        <redirect /></navigation-case>

in contactUs.xhtml:

                    <div class="form_row">
                        <h:link id="commentsSave" outcome="pretty:commentsSave" styleClass="contact">
                            #{msg.send}
                        </h:link>
                    </div>

The error happens just when I click the commentsSave Button. It gives: IllegalStateException: PWC3990: getWriter() has already been called for this response.

the pretty-config.xml for commentsSave URL mapping:

  <url-mapping id="commentsSave">
      <pattern value="/comments/save/" />
      <view-id value="contactUs.faces" />
      <action>#{commentsMB.save}</action>
  </url-mapping>                       

CommentsMB save action signature:

public String save() {
    return save(this.getEntity());
}

The commentsMB.save Bean action is not called and I get a page not found error with the exception thrown in the logs.

StackTrace:

[#|2012-03-30T13:47:54.968-0300|SEVERE|glassfish3.1.1|org.apache.jasper.servlet.JspServlet|_ThreadID=25;_ThreadName=http-thread-pool-8084(4);|PWC6117:
 File "C%3A%5CGBL%5Cprop%5Cprogs%5Cglassfish-3.1.1%5Cglassfish3%5Cglassfish%5Cdomains%5Cdomain1%5Capplications%5CatlanteusEAR%5CatlanteusPortal-1.0.0_
war%5Ccomments%5Csave%5CcontactUs.jsp" not found|#]

[#|2012-03-30T13:47:54.976-0300|WARNING|glassfish3.1.1|org.apache.catalina.core.ApplicationDispatcherForward|_ThreadID=25;_ThreadName=http-thread-pool
-8084(4);|Exception processing ErrorPage[errorCode=404, location=/pages/error/error404.xhtml]
java.lang.IllegalStateException: PWC1227: Cannot forward after response has been committed
        at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:370)
        at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:350)
        at org.apache.catalina.core.ApplicationDispatcherForward.custom(ApplicationDispatcherForward.java:253)
        at org.apache.catalina.core.ApplicationDispatcherForward.status(ApplicationDispatcherForward.java:209)
        at org.apache.catalina.core.ApplicationDispatcherForward.commit(ApplicationDispatcherForward.java:131)
        at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:353)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:300)
        at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:110)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
        at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
        at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
        at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:725)
        at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1019)
        at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
        at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
        at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
        at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
        at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
        at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
        at java.lang.Thread.run(Thread.java:619)
|#]

Seems that JSF is interpreting the navigation as a .jsp altough I use XHTML only.

Could someone help me figure out what am I doing wrong ? This is my major issue since I began using JSF2 + Facelets.


Solution

  • After considering answers I came up with the final solution so I will post here for others to understand:

    1. Changed the URL mappings in pretty-faces.xml for all JSF resources to be fully-qualified: for example if contactUs.xhtml is under /pages/ then the mapping would be:

      <url-mapping id="viewContactUs">
        <pattern value="/contactUs/" />
        <view-id value="/pages/contactUs.faces" />
        <action>#{commentsMB.openContactUs}</action></url-mapping>            
      
    2. Removed all the JSF navigation rules from faces-config.xml. For example rules like:

      <navigation-case>
          <from-outcome>contactUs</from-outcome>
          <to-view-id>/pages/contactUs.xhtml</to-view-id>         
      </navigation-case>
      

    would be removed or commented out. After doing this the actual navigation mappings would be only present in pretty-config.xml and will work correctly with the PrettyFaces Filter.