Tuesday, December 14, 2010

Deal With Time Values from Different Timezones in Webservice

The Scenario

A web service can be used by the users from all over the world. It is very common that the service will need to read and save the time when the user requests the service. Since the users can be from different timezones from the server, how can the time value be transmitted correctly?

Also the application server that hosts the web service and the database server that saves the data may be at different timezones. So this issue also needs to be taken into consideration.

A way to deal with the time values from different timezones

The following is a way to implement the code so the time values are persisted correctly. It is from an example using the Spring Web Service.
To make the whole thing work, there are several pieces.
  1. In the message schema or the wsdl file, use dateTime as the type for the time value. An example is the following:
    <element name="createTime" type="dateTime" />
    <element name="updateTime" type="dateTime" />
  2. In the RPC client generated by Weblogic, the java.util.Calendar is used as the data type for the time to send the request. The following is an example:
    request.setCurrentDateTime(Calendar.getInstance());
    
  3. In the soap request xml message sent to the web service, the java Calendar data is converted to a text string. The text string will contain the timezone offset. The following is an example of such message:
    <aud:currentDateTime>2010-12-14T16:34:27.421-05:00</aud:currentDateTime>
  4. The server will get this time value and convert this to an object of java type. In Spring web service, if xml marshalling is used, the xml data will be converted to an object of the type javax.xml.datatype.XMLGregorianCalendar. You can convert this to java.util.Date using the following code:
    xmlCal.toGregorianCalendar().getTime();
    
    This date value will keep all the original time information from the user including the timezone offset.
  5. Now you can do your java programming on the date value as usual using jdbc, etc. And the time value will be saved correctly.

There is still one important issue on the database side. The date or timestamp data type of a database may not support timezone. For example, the Oracle DATE data type does not store the timezone information. So say you have a time object with the time value of 2009-03-17-22.05.37.569000. This value can actually represent different time values depending on the timezone. But if you use JDBC to save this value, the database will just take this value as is. And the timezone that the database uses for this time will be the timezone of the database server. And this is wrong!

The following is one example. The conditions are these.

  1. The oracle database server is using the EST time. ( You can use the query "select sysdate from dual" to check this.)
  2. The java program is running on a linux machine which uses GMT time.
  3. The database table has a colum called UPDATED_DT with the data tape DATE.
  4. The java code uses the following for JDBC to update the UPDATED_DT column:
    String sql = "UPDATE foo set updated_dt = ? where ...";
    Timestamp ts = new Timestamp(System.currentTimeMillis());
    pstmt.setTimestamp(1, ts);
    ...
    System.out.println("Timestamp is " + ts);" 
    
The jar ojdbc6.jar is used for the oracle database driver. After running the program, we get the following results:
  • On the database, the query "Select * from foo where ..." returns the value for the column UPDATED_DT as
    1/12/2012 4:39:07 PM
  • The java program prints out the following time value:
    Timestamp is  2012-01-12 16:39:07.536
  • Notice that this is GMT time. The EST time should be 2012-01-12 11:39:07.536.
  • Now run a java program on a machine that is using EST time to print out the UPDATED_DT value. The code is the following:
    String sql = "SELECT updated_dt FROM foo where ...";
    ...
    Date updatedDt = rs.getTimestamp(2);
    System.out.println("Updated_dt=" + updatedDt + " Timezone Offset=" + updatedDt.getTimezoneOffset()/60);
    
    It prints out the following:
    Updated_dt=2012-01-12 16:39:07.0 Timezone Offset=5




You can see that the UPDATED_DT column has been updated with the GMT time. But when retrieving the value, the JDBC result shown is in EST time, which is wrong!

How to fix this problem? There are two ways. The first way is to change the java code. The second way is to use the new data type feature in Oracle. Using the new Oracle data type will be a better solution if it works because then the programmer won't need to worry about what timezone the servers are using.

To use the java code, one can simply apply the following API of the JDBC class PreparedStatement:

/**
     * Sets the designated parameter to the given java.sql.Timestamp value,
     * using the given Calendar object.  The driver uses
     * the Calendar object to construct an SQL TIMESTAMP value,
     * which the driver then sends to the database.  With a
     *  Calendar object, the driver can calculate the timestamp
     * taking into account a custom timezone.  If no
     * Calendar object is specified, the driver uses the default
     * timezone, which is that of the virtual machine running the application.
     *
     * @param parameterIndex the first parameter is 1, the second is 2, ...
     * @param x the parameter value 
     * @param cal the Calendar object the driver will use
     *            to construct the timestamp
     * @exception SQLException if parameterIndex does not correspond to a parameter
     * marker in the SQL statement; if a database access error occurs or 
     * this method is called on a closed PreparedStatement
     * @since 1.2
     */
    void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal)
     throws SQLException;

Still using the example before, we just need to add one line and then use the above API to update the UPDATED_DT column:
Timestamp ts = new Timestamp(System.currentTimeMillis());
 Calendar cal = Calendar.getInstance(TimeZone
     .getTimeZone("America/New_York"));
 pstmt.setTimestamp(1, ts, cal);
Then when you run the code on a machine that is using GMT, the time value saved into the database will be in EST time.

To use the Oracle to correct the problem, we need to use the database data types that support timezone. For example, Oracle supports following data type:

  • TIMESTAMP WITH TIME ZONE
  • TIMESTAMP WITH LOCAL TIME ZONE
For additional information, see [1] and [2].

The Precision Of The Time Value

In a programming task I worked on, the requirement is to update a table in DB2 database. The table has a column called UpdateTime. Its column data type is TIMESTAMP. A record is first read from the table. Then some column values are modified. And the record in the table needs to be updated with the modified data. But this updating can only be done when the UpdateTime value has not been changed since the record was read. This is to make sure that no other people has modifed the same record. First of all, I have to say that this is not a good way to update the record. A better way is to create a new column, say LockVersion, to hold an integer value for the version number of the record. The version number will be incremented everytime the record is updated. To use the UpdateTime to keep track of the version is a bad idea. That said, I did investigate the problem if UpdateTime should be used. And I found some interesting results in order to make this thing work. Basically the SQL to update the record will be like the following:
update t set x = ?, UpdateTime = current timestamp where UpdateTime = ?
where you will supply the first "?" with the new value of x, and the second "?" with the UpdateTime value that you already retrieved. Unfortunately most likely this query won't work. The two time values have to match exactly to be equal. For example, the time 2010-12-21T14:20:31:354768 is not equal to 2010-12-21T14:20:31:354767999. However, the extra zeroes at the end are OK. For example, 2010-12-21T14:20:31:354768 is equal to 2010-12-21T14:20:31:354768000. So the key here is how to keep the precision of the time value. A DB2 timestamp column has the microsecond precision. An Oracle timestamp column has the nanosecond precision. In the programming world, the precisions are the follows:
  • The java.util.Date has the millisecond precision.
  • The java.sql.Date (a subclass of java.uitl.Date) also has the millisecond precision.
  • The java.sql.Timestamp(a subclass of java.util.Date) has the nanosecond precision.
  • The java.util.GregorianCalendar has the millisecond precision.
  • The javax.xml.datatype.XMLGregorianCalendar can have optional infinite precision fractional seconds.
You can use JDBC resultSet.getTimestamp("UpdateTime") to read the time value. Then this data will go through the following phases through the web service.
  1. The java time object will be converted to an XML string and sent to the client in a SOAP message.
  2. The client will convert the time value in XML string to an java object( or other objects if different programming languages are used). The client will then do some business on the record.
  3. The client converts the time object to an XML string, and send a SOAP message to the server to update the record.
  4. The server converts the XML string in the SOAP message received from the client to a java object and then call the SQL to update the record.
In all the above steps, if any step loses the precision of the time value, the condition "UpdateTime = ?" in the SQL won't succeed and the update will not happen. An immediate observation is that if you retrieve the UpdateTime and store it in a java.util.Date (Note: not its subclass java.util.Timestamp) object or a java.util.Calendar object, the SQL won't work. This is because the Date class and the Calendar class can only save the time up to millisecond. You lose the microseconds or nanoseconds part of the timestamp in the database. I used Spring web service. The following is a list of the things that can be done to make the whole thing work.
  1. In the XML schema for the web service, you can use xsd:dateTime as the type for UpdateTime. This type can accept fractional seconds of any precision. The official definition from http://www.w3.org has the following: A single lexical representation, which is a subset of the lexical representations allowed by [ISO 8601], is allowed for dateTime. This lexical representation is the [ISO 8601] extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century, "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-" sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second respectively. Additional digits can be used to increase the precision of fractional seconds if desired i.e the format ss.ss... with any number of digits after the decimal point is supported.
  2. Database to Java. When you read the UpdateTime from the database, use resultSet.getTimestamp("UpdateTime") and save it into a java.util.Timestamp object.
  3. Java to XML. Use the following to convert the java.util.Timestamp to XMLGregorianCalendar
    public static XMLGregorianCalendar toXMLGregorianCalendar( Timestamp timestamp) throws DatatypeConfigurationException {
      java.util.GregorianCalendar c = new java.util.GregorianCalendar();
      c.setTime(timestamp);
      int fractionalSeconds = timestamp.getNanos();
      XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance()
       .newXMLGregorianCalendar(c);
      xmlCal.setFractionalSecond(new BigDecimal(fractionalSeconds + "E-9")); 
      return xmlCal;
     }
    
    Spring will convert the XMLGregorianCalendar to the xml string and send the SOAP message to the client.
  4. XML to Java.(Client side) On the client side of the web service, if it is java, the java.util.Timestamp should be used. Note that usually you will use some tool to generate the artifacts for the web service client from the WSDL file. I noticed that the RPC client generated by weblogic uses java.util.Calendar for the xsd:dateTime value. I do not know if there are other options that you can change this. But this client won't work because the java.util.Calendar class can only store milliseconds. We need the ability to store microsecond or nanosecond here.
  5. XML to java.(Server side) After receiving the SOAP message from the client, Spring will convert the time string to an XMLGregorianCalendar object. You can use the following to convert XMLGregorianCalendar to java.util.Timestamp:
    public static Timestamp xmlGregorianCalendarToDate(XMLGregorianCalendar xmlCal) {
      Timestamp timestamp = new Timestamp(xmlCal.toGregorianCalendar()
       .getTimeInMillis());
      BigDecimal fr = xmlCal.getFractionalSecond();
      BigDecimal fr2 = fr.multiply(new BigDecimal("1E9"));
      timestamp.setNanos(fr2.intValue());
      }
      return timestamp;
     }
    
  6. Java to database. Now you can use the Timestamp object to do the business and use this in JDBC to update the record. If you use the org.springframework.jdbc.object.SqlUpdate class in Spring JDBC, the parameter type for the UpdateTime can be defined as following:
    declareParameter(new SqlParameter(Types.TIMESTAMP));
