Création d’une application GWT

Modèle de projet client GWT

A quoi sert le modèle de projet client GWT?

Le modèle de projet client-gwt permet de créer des projets préfabriqués pour développer une application cliente basée sur GWT.

Structure du modèle de projet client-gwt

Je vous invite à lancer Eclipse et à parcourir le projet exemple contact-manager-client inclu dans le bundle d’installation du socle. Vous pourrez ainsi suivre au fil de la lecture et mieux vous familiariser avec ce modèle de projet.

Le modèle de projet client_gwt respecte la structure standard de répertoire de Maven2. On retrouve donc :

  • le répertoire src qui regroupe les sources java et les ressources du projet
  • le répertoire target qui contient tous les éléments générés
  • le fichier pom.xml qui contient la description détaillée du projet

Le socle ajoute un répertoire conf qui va aider à gérer les configurations différentes suivant l’environnement (dev, test, prod-test, prod).

Dans la suite de notre tutorial nous vous détaillerons le contenu de ces différents fichiers et répertoires.

le contenu du répertoire src/main/java

Ce répertoire contient l’ensemble du code source de l’application. L’architecture de GWT découpe l’application en deux parties :

  • la partie cliente (client side) regroupée dans les packages *.*.client.*
  • la partie serveur (server side) regroupée dans les packages *.*.server.*

La création d’un nouveau projet à partir du modèle de projet client-gwt nous donne donc un répertoire comportant le nom de votre package (dans notre exemple org.scub.foundation.contact.manager.client.gwt) ainsi que quatre autres packages.

le contenu du répertoire comportant le nom de votre package (org.scub.foundation.contact.manager.client.gwt)

Dans ce répertoire nous avons le fichier de configuration xml de gwt, le fichier GwtModule.gwt.xml dans lequel nous pouvons spécifier les services proposés par le serveur GWT et d’autres paramètres. Nous trouvons ensuite un répertoire public contenant les pages de connexion et des sous répertoires contenant les pages d’erreurs, les feuilles de style, ainsi que les images.

le package org.scub.foundation.contact.manager.client.gwt.client

Ce package contient tout le code java relatif au coté client de l’application GWT. La classe ClientEntryPointGwt désigne le point d’entrée de notre application client GWT et la classe Application définit la disposition de la page principale de l’application cliente.

le package org.scub.foundation.contact.manager.client.gwt.client.i18n

Ce package contient les classes liées à la norme d’internationalisation i18n. Ce package contient :

  • la classe ApplicationMessages et le fichier de propriétés
    ApplicationMessages.properties (fichier de propriétés par défaut).
  • Il peut contenir les fichiers de propriétés AppMessages_en.properties (pour la version anglaise) et AppMessages_fr.properties (pour la version française)
le package org.scub.foundation.contact.manager.client.gwt.client.view

Ce package contient les classes de l’interface graphique de l’application. Il est fortement conseillé de créer des sous packages pour hiérarchiser et regrouper les différents éléments de l’interface graphique (surtout si le nombre de vues est important). Dans le projet exemple nous avons deux vues (EditContactViewImpl et ListContactViewImpl) avec les fichiers UIBinder correspondants (EditContactViewImpl.ui.xml et ListContactViewImpl.ui.xml)

le package org.scub.foundation.contact.manager.client.gwt.server

Ce package contient tout le code lié au coté serveur de l’application GWT. Dans ce package nous définissons les classes d’implémentation des services GWT, qui appellent les services de la couche service du noyau de votre application.

le contenu du répertoire conf

Dans ce répertoire nous avons un dossier common pour la configuration commune à tous les environnements de projet et des dossiers spécifiques à chacun des environnements.

le répertoire common

Ce répertoire contient les configurations communes à tous les environnements de développement nous trouvons dans ce répertoire un ensemble de fichiers :

  • Le fichier filters/filters.properties qui doit contenir les propriétés de configuration relatives à tous les environnements de développement.
  • Le fichier resources/applicationContext.xml dans lequel vous pouvez spécifier vos services gwt ainsi que les emplacements des fichiers de mapping de vos objets grâce à dozer.
  • Nous avons ensuite les fichiers resources/dozer.properties et resources/dozer-bean-mappings.xml pour la spécification des propriétés de dozer et le mapping de vos classes.
  • Le fichier resources/rmiServiceImporterContext.xml permet d’importer les services du noyau de votre application,
  • Le fichier resources/securiteServiceContext.xml définit les configurations de sécurité propres à tous les environnements de projet.
  • Dans le fichier WEB-INF/handler-servlet.xml nous définissons le mapping entre les url à utiliser dans notre client gwt et les services gwt.
  • Dans le fichier WEB-INF/web.xml propre à toute application web nous spécifions le dispatcher, la page d’accueil etc.
