Configuring JavaServer Faces 2.1 to run on the Google App Engine Using Eclipse

Overview

This tutorial will guide users through the process of developing and deploying a JavaServer Faces 2.1 application on the 1.9.12 version of Google's App Engine for Java using the 2.1.29 version of Oracle's implementation of the specification. The application built out in the course of writing this tutorial is available from http://jsf21template.wildstartech.com/. The source code is available from the jsf21template Google Code project.

The complete Eclipse project is contained in the jsf21template.zip archive which is accessible from the following URL:

NOTE: The API and implementation files for version 2.2 of the Expression Language are obtained from version 2.3.1 of the Seam 2 framework Downloads page. The reference implementation versions provided with the Oracle Glassfish application server do not work properly.

Optional Software

You can download the Google AppEngine SDK v1.9.12 and install it locally on your system; however, this tutorial is written from the Eclipse perspective using the Google Plugin for Eclipse.

Pre-Configuration Steps

Download and install the Indigo release of the Eclipse SDK.

Follow the instructions outlined on the "Quick Start" page to install the Google Plugin for Eclipse.

Importing the Unified Expression Language Files

The following steps will guide you through the process of importing the files which provide support for the new Unified Expression Language. Prior to completing this step, you should have downloaded version 2.3.1 of the Seam 2 framework. Once you uncompress the jboss-seam-2.3.1.Final.zip archive, the el-api.jar , jboss-el-api_2.2_spec.jar, and jboss-el.jar files are located in the lib folder.

In the "Package Explorer" tab, located on the left side of your IDE, expand your project and navigate to and left-click on the WEB-INF/lib directory.

Left-click on the 'File' menu and select the 'Import...' menu item.

When presented with the "Import" dialog, left-click on the 'File System' item under the "General" option.

Left-click on the 'Next' button.

Left-click on the 'Browse...' button and select the lib folder on your local hard disk drive contained in the directory where you uncompressed the jboss-seam-2.3.1.Final.zip archive. Place check marks in the boxes to the left of the el-api.jar, jboss-el-api_2.2_spec.jar, and jboss-el.jar files.

NOTE: Passing arguments to expression language methods works regardless of whether the jboss-el-api_2.2_spec.jar file is present. For the time being, I'm going err on the side of caution and include it in this tutorial.

Left-click on the 'Finish' button.

Importing the JavaServer Faces Libraries

The following steps will guide you through the process of importing the files which provide support for the second release candidate of Oracle's implementation of the JSF 2.1 specification.

In the "Project Explorer" tab, located on the left side of your IDE, expand your project and navigate to and left-click on the WEB-INF/lib directory.

Left-click on the 'File' menu and select the 'Import...' menu item.

When presented with the "Import" dialog, left-click on the 'File System' item under the "General" option.

Left-click on the 'Next' button.

Left-click on the 'Browse...' button and select the folder on your local hard disk drive into which you saved the javax.faces-2.1.29.jar archive.

Configuration File Changes

appengine-web.xml

Edit the appengine-web.xml file found in the WEB-INF directory of the project to allow the web application to store data in the session created for clients visiting our site. Add the <sessions-enabled>true</sessions-enabled> line as shown below. Add the <threadsafe>true</threadsafe> setting to instruct the App Engine runtime environment to allow a single Instance to service multiple requests concurrently.

<?xmlversion="1.0"encoding="utf-8"?>

<appengine-web-appxmlns="http://appengine.google.com/ns/1.0">

<application>jsf21template</application>

<version>1</version>

<!--

Allows App Engine to send multiple requests to one instance in parallel:

NOTE: We have set the enabled HTTP sessions in this web application by setting the <sessions-enabled> property to true. Experiments configuring App Engine to transfer session data from memcache to the datastore asynchronously to reduce request latency using the <async-session-persistence> parameter to true cause Task queue quotas to become exhausted. For the time being it is recommended the <async-session-persistence> property be set to false. For more information, please refer to the Enabling Sessions section of the Java Application Configuration document.