The Spring web service also has a way to code the web service client. I used it and successfully updated the record.

References

[1] http://mail-archives.apache.org/mod_mbox/openjpa-users/200903.mbox/%3C49C082FC.9070501@jotobjects.com%3E
[2] http://download.oracle.com/docs/cd/B28359_01/server.111/b28318/datatype.htm#CDEGDFFE

Tuesday, December 7, 2010

Notes on WSDL and SOAP

General Concepts

Both WSDL and SOAP are related to web service. But do not get confused. The WSDL file describes the web service as its name "Web Servcice Description Language" implies. The SOAP is a protocol. Loosely speaking, a WSDL file describes what services the web service can do and what protocol is used to impement the web service. The protocol is usually SOAP.

In a WSDL file, an wsdl:operation is like an API. It has the wsdl:input, wsdl:output, and the optional wsdl:fault child elements.

The operation is implemented in a protocol described in the binding element. The protocol is usually SOAP. Within the binding, you can see that for every wsdl:operation defined in the wsdl:portType, there is the corresponding soap element for the operation, input, output, and the optional fault elements. More formally, a wsdl:binding defines message format and protocol details for operations and messages defined by a particular portType.

The web service implementation needs to be hosted at some place. For this, WSDL uses the wsdl:port element, which is a combination of a binding and a network address. A port is also called an endpoint.

A set of soap:operation forms a wsdl:portType.

A wsdl:service is a collection of ports.

Notes on Spring Web Service

Spring Web service is an open source product aiming to facilitate contract-first SOAP service development. When you use Spring Web Services, you just need to create xml schmas to define the messages. The wsdl file will be generated automatically. The generated WSDL file may not contain the wsdl:fault element at all. But this does not mean the web service will not return any fault messages. Actually Spring Web Services has a feature to convert exceptions to fault messages. The following is the bean configuration for such conversion:
<bean
class="org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver">
  <description>
   This exception resolver maps exceptions with the @SoapFault annotation to SOAP Faults. 
  </description>
  <property name="order" value="1" />
 </bean>

 <bean
class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
  <description>
   This exception resolver maps other exceptions to SOAP Faults. Both
   UnmarshallingException and ValidationFailureException are mapped to a SOAP Fault with a "Client" fault code. All other exceptions are mapped to a "Server" error code, the default.
  </description>
  <property name="defaultFault" value="SERVER" />
  <property name="exceptionMappings">
   <props>
    <prop key="org.springframework.oxm.UnmarshallingFailureException">CLIENT,Invalid request</prop>
    <prop key="org.springframework.oxm.ValidationFailureException">CLIENT,Invalid request</prop>
   </props>
  </property>
  <property name="order" value="2" />
 </bean>

The following is an actual fault message from the log file of a Spring web service:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
 <env:Header />
 <env:Body>
  <env:Fault xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
   <faultcode>ns0:Server</faultcode>
   <faultstring xml:lang="en">
PreparedStatementCallback;uncategorized SQLException for ...
   </faultstring>
  </env:Fault>
 </env:Body>
</env:Envelope>



Reference

[1]http://www.w3.org/TR/wsdl
[2]http://www.w3.org/2001/03/14-annotated-WSDL-examples.html

Tuesday, October 19, 2010

How to Run Ant Build for Weblogic in Eclipse

When you build an application and deploy it to weblogic server, you may need to set the environment first such as calling the following:
C:\bea1032\wlserver_10.3\server\bin\setWLSEnv.cmd. Then you can run the Ant targets that use the weblogic task wlcompile, wlappc, wldeploy, etc. This can be easily done on command line. But how to do it in the Eclipse IDE?

To solve this, just check what setWLSEnv.cmd does for the Ant. This script puts some jars on the classpath. We can put these jars on the classpath of the Ant that Eclipse invokes. You can configure the Ant for your build file. The following is an example for Weblogic workshop Eclipse that comes with Weblogic 10.3.
Right-click on build.xml. Select "Run as" -> "Open External tools Dialog". This will open up the configuration screen for running Ant on the build file.
In the Classpath tab, choose C:\bea10\modules\org.apache.ant_1.6.5 as the Ant Home. Then add the following jars to the classpath:
C:\bea10\wlserver_10.3\server\lib\weblogic.jar
C:\bea10\modules\features\weblogic.server.modules_10.3.0.0.jar
C:\bea10.slwerver_10.3\server\lib\webservices.jar
C:\bea10\modules\org.apache.ant_1.6.5\lib\ant-all.jar
C:\bea10\modules\net.sf.antcontrib_1.0.0.0_1-ob2\lib\ant-contrib.jar

That's it. You can now run the Ant target in the build file directly from Eclipse now.

Thursday, October 7, 2010

How to Debug Application Initialization Using Eclipse

We use weblogic here. In Eclipse, you can start a server in the debug mode. Usually you do the remote debugging of an Java enterprise application by setting break points in the source code and then invoke the application by using the web browser or other clients. But this process does not apply to the initialization process of the application. After the application is deployed and is running, its initialization such as the context initialization has already been finished. What if you want to debug the initialization?
You can start the application in the debug mode from Eclipse. Set the break points in the source code that is called during the initialization. Then invoke the ant build target from Eclipse to deploy the application again. The deploy process will stop at the break points during the initialization and you can debug it now.

Sunday, September 12, 2010

Blog One Year Anniversary

Today is Sep. 12, 2010, Sunday. It has been a year since I started this blog. The fall season seems coming earlier than last year. Some leaves are already red or fell down. There was some rain last night. Today is half cloudy and half sunny. We picked up nine tomatos and one zucchini from the garden. It has been a rewarding harvest in the last several weeks.

Sunday, August 22, 2010

IceFaces, Spring web flow and JSF lifecycle

Some people think that Icefaces is an implementation of JSF. That is actually not right. The heaviest duty that Icefaces put in is its implementation of ViewHandler: D2DViewHandler. It is configured in the faces-config.xml inside icefaces.1.6.2.jar:
<application>
        <view-handler>com.icesoft.faces.application.D2DViewHandler</view-handler>
    </application>
    <factory>
        <faces-context-factory>
             com.icesoft.faces.context.FacesContextFactoryImpl
        </faces-context-factory>
    </factory>
    ....

In the D2DViewHandler, Icefaces uses its own JsfJspDigester to parse the JSF/JSP files. But the core of the whole program is still the LifecycleImpl class ( This class implements javax.faces.lifecycle.Lifecycle ) from SUN to carry out the JSF lifecycle.

Note that the ICEfaces facelet viewHandler is a subclass of the D2DViewHandler:
public class D2DFaceletViewHandler extends D2DViewHandler
D2DFaceletViewHandler inherits from D2DViewHandler all the public methods including the following
public void renderView(FacesContext context, UIViewRoot viewToRender)
throws IOException, FacesException 
This method calls the renderResponse method:
protected void renderResponse(FacesContext facesContext) throws IOException
D2DFaceletViewHandler overrides this method.

We'll check the details of how it works. In this analysis, Icefaces 1.6.2 is used.

The major class of Icefaces is com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet.

The PersistentFacesServlet is actually a MainServlet. It has a PathDispather:
private PathDispatcher dispatcher = new PathDispatcher();
In the init() method, it has the following:
PseudoServlet resourceServer = new BasicAdaptingServlet(new ResourceServer(configuration, mimeTypeMatcher, localFileLocator));
PseudoServlet sessionServer = new SessionDispatcher() {
  protected PseudoServlet newServlet(HttpSession session, Listener.Monitor sessionMonitor) {
           return new MainSessionBoundServlet(session, sessionMonitor, idGenerator, configuration);
  }
};

dispatcher.dispatchOn(".*(\\.iface$|\\.jsf|\\.faces$|\\.jsp$|\\.jspx$|\\.html$|\\.xhtml$|\\.seam$|uploadHtml$|block\\/)", sessionServer);
dispatcher.dispatchOn(".*", resourceServer);

The method dispathOn is the following:
public void dispatchOn(String pathExpression, PseudoServlet toServer) {
        matchers.add(new Matcher(pathExpression, toServer));
}