le contenu du répertoire dev

Dans ce répertoire nous avons le fichier filters/filters.properties qui contient des propriétés destinées à accélérer la compilation du code source au niveau de gwt.

Pour les autres fichiers et sous répertoires de conf veuillez vous référer au tutorial sur le modele core qui vous donnera plus de détails.

le contenu du répertoire target

Comme dans tout projet Maven ce répertoire contient tout ce qui est généré à partir des sources de notre application lors des phases de compilation et de déploiement. Nous y trouverons par exemple: le .war de notre application qui peut être deployé dans un serveur web, ainsi que le jar du code source de toute l’application.

Nous en avons à présent fini avec la présentation de notre modèle d’application client gwt et vous devez être en mesure de connaitre l’emplacement ainsi que le rôle de chaque fichier et type de fichier.

Création du projet GWT

La première étape, avant de commencer à implémenter les différentes couches de l’application, consiste à créer les projets à partir des modèles de projets intégrés au socle. Vous allez ici créer le projet client GWT.
Pour cela, rien de plus simple, il suffit d’utiliser l’assistant accessible via :

  • Le raccourci clavier Ctrl + N
  • Le menu File → New → Other
  • Le menu contextuel New → Other (comme sur la copie d’écran ci dessous).

Saisissez dans le champ le texte ”maven” pour filtrer l’affichage et sélectionnez Maven Project puis cliquez sur Next.

Vous accédez alors à l’assistant de création d’un projet Maven, vérifiez à la première étape que la case à cocher Create a simple project (skip archetype selection) est bien décochée (comme sur la copie d’écran ci dessous).

Cliquez ensuite sur Next pour accéder à l’étape suivante qui va permettre de sélectionner l’archetype, correspondant au projet que vous souhaitez créer. Sélectionnez l’archetype scub-foundation-archetype-client-gwt puis cliquez sur Next comme indiqué sur la copie d’écran ci dessous.

Il reste ensuite à spécifier les paramètres suivants :

  • groupId : contact-manager qui est commun à tous les projets de la même application.
  • artifactId : contact-manager-client
  • version : 1.0
  • package : org.scub.foundation.contact.manager
  • publicFolderPathForBundle_This_is_the_package_variable_or_points_are_replaced_by_slash : org/scub/foundation/contact/manager

Cliquez ensuite sur Finish. Le nouveau projet apparait alors dans la vue Package Explorer.

Configuration de l’application cliente GWT

Lier le client au(x) noyau(x)

Un client peut exploiter un ou plusieurs noyaux. Pour lier le client à un noyau, il suffit de rajouter une dépendance vers le projet correspondant, dans notre cas contact-manager-core-interfaces, dans le POM du projet contact-manager-client. Ouvrez le fichier pom.xml. Vous pouvez ensuite configurer le POM (en éditant manuellement le fichier de configuration) :

  • Sélectionnez l’onglet pom.xml source (situé dans la partie inférieure)
  • Ajoutez l’élément <dependency> comme indiqué ci dessous à la fin du fichier.
	<dependency>
			<groupId>contact-manager</groupId>
			<artifactId>contact-manager-core-interfaces</artifactId>
			<version>1.0-SNAPSHOT</version>
		</dependency>

Il est nécessaire de packager puis publier le projet contact-manager-core-interfaces dans le référentiel local afin de le rendre visible aux autres projets. Pour cela, il suffit de sélectionner le projet puis de faire Run As… → Publier dans le référentiel local

Importer les services de noyau(x)

Vous pouvez importer les services mis à disposition par les noyaux déclarés comme dépendance dans le POM comme expliqué précédemment. Pour être en mesure d’utiliser les services développés dans les tutoriaux précédents :

  • Ouvrez le fichier /conf/dev/resources/rmiServiceImporterSpecContext.xml du projet contact-manager-client
  • Exemple de services importés :
<bean id="contactService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://localhost:1099/contact-manager-core-implementations/contactService"/>
		<property name="serviceInterface" value="org.scub.foundation.contact.manager.core.service.interfaces.ContactService"/>
		<property name="refreshStubOnConnectFailure" value="true"/>
		<property name="cacheStub" value="false" />
	    <property name="lookupStubOnStartup" value="false"/>
	</bean>
 
	<bean id="contactServiceConsumer" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://localhost:1099/contact-manager-ws-client/contactServiceConsumer"/>
		<property name="serviceInterface" value="org.scub.foundation.contact.manager.ws.client.side.service.interfaces.ContactServiceConsumer"/>
		<property name="refreshStubOnConnectFailure" value="true"/>
		<property name="cacheStub" value="false" />
	    <property name="lookupStubOnStartup" value="false"/>
	</bean>
 
	<bean id="listService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://localhost:1099/contact-manager-core-implementations/listService" />
		<property name="serviceInterface" value="org.scub.foundation.contact.manager.core.service.interfaces.ListService" />
		<property name="refreshStubOnConnectFailure" value="true" />
		<property name="cacheStub" value="false" />
		<property name="lookupStubOnStartup" value="false" />
	</bean>

