Hacker's - People who do great things with computers
Valhalla - In Norse myth, an afterlife of never ending battle.
Hacker's Valhalla - A Java Blog for info in the never ending battle of releases.
A focus on Spring, Testing and WebLogic Server.

Monday, August 19, 2013

JSF 2.2 with Google App Engine, Maven and Eclipse

Introduction

As noted in my last post, starting with a MavenJSP archetype (there is no JSF archetype at the time of this writing) and trying to upgrade it to JSF met with disaster. However, starting with a new JSF project and upgrading it to Maven was doable! This article outlines the steps and potential errors on the way. At the end we will have a working (including the Expression Language) JSF 2.2 Hello World with the Google App Engine (GAE) and Maven.

To begin I'd like to note that as I first started working with the Google App Engine I didn't fully appreciate that it IS your deployment container. I was thinking that there would be something like WebLogic that happened to be running on Google's infrastructure, but was incorrect. The GAE replaces the traditional managed container of WebLogic, WebSphere, Glassfish and Tomcat, and has it's own custom configuration. That brings us to the philosophy of how this deployment will be done.

Create GAE JSP Maven Guestbook Archetype

Go to the Guestbook link and view the steps under Building the Guestbook Demo using guestbook-archetype. You'll be wanting to use the Eclipse IDE to do similar.

Make a New Maven Project

In Eclipse, go to the Project Explorer -> context menu (right click) -> New -> Other... as in the following screen shot:

and then select Maven -> Maven Project:

Add the New Maven Archetype From Google

Click on Next >, and then accept the defaults for the next screen by selecting Next > again. This brings us to the Maven Archetypes box. You'll want to click on New Archetype and fill out the info for the GAE JSP Guestbook archetype:

Click on Next > after you select your newly added archetype and on the next screen give it a name like "mavenjsp" as we'll be deleting it later and an appropriate Group Id (or com.gaetest if you're not sure).

Conclusion of Creating the New Project from the GAE Archetype

Click on finish and this will give you a shiny new JSP Maven project:

You can also notice from expanding the Deployed Resources that your webapp directory is the root of your webspace (e.g. you put your landing page index.xhtml here). However, in the GAE project it creates a WebContent directory that is your root (see below) and it populates (i.e. wipes out) the Deployed Resources file. It took me several hours to figure out what was happening here. Now that we have our Maven template project, we can start on the GAE JSF project.

Create GAE Web Application Project

We start with creating and configuring another new project, with the steps largely outlined already.

Create New Web Application Project

See the right screen shot of the toolbar.

Configure the Web Application Project

Follow the steps for Creating a New Project in the Wildstar tutorial starting at step 2.

The Google App Engine Facet

The Google App Engine facet (Eclipse facets are discussed below) had some issues at the time of this writing: it was only compatible with the Dynamic Web Module 2.5 even though 3.1 was available, it would just hang until I Reverted or Canceled the Facets screen when I selected Further configuration available.... Lastly without configuration it would not even work, giving an error of

Failed While Installing Google App Engine 1

Later in this tutorial we will configure the project to be compatible with GAE manually, but if the facet can work for you, you may be able to save a lot of steps later on. As is, let's get to the facets we will install.

Add Working Facets to Project

Next up we want to JSF-ize the project by adding the Dynamic Web Module 2.5 facet and the JavaServer Faces 2.2 facet. To do this, go to the Project's context menu -> Properties -> Project Facets and select Dynamic Web Module 2.5, next enable JavaServer Faces and Further Configuration to have it manage your web.xml file and not provide a library (that's what Maven is for). See the below two images:

Confirm everything and you may see some errors like:

cvc-elt.1: Cannot find the declaration of element 'jdoconfig'

in your Eclipse project.

Fix jdoconfig.xml

Go to the Markers view at the bottom (by default) and select the error about jdo. Comment out the entire file except for the first line.
<?xml version="1.0" encoding="utf-8"?>
<!-- <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" -->
<!-- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" -->
<!-- xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> -->

Configure web.xml