In short, when an Icefaces URL is sent to the server, this servlet PersistentFacesServlet is invoked. It creates many objects and then services the request. Eventually a PageServer object services the request. The class is the following:
public class PageServer implements Server {
    private final static LifecycleFactory LIFECYCLE_FACTORY = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
    private Lifecycle lifecycle = LIFECYCLE_FACTORY.getLifecycle(LIFECYCLE_FACTORY.DEFAULT_LIFECYCLE);

    private ResponseHandler responseHandler = new ResponseHandler() {
        public void respond(Response response) throws Exception {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            response.setHeader("Cache-Control", new String[]{"no-cache", "no-store", "must-revalidate"});//HTTP 1.1
            response.setHeader("Pragma", "no-cache");//HTTP 1.0
            response.setHeader("Expires", 0);//prevents proxy caching
            response.setHeader("Content-Type", "text/html");
            com.icesoft.util.SeamUtilities.removeSeamDebugPhaseListener(lifecycle);
            lifecycle.execute(facesContext);
            lifecycle.render(facesContext);
        }
    };
Here the lifecycle object and its factory are of the following two classes:
com.sun.faces.lifecycle.LifecycleFactoryImpl
com.sun.faces.lifecycle.LifecycleImpl
The PersistentFacesServlet has created these objects already before using the PageServer.
The code of LifecycleImpl for the two methods is basically the following:
public void execute(FacesContext context) throws FacesException
    {
        if(context == null)
            throw new NullPointerException(Util.getExceptionMessageString("com.sun.faces.NULL_PARAMETERS_ERROR"));
        if(log.isDebugEnabled())
            log.debug("execute(" + context + ")");
        for(int i = 1; i < phases.length; i++)
        {
            if(context.getRenderResponse() || context.getResponseComplete())
                break;
            phase((PhaseId)PhaseId.VALUES.get(i), phases[i], context);
            if(reload((PhaseId)PhaseId.VALUES.get(i), context))
            {
                if(log.isDebugEnabled())
                    log.debug("Skipping rest of execute() because of a reload");
                context.renderResponse();
            }
        }

    }

public void render(FacesContext context) throws FacesException
    {
        if(context == null)
            throw new NullPointerException(Util.getExceptionMessageString("com.sun.faces.NULL_PARAMETERS_ERROR"));
        if(log.isDebugEnabled())
            log.debug("render(" + context + ")");
        if(!context.getResponseComplete())
            phase(PhaseId.RENDER_RESPONSE, response, context);
    }

private void phase(PhaseId phaseId, Phase phase, FacesContext context)
        throws FacesException
    {
        List firedListeners;
        PhaseEvent event;
        if(log.isTraceEnabled())
            log.trace("phase(" + phaseId.toString() + "," + context + ")");
        List ourListeners = listeners;
        int numListeners = ourListeners.size();
        firedListeners = new ArrayList(numListeners);
        if(numListeners <= 0)
            break MISSING_BLOCK_LABEL_573;
        event = new PhaseEvent(context, phaseId, this);
        int i = 0;
        try
        {
            while(i < numListeners) 
            {
                PhaseListener listener = (PhaseListener)ourListeners.get(i);
                PhaseId listenerPhaseId = listener.getPhaseId();
                if(phaseId.equals(listenerPhaseId) || PhaseId.ANY_PHASE.equals(listenerPhaseId))
                {
                    listener.beforePhase(event);
                    firedListeners.add(listener);
                }
                i++;
            }
        }
        catch(Throwable e)
        {
            if(log.isTraceEnabled())
                log.trace("beforePhase(" + phaseId.toString() + "," + context + ") threw exception: " + e + " " + e.getMessage() + "\n" + Util.getStackTraceString(e));
        }
        if(!skipping(phaseId, context))
            phase.execute(context);
        int i = firedListeners.size() - 1;
        try
        {
            while(i >= 0) 
            {
                PhaseListener listener = (PhaseListener)firedListeners.get(i);
                listener.afterPhase(event);
                i--;
            }
        }
        catch(Throwable e)
        {
            if(log.isTraceEnabled())
                log.trace("afterPhase(" + phaseId.toString() + "," + context + ") threw exception: " + e + " " + e.getMessage() + "\n" + Util.getStackTraceString(e));
        }
        break MISSING_BLOCK_LABEL_587;
        Exception exception;
        exception;
        int i = firedListeners.size() - 1;
        try
        {
            while(i >= 0) 
            {
                PhaseListener listener = (PhaseListener)firedListeners.get(i);
                listener.afterPhase(event);
                i--;
            }
        }
        catch(Throwable e)
        {
            if(log.isTraceEnabled())
                log.trace("afterPhase(" + phaseId.toString() + "," + context + ") threw exception: " + e + " " + e.getMessage() + "\n" + Util.getStackTraceString(e));
        }
        throw exception;
        if(!skipping(phaseId, context))
            phase.execute(context);
    }
    
    private Phase phases[] = {
            null, new RestoreViewPhase(), new ApplyRequestValuesPhase(), new ProcessValidationsPhase(), new UpdateModelValuesPhase(), new InvokeApplicationPhase()
    };

Looking at the method execute() and phase(), we can see the following:
  1. The execute() method basically calls each phase except for the last phase "Render Response"
  2. In each phase, the phase listerners for that phase will be invoked for their beforePhase() methods. Then the phase will execute. And then the afterPhase() method will be called. Note that these phase listeners are kept in the LifecycleImpl instance. Each phase just loops through these listeners and invokes only those with the same phase id or the ANY phase id.
  3. During the executions, the methods call the methods renderResponse() and responseComplete() of the class FacesContext frequently to check if the lifecycle needs to be short curcuited.
  4. Each phase implements its own execute() method.

Now the next activity is the call lifecycle.render(facesContext). Recall that the lifecycle.execute() method does not invoke the Render phase.

Note: The methods renderResponse() and responseComplete() of the class FacesContext just signal the JavaServer faces what has been done. They do not actually do the rendering. See Javadoc of FacesContext.

RestoreView Phase

The execute() method has the following piece of code:
public void execute(FacesContext facesContext)
        throws FacesException
    {
        if(log.isDebugEnabled())
            log.debug("Entering RestoreViewPhase");
        if(null == facesContext)
            throw new FacesException(Util.getExceptionMessageString("com.sun.faces.NULL_CONTEXT_ERROR"));
        UIViewRoot viewRoot = facesContext.getViewRoot();
        java.util.Locale locale = null;
        if(viewRoot != null)
        {
            if(log.isDebugEnabled())
                log.debug("Found a pre created view in FacesContext");
            locale = facesContext.getExternalContext().getRequestLocale();
            facesContext.getViewRoot().setLocale(locale);
            doPerComponentActions(facesContext, viewRoot);
            return;
        }
        Map requestMap = facesContext.getExternalContext().getRequestMap();
        String viewId = (String)requestMap.get("javax.servlet.include.path_info");
        if(viewId == null)
            viewId = facesContext.getExternalContext().getRequestPathInfo();
        if(viewId == null)
            viewId = (String)requestMap.get("javax.servlet.include.servlet_path");
        if(viewId == null)
        {
            Object request = facesContext.getExternalContext().getRequest();
            if(request instanceof HttpServletRequest)
                viewId = ((HttpServletRequest)request).getServletPath();
        }
        if(viewId == null)
        {
            if(log.isDebugEnabled())
                log.debug("viewId is null");
            throw new FacesException(Util.getExceptionMessageString("com.sun.faces.NULL_REQUEST_VIEW_ERROR"));
        }
        if(null == (viewRoot = Util.getViewHandler(facesContext).restoreView(facesContext, viewId)))
        {
            if(log.isDebugEnabled())
                log.debug("New request: creating a view for " + viewId);
            viewRoot = Util.getViewHandler(facesContext).createView(facesContext, viewId);
            facesContext.renderResponse();
        } else
        if(log.isDebugEnabled())
            log.debug("Postback: Restored view for " + viewId);
        Util.doAssert(null != viewRoot);
        facesContext.setViewRoot(viewRoot);
        doPerComponentActions(facesContext, viewRoot);
        if(log.isDebugEnabled())
            log.debug("Exiting RestoreViewPhase");
    }

When Spring webflow is used, the FlowPhaseListener's beforePhase() is called in this phase. If it is the start of the flow, the beforePhase() method will call
restoreFlowExecution(event.getFacesContext());
If the flowId is used in the URL, this method will call the following code:
String flowId = argumentHandler.extractFlowId(context);
   FlowDefinition flowDefinition = getLocator(context).getFlowDefinition(flowId);
   FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition);
   FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution);
   FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext);
   ViewSelection selectedView = flowExecution.start(createInput(context), context);
   holder.setViewSelection(selectedView);
We can see that the key objects are FlowDefinition and FlowExecution. At the end, it gets the ViewSelection and saves this in the holder. This ViewSelection will be used in the RenderResponsePhase to render the view.
After the beforePhase() method is called, the execute() method of the RestoreViewPhase will be called. For the first time flow entry, the execute() method will call the following:
if(null == (viewRoot = Util.getViewHandler(facesContext).restoreView(facesContext, viewId)))
        {
            if(log.isDebugEnabled())
                log.debug("New request: creating a view for " + viewId);
            viewRoot = Util.getViewHandler(facesContext).createView(facesContext, viewId);
            facesContext.renderResponse();
        } 