Développer les services GWT RPC

Avant de développer l’interface Web de l’application, il faut avoir accès aux services de la couche service de l’application noyau développée précédemment. Pour cela GWT a élaboré une architecture qui découpe le projet en deux parties :

  • la partie cliente (client side)
  • la partie serveur (server side)

Lors d’une interaction de l’utilisateur avec l’interface graphique de la partie cliente, une requête HTTP est envoyée sur le réseau contenant une requête RPC vers le service GWT de la partie serveur qui appellera le service de notre application noyau via RMI. Par exemple lorsqu’un utilisateur souhaite rechercher un contact, il va saisir du texte dans le champ de recherche, puis va cliquer sur un bouton “rechercher” qui va envoyer la requête HTTP pour appeler le service correspondant à la recherche de contacts.

Pour plus d’informations à propos de GWT RPC, je vous invite à lire le guide officiel suivant

Si on reprend le schéma de la documentation officielle, on retrouve à gauche la partie cliente, et à droite la partie serveur. On voit qu’il est nécessaire de créer deux interfaces dans la partie cliente, et implémenter une classe dans la partie serveur.

Remarque: le projet exemple contient déjà l’ensemble des classes, interfaces et configurations de ce tutoriel.

Créer un modèle

Avant tout, commencez par créer les modèles qui seront utilisés dans les signatures des fonctions de l’interface. Tout d’abord CellContactModel :

Placez le curseur sur le package org.scub.foundation.contact.manager.client.gwt.client.shared dans src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Class
Dans la fenêtre de l’assistant New Java Class, indiquez dans les champs :

Package : org.scub.foundation.contact.manager.client.gwt.client.shared
Name : CellContactModel
Interfaces : com.google.gwt.user.client.rpc.IsSerializable

Cliquez sur Finish.
Recopiez le code de la classe ci dessous.

package org.scub.foundation.contact.manager.client.gwt.client.shared;
 
import java.util.List;
 
import com.google.gwt.user.client.rpc.IsSerializable;
 
/**
 * Model for display a contact in table.
 * @author Anthony GUILLEMETTE (anthony.guillemette@scub.net)
 */
public final class CellContactModel implements IsSerializable {
 
    private Long id;
 
    private String civilite;
 
    private String lastName;
 
    private String firstName;
 
    private List listEmails;
 
    private List listPhones;
 
    /**
     * Get the value of id.
     * @return the id
     */
    public Long getId() {
        return id;
    }
 
    /**
     * Set the value of id.
     * @param id the id to set
     */
    public void setId(Long id) {
        this.id = id;
    }
 
    /**
     * Get the value of civilite.
     * @return the civilite
     */
    public String getCivilite() {
        return civilite;
    }
 
    /**
     * Set the value of civilite.
     * @param civilite the civilite to set
     */
    public void setCivilite(String civilite) {
        this.civilite = civilite;
    }
 
    /**
     * Get the value of lastName.
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }
 
    /**
     * Set the value of lastName.
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
 
    /**
     * Get the value of firstName.
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }
 
    /**
     * Set the value of firstName.
     * @param firstName the firstName to set
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
 
    /**
     * Get the value of listEmails.
     * @return the listEmails
     */
    public List getListEmails() {
        return listEmails;
    }
 
    /**
     * Set the value of listEmails.
     * @param listEmails the listEmails to set
     */
    public void setListEmails(List listEmails) {
        this.listEmails = listEmails;
    }
 
    /**
     * Get the value of listPhones.
     * @return the listPhones
     */
    public List getListPhones() {
        return listPhones;
    }
 
    /**
     * Set the value of listPhones.
     * @param listPhones the listPhones to set
     */
    public void setListPhones(List listPhones) {
        this.listPhones = listPhones;
    }
 
}

Cette classe implémente l’interface IsSerializable pour être sérialisée lors des appels RPC GWT.

Maintenant, nous pouvons définir nos services. Pour définir nos services, nous avons besoin de:

  • définir une interface asynchrone à nos services qui sera appelée depuis le client
  • définir une interface synchrone qui hérite de RemoteService. Elle contiendra les signatures des services GWT qui feront appel aux services de la couche service de l’application noyau.
  • définir une classe qui implémente l’interface définie ci-dessus.

Définir l’interface asynchrone

