Sur la route d'Oxiane digressions diverses

LeBlog OXiane

27 avr
2009

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

google_appengine1Depuis 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.

logo-wicket1

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 ».

2-creer-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.

3-decochoer-gwt

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.

4-ajout-jars-dans-le-classpath

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!

5-hello-world

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

6-deploiement

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/

8-hello-real-world

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

admin

admin

  • http://www.oxiane.com jlrousselin

    Pas mal la référence à l’article de Jeff sur Hello world :D

  • http://www.tomsquest.com/blog Tom

    Excellent ! J’étais en train de me poser cette question. L’article y répond très bien.

    Question complémentaire : quid des perfs d’une appli Wicket sur GAE ?

    Merci,
    Tom

  • http://blog.oxiane.com Gabriel Kastenbaum

    Bonsoir Tom,
    A ce jour, les retours sur les performances de GAE ne pas semblent pas convaincants.

    On peut lire par exemple cet article sur le site highscalability.com et les liens que cet article pointe ( comme celui-ci )

    Ceci dit, il faut vraiment y regarder de près et ne pas se tromper dans les tests effectués. Il semblerait que le design de la DataStore de GAE soit difficile à appréhender – surtout quand on a comme nous été bercé par les comptines parlant de gentilles bases de données relationnelles et de méchantes données dupliquées.
    « the DataStore isn’t designed to be super fast at the small scale, but rather handle large amounts of data, and be distributed (and because its distributed it can appear very fast at large scale). So you break down your database access into very simple processes. Assume your database access is VERY slow, and rethink how you do things.(Of course the peice in the puzzle ‘we’ are missing is MapReduce! – the ‘processing’ part of the BigTable mindset) »
    Ailleurs (http://highscalability.com/google-architecture) sur ce site (que j’aime bien), on peut lire sur le BigTable de google: « BigTable is a large scale, fault tolerant, self managing system that includes terabytes of memory and petabytes of storage. BigTable is a distributed hash mechanism built on top of GFS. It is not a relational database. It doesn’t support joins or SQL type queries.. BigTable provides lookup mechanism to access structured data by key ».

    Autre piège, GAE est une manière de penser, d’architecturer les données – une manière basée sur clé/valeurs. On peut en penser d’autres, toujours dans le cadre du Cloud. En ce sens, AWS (Amazon) semble être architecturé tout à fait différemment et proposer une utilisation très différente, toute basée sur l’usage CPU. Autre cas, on m’a décrit une architecture sur un autre système (IBM) où les collections étaient parcourues en java, directement, et où tout le calcul – par exemple appliquer un filtre très précis sur quelques centaines de milliers – se fait en mémoire. On a là une approche plus mainframe ou plus as400 – avec les dents sur le serveur de base de données.

    Gabriel.

  • Arnaud

    Mettre :

    add(new Label(« message », « le monde!! »));

    à la place de :

    add(new Label(« le monde!! »));

    c’est sensé matché avec l’id de l’élément html…

  • http://blog.oxiane.com Gabriel Kastenbaum

    oups c’est vrai!… C’est corrigé, merci!

  • Pingback: Mise à disposition du cours GWT | Sur la route d'Oxiane

  • http://www.facebook.com/profile.php?id=100003407073098 Ricardo

    I’m a website deoplveer and I work more with code then design. But I still have to admit that writing clean code just aren’t help much for the world wide web. Design and user experience, usability is way more important.I think a beautiful website isn’t depend on what framework you use. It depends on how you design the website. So when your friend said, you can’t build a good looking website with wicket is just wrong. He could say you can’t build a beautiful website with Paint, than that maybe right(some designers still could pull it off though).It’s quite a good collection though