Création d’un ilôt de service

Prérequis : avant de lire cet article, il est nécessaire d’avoir suivi les étapes suivantes :

  • Avoir effectué l’installation de l’usine de développement sous Linux, Windows ou Mac OS.
  • tutorial sur Maven afin de mieux comprendre la structure du projet.

Vous pouvez voir l’ensemble du projet après l’installation de l’usine de développement.

Core-interfaces

Introduction au modèle de projet core-interfaces

Cet article explique le rôle, la structure, et le contenu du modèle de projet
core-interfaces (anciennement core-model) de l’usine de développement.

A quoi sert le modèle de projet core-interfaces?

Le modèle de projet core-interfaces permet de distribuer les interfaces de services et les DTO d’un noyau à un ou plusieurs client(s). C’est possible avec Maven qui permet la gestion de dépendances à partir du POM. Les modèles de projet core, client gwt et client spring mvc contiennent dans leur POM une dépendance vers core-interfaces. Un projet basé sur core-interfaces sert à faire le lien entre le projet Eclipse core (core-implementations) et le client.

Structure du modèle de projet core-interfaces

Je vous invite à lancer Eclipse et à parcourir le projet exemple
contact-manager-core-interfaces inclu dans le bundle d’installation de l’usine de développement. Vous pourrez ainsi suivre au fil de la lecture et mieux vous familiariser avec ce modèle de projet.
Le modèle de projet core-interfaces respecte la structure standard de répertoire de Maven2. On retrouve :

    • le répertoire src qui regroupe les sources java 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

Nous allons parcourir le projet afin de mieux comprendre et analyser son contenu.

Le fichier pom.xml

Tout d’abord, on retrouve le fichier pom.xml qui décrit le POM du projet. Si on ouvre ce fichier, on obtient alors une vue d’ensemble du POM comme sur la capture d’écran ci dessous grâce au plugin m2eclipse.

L’information principale à retenir ici est le lien de parenté avec scub-foundation-superpom-core-interfaces qui permet entre autre d’hériter de ses dépendances. On peut voir les groupId/artifactId spécifiques à notre projet exemple et le packaging de type jar que l’on peut modifier très simplement par cette vue.

Le répertoire src

Le répertoire src/main/java est constitué de packages. Le nommage des packages suit les conventions des standards java. Si le nombre de classes dans un package devient trop important, alors on définit des sous packages pour les organiser de manière logique. On retrouve le contenu suivant :

    • les interfaces de la couche service sont regroupées dans un package (dans notre exemple org.scub.foundation.contact.manager.core.service.interfaces ).
    • les DTO manipulés par la couche service sont regroupés dans un ou plusieurs packages (dans notre exemple org.scub.foundation.contact.manager.core.dto ).
    • on peut implémenter des exceptions spécifiques aux services de l’application (seulement si les exceptions prédéfinies de l’usine de développement ne sont pas adéquates). On les regroupe alors dans un package (dans notre exemple, on pourrait créer le package org.scub.foundation.contact.manager.core.exceptions ).

Le répertoire target

On retrouve dans ce répertoire tous les éléments générés dont :

    • le projet packagé dans le fichier jar généré automatiquement par Maven lors des tâches de compilation et d’installation. Dans notre exemple, il s’agit de :
      contact-manager-core-interfaces-1.0-SNAPSHOT.jar
    • les sources du projet packagé dans un second fichier jar également généré par Maven. Dans notre exemple, il s’agit de : contact-manager-core-interfaces-1.0-SNAPSHOT-sources.jar

Core-implementations

Introduction au modèle de projet core-implementations

Cet article explique le rôle, la structure, et le contenu du modèle de projet core-implementations (anciennement core) de l’usine de développement.

A quoi sert le modèle de projet core-implementations?

Le modèle de projet core-implementations définit la structure pour implémenter le noyau (en anglais core ou kernel) d’une application. Un projet basé sur cette structure implémente les interfaces de services déclarées dans le projet core-interfaces associé.

Structure du modèle de projet core-implementations

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

Le modèle de projet core-implementations 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