L’interface asynchrone reprend les signatures de l’interface synchrone à la différence près que les services sont asynchrones. Les fonctions ne retournent donc rien, mais prennent un paramètre supplémentaire aSyncCallback < T > générique qui est rempli lorsqu’un appel asynchrone se termine avec ce que l’interface synchrone retourne. Cela permet de ne pas bloquer l’interface graphique pendant l’appel au service distant du noyau.

Pour ListServiceGwtAsync :

  • Placez le curseur sur le package
    org.scub.foundation.contact.manager.client.gwt.client.service dans src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Interface
  • Dans la fenêtre de l’assistant New Java Interface, indiquez dans les champs :
    • Package : org.scub.foundation.contact.manager.client.gwt.client.service
    • Name : ListServiceGwtAsync
  • Cliquez sur Finish.
  • Recopiez le code de la classe ci dessous.
package org.scub.foundation.contact.manager.client.gwt.client.service;
 
import java.util.List;
 
import org.scub.foundation.framework.gwt.module.shared.IdLabelModel;
 
import com.google.gwt.user.client.rpc.AsyncCallback;
 
/**
 * Services async for the lists.
 * @author Adrien HAUTOT (adrien.hautot@scub.net)
 */
public interface ListServiceGwtAsync {
 
    /**
     * Get a list of civilites based on a search filterd with the filter parameter.
     * @param filter the filter for search.
     * @param callback the callback
     */
    void getCivilites(String filter, AsyncCallback<List> callback);
 
    /**
     * Get a list of phone types based on a search filterd with the filter parameter.
     * @param filter the filter for search.
     * @param callback the callback
     */
    void getPhoneTypes(String filter, AsyncCallback<List> callback);
}

Pour ContactServiceGwtAsync :

  • Placez le curseur sur le package
    org.scub.foundation.contact.manager.client.gwt.client.service dans src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Interface
  • Dans la fenêtre de l’assistant New Java Interface, indiquez dans les champs :
    • Package : org.scub.foundation.contact.manager.client.gwt.client.service
    • Name : ContactServiceGwtAsync
  • Cliquez sur Finish.
  • Recopiez le code de la classe ci dessous.
package org.scub.foundation.contact.manager.client.gwt.client.service;
 
import org.scub.foundation.contact.manager.client.gwt.shared.ContactCriteriasModel;
import org.scub.foundation.contact.manager.client.gwt.shared.ContactModel;
import org.scub.foundation.framework.gwt.module.shared.pagination.RemotePagingCriteriasModel;
import org.scub.foundation.framework.gwt.module.shared.pagination.RemotePagingResultsModel;
 
import com.google.gwt.user.client.rpc.AsyncCallback;
 
/**
 * Service asynchrone sur les examples.
 * @author Anthony GUILLEMETTE (anthony.guillemette@scub.net)
 */
public interface ContactServiceGwtAsync {
    /**
     * Create or update a contact.
     * @param contact contact
     * @param callback the callback
     */
    void createOrUpdateContact(ContactModel contact, AsyncCallback callback);
 
    /**
     * Delete contact by id.
     * @param id contact id
     * @param callback the callback
     */
    void deleteContactById(Long id, AsyncCallback callback);
 
    /**
     * Get contact by id.
     * @param id contact id
     * @param callback the callback
     */
    void getContactById(Long id, AsyncCallback callback);
 
    /**
     * Get contacts by criterias.
     * @param criterias criterias for searching contacts
     * @param callback the callback
     */
    void getListContactsByCriterias(RemotePagingCriteriasModel criterias, AsyncCallback<RemotePagingResultsModel> callback);
}

Définir l’interface synchrone

Cette interface contient les méthodes de création d’un contact, de recherche d’un contact, de suppression d’un contact, de l’obtention de la liste de tous les contacts.

  • Placez le curseur sur le package org.scub.foundation.contact.manager.client.gwt.client.service dans src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Interface
  • Dans la fenêtre de l’assistant New Java Interface
      , indiquez dans les champs :

    • Package : org.scub.foundation.contact.manager.client.gwt.client.service
    • Name : ListServiceGwt
    • Interfaces : com.google.gwt.user.client.rpc.RemoteService
  • Cliquez sur Finish.
  • Recopiez le code de la classe ci dessous.
package org.scub.foundation.contact.manager.client.gwt.client.service;
 
import java.util.List;
 
import org.scub.foundation.framework.gwt.module.client.exception.GwtRunTimeExceptionGwt;
import org.scub.foundation.framework.gwt.module.shared.IdLabelModel;
 
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
/**org.scub.foundation.contact.manager.client.gwt.client.service
 * Services for the lists.
 * @author Adrien HAUTOT (adrien.hautot@scub.net)
 */
