JSF in the cloud : Finding the PaaS - Part 1 : Google App Engine

Short intro

Google App Engine is google PaaS offer. It is a fully managed PaaS. It promises automatic load balancing and handling. Given the identity of the provider, I tend to think it works.

To use Google App Engine, you must have a Google Account. Then, you can access some free offers. Currently, this incudes USD 300 in credit to spend on all Cloud Platform products over 60 days.

The simpliest way to go IMO is to follow the official "Getting started".

The only official technical requirements are Maven 3.1 and Java 7 (which are both pretty much standard).

Creating a project

Your new project will be created in two steps :

For maven users, this is just straightforward.

I created and lptestgcloud2 app in my free account then generated the app skeleton with :

mvn archetype:generate -Dappengine-version=1.9.15 -Dapplication-id=lptestgcloud2 -Dfilter=com.google.appengine.archetypes:

and chosing the first proposal :

com.google.appengine.archetypes:appengine-skeleton-archetype

Full log :

Choose archetype:
1: remote -> com.google.appengine.archetypes:appengine-skeleton-archetype (A skeleton application with Google App Engine)
2: remote -> com.google.appengine.archetypes:endpoints-skeleton-archetype (A skeleton project using Cloud Endpoints with Google App Engine Java)
3: remote -> com.google.appengine.archetypes:guestbook-archetype (A guestbook application with Google App Engine)
4: remote -> com.google.appengine.archetypes:hello-endpoints-archetype (A simple starter application using Cloud Endpoints with Google App Engine Java)
5: remote -> com.google.appengine.archetypes:skeleton-archetype (-)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1
Choose com.google.appengine.archetypes:appengine-skeleton-archetype version: 
1: 1.8.7
2: 2.0.0-1.9.10
Choose a number: 2: 
Define value for property 'groupId': : fr.penet
Define value for property 'artifactId': : testgcloud2
Define value for property 'version':  1.0-SNAPSHOT: : 
Define value for property 'package':  fr.penet: : 
[INFO] Using property: appengine-version = 1.9.15
[INFO] Using property: application-id = lptestgcloud2
Confirm properties configuration:
groupId: fr.penet
artifactId: testgcloud2
version: 1.0-SNAPSHOT
package: fr.penet
appengine-version: 1.9.15
application-id: lptestgcloud2

The following source tree is generated :

── eclipse-launch-profiles
│   ├── DevAppServer.launch
│   └── UpdateApplication.launch
├── nbactions.xml
├── pom.xml
├── README.md
└── src
    ├── main
    │   ├── java
    │   └── webapp
    │      └── WEB-INF
    │           ├── appengine-web.xml
    │           ├── logging.properties
    │           └── web.xml
    └── test

eclipse-launch-profiles files are used by eclipse to provide shortcuts to debug and publication of the app. As I prefer netbeans to eclipse, the nbactions.xml file is more interesting to me. It contains custom actions that will be proposed for the project under this IDE.

netbeans is not officially supported, but, in addition to the nbactions.xml file, one can use the gaelyk plugin. Its latest version, for Netbeans 7.4, is compatible with Netbeans 8. With this plugin, you can test, debug, profile and upload your webapp.

The original pom.xml is quite short :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>

    <groupId>fr.penet</groupId>
    <artifactId>testgcloud2</artifactId>

    <properties>
        <appengine.app.version>1</appengine.app.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <prerequisites>
        <maven>3.1.0</maven>
    </prerequisites>

    <dependencies>
        <!-- Compile/runtime dependencies -->
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-1.0-sdk</artifactId>
            <version>1.9.15</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- Test Dependencies -->
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-testing</artifactId>
            <version>1.9.15</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-stubs</artifactId>
            <version>1.9.15</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <!-- for hot reload of the web application-->
        <outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>versions-maven-plugin</artifactId>
                <version>2.1</version>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>display-dependency-updates</goal>
                            <goal>display-plugin-updates</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <version>3.1</version>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <archiveClasses>true</archiveClasses>
                    <webResources>
                        <!-- in order to interpolate version from pom into appengine-web.xml -->
                        <resource>
                            <directory>${basedir}/src/main/webapp/WEB-INF</directory>
                            <filtering>true</filtering>
                            <targetPath>WEB-INF</targetPath>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>

            <plugin>
                <groupId>com.google.appengine</groupId>
                <artifactId>appengine-maven-plugin</artifactId>
                <version>1.9.15</version>
                <configuration>
                    <enableJarClasses>false</enableJarClasses>
                    <!-- Comment in the below snippet to bind to all IPs instead of just localhost -->
                    <!-- address>0.0.0.0</address>
                    <port>8080</port -->
                    <!-- Comment in the below snippet to enable local debugging with a remove debugger
                         like those included with Eclipse or IntelliJ -->
                    <!-- jvmFlags>
                      <jvmFlag>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n</jvmFlag>
                    </jvmFlags -->
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Dependencies on com.google.appengine:appengine-api-1.0-sdk:1.9.15, javax.servlet:servlet-api:2.5 and jstl:jstl:1.2 , a few plugins configuration and that's all.

