Montons en régime !

Article d'origine: 

Construire des killer-apps qui passent à l'échelle avec App Engine pour Java

Quand une idée vous démange, vous avez besoin de la gratter pour vous sentir mieux. En tant que développeurs, nous passons beaucoup de temps à trouver des idées pour différentes sortes d'applications. C'est amusant, n'est-ce-pas ? Comprendre comment réaliser un produit logiciel représente un défi intéressant. Imaginer quelque chose puis le créer apporte une certaine satisfaction. L'alternative (une démangeaison qu'on ne gratte pas) n'amène que de la frustration.

Une des raisons pour lesquelles beaucoup d'applications ne voient jamais le jour est le besoin d'une infrastructure. Une infrastructure bien maintenue implique généralement une équipe d'administrateurs systèmes, de DBAs et d'ingénieurs réseau, ce qui jusqu'à récemment était réservé aux plus fortunés. Même payer un tiers pour héberger votre application n'est pas sans risque: qu'arrivera-t-il si la popularité de l'application subit un coup d'accélérateur et qu'elle reçoit soudainement de nombreuses visites ? Ce qu'on appelle communément l'effet Slashdot (ndT: j'imagine que le nom vient du célèbre site Slashdot) peut enterrer une bonne idée, tout simplement parce qu'il est difficile de prédire les pics de charge.

Mais, comme nous le savons tous, les choses changent. L'approche des services Web a évolué, et aujourd'hui nous avons les moyens, via le cloud computing et son cousin plus costaud Platform As A Service (PAAS) de construire, déployer et distribuer des applications plus facilement. Désormais, quand vous écrivez le prochain Twitter et que vous le déployez sur une plateforme cloud, il passera à l'échelle comme une lettre à la poste. Waou, comme c'est agréable !

Dans cette série de trois articles, vous apprendrez par la pratique pourquoi le cloud computing et PAAS sont des sauts évolutifs si importants dans le développement logiciel, et en parallèle vous commencerez à utiliser une nouvelle plateforme assez excitante pour développer en Java: Google App Engine pour Java, qui est actuellement disponible en version alpha (ndT: ce n'est plus le cas à l'heure actuelle !). Je commencerai par un survol d'App Engine pour Java, avec les différents types de services applicatifs qu'il propose. Ensuite vous plongerez directement dans une application d'exemple - la première des deux que nous verrons - en utilisant le plugin Eclipse fourni par Google. La première application se basera sur le support de l'API Servlet par App Engine pour Java, et la seconde sur le support de GWT. Dans le deuxième article, vous créerez une petite application de gestion de contacts en utilisant les mêmes technologies. Et dans la dernière partie, vous vous servirez de votre application pour explorer le support de la persistence Java par App Engine, qui est fondé sur les Java Data Objects (JDO) et la Java Persistence API (JPA).

Maintenant, assez parlé: montons en régime !

A propos de Google App Engine pour Java

Google (qui est également à l'origine d'un moteur de recherche, je crois) a mis à disposition la première version Google App Engine en avril 2008. Au grand désappointement de beaucoup de développeurs Java, cette version initiale était réservée aux programmeurs Python - des gens qui pensent que les espaces sont là pour délimiter des blocs ! (Je le sais parce que j'ai écris un livre sur Python). Google a répondu à la pression populaire en sortant Google App Engine pour Java en avril 2009.

Google App Engine pour Java est une solution complète pour le développement Java d'entreprise: une IHM Ajax dans un navigateur pour la facilité d'utilisation, le support des outils Eclipse, et Google App Engine en bout de chaine. La facilité d'utilisation et l'outillage sont les points forts de Google App Engine pour Java par rapport à d'autres solutions de cloud computing.

Le développement d'applications dans App Engine pour Java implique l'utilisation des ressources de Google pour stocker et retrouver les objets Java. Le stockage des données est basé sur BigTable, mais les intarfaces JDO et JPA permettent d'écrire du code qui n'est pas directement lié à Big Table. En fait, Google fournit un support pour bon nombre d'APIs standards afin que vous puissiez écrire du code qui n'est pas lié à 100% à la plateforme App Engine pour Java.

App Engine pour Java repose sur les APIs Java standards qui suivent:

De plus, App Engine pour Java fournit un support pour les services applicatifs suivants:

L'import/export de données est important pour importer des données provenant d'autres sources dans votre application App Engine. C'est aussi un moyen supplémentaire de n'être pas lié à App Engine. Le support de CRON par Google est basé sur une URL interne qui est invoquée à des moments spécifiés, ce qui en fait un service intéressant qui n'a pas grand chose à voir avec App Engine. Le mécanisme d'authentification et d'autorisation est spécifique à App Engine pour Java, mais on pourrait écrire un ServletFilter, un aspect ou un plugin Spring Security pour minimiser ce couplage fort.

Créez votre première application App Engine pour Java

Si vous avez lu jusqu'à ce point, vous êtes prêt à commencer à construire votre première application App Engine pour Java. La première étape est d'installer le plugin Google App Engine pour Eclipse (ndT: si vous utilisez un autre environnement de développement, Google fournit également des scripts Ant permettant d'effectuer les mêmes opérations que le plugin); une fois cela fait, vous pouvez passer à la suite.

Ouvrez l'IDE Eclipse et vous verrez trois nouveaux boutons à côté du bouton Imprimer: un G dans une balle bleue, un G dans une boite à outils rouge, et un avion à réaction en miniature, comme dans la Figure 1:

Figure 1. De tous nouveaux boutons dans votre IDE Eclipse

Icônes du plugin Google App Engine

Voici ce que font ces boutons:

Vous utiliserez l'assistant de création de projet pour créer deux nouveaux projets: le premier basé sur des servlets et le second sur GWT. Vous vous servirez de la boite à outils pour compiler le projet GWT. Vous appuierez sur l'avion quand vous voudrez déployer le projet sur App Engine, le rendant ainsi accessible.

Maintenant, commencez en créant un projet App Engine pour Java. Tout d'abord, cliquez sur la balle bleue pour accéder à l'assistant de création de projet. Puis créez une application nommée SimpleServletApp et se trouvant dans le package gaej.example, comme le montre la Figure 2:

Figure 2. Démarrage d'un nouveau projet

Démarrage d'un nouveau projet

Notez que le support de GWT n'est pas activé pour ce premier exemple. A la fin de cette étape, l'assistant créera une application simple contenant une servlet de type Hello World. La figure 3 montre une copie d'écran du projet (ndT: j'ai gardé les copies d'écran de l'article d'origine, le résultat que l'on obtient maintenant peut être légèrement différent sur certains écrans, dont celui-là):

Figure 3. Le projet SimpleServletApp

Démarrage d'un nouveau projet

Remarquez les fichiers JAR qui sont automatiquement inclus dans ce nouveau projet basé sur des servlets:

Vous apprendrez comment utiliser les APIs de persistence de l'App Engine, ainsi que quelques-uns des services applicatifs qu'il fournit, dans la seconde partie de cette série.

Notez également l'existence du fichier de configuration du conteneur d'exécution de Google App Engine, nommé appengine.xml. Dans cet exemple, il est utilisé pour configurer l'emploi du fichier logging.properties qui paramètre la manière dont App Engine va produire les logs.

Premier aperçu d'une application App Engine de type servlet

Une fois que vous avez tout configuré dans l'assistant de création de projet, App Engine vous présente un squelette d'application avec une servlet Hello World. Regardez le code et essayez de lancer l'application en utilisant les outils Eclipse pour App Engine. Le point d'entrée de cette application est SimpleServletAppServlet, que voici dans le Listing 1:

Listing 1. SimpleServletAppServlet

package gaej.example;

import java.io.IOException;
import javax.servlet.http.*;

@SuppressWarnings("serial")
public class SimpleServletAppServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/plain");
        resp.getWriter().println("Hello, world");
    }
}

La servlet est attachée à l'URI /simpleservletapp dans le fichier, comme le montre le Listing 2:

Listing 2. web.xml

<?xml version="1.0" encoding="utf-8"?>
<!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">
    <servlet>
        <servlet-name>simpleservletapp</servlet-name>
        <servlet-class>gaej.example.SimpleServletAppServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>simpleservletapp</servlet-name>
        <url-pattern>/simpleservletapp</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

L'assistant de création de projet produit également un fichier index.html qui possède un lien vers la nouvelle servlet:

Listing 3. index.html généré par l'assistant de création de projet

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set     -->
<!-- the browser's rendering engine into           -->
<!-- "Quirks Mode". Replacing this declaration     -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout.   -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
    <!--                                           -->
    <!-- Any title is fine                         -->
    <!--                                           -->
    <title>Hello App Engine</title>
  </head>

  <!--                                           -->
  <!-- The body can have arbitrary html, or      -->
  <!-- you can leave the body empty if you want  -->
  <!-- to create a completely dynamic UI.        -->
  <!--                                           -->
  <body>
    <h1>Hello App Engine!</h1>
    
    <table>
      <tr>
        <td colspan="2" style="font-weight:bold;">Available Servlets:</td>        
      </tr>
      <tr>
        <td><a href="simpleservletapp"/>SimpleServletAppServlet</td>
      </tr>
    </table>
  </body>
</html>

On a maintenant une application simple basée sur des servlets, utilisant seulement quelques APIs Java. Et c'est ce qui est important: App Engine pour Java offre d'accéder aux fonctionnalités proposées par l'App Engine au moyen d'APIs standard, ce qui permet de profiter de la vitalité des frameworks disponible sur la plateforme Java.

Qu'est-ce qui marche également avec App Engine pour Java?

Google maintient une liste des outils et des frameworks qui merchent bien avec l'App Engine pour Java (voir les Ressources). Par exemple, App Engine pour Java supporte de nombreux langages tournant sur la JVM, comme BeanShell, Groovy, Scala, JRuby, Jython, et Rhino. Du fait que l'App Engine supporte un bon nombre d'APIs Java SE et Java EE - comme les Servlets, JSP, JPA, JavaMail et JAXP (Java API for XML Processing) - beaucoup de frameworks fonctionnent sans problème. Par exemple, vous pouvez utiliser le framework Spring, bien que certaines adaptations soient nécessaires pour Spring ORM. Tapestry, Wicket, DWR, Tiles, SiteMesh, et Grails fonctionnent également. Struts 2 fonctionne avec un petit correctif. Mais d'autres frameworks et APIs tels que Hibernate et JDBC (les bases de données relationnelles ne sont pas supportées), JMX, les Web Services, JAX-RPC et JAX-WS, JCA, JNDI, JMS, EJB, et RMI ne marcheront pas.

Déploiement de l'application

Pour exécuter votre application avec l'outillage Eclipse, faites d'abord un clic droit sur le projet et sélectionnez le menu "Run As", puis choisissez "Web Application" avec la balle bleue, comme dans la Figure 4:

Figure 3. Exécution sur le serveur de développement App Engine pour Java

Exécution sur le serveur de développement App Engine pour Java

Vous devriez maintenant pouvoir accéder à l'adresse http://localhost:8080/simpleservletapp dans votre navigateur et voir l'application afficher le message Hello World.

Création d'une application GWT

Vous avez maintenant une idée du fonctionnement d'une application de type servlet dans l'App Engine pour Java, explorons donc l'outillage Eclipse pour les applications GWT. Commencez en cliquant la balle bleue dans la barre d'outil Eclipse pour lancer l'assistant de création de projet Google. Cette fois, sélectionnez le support de GWT, comme le montre la Figure 5:

Figure 5. Création d'une application GWT simple avec l'assistant de création de projet

Création d'une application GWT simple avec l'assistant de création de projet

Comme vous pouvez le voir dans la Figure 6, App Engine pour Java incorpore beaucoup plus d'artefacts dans une application GWT que dans une application de type servlet. L'application d'exemple est une IHM faite en GWT qui dialogue avec un service applicatif de salutation.

Figure 6. Artefacts de code fournis pour une application GWT

Artefacts de code fournis pour une application GWT

Il y a un JAR supplémentaire pour l'application GWT qui n'existait pas pour celle basée sur les servlets, gwt-servlet.jar.

Les autres artefacts sont les suivants:

Avant que vous n'examiniez l'architecture de l'application et le code source, regardez ce qui se passe quand on exécute l'application. Pour lancer l'application, cliquez sur la boite à outils rouges de la barre d'outils, puis cliquez sur le bouton Compile. Maintenant faites un clic droit sur le projet  et sélectionnez le menu Run As—> Web Application comme précédemment. Cette fois, comme vous travaillez sur une application GWT, vous accéderez à l'application à l'adresse http://127.0.0.1:8888/SimpleGWTApp.html (ndT: j'ai modifié l'URL et zappé la phrase suivante de l'article d'origine. Au moment de l'écriture de l'article, le client GWT ne s'exécutait pas dans un simple navigateur, comme vous le voyez sur la copie d'écran suivante. Mais fondamentalement le résultat est le même.) Allez-y et saisissez votre nom dans l'application, et observez la réponse. J'ai reçu la réponse de la Figure 7:

Figure 7. Exécution de l'application GWT de démonstration

Exécution de l'application GWT de démonstration

Dans les sections suivantes, nous parcourerons l'application de démonstration GWT. Si vous voulez en savoir plus sur GWT (ou suivre un tutoriel GWT), voir les Ressources.

Dans l'application GWT

Sur la base de la configuration fournie, l'outillage GWT d'Eclipse crée un embryon d'application contenant une page HTML (SimpleGWTApp.html, figurant dans le Listing 10) qui charge simplegwtapp.js et simplegwtapp.nocache.js. Ce code JavaScript est généré par GWT à partir de votre code Java, dans notre cas celui-ci se trouve dans le répertoire src et le package gaej.example.client (voir les Listings 6, 7 et 8).

Le point d'entrée principal pour la création de l'IHM est gaej.example.client.SimpleGWTApp, que l'on voit dans le Listing 8. Cette classe crée les éléments d'IHM GWT et les associe aux éléments du DOM HTML de SimpleGWTApp.html (voir le Listing 10). La page SimpleGWTApp.html définit deux éléments DOM nommés nameFieldContainer et sendButtonContainer (des colonnes d'une table). La classe SimpleGWTApp utilise RootPanel.get("nameFieldContainer") pour accéder au panneau associé à ces éléments DOM et les remplacer par des éléments d'IHM. La classe définit ensuite un champ texte et un bouton, que vous utilisez pour saisir un nom et l'envoyer au service de salutation (voir le Listing 10).

GWT sait que la classe SimpleGWTApp est le point d'entrée de l'application parce que SimpleGWTApp.gwt.xml le spécifie comme tel a l'aide de l'élément entry-point.

SimpleGWTApp cable le bouton, nommé sendButton, de telle manière que quand on clique sur lui SimpleGWTApp invoque la méthode greetServer de GreetingService. L'interface GreetingService est définie dans src/gaej.example.client.GreetingService.java (Listing 6).

Comme Ajax est intrinsèquement asynchrone, GWT définit une interface asynchrone pour accéder aux services distants. L'application SimpleGWTApp utilise l'interface asynchrone définie dans src/gaej.example.client.GreetingServiceAsync.java (Listing 7). GreetingServiceImpl (src/gaej.example.server.GreetingServiceImpl.java) implémente la méthode greetServer définie par GreetingService (Listing 5). La méthode GreetingServiceImpl.greetServer retourne une String qui est utilisée par SimpleGWTApp pour afficher le message d'accueil dans la boite de dialogue qu'elle crée.

Le descripteur de module GWT déclare le point d'entrée pour l'IHM de l'application, c'est-à-dire gaej.example.client.SimpleGWTApp, comme dans le Listing 4:

Listing 4. Descripteur de module GWT (src/gaej/example/SimpleGWTApp.gwt.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN"

"http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/
  distro-source/core/src/gwt-module.dtd">


<module rename-to='simplegwtapp'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>

  <!-- Inherit the default GWT style sheet.  You can change       -->
  <!-- the theme of your GWT application by uncommenting          -->
  <!-- any one of the following lines.                            -->
  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
  <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

  <!-- Other module inherits                                      -->

  <!-- Specify the app entry point class.                         -->
  <entry-point class='gaej.example.client.SimpleGWTApp'/>
</module>

GreetingServiceImpl est l'implémentation du service de salutation, que vous pouvez voir dans le Listing 5. Il s'exécute côté serveur et le code client l'appelle via un appel de procédure distante.

Listing 5. Implémentation du service de salutation (src/gaej.example.server.GreetingServiceImpl.java)

package gaej.example.server;

import gaej.example.client.GreetingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
        GreetingService {

    public String greetServer(String input) {
        String serverInfo = getServletContext().getServerInfo();
        String userAgent = getThreadLocalRequest().getHeader("User-Agent");
        return "Hello, " + input + "!<br><br>I am running " + serverInfo
                + ".<br><br>It looks like you are using:<br>" + userAgent;
    }
}

GreetingService (Listing 6) est l'interface utilisée par le code client pour effectuer l'appel distant:

Listing 6. API synchrone (src/gaej.example.client.GreetingService.java)

package gaej.example.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

/**
 * The client side stub for the RPC service.
 */
@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
    String greetServer(String name);
}

GreetingServiceAsync est l'interface qui sera réellement utilisée par le code client (Listing 7). Chaque méthode fournit un objet de callback pour être notifié de façon asynchrone quand l'appel de procédure distante est terminé. Derrière le rideau, GWT utilise Ajax. Quand le client utilise Ajax, il est préférable de ne pas bloquer le client, d'où les appels asynchrones. Le blocage du client serait aller à contresens de la finalité d'Ajax.

Listing 7. API asynchrone (src/gaej.example.client.GreetingServiceAsync.java)

package gaej.example.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * The async counterpart of <code>GreetingService</code>.
 */
public interface GreetingServiceAsync {
    void greetServer(String input, AsyncCallback<String> callback);
}

SimpleGWTApp est là où le principal se passe. Il écoute les événements IHM, puis envoie des requêtes Ajax à GreetingService.

Listing 8. Le point d'entrée de l'application construit aussi l'IHM (src/gaej.example.client.SimpleGWTApp.java)

package gaej.example.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Les classes servant de point d'entrée définit <code>onModuleLoad()</code>.
 */
public class SimpleGWTApp implements EntryPoint {
    /**
     * Message affiché à l'utilisateur quand le serveur ne peut pas être atteint
     * ou retourne une erreur.
     */
    private static final String SERVER_ERROR = "An error occurred while "
            + "attempting to contact the server. Please check your network "
            + "connection and try again.";

    /**
     * Crée un proxy du service distant pour parler au service de salutation côté serveur.
     */
    private final GreetingServiceAsync greetingService = GWT
            .create(GreetingService.class);

    /**
     * C'est la méthode servant de point d'entrée.
     */
    public void onModuleLoad() {
        final Button sendButton = new Button("Send");
        final TextBox nameField = new TextBox();
        nameField.setText("GWT User");

        // Vous pouvez ajouter des noms de style aux widgets
        sendButton.addStyleName("sendButton");

        // Ajoute nameField et sendButton à RootPanel
        // Utilisez RootPanel.get() pour obtenir l'élément body
        RootPanel.get("nameFieldContainer").add(nameField);
        RootPanel.get("sendButtonContainer").add(sendButton);

        // Donne le focus au champ nom quand l'application démarre
        nameField.setFocus(true);
        nameField.selectAll();

        // Crée la boite de dialogue
        final DialogBox dialogBox = new DialogBox();
        dialogBox.setText("Remote Procedure Call");
        dialogBox.setAnimationEnabled(true);
        final Button closeButton = new Button("Close");
        // Vous pouvez positionner l'id d'un widget en accédant à son Element
        closeButton.getElement().setId("closeButton");
        final Label textToServerLabel = new Label();
        final HTML serverResponseLabel = new HTML();
        VerticalPanel dialogVPanel = new VerticalPanel();
        dialogVPanel.addStyleName("dialogVPanel");
        dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
        dialogVPanel.add(textToServerLabel);
        dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
        dialogVPanel.add(serverResponseLabel);
        dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
        dialogVPanel.add(closeButton);
        dialogBox.setWidget(dialogVPanel);

        // Ajoute un gestionnaire pour fermer la DialogBox
        closeButton.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                dialogBox.hide();
                sendButton.setEnabled(true);
                sendButton.setFocus(true);
            }
        });

        // Crée un gestionnaire pour sendButton et nameField
        class MyHandler implements ClickHandler, KeyUpHandler {
            /**
             * Se déclenche quand l'utilisateur clique sendButton.
             */
            public void onClick(ClickEvent event) {
                sendNameToServer();
            }

            /**
             * Se déclenche quand l'utilisateur saisit dans nameField.
             */
            public void onKeyUp(KeyUpEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    sendNameToServer();
                }
            }

            /**
             * Envoie le nom saisi dans nameField au serveur et attend une réponse.
             */
            private void sendNameToServer() {
                sendButton.setEnabled(false);
                String textToServer = nameField.getText();
                textToServerLabel.setText(textToServer);
                serverResponseLabel.setText("");
                greetingService.greetServer(textToServer,
                        new AsyncCallback<String>() {
                            public void onFailure(Throwable caught) {
                                // Affiche le message d'erreur RPC
                                dialogBox
                                        .setText("Remote Procedure Call - Failure");
                                serverResponseLabel
                                        .addStyleName("serverResponseLabelError");
                                serverResponseLabel.setHTML(SERVER_ERROR);
                                dialogBox.center();
                                closeButton.setFocus(true);
                            }

                            public void onSuccess(String result) {
                                dialogBox.setText("Remote Procedure Call");
                                serverResponseLabel
                                        .removeStyleName("serverResponseLabelError");
                                serverResponseLabel.setHTML(result);
                                dialogBox.center();
                                closeButton.setFocus(true);
                            }
                        });
            }
        }

        // Ajoute un gestionnaire pour envoyer le nom au serveur
        MyHandler handler = new MyHandler();
        sendButton.addClickHandler(handler);
        nameField.addKeyUpHandler(handler);
    }
}