@RemoteServiceRelativePath("handler/listService")
public interface ListServiceGwt extends RemoteService {
 
    /**
     * Get a list of civilites based on a search filterd with the filter parameter.
     * @param filter the filter for search.
     * @return the list
     */
    List getCivilites(String filter)throws GwtRunTimeExceptionGwt;
 
    /**
     * Get a list of phone types based on a search filterd with the filter parameter.
     * @param filter the filter for search.
     * @return the list
     */
    List getPhoneTypes(String filter)throws GwtRunTimeExceptionGwt;
}
  • Placez le curseur sur le package org.scub.foundation.contact.manager.client.gwt.client.service dans src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Interface
  • Dans la fenêtre de l’assistant New Java Interface
      , indiquez dans les champs :

    • Package : org.scub.foundation.contact.manager.client.gwt.client.service
    • Name : ContactServiceGwt
    • Interfaces : com.google.gwt.user.client.rpc.RemoteService
  • Cliquez sur Finish.
  • Recopiez le code de la classe ci dessous.
package org.scub.foundation.contact.manager.client.gwt.client.service;
 
import org.scub.foundation.contact.manager.client.gwt.shared.ContactCriteriasModel;
import org.scub.foundation.contact.manager.client.gwt.shared.ContactModel;
import org.scub.foundation.framework.gwt.module.client.exception.GwtRunTimeExceptionGwt;
import org.scub.foundation.framework.gwt.module.shared.pagination.RemotePagingCriteriasModel;
import org.scub.foundation.framework.gwt.module.shared.pagination.RemotePagingResultsModel;
 
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
/**
 * Service for contacts.
 * @author Anthony GUILLEMETTE (anthony.guillemette@scub.net)
 */
@RemoteServiceRelativePath("handler/contactService")
public interface ContactServiceGwt extends RemoteService {
 
    /**
     * Get contacts by criterias.
     * @param criterias criterias for searching contacts
     * @return list of contacts
     */
    RemotePagingResultsModel getListContactsByCriterias(RemotePagingCriteriasModel criterias)
        throws GwtRunTimeExceptionGwt;;
 
    /**
     * Get contact by id.
     * @param id contact id
     * @return contact
     */
    ContactModel getContactById(Long id) throws GwtRunTimeExceptionGwt;
 
    /**
     * Create or update a contact.
     * @param contact contact
     * @return contact created or updated
     */
    ContactModel createOrUpdateContact(ContactModel contact) throws GwtRunTimeExceptionGwt;
 
    /**
     * Delete contact by id.
     * @param id contact id
     */
    void deleteContactById(Long id) throws GwtRunTimeExceptionGwt;
}

L’annotation @RemoteServiceRelativePath(value = “handler/contactService”) permet de spécifier le chemin d’accès aux services définis.

Définir l’implémentation de l’interface synchrone

Créez ensuite une classe qui implémente l’interface synchrone définie ci-dessus. Cela permet d’appeler les services de l’application noyau :

      • Placez le curseur sur le package org.scub.foundation.contact.manager.client.gwt.server.service dans src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Class
      • Dans la fenêtre de l’assistant New Java Class, indiquez dans les champs :
        • Package : org.scub.foundation.contact.manager.client.gwt.server.service
        • Name : ListServiceGwtImpl
        • Interfaces : org.scub.foundation.contact.manager.client.gwt.client.service.ListServiceGwt
      • Cliquez sur Finish.
      • Recopiez ensuite le code de la classe indiqué ci-dessous.
package org.scub.foundation.contact.manager.client.gwt.server.service;
 
import java.util.List;
 
import org.scub.foundation.contact.manager.core.service.interfaces.ListService;
import org.scub.foundation.framework.base.mapping.util.MapperDozerBean;
import org.scub.foundation.framework.gwt.module.shared.IdLabelModel;
 
/**
 * Implementation of the gwt list service.
 * @author Adrien HAUTOT (adrien.hautot@scub.net)
 */
public class ListServiceGwtImpl implements org.scub.foundation.contact.manager.client.gwt.client.service.ListServiceGwt {
 
    private ListService listService;
 
    private MapperDozerBean mapperDozerBean;
 
    @Override
    public List getCivilites(String filter) {
        return mapperDozerBean.mapList(listService.getCivilites(filter), IdLabelModel.class);
    }
 
    @Override
    public List getPhoneTypes(String filter) {
        return mapperDozerBean.mapList(listService.getPhoneTypes(filter), IdLabelModel.class);
    }
 
    /**
     * Set the value of listService.
     * @param listService the listService to set
     */
    public void setListService(ListService listService) {
        this.listService = listService;
    }
 