L’usine de développement 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 fichier pom.xml

On retrouve dans le fichier pom.xml le POM qui décrit, de manière déclarative, notre projet au sens de Maven.

Dans le POM on retrouve un groupId/artifactId spécifique à notre exemple qui a pour parent le modèle de projet core-implementations de l’usine de développement un packaging de type war (Web Application Archive). On retrouve aussi la dépendance vers le projet core-interfaces associé à notre exemple.

Le répertoire src/main/java

Le répertoire src/main/java est structuré par des packages. Le nommage des packages suit les conventions des standards java. Si le nombre de classes dans un package devient trop important, alors on définit des sous packages pour les organiser de manière logique. On retrouve le contenu suivant :

    • les classes de la couche modèle (dans notre exemple org.scub.foundation.contact.manager.core.model)
    • les interfaces de la couche DAO (dans notre exemple org.scub.foundation.contact.manager.core.dao.interfaces avec les interfaces CiviliteDao.java, ContactDao.java, EmailDao.java, TelephoneDao.java, TypeTelephoneDao.java)
    • les implémentations des interfaces de la couche service (dans notre exemple org.scub.foundation.contact.manager.core.service.implementations) situées dans le projet core-interfaces dont dépend notre projet core-implementations
    • les classes utilitaires si besoin (dans notre exemple org.scub.foundation.contact.manager.core.utill)

Le répertoire src/main/resources

Ce répertoire est destiné à accueillir les ressources nécessaires à notre application. Cela permet par exemple de déclarer tous les messages localisés (FR, EN, etc…) utilisés par l’application. Les messages sont stockés dans des fichiers texte localisés par des suffixes. On dissocie les messages d’information qu’on déclare dans le fichier message_fr_FR.properties des messages d’erreurs qu’on déclare dans messageError_fr_FR.properties.

On déclare un message au format clé/message. La clé est unique peut importe la localisation, mais le message lui est spécifique à la langue. Si on veut qu’une application qui soit disponible dans plusieurs langues, on déclare autant de fichiers localisés, par exemple message_fr_FR.properties et message_en_EN.properties si on veut une application disponible en Français et en Anglais ( messageError_fr_FR.properties et messageError_en_EN.properties pour les erreurs).

On aura alors les messages d’informations dans le fichier message_fr_FR.properties :

#MessageServiceImplementation
message.info.delete.contact.succeeded=Suppression réussie
message.info.save.contact.created=Contact crée
message.info.save.contact.updated=Contact mis à jour

Et les message d’erreurs dans le fichier messageError_fr_FR.properties :

civilite.not.found=La civilité n'a pas été trouvée.
type.telephone.not.found=Le type de téléphone n'a pas été trouvé.
 
message.error.save.contact.no.contact.found=Pas de contact correspondant
message.error.get.contacts.by.criteria.search.parmeters.required=Pas de critère de recherche fournit
message.error.get.contact.id.required=Pas de id fournit
message.error.save.contact.contact.to.update.not.found=Pas de contact à  mettre à  jour
message.error.delete.contact.id.required=id du contact à  supprimer est null
message_error_delete_contact_no_contact_to_delete_found=Aucun contact à supprimer
 
 
ContactDto.prenom.NotNull=Le prenom du contact est obligatoire
ContactDto.prenom.NotBlank=Le prenom du contact est obligatoire
 
ContactDto.nom.NotNull=Le nom du contact est obligatoire
ContactDto.nom.NotBlank=Le nom du contact est obligatoire
 
ContactDto.civilite.NotNull=La civilité est requise.
ContactDto.civilite.id.NotNull=L'identifiant de la civilité est requis.
 
ContactDto.telephones.NotNull=Les téléphones son requis.
ContactDto.telephones.MinSize=Au moins un téléphone est requis.
 