Le  descripteur de déploiement de l'application Web (web.xml, voir Listing 9), expose en tant que ressource Web de type servlet. La servlet GreetingService y est associée à l'adresse /simplegwtapp/greet pour que SimpleGWTApp puisse la charger et l'appeler. Le descripteur de déploiement indique également que la page d'accueil de l'application est SimpleGWTApp.html, afin qu'elle soit toujours chargée.

Listing 9. Descripteur de déploiement qui configure GreetingServiceImpl (war/WEB-INF/web.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!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>

  <!-- Page par défaut -->
  <welcome-file-list>
    <welcome-file>SimpleGWTApp.html</welcome-file>
  </welcome-file-list>
 
  <!-- Servlets -->
  <servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>gaej.example.server.GreetingServiceImpl</servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/simplegwtapp/greet</url-pattern>
  </servlet-mapping>

</web-app>

Le frontal HTML est SimpleGWTApp.html (Listing 10). C'est la page qui charge simplegwtapp.js et simplegwtapp.nocache.js, le code JavaScript généré par GWT à partir de votre ccode Java. Comme mentionné précédemment, on peut retrouver ce code dans le répertoire src et dans le package gaej.example.client (Listings 6, 7 et 8).

Listing 10. Page HTML qui affiche l'IHM GWT (war/SimpleGWTApp.html)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set     -->
<!-- the browser's rendering engine into           -->
<!-- "Quirks Mode". Replacing this declaration     -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout.   -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

    <!--                                                               -->
    <!-- Consider inlining CSS to reduce the number of requested files -->
    <!--                                                               -->
    <link type="text/css" rel="stylesheet" href="SimpleGWTApp.css">

    <!--                                           -->
    <!-- Any title is fine                         -->
    <!--                                           -->
    <title>Web Application Starter Project</title>
    
    <!--                                           -->
    <!-- This script loads your compiled module.   -->
    <!-- If you add any GWT meta tags, they must   -->
    <!-- be added before this line.                -->
    <!--                                           -->
    <script type="text/javascript" language="javascript" 
  src="simplegwtapp/simplegwtapp.nocache.js"></script>
  </head>

  <!--                                           -->
  <!-- The body can have arbitrary html, or      -->
  <!-- you can leave the body empty if you want  -->
  <!-- to create a completely dynamic UI.        -->
  <!--                                           -->
  <body>

    <!-- OPTIONAL: include this if you want history support -->
    <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' 
  style="position:absolute;width:0;height:0;border:0"></iframe>

    <h1>Web Application Starter Project</h1>

    <table align="center">
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter your name:</td> 
      </tr>
      <tr>
        <td id="nameFieldContainer"></td>
        <td id="sendButtonContainer"></td>
      </tr>
    </table>
  </body>
</html>

Avec GWT, vous contrôlez le look and feel de l'appliction au moyen de CSS, comme le montre le Listing 11:

Listing 11. Feuille de style de l'IHM clGWT (war/SimpleGWTApp.css)

/** Add css rules here for your application. */

/** Example rules used by the template application (remove for your app) */
h1 {
  font-size: 2em;
  font-weight: bold;
  color: #777777;
  margin: 40px 0px 70px;
  text-align: center;
}

.sendButton {
  display: block;
  font-size: 16pt;
}

/** Most GWT widgets already have a style name defined */
.gwt-DialogBox {
  width: 400px;
}

.dialogVPanel {
  margin: 5px;
}

.serverResponseLabelError {
  color: red;
}

/** Set ids using widget.getElement().setId("idOfElement") */
#closeButton {
  margin: 15px 6px 6px;
}