    /**
     * Set the value of mapperDozerBean.
     * @param mapperDozerBean the mapperDozerBean to set
     */
    public void setMapperDozerBean(MapperDozerBean mapperDozerBean) {
        this.mapperDozerBean = mapperDozerBean;
    }
 
}
      • Placez le curseur sur le package org.scub.foundation.contact.manager.client.gwt.server.service dans src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Class
      • Dans la fenêtre de l’assistant New Java Class, indiquez dans les champs :
        • Package : org.scub.foundation.contact.manager.client.gwt.server.service
        • Name : ContactServiceGwtImpl
        • Interfaces : org.scub.foundation.contact.manager.client.gwt.client.service.ContactServiceGwt
      • Cliquez sur Finish.
      • Recopiez ensuite le code de la classe indiqué ci-dessous.
package org.scub.foundation.contact.manager.client.gwt.server.service;
 
import java.util.List;
 
import org.scub.foundation.contact.manager.client.gwt.client.service.ContactServiceGwt;
import org.scub.foundation.contact.manager.client.gwt.shared.ContactCriteriasModel;
import org.scub.foundation.contact.manager.client.gwt.shared.ContactModel;
import org.scub.foundation.contact.manager.core.dto.ContactCriteresRechercheDto;
import org.scub.foundation.contact.manager.core.dto.ContactDto;
import org.scub.foundation.contact.manager.core.service.interfaces.ContactService;
import org.scub.foundation.framework.base.mapping.util.MapperDozerBean;
import org.scub.foundation.framework.base.paging.RemotePagingCriteriasDto;
import org.scub.foundation.framework.base.paging.RemotePagingResultsDto;
import org.scub.foundation.framework.base.paging.RemotePagingSortDto;
import org.scub.foundation.framework.gwt.module.shared.pagination.RemotePagingCriteriasModel;
import org.scub.foundation.framework.gwt.module.shared.pagination.RemotePagingResultsModel;
 
/**
 * Implementation du service sur les examples.
 * @author Anthony GUILLEMETTE (anthony.guillemette@scub.net)
 */
public final class ContactServiceGwtImpl implements ContactServiceGwt {
 
    private MapperDozerBean mapperDozerBean;
 
    private ContactService contactService;
 
    @Override
    public RemotePagingResultsModel getListContactsByCriterias(RemotePagingCriteriasModel criterias) {
        final ContactCriteresRechercheDto criteresDto = mapperDozerBean.map(criterias.getCriterias(), ContactCriteresRechercheDto.class);
        final RemotePagingCriteriasDto criteriasDto =
            new RemotePagingCriteriasDto(criteresDto, criterias.getFirstResult(), criterias.getMaxResult());
        final List listRemotePagingSortDto = mapperDozerBean.mapList(criterias.getListeSorts(), RemotePagingSortDto.class);
        criteriasDto.setListeSorts(listRemotePagingSortDto);
        final RemotePagingResultsDto resultsDto = contactService.getContactsByCriteria(criteriasDto);
        final List listResults = mapperDozerBean.mapList(resultsDto.getListResults(), ContactModel.class);
        final RemotePagingResultsModel results = new RemotePagingResultsModel();
        results.setListResults(listResults);
        results.setTotalResults(resultsDto.getTotalResults());
        return results;
    }
 
    @Override
    public ContactModel getContactById(Long id) {
        return mapperDozerBean.map(contactService.getContactById(id), ContactModel.class);
    }
 
    @Override
    public ContactModel createOrUpdateContact(ContactModel contact) {
        final ContactDto contactRetour = contactService.ajouterOuModifierContact((ContactDto) mapperDozerBean.map(contact, ContactDto.class));
        return mapperDozerBean.map(contactRetour, ContactModel.class);
    }
 
    @Override
    public void deleteContactById(Long id) {
        contactService.deleteContact(id);
    }
 
    /**
     * Set the value of mapperDozerBean.
     * @param mapperDozerBean the mapperDozerBean to set
     */
    public void setMapperDozerBean(MapperDozerBean mapperDozerBean) {
        this.mapperDozerBean = mapperDozerBean;
    }
 
    /**
     * Set the value of contactService.
     * @param contactService the contactService to set
     */
    public void setContactService(ContactService contactService) {
        this.contactService = contactService;
    }
}

Déclarer le service GWT dans la configuration Spring

