Mixing JSP and XHTML (Facelets) in JSF2 Project - possible?

那年仲夏 提交于 2019-11-30 13:05:02

This is answered in the Facelets FAQ: use prefix mapping on FacesServlet. You can then access JSP pages by http://example.com/faces/page.jsp and Facelets pages by http://example.com/faces/page.xhtml. Here's a cite of relevance:

How do I use Facelets and JSP in the same application?

You have to use prefix mapping for the Facelets pages in order for this to work. Leave the DEFAULT_SUFFIX with the JSF default of .jsp. Configure the Facelet's VIEW_MAPPINGS parameter:

<web-app>
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.jsp</param-value>
    </context-param>

    <!-- Facelets pages will use the .xhtml extension -->
    <context-param>
        <param-name>facelets.VIEW_MAPPINGS</param-name>
        <param-value>*.xhtml</param-value>
    </context-param>     

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    </servlet>

    <!-- Use prefix mapping for Facelets pages, e.g. http://localhost:8080/webapp/faces/mypage.xhtml -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
</web-app>

The wiki section cited by BalusC seems to be indeed out of date. In my extension mapping (*.faces) setup I had the problem with the proposed javax.faces.DEFAULT_SUFFIX set to .jsp that generated action URLs inside form tags of *.xhtml pages got a .jsp extension instead of a .faces extension (and therefore could not be mapped).

After I stepped into the corresponding classes of the Apache MyFaces 2.x implementation (see org.apache.myfaces.shared.application.DefaultViewHandlerSupport.calculateActionURL(FacesContext context, String viewId)) the following setup turned out to work in our parallel use of JSP and Facelets View Handling.

How do I use Facelets and JSP in the same application?

Besides prefix mapping you may use extension mapping (e.g. *.faces) for the Facelets pages in order for this to work. Leave the DEFAULT_SUFFIX with the JSF default of .jsp .xhtml. Configure the Facelet's VIEW_MAPPINGS parameter:

<web-app>
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.jsp .xhtml</param-value>
    </context-param>

    <!-- Facelets pages will use the .xhtml extension -->
    <context-param>
        <param-name>javax.faces.FACELETS_VIEW_MAPPINGS</param-name>
        <param-value>*.xhtml</param-value>
    </context-param>     

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    </servlet>

    <!-- use extension mapping in this sample -->
    <servlet-mapping>
            <servlet-name>Faces Servlet</servlet-name>
            <url-pattern>*.faces</url-pattern>
    </servlet-mapping>
</web-app>

For those who are interested in the details of the processing of action urls inside org.apache.myfaces.shared.application.DefaultViewHandlerSupport.calculateActionURL(FacesContext context, String viewId):

        if ( mapping.isExtensionMapping() ) {
            // See JSF 2.0 section 7.5.2
            String[] contextSuffixes = _initialized ? _contextSuffixes : getContextSuffix( context );
            boolean founded = false;
            for ( String contextSuffix : contextSuffixes ) {
                if ( viewId.endsWith( contextSuffix ) ) {
                    builder.append( viewId.substring( 0, viewId.indexOf( contextSuffix ) ) );
                    builder.append( mapping.getExtension() );
                    founded = true;
                    break;
                }
            }
            if ( !founded ) {
                // See JSF 2.0 section 7.5.2
                // - If the argument viewId has an extension, and this extension is mapping,
                // the result is contextPath + viewId
                //
                // -= Leonardo Uribe =- It is evident that when the page is generated, the
                // derived
                // viewId will end with the
                // right contextSuffix, and a navigation entry on faces-config.xml should use
                // such id,
                // this is just a workaroud
                // for usability. There is a potential risk that change the mapping in a webapp
                // make
                // the same application fail,
                // so use viewIds ending with mapping extensions is not a good practice.
                if ( viewId.endsWith( mapping.getExtension() ) ) {
                    builder.append( viewId );
                } else if ( viewId.lastIndexOf( "." ) != -1 ) {
                    builder.append( viewId.substring( 0, viewId.lastIndexOf( "." ) ) );
                    builder.append( contextSuffixes[0] );
                } else {
                    builder.append( viewId );
                    builder.append( contextSuffixes[0] );
                }
            }
        } else {
            builder.append( mapping.getPrefix() );
            builder.append( viewId );
        }

The above suggestion did not work at all for me. The wiki page is probably out of date. From the JSF2 specification I got the following parameter that worked:

  <!-- Facelets pages will use the .xhtml extension -->
  <context-param>
    <param-name>javax.faces.FACELETS_VIEW_MAPPINGS</param-name>
    <param-value>*.xhtml</param-value>
  </context-param> 

instead of:

<context-param>
    <param-name>facelets.VIEW_MAPPINGS</param-name>
    <param-value>*.xhtml</param-value>
</context-param>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!