where the "if" condition is true. This is a place where the ViewHandler comes into play. When Icefaces is used, the ViewHandler is D2dViewHandler or D2DFaceletViewHandler. The createView() method is the following:
public UIViewRoot createView(FacesContext context, String viewId) {
        initializeParameters(context);
      

        if (delegateView(viewId)) {
            return delegate.createView(context, viewId);
        }

        // #2139 Remove this token here as well
        ExternalContext externalContext = context.getExternalContext();
      
        externalContext.getRequestParameterMap().remove(
                PersistentFacesCommonlet.SEAM_LIFECYCLE_SHORTCUT);
        
        UIViewRoot root = new NamespacingViewRoot(context);
//        UIViewRoot root = new UIViewRoot();
        root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);

        Map contextServletTable =
                getContextServletTable(context);
        if (null == viewId) {
            root.setViewId("default");
            context.setViewRoot(root);
            contextServletTable
                    .put(DOMResponseWriter.RESPONSE_VIEWROOT, root);
            Locale locale = calculateLocale(context);
            root.setLocale(locale);
            return root;
        }
        root.setViewId(viewId);
        context.setViewRoot(root);
        contextServletTable.put(DOMResponseWriter.RESPONSE_VIEWROOT, root);
        return root;
    }
When Spring web flow is also used and the URL from web client is like
http://localhost:7001/foo/xyz.iface?_flowId=xyz-flow, the viewId is xyz.iface. The line
UIViewRoot root = new NamespacingViewRoot(context);
is called and is set to the context.
However, in the RenderResponsePhase, the FlowPhaseListener will update the ViewRoot in its beforePhase() method and set it to the start view of the web flow. Recall from before that the FlowPhaseListener's beforePhase() has picked up the ViewSelection and stored it in the FlowExecutionHolder. So it seems that the execute() method of the RestoreViewPhase does something but is then ignored. For details of how the ViewRoot is updated in the RenderResponsePhase, see the prepareApplicationView() method and the updateViewRoot() method of the class FlowPhaseListener.

ApplyRequestValuesPhase

public void execute(FacesContext facesContext)
        throws FacesException
    {
        if(log.isDebugEnabled())
            log.debug("Entering ApplyRequestValuesPhase");
        UIComponent component = facesContext.getViewRoot();
        Util.doAssert(null != component);
        try
        {
            component.processDecodes(facesContext);
        }
        catch(RuntimeException re)
        {
            String exceptionMessage = re.getMessage();
            if(null != exceptionMessage && log.isErrorEnabled())
                log.error(exceptionMessage, re);
            throw new FacesException(exceptionMessage, re);
        }
        if(log.isDebugEnabled())
            log.debug("Exiting ApplyRequestValuesPhase");
    }

ProcessValidationsPhase

public void execute(FacesContext facesContext)
        throws FacesException
    {
        if(log.isDebugEnabled())
            log.debug("Entering ProcessValidationsPhase");
        UIComponent component = facesContext.getViewRoot();
        Util.doAssert(null != component);
        try
        {
            component.processValidators(facesContext);
        }
        catch(RuntimeException re)
        {
            String exceptionMessage = re.getMessage();
            if(null != exceptionMessage && log.isErrorEnabled())
                log.error(exceptionMessage, re);
            throw new FacesException(exceptionMessage, re);
        }
        if(log.isDebugEnabled())
            log.debug("Exiting ProcessValidationsPhase");
    }


UpdateModelValuesPhase

public void execute(FacesContext facesContext)
    {
        if(log.isDebugEnabled())
            log.debug("Entering UpdateModelValuesPhase");
        UIComponent component = facesContext.getViewRoot();
        Util.doAssert(null != component);
        String exceptionMessage = null;
        try
        {
            component.processUpdates(facesContext);
        }
        catch(IllegalStateException e)
        {
            exceptionMessage = e.getMessage();
        }
        catch(FacesException fe)
        {
            exceptionMessage = fe.getMessage();
        }
        if(null != exceptionMessage)
        {
            Object params[] = new Object[3];
            params[2] = exceptionMessage;
            facesContext.addMessage(component.getClientId(facesContext), MessageFactory.getMessage(facesContext, "com.sun.faces.MODELUPDATE_ERROR", params));
            if(log.isErrorEnabled())
                log.error(exceptionMessage);
            if(log.isDebugEnabled())
                log.debug("Exiting UpdateModelValuesPhase");
        }
    }


InvokeApplicationPhase

public void execute(FacesContext facesContext)
        throws FacesException
    {
        if(log.isDebugEnabled())
            log.debug("Entering InvokeApplicationsPhase");
        UIViewRoot root = facesContext.getViewRoot();
        Util.doAssert(null != root);
        try
        {
            root.processApplication(facesContext);
        }
        catch(RuntimeException re)
        {
            String exceptionMessage = re.getMessage();
            if(null != exceptionMessage && log.isErrorEnabled())
                log.error(exceptionMessage, re);
            throw new FacesException(exceptionMessage, re);
        }
        if(log.isDebugEnabled())
            log.debug("Exiting InvokeApplicationsPhase");
    }

Notes on UIViewRoot:
  1. All the events are stored in this object. The instance variable events is a list of list. Each event has a PhaseId. The event is queued in the list for that phase.
  2. Each event is associated with the component which is the source of the event.
  3. The broadcastEvents() method delegate the handling to the same method of the event source component. It was done first for the ANY
    PhaseId, and then the PhaseId of this phase.
For UICommand component, broadCastEvents() calls the parent class method. And then if it is an ActionEvent, it uses MethodBinding and ActionListener to handle the event.
package javax.faces.component;
....
public class UIViewRoot extends UIComponentBase
    private int lastId;
    private String renderKitId;
    private String viewId;
    private transient List events[]; 
    private Locale locale;
    
    public void queueEvent(FacesEvent event)
        {
            if(event == null)
                throw new NullPointerException();
            int i = 0;
            int len = 0;
            if(events == null)
            {
                events = new List[len = PhaseId.VALUES.size()];
                for(i = 0; i < len; i++)
                    events[i] = new ArrayList(5);
    
            }
            events[event.getPhaseId().getOrdinal()].add(event);
    }
    
public void processApplication(FacesContext context)
    {
        broadcastEvents(context, PhaseId.INVOKE_APPLICATION);
    }
    
private void broadcastEvents(FacesContext context, PhaseId phaseId)
    {
        List eventsForPhaseId = null;
        if(null == events)
            return;
        boolean hasMoreAnyPhaseEvents = true;
        boolean hasMoreCurrentPhaseEvents = true;
        eventsForPhaseId = events[PhaseId.ANY_PHASE.getOrdinal()];
        do
        {
            if(null != eventsForPhaseId)
            {
                for(int cursor = 0; cursor < eventsForPhaseId.size(); eventsForPhaseId.remove(cursor))
                {
                    FacesEvent event = (FacesEvent)eventsForPhaseId.get(cursor);
                    UIComponent source = event.getComponent();
                    try
                    {
                        source.broadcast(event);
                    }
                    catch(AbortProcessingException e) { }
                }

            }
            if(null != (eventsForPhaseId = events[phaseId.getOrdinal()]))
            {
                for(int cursor = 0; cursor < eventsForPhaseId.size(); eventsForPhaseId.remove(cursor))
                {
                    FacesEvent event = (FacesEvent)eventsForPhaseId.get(cursor);
                    UIComponent source = event.getComponent();
                    try
                    {
                        source.broadcast(event);
                    }
                    catch(AbortProcessingException e) { }
                }

            }
            hasMoreAnyPhaseEvents = null != (eventsForPhaseId = events[PhaseId.ANY_PHASE.getOrdinal()]) && eventsForPhaseId.size() > 0;
            hasMoreCurrentPhaseEvents = null != events[phaseId.getOrdinal()] && events[phaseId.getOrdinal()].size() > 0;
        } while(hasMoreAnyPhaseEvents || hasMoreCurrentPhaseEvents);
    }

UICommand is in the package javax.faces.component
public class UICommand extends UIComponentBase
    implements ActionSource
{
   .......
    public void broadcast(FacesEvent event)
            throws AbortProcessingException
        {
            super.broadcast(event);
            if(event instanceof ActionEvent)
            {
                FacesContext context = getFacesContext();
                MethodBinding mb = getActionListener();
                if(mb != null)
                    mb.invoke(context, new Object[] {
                        event
                    });
                ActionListener listener = context.getApplication().getActionListener();
                if(listener != null)
                    listener.processAction((ActionEvent)event);
            }
    }
 }   
The HtmlCommandLink and HtmlCommandButton are subclasses of UICommand. Icefaces has the class com.icesoft.faces.component.ext.HtmlCommandLink, which extends javax.faces.component.HtmlCommandLink.
package com.sun.faces.application

public class ActionListenerImpl
    implements ActionListener
{
  .....
  public void processAction(ActionEvent event)
    {
        if(log.isDebugEnabled())
            log.debug("processAction(" + event.getComponent().getId() + ")");
        UIComponent source = event.getComponent();
        ActionSource actionSource = (ActionSource)source;
        FacesContext context = FacesContext.getCurrentInstance();
        Application application = context.getApplication();
        String outcome = null;
        MethodBinding binding = null;
        binding = actionSource.getAction();
        if(binding != null)
            try
            {
                outcome = (String)binding.invoke(context, null);
            }
            catch(MethodNotFoundException e)
            {
                throw new FacesException(binding.getExpressionString() + ": " + e, e);
            }
            catch(EvaluationException e)
            {
                throw new FacesException(binding.getExpressionString() + ": " + e, e);
            }
        NavigationHandler navHandler = application.getNavigationHandler();
        navHandler.handleNavigation(context, null == binding ? null : binding.getExpressionString(), outcome);
        context.renderResponse();
    }

}
Note that this is where the NavigationHandler comes into play . When Spring webflow is used, the NavigationHandler is FlowNavigationHandler. Its handleNavigation() method will calculate the
ViewSelection from the FlowExecution and save it in the FlowExecutionHolder:
holder.setViewSelection(selectedView);

