Déploiement d’une application Wicket sur Google App Engine

Depuis quelques jours, Google a annoncé le support de Java 6 sur son infrastructure de Cloud Computing, Google App Engine  (autrement dénommée GAE).
Cette nouvelle est tempérée par le fait que toutes les API de java ne sont pas supportées. Ainsi, interdiction par exemple de créer un Thread, d’écrire un fichier…
La plupart des limitations sont structurelles et dues au fait que l’on est « on the cloud » – où la notion de Fichier, de Thread… est largement abolie, puisque l’on ne sait pas sur quelle machine sera déployée l’application.

Mais il existe d’autres limitations. Certaines sont des choix – comme l’impossibilité d’utiliser le package java.awt.* D’autres sont des erreurs due à la jeunesse – et au caractère novateur du produit – comme l’impossibilité d’utiliser l’annotation @MappedSuperClass de JPA (mais on peut utiliser JPA) ou l’obligation de créer un serialVersionUID pour les classes sérialisables. Ou cette erreur carrément mystérieuse, un  ClassNotFoundException sur java.util.Collections.unmodifiableList(<ma liste>)… Erreur que j’ai repéré lors d’un déploiement sur la plate-forme cible, alors que tout se passait bien sur la plate-forme de test en local…. Erreur de jeunesse là encore.
Google a écrit une White List qui liste les packages de la JRE autorisés sur son infrastructure. ( http://code.google.com/intl/fr/appengine/docs/java/jrewhitelist.html ) (et on trouve pourtant bien le support de la classe Collections).

Depuis cette annonce du support de java6 par la plate-forme GAE, tous leurs mainteneurs de frameworks Java se demandent si le fruit de leur travail fonctionneront sur GAE. Le test a été positif pour Groovy, Scala, Restlet, moins pour Jersey, pas bon les WebServices (JAX-RPC or JAX-WS) , OK pour Spring mais on ne peut pas utiliser la gestion de la transaction pour JPA, etc…
Le tableau de la compatibilité avec GAE est complexe, et n’a pas fini d’être rempli. Vous trouverez ici une liste des tests actuellement en cours sur chacun des frameworks.

Aujourd’hui nous allons voir qu’il est possible de déployer une application Wicket sur GAE – nous le verrons sur une application simple de type Hello World, partant de la création de l’application jusqu’à son déploiement.

Pour créer un projet Wicket qui tournera sur Google AppEngine (aussi nommé GAE), voici la démarche à suivre:
Installer le plugin Google pour Eclipse. Pour cela, aller sur le Menu « Help > Software update ». Sélectionner l’onglet « Available software » et ajouter l’adresse qui permet les mises à jour automatiques de ce plugin – nous avons utilisé, pour Eclipse 3.4.



    http://dl.google.com/eclipse/plugin/3.4

Puis, sur l’écran résumant tous les « Sites » gérés par Eclipse, cocher les mises à jour du plugin Google : Plugin et SDK.
1-install-google-plugin
Cliquer sur le bouton Install, acceptez les conditions, puis à la fin de l’installation, relancer son instance d’Eclipse.Pour plus de détails on peut se référer à cette page.

Une fois que l’installation est terminée on se retrouve devant un superbe Workspace tout neuf et qui marche puisqu’il ne contient aucune application. ça ne va pas durer longtemps.

On va créer un projet à déployer sur GAE. Pour cela : cliquer sur « new > Other » et aller dans la catégorie Google (ah bon?!) pour créer un « Web Application Project ».

On se retrouve devant une fenêtre de création de projet Web. Décocher l’utilisation de GWT. ça n’est pas le but aujourd’hui.

Voilà, on se retrouve devant un magnifique projet web. Il n’a pas la structure d’un projet Maven, il n’a pas la nature d’un projet Eclipse Jee – mais il peut se lancer avec le plugin Jetty intégré.

Pour ajouter Wicket à ce projet il nous faut maintenant récupérer ces différentes bibliothèques:

  • log4j (log4j-1.2.15.jar)
  • slf4j (slf4j-log4j12-1.5.6.jar et slf4j-api-1.5.6.jar)
  • et wicket (wicket-1.3.5.jar).

Copier ces jar dans war/WEB-INF/lib. Et les déclarer dans le BuildPath (cf. les propriétés du projet > Java Build Path) – et oui ce n’est pas automatique contrairement aux mécanismes de gestion du BuildPath des projets Web d’Eclipse.

Créer un fichier de configuration log4j.properties et fixez le niveau de logs pour Wicket


     log4j.properties: log4j.logger.org.apache.wicket=DEBUG, stdout

L’environnement de développement est mis en place. On peut maintenant commencer à faire un peu de code

Créer une classe HomePage héritant de WebPage :



package com.oxiane.wicketongae.page;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;

public class HomePage extends WebPage{

    public HomePage() {
        add(new Label("message", "le monde!!"));
    }
}

(attention à ne pas se tromper lors de l’import des classes! Le nom de la classe Label se retrouve en Swing  et en Wicket)

Au même niveau que votre classe HomePage.java, dans le même package, créer sa page HomePage.html (du même nom que la classe créée)

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
        <title>Bonjour les nuages</title>
    </head>
    <body>
    Hello, <span wicket:id="message">
               Ici on devrait voir s'afficher le message défini dans HomePage.java
             </span>
    </body>
</html>

Créer une classe qui étend la classe WebApplication et qui permet notamment de définir la première page de son application (il s’agit de la classe : HomePage.class).


package com.oxiane.wicketongae;

import org.apache.wicket.protocol.http.HttpSessionStore;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.session.ISessionStore;

import com.oxiane.wicketongae.page.HomePage;

public class WicketOnGaeApp extends WebApplication {
	@Override
	public Class getHomePage() {
		return HomePage.class;
	}

	@Override
	protected void init() {
		super.init();
		getResourceSettings().setResourcePollFrequency(null);
	}

	@Override
	protected ISessionStore newSessionStore() {
		return new HttpSessionStore(this);
	}
}

Modifier le web.xml : d’une part, pour ajouter le filtre Wicket  (avec comme paramètre le chemin de la classe WebApplication que l’on vient de créer); et d’autre part, pour modifier les dtd ou xsd qui sont utilisés. Dans la version du web.xml créée par le plugin google, on a en entête:
<!DOCTYPE web-app PUBLIC
« -//Sun Microsystems, Inc.//DTD Web Application 2.3//EN »
« http://java.sun.com/dtd/web-app_2_3.dtd »>
<web-app xmlns= »http://java.sun.com/xml/ns/javaee » version= »2.5″>
(…)
Ce qui fait tiquer le validateur xml (« Attribute xmlns (et version) must be declared for element « web-app »). Il faut mettre à jour le début du fichier web.xml pour que la validation perde son inquiétante couleur rouge.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns: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"
			id="WebApp_ID" version="2.5">
	<filter>
		<filter-name>wicket.caveavins-wicket</filter-name>
 		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
		<init-param>
			<param-name>applicationClassName</param-name>
			<param-value>com.oxiane.wicketongae.WicketOnGaeApp</param-value>
 		</init-param>
		<init-param>
		            <param-name>configuration</param-name>
		            <param-value>deployment</param-value>
		</init-param>

 	</filter>

 <filter-mapping>
  <filter-name>wicket.caveavins-wicket</filter-name>
	<url-pattern>/*</url-pattern>
 </filter-mapping>
	<servlet>
		<servlet-name>wicketongae</servlet-name>
		<servlet-class>com.oxiane.wicketongae.wicketongaeServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>wicketongae</servlet-name>
		<url-pattern>/wicketongae</url-pattern>
	</servlet-mapping>

</web-app>

Et supprimer le fichier index.html qui a été créé par défaut à racine du site.

Modifier le fichier de conf app-engine.xml pour permettre la gestion des sessions HTTP en rajoutant le tag session-enabled. (http://code.google.com/intl/fr/appengine/docs/java/config/appconfig.html#Enabling_Sessions)
Ne pas mettre le tag dans <system-properties>…</system-properties> car dans ce cas il ne sera pas pris en compte.

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
	<application>gaktests</application>
	<version>5</version>
	<!-- Configure java.util.logging -->
	<system-properties>
		<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
	</system-properties>
	<sessions-enabled>true</sessions-enabled>
</appengine-web-app>

Dans la Class qui surcharge WebApplication , désactiver le système qui vérifie les mises à jour dans le système de fichiers – car ce système est lancé par un thread indépendant  – ce qui n’est pas autorisé sur GAE.


@Override
protected void init() {
    super.init();
    getResourceSettings().setResourcePollFrequency(null);
}

Enfin, toujours dans cette classe qui hérite de WebApplication, désactiver le recours au cache de second niveau – car ce cache utilise le disque pour sérialiser les pages. Or, nous sommes sur le « cloud » et , tout comme on ne lance pas de thread, on n’écrit pas de fichier sur GAE. A la place, on peut utiliser une simple HttpSessionStore, ce qui augmentera l’utilisation de la RAM, mais maintenant que nous déployons chez Google, ce n’est plus tout à fait notre souci 🙂


@Override
protected ISessionStore newSessionStore() {
    return new HttpSessionStore(this);
}

Enfin! On peut faire tourner notre application en local, grâce à un Jetty installé avec le plugin.
Il suffit de faire clic droit > Run as > Web Application

Voici les logs de la console



********************************************************************
*** WARNING: Wicket is running in DEVELOPMENT mode.              ***
***                               ^^^^^^^^^^^                    ***
*** Do NOT deploy to your live server(s) without changing this.  ***
*** See Application#getConfigurationType() for more information. ***
********************************************************************
The server is running at http://localhost:8080/

On va sur l’adresse indiquée et – effectivement – ça marche!

On voit que la page HomePage.html a bien pris en compte les instructions de la classe Wicket – et le texte dans le <span> a été remplacé par le texte défini dans l’instance de Label utilisée dans HomePage.java

… Mais est-ce que ce qui marche sur son poste de travail va se déployer sur les nuages googléiens?
Pour cela, il faut avoir un compte sur Google App Engine. On va ici faire la supposition que c’est le cas.

On retourne sur l’arborescence de son projet, et sur le projet créé, on fait : clic droit > Google > Deploy to app engine

Les applications Google sont accessibles depuis une URL <APPLICATION_ID>.appspot.com . Chaque personne ayant un compte GAE a le droit de créer dix identifiants pour ses applications – sous le nom de  « Application_ID » ce qui peut permettre de déployer une dizaine d’applications.
Pour déployer notre indispensable application il nous faut donc fixer cet « Application ID » . Pour cela cliquer sur le lien : « App Engine projects settings » – ce qui ouvre une nouvelle fenêtre – et renseigner le champ « Application ID » – il faut déjà avoir créé cet Application_id sur l’interface d’administration de GAE – mais pour nous aider à faire cela on peut cliquer sur « My Applications… » et « Existing versions… » qui nous redirige vers le site d’admin.
7-deploiement2-fixer-lapplication-id

Cliquer sur OK, puis « Deploy », ce qui lance … le déploiement (….trop fort…). Voici les messages qui s’affichent sur la console Eclipse.


Creating staging directory
Scanning for jsp files.
Scanning files on local disk.
Initiating update.
Cloning 1 static files.
Cloning 20 application files.
Uploading 0 files.
Deploying new version.
Will check again in 1 seconds
Will check again in 2 seconds
Closing update: new version is ready to start serving.
Uploading index definitions.
Deployment completed successfully

Il suffit maintenant de prendre son navigateur pour aller sur l’URL paramétrée : http://gaktests.appspot.com/

Derrière cette url http://gaktests.appspot.com/ se trouve donc une application wicket , totalement scalable, déployée sur le Cloud made in Google.
Et voilà 🙂

Auteur : Gabriel Kastenbaum

Ressources:

Google App Engine
* http://appengine.google.com/. Il faut s’inscrire pour pouvoir déployer une application sur cette infrastructure.
* La White List des package autorisés de la JRE : http://code.google.com/intl/fr/appengine/docs/java/jrewhitelist.html
* Est-ce que votre framework favori marche sur GAE ? http://groups.google.com/group/google-appengine-java/web/will-it-play-in-app-engine

Wicket
* Excellent post : http://stronglytypedblog.blogspot.com/2009/04/wicket-on-google-app-engine.html
* GAE et les problèmes rencontrés, pour Wicket mais pour tout développement Java en général : http://www.xaloon.org/blog/apache-wicket-issues-on-google-app-engine-for-java

Les bibliothèques nécessaires pour créer un projet Wicket (sans utiliser Maven).
* log4j : http://logging.apache.org/log4j/1.2/index.html
* slf4j : http://www.slf4j.org/download.html
* wicket : http://wicket.apache.org/

Oxiane : Formation Wicket