Déploiement sur Google App Engine

Une fois que vous aurez créé la prochaine killer app (parce qu'on a vraiment besoin d'une application de salutation conviviale), vous voudrez la déployer. La finalité de l'utilisation de Google App Engine est de pouvoir déployer l'application sur l'infrastructure de Google, solide et qui facilite le passage à l'échelle. Google App Engine est conçu pour fournir une plateforme permettant de construire des applications qui passent à l'échelle, "pouvant passer de de un à plusieurs millions d'utilisateurs sans soucis d'infrastructure" (comme indiqué sur la page d'accueil de l'App Engine). Pour utiliser cette infrastructure, vous avez besoin d'un compte Google App Engine pour Java.

Comme beaucoup de choses ici bas, au départ c'est gratuit. La version gratuite de l'App Engine pour Java fournit à l'application déployée assez de CPU, de bande passante et d'espace de stockage pour servir environ 5 millions de pages par mois. Au delà, vous payez ce que vous dépensez. (Gardez présent à l'esprit que c'est ce qui est disponible pour la préversion d'App Engine pour Java (ndT: a priori au moment de la traduction les conditions n'ont pas changé)).

Une fois que vous avez un compte, vous devriez voir une liste vide d'applications sur le site d'App Engine pour Java. Cliquez le bouton Create New Application et un formulaire comme celui de la Figure 8 devrait apparaitre. Entrez un nom d'application qui soit unique et une description, après quoi vous verrez un message de confirmation avec votre identifiant d'application.