Bring up /WebContent/WEB-INF/web.xml. Not the one in /war, not the one in /Deployed Resources. Put the following just before the last closing tag. This is the "good stuff" from the Wildstar tutorial:

<!-- ***** 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>

<welcome-file-list>

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

</welcome-file-list>

Delete the preconfigured welcome-file-list at the top. I had issues where the GAE got confused if there was more than one welcome-file and would just display a blank-screen. It's easier to have an index.html page automatically redirect to the .jsf or .xhtml page you want. Save your change and we're ready to test it out!

Convert to Maven Project

If you forget this step before you run maven verify (see below) you'll get something like the following:

[ERROR] The goal you specified requires a project to execute but there is no POM in this directory

Configure for Google App Server

There are a few steps to do before you can run the Google App Server though; just because Maven is OK with the code doesn't mean the app server is.

Add Google Dependencies to pom.xml

If you try to run the app server now (see below for instructions) you'll get an error like:

[ERROR] No plugin found for prefix 'appengine' in the current project and in the plugin groups [org.apache.maven.plugins, org.codehaus.mojo] available from the repositories...

Clearly the pom.xml needs some work. There are a lot of elements that depend or the app engine version, so make a POM property (edit pom.xml, Overview tab, Properties -> Create) of appengine.target.version with value "1.8.3". Add the following dependencies:

Manually edit (the pom.xml tab) project/build/plugins to add the following:

<plugin>

<groupId>com.google.appengine</groupId>

<artifactId>appengine-maven-plugin</artifactId>

<version>${appengine.target.version}</version>

</plugin>

Save your pom.xml and move on to the appengine-web file.

Copy appengine-web.xml to project

Now if you mvn verify and run the app engine you'll get something like:

[INFO] com.google.apphosting.utils.config.AppEngineConfigException: Could not locate ...\WEB-INF\appengine-web.xml

When we converted the project to JSP it started using /WebContent instead of /war as the base path; move the appengine-web.xml from war/WEB-INF to WebContent/WEB-INF.

Update Version (if needed)

If you verify and run the server again, you may get a major warning about having an out of date GAE. If you get no such warning, skip on to the next section. The instructions were simple but the new GAE is quite large and can take 10 minutes to download on a good cable connection. If you have to upgrade, grab some coffee or a spot of tea while you verify and run the server again.

Copy Logging properties file

If you verify and devserver again, the server will run with many errors and warnings. The top-most of these will be something like:

[INFO] java.io.FileNotFoundException: ...\WEB-INF\logging.properties (The system cannot find the file specified)

If you actually ran the server from inside of Eclipse, be sure to see the Killing the Zombie Server section below. Fixing this error is the same as the prior section, copy the file in question from /war to /WebContent.

Add JSF Dependency

If you did a verify and devserver cycle again, you would not get the logger error, but would get:

Looking back I put a 2.1.11 version into a JSF 2.2 project... what did I expect?

GlassFish has a provided dependency... only for the GlassFish server.

Amazingly, Oracle made something that worked, and I wound up going with Oracle JSF 2.2.2.

AKA Mojarra, that is part of Glassfish but works on other application containers.

To add the Oracle JSF dependency create a new Maven property, jsf.version with the value of 2.2.2, then add in com.sun.faces:jsf-api:${jsf.version}, com.sun.faces:jsf-impl:${jsf.version}.

Create index.html and index.xhtml

If you cycle your server again... it will work with no errors!
Let's bring up our page at localhost:8080... and get a 403 error. If you try index.html manually you'll get a 500 error about an ExternalContext (detailed below). Wait, we don't have an actual faces file. Let's go get one.
Create a new HTML file in /WebContent with the following content, that I found on stackoverflow:
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="1;url=index.xhtml">
<script type="text/javascript">
window.location.href = "index.xhtml"
</script>
<title>Page Redirection</title>
</head>
<body>
<!-- Note: don't tell people to `click` the link, just tell them that it is a link. -->
If you are not redirected automatically, follow the <a href='index.xhtml'>link to example</a>
</body>
</html>