RenderResponsePhase

The execute() method has the following main code:
public void execute(FacesContext facesContext)
         throws FacesException
     {
         if(log.isDebugEnabled())
             log.debug("Entering RenderResponsePhase");
         if(log.isDebugEnabled())
             log.debug("About to render view " + facesContext.getViewRoot().getViewId());
         try
         {
             facesContext.getApplication().getViewHandler().renderView(facesContext, facesContext.getViewRoot());
         }
         catch(IOException e)
         {
             throw new FacesException(e.getMessage(), e);
         }
         if(log.isDebugEnabled())
             log.debug("Exiting RenderResponsePhase");
    }
Notice that this is another place where the ViewHandler comes into play. It renders the view which is the ViewRoot from the FacesContext. When Spring webflow is used, the FlowPhaseListener sets the ViewRoot in its beforePhase() method. In this method, it calls the prepareResponse() method. This method gets the ViewSelection in the FlowExecutionHolder:
ViewSelection selectedView = holder.getViewSelection();
Recall that in the InvokeApplicationPhase, the Spring NavigationHandler has set the ViewSelection in the holder. Then the prepareResponse() method creates a ResponseInstructionHandler object and calls its handleQuietly() method.
The handleQuietly() method deals with several cases. For one of the cases, it calls
handleApplicationView((ApplicationView)responseInstruction.getViewSelection());
The ResponseInstructionHandler object implements this method as follows:
protected void handleApplicationView(ApplicationView view) throws Exception {
prepareApplicationView(context.getFacesContext(), holder);
}
The method prepareApplicationView() is a method in the FlowPhaseListener class as the ResponseInstructionHandler object is a created inside this class:
protected void prepareApplicationView(FacesContext facesContext, FlowExecutionHolder holder) {
ApplicationView view = (ApplicationView) holder.getViewSelection();
if (view != null) {
// expose the view's "model map" in the request map
putInto(facesContext.getExternalContext().getRequestMap(), view.getModel());
// update the root component if necessary
updateViewRoot(facesContext, viewIdMapper.mapViewId(view.getViewName()));
}
String flowExecutionKey = holder.getFlowExecution().isActive() ? holder.getFlowExecutionKey().toString() : null;
if (flowExecutionKey != null) {
saveInViewRoot(facesContext, flowExecutionKey);
}
Map requestMap = facesContext.getExternalContext().getRequestMap();
argumentHandler.exposeFlowExecutionContext(flowExecutionKey, holder.getFlowExecution(), requestMap);
}
We can see that this method updates the ViewRoot in the FacesContext. And this completes the preparation because a ViewHandler will get the ViewRoot from FacesContext and render it.

Some key Classes

PhaseId
package javax.faces.event;

import java.util.*;

public class PhaseId
implements Comparable
{
  ......
  public static final List VALUES;

  static 
  {
    ANY_PHASE = new PhaseId("ANY");
    RESTORE_VIEW = new PhaseId("RESTORE_VIEW");
    APPLY_REQUEST_VALUES = new PhaseId("APPLY_REQUEST_VALUES");
    PROCESS_VALIDATIONS = new PhaseId("PROCESS_VALIDATIONS");
    UPDATE_MODEL_VALUES = new PhaseId("UPDATE_MODEL_VALUES");
    INVOKE_APPLICATION = new PhaseId("INVOKE_APPLICATION");
    RENDER_RESPONSE = new PhaseId("RENDER_RESPONSE");
    values = (new PhaseId[] { ANY_PHASE, RESTORE_VIEW, APPLY_REQUEST_VALUES, PROCESS_VALIDATIONS, UPDATE_MODEL_VALUES, INVOKE_APPLICATION, RENDER_RESPONSE
    });
    VALUES = Collections.unmodifiableList(Arrays.asList(values));
  }
}

FlowPhaseListener

The Spring class FlowPhaseListener is a key in integrating Spring web flow into JSF. The following are some important methods of the class.
public PhaseId getPhaseId() {
  return PhaseId.ANY_PHASE;
 }

 public void beforePhase(PhaseEvent event) {
  FacesContext context = event.getFacesContext();
  if (event.getPhaseId() == PhaseId.RESTORE_VIEW) {
   ExternalContextHolder.setExternalContext(new JsfExternalContext(context));
   restoreFlowExecution(event.getFacesContext());
   // we do not need to worry about clean up here since other phases will continue to run even if an exception
   // occurs in restoreFlowExecution(FacesContext)
  } else if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
   if (FlowExecutionHolderUtils.isFlowExecutionRestored(event.getFacesContext())) {
    try {
     prepareResponse(getCurrentContext(), FlowExecutionHolderUtils.getFlowExecutionHolder(context));
    } catch (RuntimeException e) {
     // we must cleanup here since this is the render response phase and the after phase callback will
     // NOT run when an exception occurs (which typically does the cleanup--see below)
     cleanupResources(context);
     throw e;
    } catch (Error e) {
     cleanupResources(context);
     throw e;
    }
   }
  }
 }

 public void afterPhase(PhaseEvent event) {
  FacesContext context = event.getFacesContext();
  if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
   if (FlowExecutionHolderUtils.isFlowExecutionRestored(context)) {
    try {
     saveFlowExecution(getCurrentContext(), FlowExecutionHolderUtils.getFlowExecutionHolder(context));
    } finally {
     // always cleanup after save - we are done with flow execution request processing
     cleanupResources(context);
    }
   }
  } else {
   // cleanup if some other JSF artifact marked 'response complete' to short-circuit the lifecycle early
   if (context.getResponseComplete()) {
    cleanupResources(context);
   }
  }
 }

protected void restoreFlowExecution(FacesContext facesContext) {
  JsfExternalContext context = new JsfExternalContext(facesContext);
  if (argumentHandler.isFlowExecutionKeyPresent(context)) {
   // restore flow execution from repository so it will be available to JSF artifacts
   // (this could happen as part of a flow execution redirect or browser refresh)
   FlowExecutionRepository repository = getRepository(context);
   FlowExecutionKey flowExecutionKey = repository.parseFlowExecutionKey(argumentHandler
     .extractFlowExecutionKey(context));
   try {
    FlowExecutionLock lock = repository.getLock(flowExecutionKey);
    lock.lock();
    try {
     FlowExecution flowExecution = repository.getFlowExecution(flowExecutionKey);
     if (logger.isDebugEnabled()) {
      logger.debug("Loaded existing flow execution with key '" + flowExecutionKey
        + "' due to browser access "
        + "[either via a flow execution redirect or direct browser refresh]");
     }
     FlowExecutionHolderUtils.setFlowExecutionHolder(new FlowExecutionHolder(flowExecutionKey,
       flowExecution, lock), facesContext);
    } catch (RuntimeException e) {
     lock.unlock();
     throw e;
    } catch (Error e) {
     lock.unlock();
     throw e;
    }
   } catch (FlowExecutionAccessException e) {
    // thrown if access to the execution could not be granted
    handleFlowExecutionAccessException(e, facesContext);
   }
  } else if (argumentHandler.isFlowIdPresent(context)) {
   // launch a new flow execution
   // (this could happen as part of direct browser access or a flow definition redirect)
   String flowId = argumentHandler.extractFlowId(context);
   FlowDefinition flowDefinition = getLocator(context).getFlowDefinition(flowId);
   FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition);
   FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution);
   FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext);
   ViewSelection selectedView = flowExecution.start(createInput(context), context);
   holder.setViewSelection(selectedView);
   if (logger.isDebugEnabled()) {
    logger.debug("Launched a new flow execution due to browser access "
      + "[either via a flow redirect or direct browser URL access]");
   }
  }
 }

/**
  * Prepare the JSF view for rendering.
  * @param facesContext the faces context
  * @param holder the holder of the current flow execution
  */
 protected void prepareApplicationView(FacesContext facesContext, FlowExecutionHolder holder) {
  ApplicationView view = (ApplicationView) holder.getViewSelection();
  if (view != null) {
   // expose the view's "model map" in the request map
   putInto(facesContext.getExternalContext().getRequestMap(), view.getModel());
   // update the root component if necessary
   updateViewRoot(facesContext, viewIdMapper.mapViewId(view.getViewName()));
  }
  String flowExecutionKey = holder.getFlowExecution().isActive() ? holder.getFlowExecutionKey().toString() : null;
  if (flowExecutionKey != null) {
   saveInViewRoot(facesContext, flowExecutionKey);
  }
  Map requestMap = facesContext.getExternalContext().getRequestMap();
  argumentHandler.exposeFlowExecutionContext(flowExecutionKey, holder.getFlowExecution(), requestMap);
 }
private void updateViewRoot(FacesContext facesContext, String viewId) {
  UIViewRoot viewRoot = facesContext.getViewRoot();
  if (viewRoot == null || hasViewChanged(viewRoot, viewId)) {
   // create the specified view so that it can be rendered
   ViewHandler handler = facesContext.getApplication().getViewHandler();
   UIViewRoot view = handler.createView(facesContext, viewId);
   facesContext.setViewRoot(view);
  }
 }

So the beforePhase only reacts to the RESTORE_VIEW and RENDER_RESPONSE phases.