ContactDto.telephones.*.type.NotNull=Le type du téléphone est requis.
ContactDto.telephones.*.value.NotNull=Le numéro du téléphone est requis.
ContactDto.telephones.*.value.NotBlank=Le numéro du téléphone est requis.
ContactDto.telephones.*.value.numero.NotNull=Le numéro du téléphone est requis.
ContactDto.telephones.*.value.numero.NotBlank=Le numéro du téléphone est requis.
ContactDto.telephones.*.value.numero.Format=Le format du téléphone est incorect
ContactDto.telephones.*.value.tag.NotNull=Le tag du téléphone est requis.
ContactDto.telephones.*.value.tag.NotBlank=Le tag du téléphone est requis.
ContactDto.telephones.*.value.format.NotNull=Le format de validation du téléphone est requis.
ContactDto.telephones.*.value.format.NotBlank=Le format de validation du téléphone est requis.
ContactDto.telephones.*.value.indicatif.NotNull=L'indicatif du téléphone est requis.
ContactDto.telephones.*.value.indiciatif.NotBlank=L'indicatif du téléphone est requis.
 
ContactDto.emails.NotNull=Les emails sont requis.
ContactDto.emails.MinSize=Au moins un email est requis.
 
ContactDto.emails.*.Format=Le format de l'adresse email est incorrect.
ContactDto.emails.*.NotNull=L'email ne doit pas être vide.

Le répertoire src/test/java

Pour chaque service à implémenter, on développe les tests unitaires associés auparavant dans ce répertoire. Dans notre exemple nous avons les classes ContactServiceTest et ListServiceTest qui ont pour but de tester les services implémentés dans les classes ContactServiceImpl et ListServiceImpl. En général, pour chaque classe de la couche service, il correspondra une classe de test qui permettra de tester les différents services qu’elle propose.

Le contenu du répertoire conf

Ce répertoire contient cinq sous répertoires contenant les fichiers de configuration propres à des environnements différents dans lesquels l’application peut être utilisée:

  • le répertoire common contient les fichiers de configuration communs à tous les environnements de projet.
  • le répertoire dev contient les fichiers de configuration propres à l’environnement de développement c’est à dire quand l’application est en cours de développement par les développeurs.
  • le répertoire prod contient les fichiers de configuration propres à l’environnement de production du projet c’est à dire quand l’application est en production chez le client.
  • le répertoire prod-test contenant les fichiers de configuration propres à l’environnement de test en mode production c’est à dire quand le projet est en cours de test chez le client.
  • le répertoire test contenant les fichiers de configuration propres à l’environnement de test, lorsqu’on lance les tests unitaires.

Grâce à ce système, on peut facilement utiliser l’application dans différents environnements. Par exemple les développeurs n’ont pas à mettre en place de base de données PostGreSQL lors de l’exécution des tests unitaires, mais ils utilisent un ou plusieurs jeux de données décrits dans des fichiers XML exploités par HSQLDB pour tester plus facilement les services. Chacun de ces sous répertoires possède un dossier filters contenant un fichier filters.properties et un dossier resources contenant les autres fichiers de configuration.
Après avoir décrit le contenu du répertoire common nous donnerons une description du contenu des répertoires propres à chaque environnement de projet.

Le répertoire common

Le fichier filters.properties du répertoire conf/common/filters/

Lorsqu’une authentification est nécessaire à l’exécution d’une application et qu’un serveur tente d’exécuter cette application l’authentification se fait en utilisant une clé d’authentification (runAsKey.local) et un nom de rôle (RUN_As_server). Nous précisons dans ce fichier la clé d’authentification ainsi que le nom de rôle à utiliser. Ces valeurs existent par défaut dès la création de notre projet.

Le fichier applicationContext.xml du répertoire conf/common/resources/