Déclarez ensuite l’implémentation du service GWT avec un nouveau bean dans le fichier /conf/common/resources/applicationContext.xml en ajoutant les éléments suivants au fichier xml :

	<!-- rajouter ici l'ensemble de vos service gwt -->
	<bean id="contactServiceGwt" class="org.scub.foundation.contact.manager.client.gwt.server.service.ContactServiceGwtImpl" autowire="byName"/>
	<bean id="contactWsServiceGwt" class="org.scub.foundation.contact.manager.client.gwt.server.service.ContactWsServiceGwtImpl" autowire="byName"/>
	<bean id="listServiceGwt" class="org.scub.foundation.contact.manager.client.gwt.server.service.ListServiceGwtImpl" autowire="byName"/>
 
	<!-- Configuration Dozer ne pas editer la structure du bean -->	
	<bean id="mapperDozerBean" class="org.scub.foundation.framework.base.mapping.util.MapperDozerFactoryBean">
		<property name="mappingFiles">
			<list>		  
		    	<value>dozer-bean-mappings.xml</value>
		    </list>
		</property>
	</bean>

Définir le mapping entre les url et les services GWT

Ajoutez un mapping entre le bean déclaré ci-dessus et l’url à laquelle le service sera disponible en éditant le fichier /conf/common/WEB-INF/handler-servlet.xml. Il existe déjà dans ce fichier un mapping vers l’exemple inclus dans le projet. Supprimer ce mapping et remplacez le par le code suivant :

L’url /contactService est celle que nous avons indiquée comme valeur dans l’annotation @RemoteServiceRelativePath de l’interface synchrone. contactServiceGwt est l’identifiant que nous avons donné à notre service GWT.

Développer une interface Web avec GWT

Vous allez maintenant voir une interface Web simple avec GWT pour assimiler les bases de GWT.

Créer une vue

Méthode pour la création d’une vue :

      • Placez le curseur sur src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Class
      • Dans la fenêtre de l’assistant New Java Class, indiquez dans les champs :
        • Package : org.scub.foundation.contact.manager.client.gwt.client.view
        • Name : EditContactViewImpl
      • Cliquez sur Finish.

Voir les classes EditContactViewImpl et ListContactViewImpl avec les fichiers IuBinder associés EditContactViewImpl.ui.xml et ListContactViewImpl.ui.xml dans le package org.scub.foundation.contact.manager.client.gwt.client.view.

Créer un presenter

Méthode pour la création d’un presenter :

      • Placez le curseur sur src/main/java, faites un clic droit pour accéder au menu contextuel et allez dans New → Class
      • Dans la fenêtre de l’assistant New Java Class, indiquez dans les champs :
        • Package : org.scub.foundation.contact.manager.client.gwt.client.presenter
        • Name : EditContactPresenter
      • Cliquez sur Finish.

Voir les classes EditContactPresenter et ListContactsPresenter dans le package org.scub.foundation.contact.manager.client.gwt.client.presenter

Les messages

Les messages sont à écrire dans la classe AppMessages du package org.scub.foundation.contact.manager.client.gwt.client.i18n. Cette classe permet d’indiquer les messages de la vue et la gestion de l’internationalisation.

La sécurité d’une application cliente

L’application cliente est le point d’entrée d’utilisateurs dans une application. Il est nécessaire d’identifier quel utilisateur accède à l’application pour ensuite être en mesure de déterminer à quelles zones il aura accès, et quels sont les services de noyaux qu’il sera en mesure d’utiliser.

Nous allons voir le processus visant à sécuriser une application cliente en utilisant Spring Security. Spring Security offre de nombreuses possibilités en matière de sécurité. Du coté d’une application cliente, le principe est basé sur le filtrage des urls. Le principe est le suivant :

      • L’utilisateur s’authentifie pour accéder à la page d’accueil de l’application.
      • Une fois authentifié un contexte de sécurité est créé, permettant de connaître les différents rôles de l’utilisateur.
      • Ce contexte sera propagé vers le noyau lors de l’appel de services.

Nous expliquerons quelle configuration est en place dès la création d’une application cliente basée sur les modèles de projet du socle, pour ensuite voir comment on peut facilement l’adapter à nos besoins.

La configuration du fichier web.xml

Si vous ouvrez le fichier /conf/common/WEB-INF/web.xml de votre application cliente, vous trouverez la déclaration d’un filtre springSecurityFilterChain qui fait référence à la classe DelegatingFilterProxy du Framework Spring. Ce filtre s’applique pour toutes les urls de l’application.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
    This file is part of Scub Foundation.
    Copyright (C) 2006-2013  SCUB
 
    Scub Foundation is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    Scub Foundation is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public License
    along with Scub Foundation.  If not, see <http://www.gnu.org/licenses/>.
