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.
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'sVIEW_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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With