Ce fichier correspond au fichier de configuration spring dans lequel nous définissons la plupart de nos beans. Nous trouvons dans ce fichier la déclaration des beans de notre application à savoir :

  • les beans de la couche modèle :
    org.scub.foundation.contact.manager.core.model.Contact
    org.scub.foundation.contact.manager.core.model.Civilite
    org.scub.foundation.contact.manager.core.model.Email
    org.scub.foundation.contact.manager.core.model.TypeTelephone
    org.scub.foundation.contact.manager.core.model.Telephone
  • les beans de la couches dao :
            <bean id="contactDao" class="org.scub.foundation.contact.manager.core.dao.implementations.ContactDaoImpl" autowire="byName" />
    	<bean id="civiliteDao" class="org.scub.foundation.contact.manager.core.dao.implementations.CiviliteDaoImpl" autowire="byName" />
    	<bean id="telephoneDao" class="org.scub.foundation.contact.manager.core.dao.implementations.TelephoneDaoImpl" autowire="byName" />
    	<bean id="emailDao" class="org.scub.foundation.contact.manager.core.dao.implementations.EmailDaoImpl" autowire="byName" />
    	<bean id="typeTelephoneDao" class="org.scub.foundation.contact.manager.core.dao.implementations.TypeTelephoneDaoImpl" autowire="byName" />
  • les beans de la couche service :
            <bean id="contactService" class="org.scub.foundation.contact.manager.core.service.implementations.ContactServiceImpl" autowire="byName" />
    	<bean id="listService" class="org.scub.foundation.contact.manager.core.service.implementations.ListServiceImpl" autowire="byName" />
  • ainsi que d’autres beans concernant les log, les mapping d’objets (configuration de dozer) etc.

Afin de mieux comprendre les lignes de ce fichier référez vous aux différents tutoriels sur spring : spring ioc, spring aop, spring mvc.

Le fichier dozer.properties

Ce fichier contient toutes les propriétés configurables de dozer avec leurs valeurs par défaut.

Le fichier dozer-bean-mappings.xml

Ce fichier contient le code xml correspondant aux différentes classes que nous désirons mapper, référez vous au tutoriel sur dozer.

Le fichier rmiServiceExporterContext.xml

Ce fichier permet de spécifier tous les services que l’on veut exporter afin que d’autres modules puissent y accéder. Voici un exemple d’exportation de service :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
 
   <!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ACCES DISTANT AUX SERVICES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    *  
    *   - EXEMPLE : EXPORT VIA RMI -
    *
	*	<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
	*		<property name="serviceName" value="${pom.artifactId}/MessageService"/>
	*		<property name="service" ref="messageService"/>
	*		<property name="serviceInterface" value="org.scub.foundation.applicationblanche.noyau.service.interfaces.MessageService"/>
	*		<property name="registryPort" value="1099"/>
	*	</bean>
	*
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *   -->
 
	<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
		<property name="serviceName" value="${pom.artifactId}/contactService"/>
		<property name="service" ref="contactService"/>
		<property name="serviceInterface" value="org.scub.foundation.contact.manager.core.service.interfaces.ContactService"/>
		<property name="registryPort" value="1099"/>
	</bean>
 
	<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
		<property name="serviceName" value="${pom.artifactId}/listService"/>
		<property name="service" ref="listService"/>
		<property name="serviceInterface" value="org.scub.foundation.contact.manager.core.service.interfaces.ListService"/>
		<property name="registryPort" value="1099"/>
	</bean>
</beans>
Le fichier rmiServiceImporter.xml

Ce fichier spécifie les différents services à importer afin de pouvoir les utiliser, référez vous également au tutoriel de création d’un projet client Spring MVC.

Le fichier securiteServiceContext.xml

Dans ce fichier nous devons définir les droits des utilisateurs sur chacun des services dans le bean sercurityInterceptor ⇒ objetDefinitionSource il est également possible de modifier la configuration par défaut si nécessaire. (voir la partie sécurité plus bas)

Le fichier web.xml (dans WEB-INF)

Dans ce fichier nous spécifions les chemins des fichiers de configuration du context de l’application ainsi que la classe Spring permettant de charger le context de l’application.

Le répertoire dev

Le fichier filters.properties (dans filters)

Ce fichier contient la définition des variables nécessaires :

  • à la connexion à la base de données postgresql ;
  • à la configuration d’hibernate ;
  • aux logs ;
  • à la configuration scp pour le déploiement de l’application sur un serveur distant ;
  • à la configuration monitoring ;
Le fichier init-db.sql (dans resources)

