Monday, August 19, 2013

JSF 2.2 with Google App Engine, Maven and Eclipse

Introduction

As noted in my last post, starting with a Maven JSP 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.

Outline

We will essentially be merging the Maven JSF archetype provided by Google with a new JSF project created via the GAE plugin for Eclipse.  The JSF part is essentially a modification to a current tutorial provided by Wildstar Technologies with info from the GAE Guestbook archetype.  This leads us to having the required starting technologies of Eclipse (I used version 4.3 Kepler), the GAE plugin for Eclipse, Maven 3.1 and the Maven plugin for Eclipse.  The first step will be to create the JSP Archetype.

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"> -->

<!--    <persistence-manager-factory name="transactions-optional"> -->
<!--        <property name="javax.jdo.PersistenceManagerFactoryClass" -->
<!--            value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> -->
<!--        <property name="javax.jdo.option.ConnectionURL" value="appengine"/> -->
<!--        <property name="javax.jdo.option.NontransactionalRead" value="true"/> -->
<!--        <property name="javax.jdo.option.NontransactionalWrite" value="true"/> -->
<!--        <property name="javax.jdo.option.RetainValues" value="true"/> -->
<!--        <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> -->
<!--        <property name="datanucleus.appengine.singletonPMFForName" value="true"/> -->
<!--    </persistence-manager-factory> -->
<!-- </jdoconfig> -->
That should do the trick.

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
oops (I did this).  To Maven-ize your project go to the project's context menu -> Configure -> Convert to Maven project.

Pick your Group Id, name and description and you're done Maven-izing the project, but it still won't verify.

Adding Maven Dependencies for Servlets

The first error if you try to mvn verify will be something like:
[ERROR] ... package javax.servlet.http does not exist
So we add servlets as a dependency to the pom.xml.  Open the pom.xml for the project, go to the Dependencies tab and add the servlet-api with the information below:

To mark the dependency as provided you'll need to select it and click on Properties..., which brings up the above screen.  Now you can run mvn verify:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.503s
[INFO] Finished at: Mon Aug 19 12:54:31 PDT 2013
[INFO] Final Memory: 15M/218M
[INFO] ------------------------------------------------------------------------

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:

  • com.google.appengine:appengine-api-1.0-sdk:${appengine.target.version}
  • com.google.appengine:appengine-testing:${appengine.target.version}, scope: test
  • com.google.appengine:appengine-api-stubs:${appengine.target.version}, scope: test
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:
[INFO] java.lang.ClassNotFoundException: javax.faces.webapp.FacesServlet
Looks like it's time to put in a JSF Maven dependency.  This is a little tricky.  For implementations we have the following choices:

  • java 7 ee, provided dependency that comes with JSF
    • I couldn't get this to work; same error
  • Apache MyFaces
  • 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:
HTTP ERROR 500 ... java.lang.IllegalStateException: Response has already been committed
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 properly
set.
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!

Happy coding all.

Wednesday, August 14, 2013

JSF 2.2 with Google App Engine: A Hurricane of Errors

Three days trying to do what should be a simple upgrade from the JSP Hello World to JSF and it still isn't working.

Long story short, when you get started with Google App Engine you'll find a nifty tutorial from Google that gets you up and going with a simple Guestbook JSP project.  I use Maven with my Eclipse so I used the Maven Archetype instead (also JSP).  At the time I was unable to find a detailed tutorial that starts with JSF to begin with.  There will be a future post with my experience of using that one instead (UPDATE: done, see this post).

As is, I setup the configuration for JSF and Apache MyFaces doing it the old-fashioned, manual way.  I had problems finding documentation on how to do it (and couldn't even find it again).  So, this is what NOT to do:

  1. Include Apache MyFaces in your Maven dependencies.
  2. Update your web.xml to be at least version 2.5 (this is actually a good idea) and include the following
(UPDATE: below code is properly presented now)

  Faces Servlet
  javax.faces.webapp.FacesServlet
  1



  Faces Servlet
  /faces/*
  3. Put a simple JSF hello world file into your /faces directory and load it up.  Even better, don't use an EL #{} expression so that you don't know that EL is broken until much, much later.

As stated, this worked three years ago with Eclipse 3.6 or so, but NOW Eclipse 4.3 Kepler has a concept called Project Facets under the Project properties screen and if you don't specify that you're using JSF here... it blows away your custom web.xml to the default jsp version.  This was Gotcha #1.

After reverting that mess and configuring the Facet I'm expecting a smooth ride... but get the now dredded: javax.naming.InitialContext is a restricted class error, and try the fix suggested by the link just previous.  I do not catch the subtext that this fix is only for Sun's (not Apache's) JSF implementation, and for an older version than I'm using at that!  So I change to Sun's JSF, track down the new WebConfiguration.java and comment out the needed code and at least it compiles but it still doesn't work, so I go back to MyFaces, download the source and comment out their references that I track down.  Then I have to track down a couple of other libraries that the one source file uses and it finally compiles.  At least the Hello World works at this point.  Now I know that the more recent JSF implementations have this issue fixed!  So don't mess around with commenting out the InitialContext code, just change the JSF implementation (the latest Mojarra works).  If you're keeping track, these are Gotchas 2 to 7.

Upgrading to Facelets is as easy as it is supposed to be.  However, this is when I realize that my EL isn't working.  After trying and reading various articles I came across new errors.  I found that I got ClassNotFoundException: javax.faces.webapp.FacesServlet and the ELResolvers for JSF were not registered with the JSP container not because it could not be found, but because it could be found too often (and I had to include it as provided instead of as a jar).  The novel cannot_find_facelet_taglib turned out to just be an Eclipse thing.  All in all, I still wasn't able to patch it up after finding about 6-12 other gotchas.