Do not forget to modify the contents of the <application></application> tag to reflect the name of your App Engine project. Save the changes to the appengine-web.xml file and close it.

web.xml

Left double-click on the contents of the web.xml file found in the WEB-INF directory and replace the contents with what is shown below.

<?xmlversion="1.0"encoding="utf-8"?>

<web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"version="2.5">

<display-name>

Wildstar Technologies, LLC. Google App Engine JSF 2.1 Template

</display-name>

<description>

Template JSF 2.1 application configured to run on the Google

AppEngine for Java.

</description>

<!-- ***** Designate client-side state saving. ***** -->

<context-param>

<param-name>javax.faces.STATE_SAVING_METHOD</param-name>

<param-value>client</param-value>

</context-param>

<!-- Set the default suffix for JSF pages to .xhtml -->

<context-param>

<param-name>javax.faces.DEFAULT_SUFFIX</param-name>

<param-value>.xhtml</param-value>

</context-param>

<!-- Disable use of threading for single-threaded environments such as

the Google AppEngine. -->

<context-param>

<param-name>com.sun.faces.enableThreading</param-name>

<param-value>false</param-value>

<description>

When enabled, the runtime initialization and default ResourceHandler

implementation will use threads to perform their functions. Set this

value to false if threads aren't desired (as in the case of running

within the Google Application Engine).

Note that when this option is disabled, the ResourceHandler will not

pick up new versions of resources when ProjectStage is development.

</description>

</context-param>

<!-- ***** Specify JBoss Expression Language Over Default -->

<context-param>

<param-name>com.sun.faces.expressionFactory</param-name>

<param-value>org.jboss.el.ExpressionFactoryImpl</param-value>

</context-param>

<!-- ***** Load the JavaServer Faces Servlet ***** -->

<servlet>

<servlet-name>Faces Servlet</servlet-name>

<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>Faces Servlet</servlet-name>