Ce fichier contient le script d’initialisation de la base de données que vous avez créé dans postgresql.

Le fichier log4j.properties (dans resources)

Ce fichier contient les différentes variables nécessaires aux logs.

les fichiers rmiServiceImporterSpecContext.xml et securiteServiceSpecContext.xml (dans resources)

Ces fichiers jouent les mêmes rôles que les fichiers rmiServiceImporterContext.xml et securiteServiceContext.xml du répertoire common à la différence qu’ils permettent d’importer ou de sécuriser des services propres à un environnement particulier (ici l’environnement de développement).

Le répertoire prod

  • Le fichier filters.properties : Ce fichier contient les mêmes informations que le fichier filters de l’environnement dev à une différence près, dans le fichier filters.properties que nous avons ici il y a des propriétés de configuration de CAS (Central Authentification Service : système d’authentification unique) permettant de configurer le serveur CAS car nous sommes en mode production ce qui implique un renforcement du niveau de sécurité de l’application.
  • Les autres fichiers : Les autres fichiers de l’environnement prod sont les mêmes que ceux de l’environnement dev, la différence se trouvant au niveau de leur contenu, en effet dans chaque fichier nous avons des informations de configuration propres à l’environnement.

Le répertoire prod-test

Les fichiers de cet environnement ont exactement les mêmes informations que les fichiers correspondants dans l’environnement dev, à une petite différence près, dans l’environnement prod-test on tient compte de l’existence des différents utilisateurs et on leur associe des droits.

Le répertoire test

Le fichier filters.properties

Ce fichier contient les propriétés nécessaires à la configuration de hsqldb, d’hibernate, et du monitoring.

Le fichier rmiServiceImporterMockContext.xml

Ce fichier xml permet de définir les Mocks nécessaires pour simuler les services externes à l’application ainsi nous n’avons pas besoin de démarrer les services externes à notre application pour pouvoir lancer nos tests.

Le fichier dataset.xml

Dans notre exemple le fichier est contact.dataset.xml.
Dans ce fichier nous définissons en format xml les différents objets qui seront utilisés pendant l’exécution de nos tests.
Dans notre exemple nous avons une table avec pour nom CONTACT et ayant six champs (ID, NOM, PRENOM, version, ID_CIVILITE et DATE_NAISSANCE). Les objets de cette table peuvent être définis de la manière suivante :

<!--?xml version='1.0' encoding='UTF-8'?-->
        <CONTACT ID="1" NOM="KONKOLE" PRENOM="Evariste" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-01" />
	<CONTACT ID="2" NOM="SOUBEIGA" PRENOM="Rodrigue" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-02" />
	<CONTACT ID="3" NOM="TRAUMAT" PRENOM="Stéphanie" version="0" ID_CIVILITE="3" DATE_NAISSANCE="2012-01-03" />
	<CONTACT ID="4" NOM="GONCALVES" PRENOM="Juanito" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-04" />
	<CONTACT ID="5" NOM="GOUMARD" PRENOM="Stéphane" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-05" />
	<CONTACT ID="6" NOM="PELTIER" PRENOM="Nicolas" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-06" />
	<CONTACT ID="7" NOM="GUILLEMETTE" PRENOM="Anthony" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-07" />
	<CONTACT ID="8" NOM="NADEAU" PRENOM="Nicolas" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-08" />
	<CONTACT ID="9" NOM="PROUTEAU" PRENOM="Nicolas" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-09" />
	<CONTACT ID="10" NOM="BRUGIER" PRENOM="Raphael" version="0" ID_CIVILITE="1" DATE_NAISSANCE="2012-01-10" />

les autres fichiers de cet environnement de développement se retrouvent dans les autres environnements de développement.
Dans le dossier target nous retrouvons l’ensemble des fichiers et documents générés lors des phases de compilation nous avons par exemple les .class des codes java,les fichiers relatifs aux tests unitaires, le fichier jar du code source ainsi que le jar final de l’application.

Créer un projet