Adding the stack components

First thing is to add dependencies to MyFaces 2.2.6, Primefaces 5.1, Deltaspike 1.1 and OpenWebBeans 1.2.7-SNAPSHOT.

    <!-- JSF -->
    <dependency>
      <groupId>org.apache.myfaces.core</groupId>
      <artifactId>myfaces-api</artifactId>
      <version>2.2.6</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.myfaces.core</groupId>
      <artifactId>myfaces-impl</artifactId>
      <version>2.2.6</version>
      <scope>runtime</scope>
    </dependency>


    <!-- primefaces -->
    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>5.1</version>
      <type>jar</type>
    </dependency>

    <!-- Specifications -->
    <!-- JSR-330 -->
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-atinject_1.0_spec</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
    </dependency>
    
    <!-- JSR-299 -->
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-jcdi_1.0_spec</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
    </dependency>
    
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-interceptor_1.1_spec</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-validation_1.0_spec</artifactId>
      <version>1.1</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-servlet_3.0_spec</artifactId>
      <version>1.0</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-el_2.2_spec</artifactId>
      <version>1.0.4</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-jsp_2.1_spec</artifactId>
      <version>1.0.1</version>
    </dependency>

     <!-- OpenWebBeans -->
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-impl</artifactId>
      <version>1.2.7-SNAPSHOT</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-jsf</artifactId>
      <version>1.2.7-SNAPSHOT</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-web</artifactId>
      <version>1.2.7-SNAPSHOT</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-spi</artifactId>
      <version>1.2.7-SNAPSHOT</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-el22</artifactId>
      <version>1.2.7-SNAPSHOT</version>
    </dependency>

    <dependency>
      <groupId>org.apache.xbean</groupId>
      <artifactId>xbean-asm4-shaded</artifactId>
      <version>3.15</version>
    </dependency>

    <dependency>
        <groupId>org.apache.deltaspike.core</groupId>
        <artifactId>deltaspike-core-api</artifactId>
        <version>1.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.core</groupId>
        <artifactId>deltaspike-core-impl</artifactId>
        <version>1.1.0</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-jsf-module-api</artifactId>
        <version>1.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-jsf-module-impl</artifactId>
        <version>1.1.0</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-security-module-api</artifactId>
        <version>1.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-security-module-impl</artifactId>
        <version>1.1.0</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-bean-validation-module-api</artifactId>
        <version>1.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-bean-validation-module-impl</artifactId>
        <version>1.1.0</version>
        <scope>runtime</scope>
    </dependency>

I use Primefaces 5.1 there only because it is the latest community version available. It is however crucial to use the version I indicate or later for the other packages :

  • MyFaces 2.2.6 corrects MYFACES-3923 . As Google App Engine is "only" servlet spec 2.5, it is required. GAE being servlet 2.5 rather than 3.0 is not big deal.I can see no other feature than jsf file upload that will not work, and other solutions are available.
  • OpenWebBeans 1.2.6 does not work because it uses sun.misc.Unsafe. Google App Engine uses a somewhat strict whitelist mechanism, which prevents access to this class. In 1.2.7-SNAPSHOT, Mark Struberg commited a patch that falls back to java.lang.Class#newInstance in this case. I am not sure that this solution is the best one. I proposed a patch to 1.5.0-SNAPSHOT that leverages google reflection API.

We also have to modify the appengine-web.xml file as explained in this useful blog on JSF Corner. So, our appengine-web.xml file is now :

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <application>lptestgcloud2</application>
    <version>${appengine.app.version}</version>
    <threadsafe>true</threadsafe>
    
    <system-properties>
        <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
    </system-properties>

    <sessions-enabled>true</sessions-enabled> 
    <threadsafe>true</threadsafe> 
    <static-files> 
      <exclude path="/**.xhtml" /> 
    </static-files>
</appengine-web-app>

As explained in JSF Corner, we have to provide our own EL 2.2 implementation. After toying a bit, I found not better solution than JSF Corner' one, so let's also add a dependency on org.jboss.el:jboss-el:1.0_02.CR6

    <dependency>
        <groupId>org.jboss.el</groupId>
        <artifactId>jboss-el</artifactId>
        <version>1.0_02.CR6</version>
    </dependency>

and the JBoss repository :

<repositories>
    <repository>
      <id>JBoss</id>
      <url>https://repository.jboss.org/nexus/content/repositories/releases/</url>
    </repository>
 </repositories>

I also use lombok and commons-lang3 in almost all my projects :

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.1</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.14.4</version>
      <scope>provided</scope>
    </dependency>

In web.xml, we have to add a context-param to use this EL implementation :

    <context-param>
        <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
        <param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
    </context-param>
 