L'identifiant figure également dans le fichier app.yaml de votre application (ndT: serait-ce plutôt appengine-web.xml ?). Notez que cet identifiant ne peut pas être modifié. Si vous utilisez l'authentification de Google pour votre application, "GAEj Article For Rick Part 1" sera affiché dans les pages d'authentification pour l'accès à l'application. Vous utiliserez gaejarticleforrick pour déployer l'application sur l'App Engine avec le plugin App Engine d'Eclipse.

Figure 8. Création d'une nouvelle application sur l'App Engine pour Java

Création d'une nouvelle application sur l'App Engine pour Java

Une fois que vous avez paramétré l'ID d'application, vous pouvez déployer votre application depuis Eclipse. D'abord, appuyez sur le bouton de la barre d'outils qui ressemble au logo Google App Engine (un avion à réaction), comme dans la Figure 9:

Figure 9. Plugin Eclipse d'App Engine pour Java

Plugin Eclipse d'App Engine pour Java

Assurez-vous d'avoir sélectionné votre projet App Engine pour Java avant de cliquer sur Deploy dans la boite de dialogue de la Figure 10. On vous demandera vos identifiants Google, qui sont votre adresse mail et votre nom d'utilisateur.

Figure 10. Déploiement d'une nouvelle application sur l'App Engine pour Java