<url-pattern>/faces/*</url-pattern>

<url-pattern>*.jsf</url-pattern>

</servlet-mapping>

<!-- ***** Specify session timeout of thirty (30) minutes. ***** -->

<session-config>

<session-timeout>30</session-timeout>

</session-config>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

<welcome-file>index.xhtml</welcome-file>

<welcome-file>index.html</welcome-file>

</welcome-file-list>

</web-app>

The com.sun.faces.enableThreading context parameter is set in the event you use the "Development" value for the javax.faces.PROJECT_STAGE context parameter. As indicated in the comments, setting com.sun.faces.enableThreading equal to false instructs the Mojarra implementation of the JSF 2.1 API to NOT attempt to use threads as this is not allowed by the Google App Engine platform (as described in the "Sandbox" section of the "Java Servlet Environment" article on the Google App Engine documentation site.

Modifying the Web Application's Default Page

If your web project does not already contain an index.html file in the war directory, then skip to the "Creating index.jsp" sub-section. If there is already an index.html, continue with the "Renaming index.html to index.jsp and Modifying Its Contents" section which follows.

Renaming index.html to index.jsp and Modifying Its Contents

A Google AppEngine web project will create an index.html file as the default file for your web application. In the section above, we specified a change to the <welcome-file-list> configuration parameter which instructs the AppEngine's runtime environment to look for the following default pages in the specified order:

index.jsp

index.xhtml

index.html

As such, we we will rename the AppEngine's default index.html file to index.jsp. We will ensure that the browser's "back" button will continue to function properly by following the guidelines prescribed in the "Avoid Redirects" section of Yahoo!'s "Best Practices for Speeding Up Your Web Site" document. The index.jsp file will be configured to send a redirect to the browser rather than leverage an <metahttp-equiv="Refresh"content="0,welcome.jsf"/> tag in the <head> of an HTML document.

Locate and right-click on the index.html file in the war directory shown in the "Project Explorer" window.

When the context-sensitive menu is displayed, left-click on the 'Rename...' menu item found on the 'Refactor' sub-menu.

When presented with the "Rename Resource" dialog, ensure the 'New name:' field indicates the index.html file should be renamed to index.jsp.

Left-click on the 'OK' button to complete the file rename operation.

Left double-click on the newly renamed index.jsp file to open the file in the editor. Replace the file contents with the following:

<html> <head> <title>Initial Redirect Page</title> </head> <body><% response.sendRedirect("welcome.jsf"); %> <body></html>NOTE: If you decide to use a page other than welcome.xhtml as your initial landing page, please make the appropriate change to the index.jsp shown above.

Save your changes to the index.jsp and close the file.

In the "Package Explorer" window, right-click on the war directory. Left-click on the "New" menu and then left-click on the "File" menu item.

When presented with the "New File" dialog, type in welcome.xhml in the 'File name:' field and then left-click on the 'Finish' button.

The newly created welcome.xhtml file will be presented in the editor. Use the following as the contents of the file.

<!DOCTYPEhtmlPUBLIC"-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><htmlxmlns="http://www.w3.org/1999/xhtml"xml:lang="en"lang="en"xmlns:f="http://java.sun.com/jsf/core"xmlns:h="http://java.sun.com/jsf/html"xmlns:ui="http://java.sun.com/jsf/facelets"><h:headid="head"><metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/><title>Welcome to JSF 2.1 on the Google AppEngine!</title></h:head><h:bodyid="body"><f:viewcontentType="text/html"><p><h:outputTextvalue="You are now up and running with JavaServer Faces 2.1 on the Google App Engine."/></p></f:view>

</h:body>

</html>

NOTE: The first three lines of the file above instruct web browsers to interpret this document as a well-formed XHTML document. As described in the "The !DOCTYPE 'Switch'" section of the MSDN article entitled "CSS Enhancements in Internet Explorer 6", this text instructs versions of Internet Explorer 6 and greater to switch on standards compliance mode.

Creating index.jsp

The default welcome page for our web application will be a JSP page entitled index.jsp and we will ensure that the browser's "back" button will continue to function properly by following the guidelines prescribed in the "Avoid Redirects" section of Yahoo!'s "Best Practices for Speeding Up Your Web Site" document. The index.jsp file will be configured to send a redirect to the browser rather than leverage an <metahttp-equiv="Refresh"content="0,welcome.jsf"/> tag in the <head> of an HTML document.

Locate and right-click on the war directory shown in the "Project Explorer" window.

Left-click on the "New" menu and then left-click on the "File" menu item.

When presented with the "New File" dialog, enter "index.jsp" as the value for the "File name:" field and left-click on the "Finish" button.

The newly created index.jsp file will be presented in the editor. Use the following as the contents of the file.

NOTE: If you decide to use a page other than welcome.xhtml as your initial landing page, please make the appropriate change to the index.jsp shown above.

Return to the "Project Explorer" window and right click on the war directory.

Left-click on the "New" menu and then left-click on the "File" menu item.

When presented with the "New File" dialog, enter "welcome.xhtml" as the value for the "File name:" field and left-click on the "Finish" button.

The newly created welcome.xhtml file will be presented in the editor. Use the following as the contents of the file.

<!DOCTYPEhtmlPUBLIC"-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><htmlxmlns="http://www.w3.org/1999/xhtml"xml:lang="en"lang="en"xmlns:f="http://java.sun.com/jsf/core"xmlns:h="http://java.sun.com/jsf/html"xmlns:ui="http://java.sun.com/jsf/facelets"><h:headid="head"><metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/><title>Welcome to JSF 2.1 on the Google AppEngine!</title></h:head><h:bodyid="body"><f:viewcontentType="text/html"><p><h:outputTextvalue="You are now up and running with JavaServer Faces 2.1 on the Google App Engine."/></p></f:view>

</h:body>

</html>

NOTE: The first three lines of the file above instruct web browsers to interpret this document as a well-formed XHTML document. As described in the "The !DOCTYPE 'Switch'" section of the MSDN article entitled "CSS Enhancements in Internet Explorer 6", this text instructs versions of Internet Explorer 6 and greater to switch on standards compliance mode.

Add JavaServer Faces Library to Java Build Path

To successfully run the reference implementation of the 2.1 version of the JavaServer Faces API on the Google App Engine platform, we are going to have to modify one of the classes that ship with the API. Before we do that, we are going to need to add the JavaServer Faces API to the Java Build Path of our project so we do not encounter any compilation errors.

Right-click on the name of the project, "Google App Engine JSF 2.1 Template", in the "Package Explorer" window.

When the context-sensitive menu is displayed, left-click on the 'Properties' menu item.

When you are presented with the "Properties for Google App Engine JSF 2.1 Template" dialog, left-click on the "Java Build Path" settings and then left-click on the 'Add JARs...' button.

Navigate to the war/WEB-INF/lib folder of the project, left-click on the javax.faces-2.1.29.jar file, then left-click on the 'OK' button.

When you are returned to the "Properties for Google App Engine JSF 2.1 Template" dialog, left-click on the 'OK' button.

Ensuring Contents of HttpSession Are Saved

Any time the Google App Engine framework detects a change in the HTTP session, that information is written out to the datastore. This works very well for immutable objects; however, it becomes more challenging when dealing with mutable objects as the GAE framework has no way of knowing when a field inside an object stored in the HTTP session is changed. A Stack Overflow question entitled "Session lost in Google App Engine using JSF" outlines a great strategy using a JSF PhaseListener to store the current date/time in the HTTP session at the end of each Phase.

The following process will guide you through the steps necessary to create this PhaseListener.

In the "Project Explorer" tab, located on the left side of your IDE, expand your project. Expand the "src" branch and left-click on the package to which you would like to add this class. For the purpose of this tutorial, we are going to select com.wildstartech.gae.jsf21template.

Right-click on the package, display the "New" menu and then left-click on the "Class" menu item.

Enter "SessionPhaseListener" in the "Name" field.

Left-click on the "Add..." button that appears to the right of the "Interfaces" section.

When the "Implemented Interfaces Selection" dialog appears, enter "PhaseListener" in the "Choose Interfaces" field. The "Matching items:" field should contain "PhaseListener - javax.faces.event" as depicted in the screen shot below. Left-click on the "OK" button.

When you are returned to the "New Java Class" dialog, shown in the figure below, left-click on the "Finish" button to create the class.

The source code for the SessionPhaseListener.java file will be presented and you can replace it with the code shown below.

package com.wildstartech.gae.jsf21template;

import java.util.Map;

import java.util.logging.Logger;

import javax.faces.context.ExternalContext;

import javax.faces.context.FacesContext;

import javax.faces.event.PhaseEvent;

import javax.faces.event.PhaseId;

import javax.faces.event.PhaseListener;

/**

* PhaseListener to ensure the HttpSession data is written to the datastore.

The getPhaseId() method returns PhaseId.ANY_PHASE which tells the JSF framework the beforePhase and afterPhase methods of the listener should be called for each phase of the JavaServer Faces lifecycle. The beforePhase method of this class does nothing; however, the afterPhase method will:

Obtain a reference to the FacesContext.

Using the FacesContext, obtain a reference to the ExternalContext.

The ExternalContext will be used to get access to the HttpSession in the form as a Map.

The current date/time (expressed as milliseconds) is stored in the HttpSession.

Unfortunately, the JSF 2.1 API does not provide an annotation to declare a PhaseListener programmatically, so we are going to have to create a faces-config.xml configuration file.

In the "Project Explorer" tab, located on the left side of your IDE, expand your project.

Expand the "war" branch of your project.

Right-click on the "WEB-INF" folder, left-click on the "New" menu and then left-click on the "File" menu item.

Enter "faces-config.xml" as the value for the "File name:" field as depicted in the screen shot below.

Left-click on the "Finish" button.

Use the following as the contents for the contents of the "faces-config.xml" file and then close the editor view.