FlowNavigationHandler
The class FlowNavigationHandler is another key in integration of JSF and Spring web flow:
public class FlowNavigationHandler extends DecoratingNavigationHandler {

......

public void handleNavigation(FacesContext facesContext, String fromAction, String outcome,
   NavigationHandler originalNavigationHandler) {
  try {
   JsfExternalContext context = getCurrentContext();
   // record the navigation handler context
   context.handleNavigationCalled(fromAction, outcome);
   // first see if we need to launch a new flow execution if the flow id is present
   if (argumentExtractor.isFlowIdPresent(context)) {
    // a flow execution launch has been requested - create the new execution
    String flowId = argumentExtractor.extractFlowId(context);
    FlowDefinition flowDefinition = getLocator(context).getFlowDefinition(flowId);
    FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition);
    // check to see if this execution was created while another was running
    if (FlowExecutionHolderUtils.isFlowExecutionRestored(facesContext)) {
     // replace the current flow execution with the new one
     FlowExecutionHolderUtils.getFlowExecutionHolder(facesContext).replaceWith(flowExecution);
    } else {
     // bind the new execution as the 'current execution'
     FlowExecutionHolderUtils.setFlowExecutionHolder(new FlowExecutionHolder(flowExecution),
       facesContext);
    }
    // start the new execution
    ViewSelection selectedView = flowExecution.start(createInput(context), context);
    // set the starting view to render
    FlowExecutionHolderUtils.getFlowExecutionHolder(facesContext).setViewSelection(selectedView);
   } else {
    // not a launch request - see if this is a resume request to continue an existing execution
    if (FlowExecutionHolderUtils.isFlowExecutionRestored(facesContext)) {
     // a flow execution has been restored - see if we need to signal an event against it
     if (argumentExtractor.isEventIdPresent(context)) {
      // signal the event against the current flow execution
      String eventId = argumentExtractor.extractEventId(context);
      try {
       FlowExecutionHolder holder = FlowExecutionHolderUtils.getFlowExecutionHolder(facesContext);
       ViewSelection selectedView = holder.getFlowExecution().signalEvent(eventId, context);
       // set the next view to render
       holder.setViewSelection(selectedView);
      } catch (NoMatchingTransitionException e) {
       if (logger.isDebugEnabled()) {
        logger.debug("No flow state transition found for event '" + eventId
          + "'; falling back to standard navigation handler.");
       }
       // not a valid event in the current state: proceed with standard navigation
       originalNavigationHandler.handleNavigation(facesContext, fromAction, outcome);
      }
     }
    } else {
     // neither a flow launch or resume request: proceed with standard navigation
     originalNavigationHandler.handleNavigation(facesContext, fromAction, outcome);
    }
   }
  } catch (RuntimeException e) {
   cleanupResources(facesContext);
   throw e;
  } catch (Error e) {
   cleanupResources(facesContext);
   throw e;
  }
 }

D2dViewHandler
package com.icesoft.faces.application;
...
public class D2DViewHandler extends ViewHandler {

  ......

 /**
     * Create a new ViewRoot
     *
     * @param context FacesContext
     * @param viewId  ViewId identifying the root
     * @return A new viewRoot
     */
    public UIViewRoot createView(FacesContext context, String viewId) {
        initializeParameters(context);
      

        if (delegateView(viewId)) {
            return delegate.createView(context, viewId);
        }

        // #2139 Remove this token here as well
        ExternalContext externalContext = context.getExternalContext();
      
        externalContext.getRequestParameterMap().remove(
                PersistentFacesCommonlet.SEAM_LIFECYCLE_SHORTCUT);
        
        UIViewRoot root = new NamespacingViewRoot(context);
//        UIViewRoot root = new UIViewRoot();
        root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);

        Map contextServletTable =
                getContextServletTable(context);
        if (null == viewId) {
            root.setViewId("default");
            context.setViewRoot(root);
            contextServletTable
                    .put(DOMResponseWriter.RESPONSE_VIEWROOT, root);
            Locale locale = calculateLocale(context);
            root.setLocale(locale);
            return root;
        }

        root.setViewId(viewId);
        context.setViewRoot(root);
        contextServletTable.put(DOMResponseWriter.RESPONSE_VIEWROOT, root);

        return root;
   }
}

Tuesday, August 10, 2010

Some Typical Hibernate Operations

First some concepts of Hibernate. Hibernate defines the following object states:
  • Transient - an object is transient if it is just created but has no primary key (identifier) and not associated with session.
  • Persistent - The object is in persistent state if session is open, and you just saved the instance in the database or retrieved the instance from the database.
  • Detached - a detached instance is an object that has been persistent, but its Session has been closed. A detached instance can be reattached to a new Session at a later point in time, making it persistent again. After detached state, object comes to persistent state if you call lock() or update() method.
In the following code, Spring is used. HibernateTemplate is a Spring class.

1. To Load and Update an Object from Database
HibernateTemplate hTemplate = getHibernateTemplate();
MyDto dto = (MyDto)hTemplate.load(MyDto.class, myObjectId);
dto.setUpdatedBy(user.getUsername());
dto.setUpdatedDt(new Date());
dto.setStatus(status);
hTemplate.flush();

2. To Update an Object Whose Hibernate State may be Transient or Detached
HibernateTemplate hTemplate = getHibernateTemplate();
// myDto is an input. It may be in transient or detached state
MyDto persistentDto = (MyDto) hTemplate.merge(myDto);
hTemplate.update(persistentDto);
Note: The merge method returns an object in the persistent state

3. To Load and Initialize an Object
MyDto dto = (MyDto) getHibernateTemplate().load(MyDto.class,myObjectId);
Hibernate.initialize(dto);
Or use the following that does not need initialization:
Mydto dto= (MyDto) getHibernateTemplate().get(MyDto.class, myObjectId);

References

  1. Hibernate Reference Documentation Version 3.2.2

Thursday, August 5, 2010

How to Use Weblogic WLST to Redeploy An Application, etc.

How to redeploy an application

Suppose the application is an ear file and it has already been deployed. Now you have made some changes and generated a new ear file. To redeploy the ear, you can do the following steps:

  1. Overwrite the old ear with the new ear file.
  2. Open wlst and perform the redeployment.

Suppose the name of the application is "myApp". The following is some trace of the wlst redeployment:
C:\bea921\weblogic92\common\bin>wlst

......

Initializing WebLogic Scripting Tool (WLST) ...

Welcome to WebLogic Server Administration Scripting Shell

Type help() for help on available commands


wls:/offline> connect("userFoo","password","t3://localhost:7001");
Connecting to t3://localhost:7001 with userid userFoo

wls:/mydomain/serverConfig>

wls:/mydomain/serverConfig> progress=redeploy('myApp');
Redeploying application myApp ...
<Aug 5, 2010 11:45:06 AM EDT> <Info> <J2EE Deployment SPI> <BEA-260121> <Initiat
ing redeploy operation for application, ......
>
..................Completed the redeployment of Application with status complete
d
Current Status of your Deployment:
Deployment command type: redeploy
Deployment State       : completed
Deployment Message     : no message
wls:/mydomain/serverConfig>

Note 1: The WLST redeploy command takes some optional arguments. You can use appPath to specify the name of the archive file or root of the exploded archive directory to be redeployed. My experiment is that if you put the new ear in a different location and use appPath to point to it, you will get the error message "Changing the source location is not allowed for a previously attempted deployment. Try deploying without specifying the source."
Note 2: The redeploy command will create a directory "config" under the current directory. In the example above, it will be C:\bea921\weblogic92\common\bin\config. If you do not have the right to create a sub-directory, you will get an error like "java.io.IOException: [J2EE Deployment SPI:260080]Unable to define application install directory at '/opt/bea/wl9.2.1/weblogic92/common/bin/config/deployments/...'. Directory could not be created or is a file.". If that is the case, you can go to a different directory where you can create a sub-directory and call the WLST command there.

Other WLST Commands for Deployment

You can also use WLST to start/stop/deploy/undeploy an application. The following is the simplified trace of performing these tasks.
C:\bea921\weblogic92\common\bin>wlst
wls:/offline> progress=stopApplication('myApp')
You will need to be connected to a running server to execute this command

wls:/offline> connect("userFoo","password","t3://localhost:7001");
Connecting to t3://localhost:7001 with userid userFoo ...

wls:/mydomain/serverConfig> progress=stopApplication('myApp');

..Completed the stop of Application with status completed

wls:/mydomain/serverConfig> progress=startApplication('myApp');
Starting application myApp.

......Completed the start of Application with status completed

wls:/mydomain/serverConfig> stopApplication('myApp');
Stopping application myApp.

.Completed the stop of Application with status completed

wls:/mydomain/serverConfig> undeploy('myApp', timeout=60000);
Undeploying application myApp ...

.Completed the undeployment of Application with status completed