Déploiement d'une nouvelle application sur l'App Engine pour Java

La boite de dialogue de la Figure 10 a un lien vers "App Engine Project setting". Cliquez ce lien (qui est aussi accessible dans le fichier de propriétés du projet) et entre l'ID d'application (ici gaejarticleforrick), comme dans la Figure 11. Cliquez ensuite OK, puis Deploy.

Figure 11. Propriétés du projet Google App Engine

Propriétés du projet Google App Engine

Une fois votre application déployée, elle sera disponible à l'adresse http://<application id>.appspot.com/. Vous pouvez voir cette application tourner à http://gaejarticleforrick.appspot.com/.

Conclusion

Ceci conclut la première partie de mon introduction à Google App Engine pour Java. Pour l'instant, vous avez une vue générale de ce que propose l'App Engine pour Java et vous avez fait les premiers pas pour ce qui est d'utiliser le plugin App Engine d'Eclipse. Vous avez créé deux petites applications de test (une à base de servlets et l'autre utilisant GWT) et vous avez déployé l'application GWT sur la plateforme Google App Engine.

Les exemples utilisés jusque là ont concerné l'outillage et les fonctionnalités qui facilitent la création et le déploiement d'applications java qui passent à l'échelle - potentiellement on peut même atteindre la taille de YouTube ou Facebook. Dans la seconde partie, vous continuerez à explorer les possibilités offertes aux développeurs Java qui travaillent sur l'App Engine. Pour aller plus loin que les applications de démonstration de cet article, vous construirez une application de gestion de contacts. Cette application sera aussi l'élément central des exercices de la troisième partie, qui étudiera le magasin de données de l'App Engine.