A minimal web.xml file looks like :

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <display-name>Test application</display-name>
    <welcome-file-list>
        <welcome-file>accueil.xhtml</welcome-file>
    </welcome-file-list>
    <context-param>
        <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
        <param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.EL_RESOLVER_COMPARATOR</param-name>
        <param-value>org.apache.myfaces.el.unified.OpenWebBeansELResolverComparator</param-value>
    </context-param>
    <listener>
        <listener-class>
            org.apache.webbeans.servlet.WebBeansConfigurationListener
        </listener-class>
    </listener>
     <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>*.xhtml</url-pattern>
    </servlet-mapping>
 </web-app>

Finally, let's add empty beans.xml and and faces-config.xml files in WEB-INF :

beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans  xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd" />

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
              version="2.2" />

If we start it right now with :

mvn clean install appengine:devserver

it almost works. Almost because there are still a few JNDI related errors during DeltaSpike initialization. GAE does not support JNDI. So, until we can cleanly disable it, we have to shadow the BeanManagerProvider and JNDIUtils classes.

In our BeanManagerProvider version, we shall comment the line

result = resolveBeanManagerViaJndi();

and in JNDIUtils we shall comment the static initialization block, where an attempt to access InitialContext is performer, throwing an exception :

    static
    {
        try
        {
            initialContext = new InitialContext();
        }
        catch (Exception e)
        {
            throw new ExceptionInInitializerError(e);
        }
    }

...and then, it finally works as expected. :-)

Source

The source of this (very) simple example is downloadable there.

Conclusion

A few hacks are required to run this PrimeFaces/MyFaces/DeltaSpike/OpenWebbeans stack on Google App Engine. Some workarounds, like using java.lang.Class#newInstance instead of sun.misc.Unsafe can have a performance hit or even raise issues (what happens when you use a proxy for a singleton ???). However, it is still quite acceptable and usable for who wants to leverage all the features of GAE without giving up the power of JSF 2.2 and CDI extensions such as DeltaSpike.

As a tomcat user, I have to learn again how to perform access control and such things, but Google seems to offer rich, yet specific API.

It seems that you can even have Google App Engine on your own private cloud, thanks to a collaboration between Google and Red Hat that led to the project CapeDwarf

JSF in the cloud : Finding the PaaS - Part 0 : Introduction

I have been developping a lot in java those last years. For so called «enterprise» web based applications, there are so many nice libraries and packages available... Even if you are like me a C/C++ geek and think that getting too far from the hardware and forget memory allocation is an highway to performance bottlenecks, it is a good way to go, just because lots of others also travel on this road.

In the world of java webapps, I opted for a PrimeFaces / MyFaces / Deltaspike / OpenWebBeans stack, running in a Tomcat 7 container.

The first reason is my preference for open standards. I do not like to depend on a single provider.

The second reason is that all those packages are Free Software. So, one can study and modify the code, which is just always needed when you do real work.

The third reason is that all those projects, excepted PrimeFaces, are supported by the Apache Foundation. I find that it does a very good long term job of developping libraries and other valuable tools. Moreover, I prefer non-copyleft licences. The developpers of those projects know each other and collaborate. When you ask a question on Deltaspike and it happens to be an OWB question, you are friendly redirected to the right place.

The fourth reason is that all those projects have vibrant communities. I had answers to most of my questions and requests in less than a day.

The fifth reason, at least for PrimeFaces, is that they are de facto standards.

Since 2010, «cloud» has emerged as a buzz word, a magic marketing solution to all problems. Despite the regretable ton of ******, the development of Iaas (Infrastructure as a Service) and PaaS (Platform as a Service) opens new perspectives in the way we, developpers, work.

IaaS is, in very few words, a way to have hardware as a commodity. an IaaS provider such as Amazon will provide you servers and databases instances for a very low fee per hour. You then have to setup your system. PaaS goes a step further. A provider of PaaS also provides a set of libraries and tools. It upgrades and patches them as needed. It sometimes provides features such as automatic load balancing, and so on.

IaaS and PaaS can be public or private (on premise). Big organisations might have security needs that requires the second option.

There are still less PaaS offers than IaaS offers. I think that it will change given the big advantages that a good PaaS offer provides. For one developping software from scratch, it would just be very bad idea not to opt for such an offer, excepted if you have very special requirements.

I asked myself a simple question : how does my usual stack fit in the leading PaaS offers ? As this question seemed interesting to friends and colleagues, I decided to write a few blog notes on this topic. I will try to explain you how you can do JSF in the cloud in a snap (or kind of, as you will see).I will first look at Google App Engine, then OpenShift, then Jelastic, then CloudFoundry, then Heroku. In this first look, I will just try to see how easy it is to get my stack up and running and have a look at the main features. Benchmarks like this one are already performed by experts and I have no value nor time to try to do better than them.