Tuesday, May 4, 2010

Integrate Spring Webflow, IceFaces and Facelets

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:j2ee="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"
 version="2.4">

 <!-- other stuff -->

 <!-- Facelets -->
 <context-param>
  <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
  <param-value>.xhtml</param-value>
 </context-param>

 <context-param>
  <param-name>facelets.DEVELOPMENT</param-name>
  <param-value>true</param-value>
 </context-param>

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

 <context-param>
  <param-name>facelets.LIBRARIES</param-name>
  <param-value>/WEB-INF/tld/myTaglib.xml</param-value>
 </context-param>

 <servlet>
  <servlet-name>xyz</servlet-name>
  <servlet-class>
   org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet>
  <servlet-name>Faces Servlet</servlet-name>
  <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  <init-param>
   <param-name>debug</param-name>
   <param-value>true</param-value>
  </init-param>
  <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
    /WEB-INF/xyz-servlet.xml /WEB-INF/xyz-webflow-config.xml
   </param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <!-- Persistent Faces Servlet -->
 <servlet>
  <servlet-name>Persistent Faces Servlet</servlet-name>
  <servlet-class>
   com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <!-- Blocking Servlet -->
 <servlet>
  <servlet-name>Blocking Servlet</servlet-name>
  <servlet-class>
   com.icesoft.faces.webapp.xmlhttp.BlockingServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>xyz</servlet-name>
  <url-pattern>*.htm</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
  <servlet-name>xyz</servlet-name>
  <url-pattern>*.info</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
  <servlet-name>Faces Servlet</servlet-name>
  <url-pattern>/faces/*</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
  <servlet-name>Faces Servlet</servlet-name>
  <url-pattern>*.faces</url-pattern>
 </servlet-mapping>

 <!-- Persistent Faces Servlet Mappings -->
 <!--
  the following mapping will load stylesheet and other resources out of
  the icefaces jar by default
 -->
 <servlet-mapping>
  <servlet-name>Persistent Faces Servlet</servlet-name>
  <url-pattern>/xmlhttp/*</url-pattern>
 </servlet-mapping>

 <servlet-mapping>
  <servlet-name>Persistent Faces Servlet</servlet-name>
  <url-pattern>*.iface</url-pattern>
 </servlet-mapping>

 <!-- Blocking Servlet Mapping -->
 <servlet-mapping>
  <servlet-name>Blocking Servlet</servlet-name>
  <url-pattern>/block/*</url-pattern>
 </servlet-mapping>

 <!-- other stuff -->


</web-app>


faces-config.xml

<?xml version="1.0"?>

<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config>

 <application>
  <navigation-handler>
   org.springframework.webflow.executor.jsf.FlowNavigationHandler
  </navigation-handler>
  <variable-resolver>
   org.springframework.webflow.executor.jsf.DelegatingFlowVariableResolver
  </variable-resolver>

  <view-handler>com.xyz.MyViewHandler</view-handler>


 </application>

 <lifecycle>
  <phase-listener>
   org.springframework.webflow.executor.jsf.FlowPhaseListener
  </phase-listener>
 </lifecycle>

 <!-- other stuff -->

</faces-config>

The view handler class MyViewHandler.java:
package com.xyz;

import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

import com.icesoft.faces.facelets.D2DFaceletViewHandler;
import com.icesoft.faces.webapp.http.core.ResourceServer;
import com.icesoft.faces.webapp.http.core.ServeCSSResource;

public class MyViewHandler extends ViewHandler
  {
    ViewHandler defaultHandler;
    ViewHandler faceletViewHandler;

    public MyViewHandler(ViewHandler defaultHandler)
    {
      this.defaultHandler = defaultHandler;
      this.faceletViewHandler = new D2DFaceletViewHandler(defaultHandler);
    }

    private ViewHandler getViewHandler(String viewId)
    {
      if (viewId.endsWith(".xhtml"))
      {
        return faceletViewHandler;
      }
      return defaultHandler;
    }

    public UIViewRoot createView(FacesContext context, String viewId)
    {
      ViewHandler viewHandler = getViewHandler(viewId);
      return viewHandler.createView(context, viewId);
    }

    public void renderView(FacesContext context, UIViewRoot viewToRender)
        throws IOException, FacesException
    {
      ViewHandler viewHandler = getViewHandler(viewToRender.getViewId());
      viewHandler.renderView(context, viewToRender);
    }

    public UIViewRoot restoreView(FacesContext context, String viewId)
    {
      ViewHandler viewHandler = getViewHandler(viewId);
      return viewHandler.restoreView(context, viewId);
    }
    
    // other methods
  }
Notes:
  1. MyViewHandler acts as a delegator. By default, it will delegate to the default JSF ViewHandler. But for .xhtml files, it will delegate to D2DFaceletViewHandler, which is an implementation from IceFaces for facelet. This is the glue of Icefaces and Facelets.
  2. The NavigationHandler is the Spring FlowNavigationHandler. This is the glue of Spring webflow and JSF or Icefaces because we are using Icefaces for JSF here.

xyz-webflow-config.xml

This file is linked in the web.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:flow="http://www.springframework.org/schema/webflow-config"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd">
 <flow:executor id="flowExecutor" registry-ref="flowRegistry">
  <flow:execution-attributes>
   <flow:alwaysRedirectOnPause value="false"/>
  </flow:execution-attributes>
 </flow:executor>
    </flow:executor>
 <flow:registry id="flowRegistry"> 
       <flow:location path="/WEB-INF/flows/mytask-flow.xml" />
 </flow:registry>
</beans>

The href link in the jsp page to invoke the webflow

The following link can be put in some menu jsp file. Clicking this will invoke the Spring webflow:

<a href="mytask.iface?_flowId=mytask-flow">My Task</a>

Note that the URL has .iface in it. Because of this, the IceFaces servlet "Persistent Faces Servlet" will be used to process this request as configured in web.xml. And it will invoke Spring webflow because of "_flowId=mytask-flow".

Facelet Template

The real advantage of Facelets is its use of template. You can base all of our concrete xhtml files on the same template so they all have the same look and feel. You can even do sub-template. The following is an example template file (mainTemplate.xhtml):
<f:view  xmlns:f="http://java.sun.com/jsf/core"
         xmlns:h="http://java.sun.com/jsf/html"
         xmlns:ice="http://www.icesoft.com/icefaces/component" 
         xmlns:ui="http://java.sun.com/jsf/facelets">

<html xml:lang="en" lang="en">
<head>
<title>My APP</title>

    <meta http-equiv="Expires" content="-1" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Cache-Control" content="no-cache" />

 <link rel="stylesheet" type="text/css" media="screen" href="/xyz/css/xp.css" />
 <script type="text/javascript" src="/xyz/js/xyzcommon.js"></script>
 <ui:insert name="otherSettings"/>
 
</head>

<body id="page-home">
<div id="page">
    <div id="header" class="foo">  
       <div id="branding">
          <img height='80' src="images/xyz-web-banner.jpg" />
       </div>
    </div>
 <hr />
 
 <div>
     <ui:insert name="content">
            Default Content
     </ui:insert>
 </div>
 
</div>


<div id="footer">
   <p>&copy; Copyright </p>
</div>
</div>

</body>
</html>

</f:view>

Note that it defines two components that that need to be filled out by its sub-tempaltes or the actual concrete xhtml files.
<ui:insert name="otherSettings"/>
and
<ui:insert name="content">
            Default Content
     </ui:insert>
The following is an example sub-template(myTaskTemplate.xhtml):
<ui:composition template="/WEB-INF/jsp/shared/mainTemplate.xhtml"
                xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
               >
   
<ui:define name="otherSettings">
    <link rel="stylesheet" type="text/css" media="screen" href="/xyz/css/listmenu_o.css" />
    <script type="text/javascript" src="/xyz/js/fsmenu.js"></script>  
    <ui:include src="/WEB-INF/jsp/shared/menuheader.jsp"/>
</ui:define>

</ui:composition>
Note that this sub-template fills out the "otherSettings" part in the main template. But it still leaves the "content" to be filled out. Or in terms of the programming language, it is still an "abstract" class.

The following is an example xhtml file that uses the sub-template:
<ui:composition template="myTaskTemplate.xhtml"
                xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:ice="http://www.icesoft.com/icefaces/component">
   
<ui:define name="content">
          
<h:form >
  ..................
</h:form>
        
</ui:define>
    
</ui:composition>

Use <ui:include> Tag in Facelet File

Often times there are some common part in an xhtml file that can be shared by multiple xhtml files. The tag <ui:include> can be used to factor out that common part.

The following is an example. Note that this xhtml file(sharedComponent.xhtml) does not use any template because it is an independent component. Also note that it accepts variables:
<ui:component  xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:ice="http://www.icesoft.com/icefaces/component"
                xmlns:c="http://java.sun.com/jstl/core">
               
<ice:panelGroup>
<ice:outputLabel  value="#{myVar.eventType} Name: " />
<ice:outputText value="#{myVar.name} " />
</ice:panelGroup>

</ui:component>

To use this component, one can use the <ui:include> tag and also pass the value to the variabes defined in the component file sharedComponent.xhtml. The following is an example file sample.xhtml
<ui:composition template="myTaskTemplate.xhtml"
                xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:ice="http://www.icesoft.com/icefaces/component"
               >
   
<ui:define name="content">
<ice:outputText styleClass="pageHeading" value="Hello World" />
<ui:include src="sharedComponent.xhtml">
  <ui:param name="myVar" value="#{myForm.aValue}"/>
</ui:include>
<ice:form> 
 .....
</ice:form>
 
</ui:define>
    
</ui:composition>

4 comments:

  1. O! Thanks a lot for this topic! I made it work finally with your help!

    ReplyDelete
  2. What version of Icefaces, SWF are you using?

    Thank you!

    ReplyDelete
  3. My examples here use Icefaces 1.6.2 and SWF 1.0.5 and Weblogic 9.2

    ReplyDelete