wls:/mydomain/serverConfig> progress=deploy(appName='myApp',path='C:/home/deploy
/LOC/app/myApp.ear');

.........................Completed the deployment of Application with status completed

wls:/mydomain/serverConfig>

Note that you can undeploy the application without stopping it first.
It seems that when the ear is uploaded to weblogic, undeploy will delete the ear file. But if the ear is at outside location, undeploy will not delete the ear file. However, more investigation is needed for this.

Reference

http://download.oracle.com/docs/cd/E13222_01/wls/docs92/config_scripting/reference.html#wp1024321

Sunday, May 30, 2010

Client Request, Thread, and Java EE Container

For each web request, in general, the web container will not create a new servlet instance. Actually only one servelet instance object is needed for each type. But the web container will create a new thread to handle each request. The following link explains well how the web container creates threads and use the servlet instance to serve the new client request.
http://www.datadisk.co.uk/html_docs/jsp/jsp_web_app_architecture.htm

Similarly, for each new remote client request to the EJB container, the container will create a new EJB object ( and a new thread to handle the request, I think). The EJB object will use a bean instance. But the bean instance does not need to be created everytime. EJB container uses instance pooling for the bean classes.
The following is an article that explains java RMI internal details:
http://www.developer.com/java/other/article.php/10936_3455311_1/Understanding-Java-RMI-Internals.htm

Thursday, May 27, 2010

Private Keys, Digital Certificate, and Trusted Certificate Authorities Concept

Concepts

Servers need a private key, a digital certificate containing the matching public key, and a certificate for at least one trusted certificate authority. We use CA to denote Certificate Authority.

A private key and digital certificate provide identity for the server.

The digital certificate for identiy and the trusted CA certificate are both certificates. But they serve for very different purposes for the server. Basically what these two things are saying are the following:
  1. I have a digital certificate.
  2. I would like to talk with the following CAs (the trusted CAs).
So the digital certificate is for my own identity. The trusted CA certificates are for others. They are what I would like to talk to.

The two words CA and CA certificate are often used as the same thing. This is because CA is just a concept while the CA certificate is the actual material used for the identity of a CA.

My digital certificate itself needs to be digitally signed with a CA's digital certificate. This CA can be different from my trusted CAs. You can even sign your digital certificate by yourself (self-signed digital certificate). As in the web browser, you can add new trusted CAs to the existing trusted CA list for the browser. The digital certificate itself does not have any information about what others I would like to talk to. There must be a separate security process that can make decision using my digital certificate and the trusted CA certificates.

What Does a Digital Certificate Contain?

A digital certificate contains the owner(or subject)'s DN(name, address, company, etc), the public key, the issuer(or signer)'s DN, the signature of the issuer, etc. The issuer is the CA for this certificate.

How the Parties Communicate?

Data encrypted with the public key can only be decrypted using the corresponding private key, and the data encrypted with the private key can only be decrypted using the corresponding public key.

I think it may work this way. When A talks to B, A will receive the digital certificate of B. The digital certificate of B contains the CA who signs it for B. If this CA is in the trusted CA list of A, A will talk to B. Otherwise, A can refuse to talk to B. In the case that it is a trusted CA, A will use the public key embedded in B's digital certificate and encrypt the data and then send the data to B. B will use its own private key to decrypt the data. Note that only B can decrypt the data. Even A itself won't be able to decrypt the data it sent because A does not know B's private key. On the reverse direction, once A acknowledges B as trusted, A can send its degital certificate to B and B can check to see if A is in its trusted CA list and do the data encryption and sending. For details about SSL handshake protocol, see [2].

Create, Store and View the Keys And Certificates

1. You can obtain digital certificates, private keys, and trusted CA certificates from the CertGen utility, Sun Microsystem's keytool utility, or a reputable vendor such as Entrust or Verision. You can also use the command "keytool -genkeypair ". Note that this command will generate only one file, which is a keystore. But this file contains both the private key and the self-signed certificate.
2. Store the private keys, digital certificates, and trusted CA certificates in a keystore.
3. The command "keytool -list -v -keystore yourKeyStore -storepass yourKeyStorePassword" can be used to print out all the certificates in your key store.

Personal Information Exchange File

The file with the extension .p12 is a special type of certificate file. It is called the Personal Information Exchange file. The certificate contains both the public key and the private key.

Certificate Chain, Certificate Verification

Reference [5] shows a good diagram about how the certificate chain works. I think the procedure works in the following way according to the diagram.
  1. You receive someone's certificate C_1.
  2. Since every certificate contains the CA who signed the certificate, you can get the information of that CA ( say CA_2). But how do you know that this is truly the CA as it claimes? CA_2 can say it is Verisign even thouhg it is actually not. So the issue here is to check if the CA is really who it claims to be.
  3. So from C_1, you can get the DN(Distinguished Name ) of CA_2. From this DN you can get the public key of CA_2.
  4. Use the public key in step 3 and the signature of CA_2 ( which is contained in C_1) to check if they agree. If not, it is fake. This is the essential part of this whole procedure. How can you verify the signature? Reference[6] says that
    "The CA then signs the certificate using its private key, which is used to verify the certificate."
    "To verify a certificate, all that is necessary is the public key of the CA and a check against the CRL(certificate revocation lists) published by that CA."
    Reference[7] has more information about how the certificate is verified. Note that C_1 does not contain the private key of CA_2. It just contains the data ( signature ) that CA_2 uses its private key to generate. This data (signature) can be decrypted with the public key of CA_2.
  5. If step 4 is good, continue the same verification for C_2.

The above is roughly the idea. Note that step 5 and 6 may be not exactly that if the algorithm to vefify the CA's is a recursive one.

Various standards and implementations

It seems that there are many implementations in the java security area. For example, the way to validate a certificate is not fixed. Weblogic server SSL has two certificate lookup and validation providers. And you can even write a custom CertPath Validator to provide additional validation on the certificate chain. So a certificate may or may not pass the validation depending on the validation providers used.

References

  1. Weblogic Document "Securing WebLogic Server"
  2. http://docs.sun.com/source/816-6156-10/contents.htm#1041640
  3. http://download.oracle.com/javase/1.5.0/docs/tooldocs/windows/keytool.html
  4. http://www.flatmtn.com/article/creating-pkcs12-certificates
  5. http://publib.boulder.ibm.com/infocenter/wmqv6/v6r0/index.jsp?topic=/com.ibm.mq.csqzas.doc/sy10600_.htm
  6. http://support.microsoft.com/kb/195724
  7. http://en.wikipedia.org/wiki/X.509
  8. http://tools.ietf.org/html/rfc2459#page-18
  9. http://docs.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html

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>

Friday, April 23, 2010

Sortable JSF DataModel for Spring Webflow

When JSF is integrated with Spring webflow, the DataModel needs to be Serializable. But the standard javax.faces.model.DataModel does not implement the Serializable interface. To get around this problem, we can extend the model. We can also add some artifacts for the column sorting purpose. The following are the two classes for this.

The Serializable DataModel

package mypackage;

import java.io.Serializable;
import java.util.List;

import javax.faces.model.DataModel;
import javax.faces.model.DataModelEvent;
import javax.faces.model.DataModelListener;

/**
 
 * SerializableListDataModel is basically the ListDataModel class from
 * jsf-api-1.1, except that it implements the Serializable interface. This
 * interface is needed by ICEfaces when rendering the table.
 */
public class SerializableListDataModel extends DataModel
    implements
      Serializable
{

  public SerializableListDataModel()
  {
    this(null);
  }

  public SerializableListDataModel(List list)
  {
    index = -1;
    setWrappedData(list);
  }

  public boolean isRowAvailable()
  {
    if (list == nullreturn false;
    return index >= && index < list.size();
  }

  public int getRowCount()
  {
    if (list == nullreturn -1;
    else
      return list.size();
  }

  public Object getRowData()
  {
    if (list == nullreturn null;
    if (!isRowAvailable()) throw new IllegalArgumentException();
    else
      return list.get(index);
  }

  public int getRowIndex()
  {
    return index;
  }

  public void setRowIndex(int rowIndex)
  {
    if (rowIndex < -1throw new IllegalArgumentException();
    int old = index;
    index = rowIndex;
    if (list == nullreturn;
    DataModelListener listeners[] = getDataModelListeners();
    if (old != index && listeners != null)
    {
      Object rowData = null;
      if (isRowAvailable()) rowData = getRowData();
      DataModelEvent event = new DataModelEvent(this, index, rowData);
      int n = listeners.length;
      for (int i = 0; i < n; i++)
        if (null != listeners[i]) listeners[i].rowSelected(event);

    }
  }

  public Object getWrappedData()
  {
    return list;
  }

  public void setWrappedData(Object data)
  {
    if (data == null)
    {
      list = null;
      setRowIndex(-1);
    }
    else
    {
      list = (Listdata;
      index = -1;
      setRowIndex(0);
    }
  }

  private int index;
  private List list;
}

The Sortable DataModel

package mypackage;

import java.io.Serializable;
import java.util.Comparator;
import java.util.Date;

import javax.faces.model.DataModel;
import javax.faces.model.DataModelListener;

import org.springframework.util.StringUtils;

/**
 
 * SortableDataModel to make the table columns sortable.
 
 */
public class SortableDataModel extends DataModel implements Serializable
{

  private DataModel model;

  private Row[] rows;

  public SortableDataModel()
  {
  }

  public SortableDataModel(DataModel dataModel)
  {
    this.model = dataModel;
    if (model != null)
    {
      initializeRows();
    }
    else
    {
      rows = null;
    }
  }

  protected Row[] getRows()
  {
    return rows;
  }

  private void initializeRows()
  {
    int rowCnt = model.getRowCount();
    if (rowCnt != -1)
    {
      rows = new Row[rowCnt];
      for (int i = 0; i < rowCnt; ++i)
      {
        rows[inew Row(i);
      }
    }
  }

  @Override
  public int getRowCount()
  {
    return model.getRowCount();
  }

  @Override
  public Object getRowData()
  {
    return model.getRowData();
  }

  @Override
  public int getRowIndex()
  {
    return model.getRowIndex();
  }

  @Override
  public Object getWrappedData()
  {
    if (model == null)
    {
      return null;
    }
    return model.getWrappedData();
  }

  @Override
  public boolean isRowAvailable()
  {
    return model.isRowAvailable();
  }

  @Override
  public void setWrappedData(Object data)
  {
    model.setWrappedData(data);
  }

  @Override
  public void addDataModelListener(DataModelListener listener)
  {
    model.addDataModelListener(listener);
  }

  @Override
  public DataModelListener[] getDataModelListeners()
  {
    return model.getDataModelListeners();
  }

  @Override
  public void removeDataModelListener(DataModelListener listener)
  {
    model.removeDataModelListener(listener);
  }

  public void setRowIndex(int rowIndex)
  {
    if (rowIndex == -|| rowIndex >= model.getRowCount())
    {
      model.setRowIndex(rowIndex);
    }
    else
    {
      model.setRowIndex(rows[rowIndex].row);
    }
  }

  protected class Row implements Serializable
  {
    private int row;

    public Row(int row)
    {
      this.row = row;
    }

    public Object getData()
    {
      int originalIndex = model.getRowIndex();
      model.setRowIndex(row);
      Object thisRowData = model.getRowData();
      model.setRowIndex(originalIndex);
      return thisRowData;
    }
  }

  protected abstract static class BaseComparator implements Comparator<Row>
  {
    private boolean ascending;

    public BaseComparator(boolean ascending)
    {
      this.ascending = ascending;
    }

    public abstract int compareRowData(Row o1, Row o2);

    // if the compareRowData returns 0, then use this method to further sort the
    // rows. Usually this will use the column that will definitely return a
    // non-zero value. The subclass comparator can override this method.
    public int compareDefault(Row o1, Row o2)
    {
      return 0;
    }

    protected int compareNullValues(Object obj1, Object obj2)
    {
      if (obj1 != null && obj2 != null)
      {
        throw new IllegalArgumentException("Neither input is null.");
      }
      boolean s1 = (obj1 == null);
      boolean s2 = (obj2 == null);
      int diff = 0;
      if (s1 && !s2)
      {
        diff = 1;
      }
      else if (!s1 && s2)
      {
        diff = -1;
      }
      return diff;
    }

    public int compare(Row o1, Row o2)
    {
      int diff = compareRowData(o1, o2);
      if (diff == 0)
      {
        diff = compareDefault(o1, o2);
      }
      return ascending ? diff : -diff;
    }
  }

  protected abstract static class StringComparator extends BaseComparator
  {
    public StringComparator(boolean ascending)
    {
      super(ascending);
    }

    public abstract String getString(Row row);

    public int compareRowData(Row row1, Row row2)
    {
      String str1 = getString(row1);
      String str2 = getString(row2);
      boolean s1 = StringUtils.hasText(str1);
      boolean s2 = StringUtils.hasText(str2);
      int diff = 0;
      if (s1 && !s2)
      {
        diff = 1;
      }
      else if (!s1 && s2)
      {
        diff = -1;
      }
      else if (!s1 && !s2)
      {
        diff = 0;
      }
      else if (s1 && s2)
      {
        diff = str1.compareTo(str2);
      }
      return diff;
    }
  }

  protected abstract static class IntegerComparator extends BaseComparator
  {
    public IntegerComparator(boolean ascending)
    {
      super(ascending);
    }

    public abstract Integer getInteger(Row row);

    public int compareRowData(Row bean1, Row bean2)
    {
      Integer int1 = getInteger(bean1);
      Integer int2 = getInteger(bean2);
      if (int1 != null && int2 != null)
      {
        return int1.intValue() - int2.intValue();
      }
      return compareNullValues(int1, int2);
    }
  }
  protected abstract static class LongComparator extends BaseComparator
  {
    public LongComparator(boolean ascending)
    {
      super(ascending);
    }

    public abstract Long getLong(Row row);

    public int compareRowData(Row row1, Row row2)
    {
      Long n1 = getLong(row1);
      Long n2 = getLong(row2);
      if (n1 != null && n2 != null)
      {
        Long temp = n1 - n2;
        return temp.intValue();
      }
      return compareNullValues(n1, n2);
    }
  }

  protected abstract static class DateComparator extends BaseComparator
  {
    public DateComparator(boolean ascending)
    {
      super(ascending);
    }

    public abstract Date getDate(Row row);

    public int compareRowData(Row row1, Row row2)
    {
      Date date1 = getDate(row1);
      Date date2 = getDate(row2);
      if (date1 != null && date2 != null)
      {
        return date1.compareTo(date2);
      }
      return compareNullValues(date1, date2);
    }
  }
}

A Sample JSP File Using the Sortable DataModel

The following is a sample jsp file. It is actually an xhtml file because it uses a facelet template. This page also uses IceFaces to display the data page by page. It also uses Spring webflow.

<ui:composition template="myFaceletTemplate.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"
  xmlns:c="http://java.sun.com/jstl/core">

  <ui:define name="content">

    <ice:outputText styleClass="pageHeading"
      value="My Page heading" />
    <br />
    <br />

    <ice:form>
      <ice:panelGroup>
        <!-- Display counts about the table and the currently displayed page -->
        <ice:dataPaginator id="dataScroll_2"
          for="myAppData" rowsCountVar="rowsCount"
          displayedRowsCountVar="displayedRowsCountVar"
          firstRowIndexVar="firstRowIndex" lastRowIndexVar="lastRowIndex"
          pageCountVar="pageCount" pageIndexVar="pageIndex">
          <ice:outputFormat
            value="{0} records found. Displaying reocrds {2} to {3} on page {4} of {5}."
            styleClass="standard">
            <f:param value="#{rowsCount}" />
            <f:param value="#{displayedRowsCountVar}" />
            <f:param value="#{firstRowIndex}" />
            <f:param value="#{lastRowIndex}" />
            <f:param value="#{pageIndex}" />
            <f:param value="#{pageCount}" />
          </ice:outputFormat>
        </ice:dataPaginator>

        <ice:panelGrid columns="2">
          <!-- Layout table columns with column headings and 11 table rows per page -->
          <ice:dataTable id="myAppData"
            var="myAppSelection" styleClass="dataPaginatorTable"
            value="#{myAppAdminForm.myAppSelections}"
            rows="11" columnClasses="firstCol,lastCol,phoneCol,emailCol">
            <ice:rowSelector
              value="#{myAppSelection.selected}" multiple="true" />
            <ice:column>
              <f:facet name="header">
                <ice:outputText id="columnSelection"
                  value="Selected" />
              </f:facet>
              <ice:selectBooleanCheckbox id="selection"
                value="#{myAppSelection.selected}"
                partialSubmit="true" />
            </ice:column>

            <ice:column>
              <f:facet name="header">
                <h:commandLink
                  action="#{myAppAdminForm.myAppSelections.sortByName}">
                  <ice:outputText id="columnName"
                    value="Name" />
                </h:commandLink>
              </f:facet>
              <ice:outputText id="name"
                value="#{myAppSelection.myApp.name}" />
            </ice:column>

            <ice:column>
              <f:facet name="header">
                <h:commandLink
                  action="#{myAppAdminForm.myAppSelections.sortByStartDate}">
                  <ice:outputText id="columnStartDate"
                    value="Start Date" />
                </h:commandLink>
              </f:facet>
              <ice:outputText id="startDate"
                value="#{myAppSelection.myApp.startDate}">
                <f:convertDateTime type="date"
                  dateStyle="medium" />
              </ice:outputText>
            </ice:column>

          </ice:dataTable>

        </ice:panelGrid>
      </ice:panelGroup>

      <!-- Set up the buttons and links for browsing through the table
        No. of pages to fast forward or rewind: 3
        No. of direct links to pages: -->
      <ice:dataPaginator id="dataScroll_1"
        for="myAppData" fastStep="3" pageCountVar="pageCount"
        pageIndexVar="pageIndex" paginator="true" paginatorMaxPages="20"
        styleClass="formBorderHighlight" renderFacetsIfSinglePage="false">
        <f:facet name="first">
          <ice:graphicImage id="firstpage_1"
            url="./xmlhttp/css/xp/css-images/arrow-first.gif"
            style="border:none;" title="first page" />
        </f:facet>
        <f:facet name="last">
          <ice:graphicImage id="lastpage_1"
            url="./xmlhttp/css/xp/css-images/arrow-last.gif"
            style="border:none;" title="last page" />
        </f:facet>
        <f:facet name="previous">
          <ice:graphicImage id="previouspage_1"
            url="./xmlhttp/css/xp/css-images/arrow-previous.gif"
            style="border:none;" title="previous page" />
        </f:facet>
        <f:facet name="next">
          <ice:graphicImage id="nextpage_1"
            url="./xmlhttp/css/xp/css-images/arrow-next.gif"
            style="border:none;" title="next page" />
        </f:facet>
        <f:facet name="fastforward">
          <ice:graphicImage id="fastforward_1"
            url="./xmlhttp/css/xp/css-images/arrow-ff.gif"
            style="border:none;" title="fast forward" />
        </f:facet>
        <f:facet name="fastrewind">
          <ice:graphicImage id="fastrewind_1"
            url="./xmlhttp/css/xp/css-images/arrow-fr.gif"
            style="border:none;" title="fast backward" />
        </f:facet>
      </ice:dataPaginator>

      <table>
        <tr>
          <td>
            <ice:commandButton id="cmdSubmitSelection"
              action="reviewSelections" value="Review Selections" />
            <ice:commandButton action="cancel"
              value="Cancel" immediate="true" />
          </td>
        </tr>
      </table>
    </ice:form>
  </ui:define>
</ui:composition>