Les démarches de création d’un projet sont données à titre indicatif. La création de l’ensemble des classes, fichiers de configuration…n’est pas décrite ici. Les sources sont disponibles à la fin de cette page.

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 à l’usine de développement. Vous allez ici créer les projets du noyau.
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 (voir les articles sur les Modèles de projet), correspondant au projet que vous souhaitez créer. Commencez par créer le projet core-interfaces puis le projet core-implementations.

Creer un projet core-interfaces

Pour créer le projet core-interfaces, sélectionnez l’archetype scub-foundation-archetype-core-interfaces (ou scub-foundation-archetype-core-model) 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, ici notre gestionnaire de contact.
    • ArtifactId : contact-manager-core-interfaces
    • Version : 1.0
    • Package : org.scub.foundation.contact.manager

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

Le package org.scub.foundation.contact.manager.core.dto

Ce package permet de définir les dto. Dans notre cas ContactCriteresRechercheDto, ContactDto, IdLabelDto et TelephoneDto.

Le package org.scub.foundation.contact.manager.core.service.interfaces

Ce package permet de définir les interfaces de services qui seront utilisées par l’application cliente et implémenter par le projet core-implementations. Dans notre cas ContactService et ListService.

Creer un projet core-implementations

Pour créer le projet core, reprenez la même procédure décrite plus haut puis sélectionnez maintenant l’archetype scub-foundation-archetype-core-implementations (ou scub-foundation-archetype-core) 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
    • ArtifactId : contact-manager-core-implementations
    • Version : 1.0
    • Package : org.scub.foundation.contact.manager

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

Le package org.scub.foundation.contact.manager.core.dao.implementations

Ce package implémente les interfaces des dao. Dans notre cas CiviliteDaoImpl, ContactDaoImpl, EmailDaoImpl, TelephoneDaoImpl et TypeTelephoneDaoImpl.

Le package org.scub.foundation.contact.manager.core.dao.interfaces

Ce package contient l’ensemble des interfaces des DAO. Dans notre cas CiviliteDao, ContactDao, EmailDao, TelephoneDao et TypeTelephoneDao.

Le package org.scub.foundation.contact.manager.core.model

Ce package contient l’ensemble des modèles qui définissent la persistance avec la base de données. Dans notre cas Civilite, Contact, Email, Telephone, TypeTelephone.

Le package org.scub.foundation.contact.manager.core.service.implementations

Ce package contient les classes qui implémentent les services en appelant les dao. Dans notre cas ContactServiceImpl et ListServiceImpl.

Le package org.scub.foundation.contact.manager.core.util

Package qui permet la gestion des messages.

Tester l’application avec les tests unitaires

Pourquoi tester des services qui ne sont pas encore fonctionnels? Tout simplement parce que chaque test unitaire va permettre de tester les différentes erreurs possibles lors de l’appel à un service. Cela permet au développeur d’envisager les différentes vérifications qu’il devra implémenter dans son service.

Les tests unitaires sont dans le package org.scub.foundation.contact.manager.core.service.test dans notre application exemple. Nous avons deux classes de tests unitaires ContactServiceTest et ListServiceTest. Les tests unitaires se servent d’un jeu de données (static.dataset.xml). Il est possible de renforcer la validité du static.dataset.xml avec un dtd (dans notre cas database.dtd).

Le static.dataset.xml :

<!--?xml version='1.0' encoding='UTF-8'?-->
<dataset>
 
	<CIVILITE ID="1" LIBELLE="Monsieur" version="0" />
	<CIVILITE ID="2" LIBELLE="Madame" version="0" />
	<CIVILITE ID="3" LIBELLE="Mademoiselle" version="0" />
 
	<TYPE_TELEPHONE ID="1" LABEL="Mobile" version="0" />
	<TYPE_TELEPHONE ID="2" LABEL="Domicile" version="0" />
	<TYPE_TELEPHONE ID="3" LABEL="Profesionnel" version="0" />
	<TYPE_TELEPHONE ID="4" LABEL="Fax" version="0" />
	<TYPE_TELEPHONE ID="5" LABEL="Autre" version="0" />
</dataset>