Now that we have redirection to index.xhtml let's put up a hello world. Create a new XHTML file (also under /WebContext) and replace the contents with what you find under Creating index.jsp, step 8 on the wildstar tutorial.

Celebrate

Upon cycling the server again (or if you haven't been following along every road block, start the server for the first time as detailed below) and brining up http://localhost:8080/faces you may get a:

following the url with a / does the trick: http://localhost:8080/faces/.
In fact, you can try out some EL by including
<p>Hello #{param.name}!</p>
inside of the f:view element in the xhtml page, then go to http://localhost:8080/faces/index.xhtml?name=you.
I know, this was a @^$@ long Hello World.

(Optional) Configure the Expression Language

You may come across an error like:

com.sun.faces.config.ConfigurationException: It appears the JSP version of the container is older than 2.1 and unable to locate the EL RI expression factory, com.sun.el.ExpressionFactoryImpl. If not using JSP or the EL RI, make sure the context initialization parameter, com.sun.faces.expressionFactory, is properlyset.

Even though it's written for Tomcat 6, this tutorial should help you out if you get the error and this one by my old teacher at SJSU helps as well (it's a small dev world afterall).
You can find the el-api in both 2.2 and 3.0 versions... but 3.0 is still a draft with no implemenation at the time of writing, so use 2.2 for the API and the GlassFish (works with non-GlassFish servers) impl.

Run the Google App Engine Server From Eclipse

To actually run the server, go to Run (either toolbar icon or top menu) -> Run Configurations, you will want to input something like the following (but with a Goals of "appengine:devserver"):

Your base directory will be different, but the Maven Runtime must be the external 3.1 runtime. Click on Apply and then Run, load up localhost:8080 and win!

Killing the Zombie Server

Unfortunately when it's time to say goodbye to the server (usually because you want to mvn clean) Eclipse doesn't pass through a control-c to kill the program but gives you a red box to stop it. The server is immune to such petty things as red boxes, but only the mighty control-c or TaskManager (or ProcessExplorer) can lay it low. If you go to TaskManager and the Processes tab you can see multiple java.exe's from servers that didn't truly die. You can kill them manually every time you need to (rememer, "java.exe", "javaw.exe" is your Eclipse!) or you can run the mvn command for the server from the command line. I do this personally... when I remember, and love to use the program Console2 instead of the standard console, and came across a great article about configuring it for awesome.

Other Errors

You may get a warning like

The ELResolvers for JSF were not registered with the JSP container.

This is irrevelant (who wants to go back to JSP anyway?)

Upon loading up a page, you may get a warning like the following:

[INFO] WARNING: /faces/index.xhtml: com.sun.faces.context.FacesFileNotFoundException: /index.xhtml Not Found in ExternalContext as a Resource

This is a fancy way of saying 404 not found and is most likely caused by confusion about servlet mappings and where the real root of your webspace is. We mentioned that the root was /Deployed Resources as a non-jsf project and this looked to be a duplicate of /war. After converting to JSF only /WebContent mattered and you could delete /war after gutting it for files (e.g. an index.xhtml put into /war won't show up in the WAR file, it's super intuitive). Also, just because the servlet mapping is for /facelets/ doesn't mean that there has to be an actual directory called facelets holding your index.xhtml. The mapping removes the facelets part and searches your regular old /WebContent directory for the file. If you put the file into /WebContent/facelets/index.xhtml it'll be missed and you'll get this error! This part is also hyper-intuitive. Stackoverflow, again, helped me out a lot to figure this out. You can also review your project settings by going to your project properties -> Web Deployment Assembly to see how it works for yourself.

Conclusion

This was quite a long ride with, yes I counted, fifteen (15!) possible errors on the way and one zombie process... that would cause a locked file error on a mvn clean. We covered how to create a Maven Google App Engine JSP project, how to create a JSF project, and how to convert the JSF project to use Maven and the Google App Engine. Now that you have this working, you can actually get going on your features!