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

22,363

Solution 1

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>

Solution 2

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 );
        }

Solution 3

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>
Share:
22,363
Dick Chesterwood
Author by

Dick Chesterwood

Full Stack JVM Programmer and Architect. DevOps a prime focus at present.

Updated on July 26, 2022

Comments

  • Dick Chesterwood
    Dick Chesterwood almost 2 years

    I have a client who wants to use JSF2 and they like that XHTML is now the default (Facelets).

    However, they have a huge amount of "legacy" JSP from their JSF1.x codebase.

    I know it's probably not desirable, but will it be possible to support a mix of both in JSF2, at least for a transition period whilst they port?

    I know it was possible to mix the two in JSF1.x, but I can't find any information about this in JSF2.

    I've googled hard but naturally all the JSF2 focus is on Facelets. Also my brief attempt at mixing (I'm not an expert at JSF!) has led to failure.

  • Dick Chesterwood
    Dick Chesterwood over 13 years
    Ah thankyou. I had seen that page but discounted it as I thought it was JSF1.x and out of date. I had tried a similar strategy as the web.xml above with a failure - problem my stupid mistake. Will retry thanks!
  • BalusC
    BalusC over 12 years
    FYI: another way is to use a suffix mapping of *.jsf. JSF2 will first scan for XHTML file. If absent, then it will scan for JSP file. See also stackoverflow.com/questions/4441713/…
  • Karl Kildén
    Karl Kildén over 12 years
    however doing the change to .jsf mapping did work but a change into that would take a long time to fix but it works well if you don't mind changing urls.