Dans la classe de tests unitaires la méthode setUp() avec l’annotation @Before se lance avant chaque test unitaire (une méthode avec l’annotation @After fera l’inverse et pourra par exemple servir à fermer une connexion ou des fichiers). Il est intéressant de récupérer les beans dont on va se servir pour le test, exemple :

   /**
     * Méthode appellée avant chaque test unitaire.
     */
    @Before
    public void setUp() {
        contactService = (ContactService) getBeanSpring("contactService");
        messageSourceUtil = (MessageSourceUtil) getBeanSpring("messageSourceUtil");
    }

Exemple de test unitaire :

    /**
     * Test unitaire du service deleteContact().
     */
    @Test
    public void testDeleteContact() {
        // Initialisation de l'identifiant du contact à supprimer
        final Long idContactToDelete = 1L;
 
        try {
            // Appel du service à tester
            contactService.deleteContact(idContactToDelete);
 
            // On vérifie que le contact a bien été effacé
            contactService.getContactById(idContactToDelete);
        } catch (TechnicalException t) {
            assertEquals(ERROR_WRONG_ERROR,
                messageSourceUtil.get(ContactKeyUtil.MESSAGE_ERROR_GET_CONTACT_NO_CONTACT_FOUND, new String[] {idContactToDelete.toString()}), t.getMessage());
        }
 
    }

Nous testons ici le service deleteContact de la classe ContactService et nous nous servons de l’instance de MessageSourceUtil (messageSourceUtil) pour la gestion des messages d’erreurs.

Pour lancer les tests cliquer sur :

    • puis : Running the tests
    • ou avec JUnit : Run As -> JUnit Test

Résultat après le lancement de l’ensemble des tests unitaires :

Nous pouvons lancer les tests indépendamment les uns des autres via l’onglet JUnit.

Lancer l’application noyau

Lancez un terminal et jouez les commandes suivantes pour créer la base de données puis un rôle :

sudo -s -u postgres
[tapez votre mot de passe]
[entrez dans la console psql]
psql
CREATE DATABASE contact_manager;
[quittez la console psql : ctrl + z]
[entrez dans la console psql avec la base contact_manager]
psql contact_manager
CREATE USER contact_manager WITH PASSWORD 'contact_manager'

Placez-vous sur le projet contact-manager-core-implementations et déployez-le dans Jetty.

Cliquez sur :

Puis sur : Deploy to jetty

Jouez le script contenu dans le fichier suivant : /contact-manager-core-implementations/conf/dev/resources/init-db.sql

Pour ce faire utiliser la commande suivante (dans la console psql):
\i /[monChemin]/Scub-Foundation/scub-foundation/workdir/sample_workspace/contact-manager-core-implementations/conf/dev/resources/init-db.sql

Nous avons donc ici un noyau déployé et lié à la base de données.

Dans le fichier /contact-manager-core-implementations/conf/dev/filters/filters.properties vous avez accès aux informations pour se connecter à la base.

Sécuriser une application noyau

Cette partie a pour objectif de vous guider dans le processus visant à sécuriser une application noyau en utilisant Spring Security. Spring Security offre de nombreuses possibilités en matière de sécurité. Dans le cas d’une application noyau, le principe est simple, il suffit de restreindre l’accès aux services développés aux utilisateurs authentifiés ayant les rôles suffisants pour les exécuter.

Sécuriser les services