-->
	<!-- A tweaked version of the default Tomcat web.xml to remove everything except the stuff we want to use -->
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
 
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/classes/applicationContext.xml
			/WEB-INF/classes/securiteServiceContext.xml
			/WEB-INF/classes/rmiServiceImporterContext.xml
			/WEB-INF/classes/securiteServiceSpecContext.xml
			/WEB-INF/classes/rmiServiceImporterSpecContext.xml
		</param-value>
	</context-param>
 
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
 
	<servlet>
		<servlet-name>handler</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
 
	<servlet-mapping>
		<servlet-name>handler</servlet-name>
		<url-pattern>/handler/*</url-pattern>
	</servlet-mapping>
 
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
 
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
 
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
 
	<error-page>
		<error-code>403</error-code>
		<location>/erreurs/403.jsp</location>
	</error-page>
 
	<error-page>
		<error-code>404</error-code>
		<location>/erreurs/404.jsp</location>
	</error-page>
 
</web-app

Cela permet d’accéder à l’infrastructure Web de Spring Security. DelegatingFilterProxy délègue le filtrage à springSecurityFilterChain qui est un bean faisant partie de l’infrastructure interne de Spring Security créée par l’espace de nommage en charge de la sécurité. Vous n’avez pas à déclarer ce bean vous même. Il vous suffit d’éditer le fichier de configuration securiteServiceContext.xml qui définit le contexte de sécurité de l’application cliente.

Le contexte de sécurité de l’application cliente

Ouvrez maintenant le fichier /conf/common/resources/securiteServiceContext.xml de votre application cliente. Vous trouverez la déclaration de la configuration de sécurité http automatique par défaut qui se présente ainsi :

	<http pattern="/handler/**">
		<anonymous enabled="false" />
		<intercept-url pattern="/**" access="ROLE_USER" />
		<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/index.html" />
		<logout logout-success-url="/index.jsp" logout-url="/j_spring_security_logout" />
		<custom-filter after="EXCEPTION_TRANSLATION_FILTER" ref="exceptionTranslationFilterGwt" />
	</http>
 
	<http pattern="/login.jsp*" security="none" />
	<http pattern="/images/**" security="none" />
	<http pattern="/styles" security="none" />
	<http pattern="/clear.cache.gif" security="none" />
 
	<http auto-config="true">
		<anonymous enabled="false" />
		<intercept-url pattern="/**" access="ROLE_USER" />
		<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/index.html" />
		<logout logout-success-url="/index.jsp" logout-url="/j_spring_security_logout" />
	</http>

Vous l’avez remarqué lors du développement de vos applications clientes, par défaut, une authentification avec les identifiants user/user est nécessaire pour accéder à la page d’accueil. Si on décortique la configuration définie ci-dessus :

      • La page pour s’authentifier est accessible aux utilisateurs anonymes. L’accès aux autres pages est restreint aux utilisateurs authentifiés ayant le rôle ROLE_USER. Le formulaire d’authentification correspond à la page login.jsp avec une url en cas d’échec d’authentification et url cible si l’authentification réussie.
      • L’url chargée lors de la dés-authentification est /index.jsp et l’url qui permet de se dés-authentifier est /j_spring_security_logout.

Tout comme c’était le cas pour l’application noyau, on a un fichier de configuration de sécurité spécifique pour chaque environnement , securiteServiceSpecContext.xml dans lequel est déclaré un authentication-provider qui fournit les utilisateurs/mot de passes et leur rôle. Ouvrez le fichier /conf/dev/resources/securiteServiceSpecContext.xml et vous trouverez la configuration par défaut :

	<authentication-manager>
		<authentication-provider>
			<user-service>
				<user name="user" password="user" authorities="ROLE_USER" />
			</user-service>
		</authentication-provider>
	</authentication-manager>

Par défaut l’application cliente créer donc un contexte sécurisé pour un utilisateur user qui possède le rôle ROLE_USER.

Lancer l’application cliente GWT

Il faut se placer sur le projet contact-manager-client puis le déployer dans Jetty.

Cliquez sur :

Puis sur : Deploy to jetty

Déployez également contact-manager-core-implementation de la même manière.

Puis copier l’url suivant dans un navigateur:
http://localhost:8080/contact-manager-client/login.jsp

Il est également possible de procéder de la façon suivante :

      • Cliquez sur :
      • Puis sur : Run GWT
      • La fenêtre suivante doit apparaître :
      • Cliquez alors sur Launch Default Browser
      • S’il y a ‘contact-manager-client-1.0-SNAPSHOT’ dans l’url écrire simplement à la place ‘contact-manager-client’.
        Vous pouvez alors suivre l’évolution du client GWT avec l’onglet index (FF).
      • Ce message peut apparaître :
      • Cliquez sur ok.
      • Cliquez sur Envoyer

Vous devez alors accéder à l’application comme ci-dessous:

Télécharger les sources

Les sources du projet Eclipse pour ce tutoriel sont téléchargeables sur Source Forge à l’adresse suivante.