Spring Security permet de gérer l’accès aux ressources d’une application Java. Ces ressources peuvent être des pages web, mais aussi des objets de services métier. Toute ressource sollicitée par un appelant est rendue accessible si, d’une part, l’appelant s’est identifié, et si d’autre part, il possède les droits nécessaires (des rôles dans le vocabulaire Spring Security).
(voir : http://www.jtips.info/index.php?title=Spring_Security)

global-method-security est une méthode qui permet de sécuriser l’ensemble des beans enregistrés dans l’application contexte. Elle fait référence au manager de décisions (dans notre cas accessDecisionManager qui est de type org.springframework.security.access.vote.AffirmativeBased).

Il existe 3 types de managers de décision :

  • AffirmativeBased (c’est notre cas) : Le manager de décisions garantit l’accès à la condition que n’importe lequel des votants l’acceptent.
  • UnanimousBased: Le manager de décisions garantit l’accès à la condition que tous les votants l’acceptent.
  • ConsensusBased : Le manager de décisions garantit l’accès en fonction de la décision de la majorité.



Les votants :
Le manager de décisions fait appel aux différents votants. Les votants sont à mettre ici :

    <property name="decisionVoters">
        <list>
          <!-- Les votants -->
        </list>
    </property>

Dans notre cas nous avons un seul votant roleVoter de type org.springframework.security.access.vote.RoleVoter (il existe également des AuthenticatedVoter).

RoleVoter :
Il y a deux configurations possible pour le role du votant :

  • Le préfixe est présent : Le votant vote. Par défaut le préfixe est « ROLE_ ».
  • Le préfixe est absent : Le votant s’abstient de voter.



runAsAuthenticationProvider : Gère l’authentification.

Le fichier /conf/common/resources/securiteServiceContext.xml :

<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
 
	<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
		<property name="decisionVoters">
			<list>
				<ref bean="roleVoter" />	<!-- le vote se fait via le rôle de l'utilisateur -->
			</list>
		</property>
	</bean>
 
 
	<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter">
		<property name="rolePrefix" value="ROLE_" />
	</bean>
 
	<bean id="runAsAuthenticationProvider" class="org.springframework.security.access.intercept.RunAsImplAuthenticationProvider">
		<property name="key">
			<value>${runAsKey.local}</value>
		</property>
	</bean>
 
	<sec:global-method-security access-decision-manager-ref="accessDecisionManager">
 		<!-- Exemple de configuration -->
	</sec:global-method-security>
</beans>

Paramétrer la source d’authentification

Maintenant que les services sont sécurisés, il faut définir quelle source va fournir les utilisateurs et leur rôle à l’application. C’est le rôle du authentication-provider, (traduisible par fournisseurs d’authentification) de Spring Security qui est spécifique à l’environnement.
Un fichier securiteServiceSpecContext.xml se trouve dans chaque répertoire de configuration (conf/dev/resources/securiteServiceSpecContext.xml, conf/prod/resources/securiteServiceSpecContext.xml, conf/prod-test/resources/securiteServiceSpecContext.xml et conf/test/resources/securiteServiceSpecContext.xml).
L’élément ‹sec:user-service› permet de définir directement des utilisateurs dans le fichier de configuration, ce qui est pratique dans le cadre des tests unitaires. Mais on peut par exemple aussi spécifier qu’on souhaite récupérer les utilisateurs à partir d’une base de données ou d’un annuaire LDAP, qui est plus spécifique à un environnement de production. Vous retrouverez dans les différents environnements des exemples.

Exemple pour le fichier /conf/test/resources/securiteServiceSpecContext.xml. Vous retrouvez la configuration suivante :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
              http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
	<sec:authentication-manager>
		<sec:authentication-provider>
			<sec:user-service>
	    		<sec:user name="${pom.artifactId}_user" password="${pom.artifactId}_user" authorities="${pom.artifactId}_user" />
			</sec:user-service>
		</sec:authentication-provider>
	</sec:authentication-manager>
</beans>

Dans ce fichier nous précisons la base de données utilisateur avec leur identifiants, leur mot de passe et leur rôle.

Tester la sécurité de ses services

Pour permettre aux tests unitaires de s’exécuter, vous devez maintenant créer un contexte de sécurité  avant l’appel de chaque service. Ici, il suffira de créer ce contexte qui permettra d’être authentifié en tant que l’utilisateur contact-manager-core-implementations_user ayant le rôle portant le même nom dans la méthode setUp() annotée par @Before qui s’exécute avant chaque test unitaire.

    • Ouvrez la classe de test unitaire ContactServiceTest
    • Cherchez la méthode setUp()
    • Ajoutez la ligne de code suivante :
createSecureContext("contact-manager-core-implementations_user", "contact-manager-core-implementations_user");

Télécharger les sources

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