formationJ2EE by LauraLimem

VIEWS: 165 PAGES: 346

									                     Introduction J2EE - Applications d'entreprise



1.Introduction

1.1.Pourquoi utiliser une plateforme ?

Une plateforme est une base générique qui fournit un ensemble de fonctionnalités utiles pour
une majorité d’applications. Une plateforme se construit sur la base d’un ensemble de
besoins génériques partagés entre plusieurs applications.
Il peut exister plusieurs types de plateformes. De la plus générique à la plus spécifique
(optimisée pour un type de métier précis par exemple). Bon nombre de grandes entreprises
ont déjà développé des plateformes tels que : IBM (WebSphere), SAP …
L’avantage principal de partir d’une plateforme est que l’équipe de développement n’a pas à
s’acquitter de développer certaines tâches (connexion à la base de données par exemple,
gestion d’objets …). Ce sont des tâches que l’on retrouve très souvent dans un grand
nombre de projet et qui n’ont pas d’intérêt à être re-coder à chaque fois (perte de temps et
d’argent). De plus, mieux vaut travailler sur une plateforme qui présente une forte stabilité
(ça évite des débuggages inutiles !).
Un autre avantage est la facilité de prise en main des API de cette plateforme. En effet,
celle-ci cache très souvent la complexité d’accès à telle ou telle ressource et permet donc un
gain de temps énorme pour le développeur qui a donc plus de temps pour se préoccuper du
fonctionnement réel de son application (pas de tâche ardue ou générique à développer).


1.2.Qu’est ce que J2EE ?

2EE (Java 2 Enterprise Edition) est une norme proposée par la société Sun, portée par un
consortium de sociétés internationales, visant à définir un standard de développement
d'applications d'entreprises multi-niveaux, basées sur des composants.

On parle généralement de «plate-forme J2EE» pour désigner l'ensemble constitué des
services (API) offerts et de l'infrastructure d'exécution. J2EE comprend notamment :

   •   Les spécifications du serveur d'application, c'est-à-dire de l'environnement
       d'exécution : J2EE définit finement les rôles et les interfaces pour les applications
       ainsi que l'environnement dans lequel elles seront exécutées. Ces recommandations
       permettent ainsi à des entreprises tierces de développer des serveurs d'application
       conformes aux spécifications ainsi définies, sans avoir à redévelopper les principaux
       services.
   •   Des services, au travers d'API, c'est-à-dire des extensions Java indépendantes
       permettant d'offrir en standard un certain nombre de fonctionnalités. Sun fournit une
       implémentation minimale de ces API appelée J2EE SDK (J2EE Software
       Development Kit).

Dans la mesure où J2EE s'appuie entièrement sur le Java, il bénéficie des avantages et
inconvénients de ce langage, en particulier une bonne portabilité et une maintenabilité du
code.
De plus, l'architecture J2EE repose sur des composants distincts, interchangeables et
distribués, ce qui signifie notamment :

   •   Qu'il est simple d'étendre l'architecture.
   •   Qu'un système reposant sur J2EE peut posséder des mécanismes de haute
       disponibilité, afin de garantir une bonne qualité de service.
   •   Que la maintenabilité des applications est facilitée.
1.3.Les acteurs d’une application J2EE

La réalisation d’une application basée sur l’architecture J2EE fait appel à différents types de
compétences que l’on trouve rarement chez une même personne car cela va de la conception jusqu’à
la supervision de l’application en passant par le développement et le déploiement. Afin de pouvoir
maîtriser ce processus J2EE adopte l’approche des partages des responsabilités.
Plus spécifiquement pour les EJB, cette approche définit plusieurs niveaux de responsabilité :

    •   Le fournisseur des EJB : c’est l’acteur qui fournit des composants métiers réutilisables soit par
        l’achat à un fournisseur de composants, soit par développement interne ;
    •   L’assembleur d’applications : l’acteur qui est capable de construire une application à partir
        d’EJB existants ;
    •   Le déployeur : l’acteur qui récupère l’application et s’occupe de son déploiement dans un
        serveur d’applications ;
    •   L’administrateur : l’acteur qui contrôle le fonctionnement du serveur d’application et assure la
        supervision des applications ;
    •   Le fournisseur de conteneur : l’éditeur qui commercialise un conteneur web ou un conteneur
        EJB ; cet éditeur commercialise souvent un serveur d’application incluant ces conteneurs ;
    •   Le fournisseur de serveur : c’est l’éditeur qui commercialise un serveur d’application (BEA,
        IBM etc...)




2.J2EE en détail

2.1.Architecture du JDK J2EE

J2EE décrit l’architecture d’un standard pour les serveurs d’application. Nous allons étudier plus
précisément les composants, les dépendances et les protocoles utilisés.
Schéma des relations entre composants et « tiers » dans l’architecture J2EE :
Les différents rectangles définissent les conteneurs (de l’environnement d’exécution J2EE) qui
fournissent les services pour les différents composants (représenter par les rectangles dans les
rectangles). L’ensemble des composants sera expliqué dans le chapitre suivant.
Les flèches représentent les types d’accès que le type d’application peut avoir avec les autres
applications distantes. Par exemple, l’application client peut se connecter au « Web Container » par
l’intermédiaire des JSP / Servlet, elle peut également se connecter à « EJB Container » …

Voici à présent le schéma de l’interopérabilité entre la plateforme J2EE et les autres programmes (les
différents protocoles utilisés).




Ce schéma est très important dans le domaine de l’interopérabilité entre différentes applications. Il
fournit les informations concernant les protocoles utilisés pour chacune des connexions distantes
possibles. Par exemple, « Web Container » fournit des accès via HTTP / SSL ou SOAP ; « EJB
Container » fournit des accès HTTP / IIOP (RMI : Internet Inter-Orb Protocol) / SSL …
Nous pouvons alors penser qu’un client en C#, par exemple, peut se connecter sur un EJB en mode
HTTP (dans l’idéal) ou via le protocole IIOP (plus répandu).

2.2.Les différents outils de « bas niveau »

Nous venons de voir (très succinctement) l’architecture globale de la plateforme J2EE. Nous allons
maintenant présenter les différents outils.
Il existe 3 grands types d’outils :

    •   Composants
    •   Services d’infrastructures
    •   Services de communications

2.2.1.Composants
On distingue, en général, 2 catégories de composants :

    •   Web
    •   Métiers

2.2.1.1.Web
Il s’agit de la partie présentation (interface de l’utilisateur et les traitements). Le client reçoit seulement
du texte HTML, mais il s’agit seulement de la partie visible de l’application. Derrière la scène,
différentes technologies permettent à votre code d’être plus performant, plus robuste, et plus facile à
mettre à maintenir.
                                     1. JSP
Les JSP (Java Server Page) sont les pages servant à générer l’ensemble du code HTML de l’interface
utilisateur. On y intègre aussi bien du code HTML que des scriplet Java (code java) ou encore des
balises personnalisées (tag-lib).
Cette technologie est donc dédiée à la génération de HTML et non au traitement de la requête de
l’utilisateur. On l’appelle généralement : Vue.

                                     2. Servlet
Une Servlet est une classe Java qui permet de traiter une requête venant d’un client. Cette
technologie doit s’occuper de traiter les données envoyées par l’utilisateur et choisir la Vue à retourner
à celui-ci.
On appelle cette partie : Contrôleur. En général, la classe Java ne doit quasiment pas générer de
code HTML (sauf dans certains cas précis).

2.2.1.2.Métier - EJB (Entreprise JavaBean)
Il s'agit de composants spécifiques chargés des traitements des données propres à un secteur
d'activité (on parle de logique métier ou de logique applicative) et de l'interfaçage avec les bases de
données.
On parle de la partie : Modèle.

2.2.2.Services d’infrastructures

2.2.2.1.JDBC - Java Database Connectivity
C’est une API d’accès aux bases de données. Les serveurs d’application fournissent en plus des
pools de connexion avec les bases de données. Cela réduit le nombre de lignes à écrire et optimise
son utilisation.

2.2.2.2.JNDI
C’une API d'accès aux services de nommage et aux annuaires d'entreprises tels que DNS, NIS,
LDAP, etc.

2.2.2.3.JTA / JTS - Java Transaction Api / Java Transaction Services
C’est un API définissant des interfaces standard avec un gestionnaire de transactions.

2.2.2.4.JCA (J2EE Connector Architecture)
C’ est une API de connexion au système d'information de l'entreprise, notamment aux systèmes dits
«Legacy» tels que les ERP.

2.2.2.5.JMX (Java Management eXtension)
Cette API fournit des extensions permettant de développer des applications web de supervision
d'applications.

2.2.3.Services de communications

2.2.3.1.JAAS (Java Authentification and Authorization Service)
C’est une API de gestion de l'authentification et des droits d'accès.

2.2.3.2.RMI (Remote Method Invocation)
C’est une API permettant la communication synchrone entre objets.

2.2.3.3.Web services
Les Web services permettent de « partager » un ensemble de méthodes qui pourront être appelées à
distance. Cette technologie utilise XML, ce qui permet d’être utilisée par n’importe quel langage et
n’importe quelle plateforme.

2.2.3.4.JMS (Java Message Service)
Cette API fournit des fonctionnalités de communication asynchrone (appelées MOM pour Middleware
Object Message) entre applications.
2.2.3.5.JavaMail
C’est une API permettant l'envoi de courrier électronique.


2.3.Implémentation de J2EE : les serveurs d’application

Il est avant tout indispensable de définir clairement ce qu'est un serveur d'application. En effet, une
confusion règne dans les esprits quant à la notion de serveur d'application. Cette confusion a été
introduite en grande partie par les éditeurs de serveurs d'application J2EE (Java2 Entreprise Edition)
afin de s'approprier ce marché. La notion de serveur d'application a en effet été mélangée avec celle
de serveur d'objet qui n'a absolument rien à voir.

2.3.1.Qu’est ce qu’un serveur d’application ?
Le serveur d'application est l'environnement d'exécution des applications côté serveur. Il prend en
charge l'ensemble des fonctionnalités qui permettent à N clients d'utiliser une même application :

    •   Gestion de la session utilisateur : N clients utilisant une même instance d'application sur le
        serveur, il est nécessaire que le serveur d'application puisse conserver des contextes propres
        à chaque utilisateur (par exemple, un panier de commandes). La plupart des serveurs
        d'application génèrent un identifiant unique pour chaque nouveau client et transmettent cet
        identifiant lors de chaque échange HTTP par URL longs, variables cachées ou cookies.
    •   Gestion des montées en charge et reprise sur incident : Afin de gérer toujours plus
        d'utilisateurs, le serveur d'application doit pouvoir se déployer sur plusieurs machines et
        éventuellement offrir des possibilités de reprise sur incident (même si dans la grande majorité
        des cas, on se contente d'une gestion des montées en charge au niveau réseau - boîtier de
        répartition, DNS round-robin, reverse proxy ...).
    •   Ouverture sur de multiples sources de données : C'est le serveur d'application qui rend
        accessible les données des applications du système d'information. Il doit donc pouvoir
        accéder à de nombreuses sources de données. On s'attend également à ce qu'il fournisse
        des mécanismes performants comme le pooling de connexion base de données.
    •   ...


Le serveur d'application est donc indispensable si l'on souhaite éviter de re-développer l'ensemble de
ces fonctionnalités (cas des GGI). Les moteurs JSP/Servlets, Microsoft ASP, Cold Fusion, PHP ...
sont à ce titre des serveurs d'application (même si il sont intégrés au ServeurWeb PHP/ASP).

2.3.2.Qu’est ce qu’un serveur d’objet ?
Pour aborder la notion de serveur d'objets, il faut comprendre qu'il existe deux méthodes pour accéder
aux données et aux traitements.

    •   La première consiste à accéder directement aux sources de données. Cette méthode de
        programmation n'empêche en aucun cas de structurer ses développements.
    •   La deuxième méthode consiste à s'appuyer sur des objets métier (client, fournisseur ...) afin
        de masquer la complexité d'accès aux données. Un objet AssuréSocial possédera par
        exemple une méthode débit() et une méthode crédit () qui à chaque appel iront modifier les
        données dans une ERP (Entreprise Resource Planning), un système de CRM (Customer
        Relation Ship Managment) ou une base de données.
    1.   Requête du client
    2.   Le serveur web passe la requête au serveur d’application
    3.   Le serveur d’application traite la requête par des appels au serveur d’objets
    4.   Le serveur d’objet traite les données avec les bases de données (en tout genre)
    5.   Le serveur d’objet retourne les objets au serveur d’application
    6.   Le serveur d’application renvoie le résultat au serveur web
    7.   Le serveur web fait suivre le résultat au client


Pour gérer ces objets, un environnement d'exploitation est nécessaire : le serveur d'objets. Ce serveur
d'objets va devoir fournir des services tout à fait différents de ceux des serveurs d'application :

    •    Gestion de la persistance des objets,
    •    Gestion des transactions objets métier
    •    Gestion des montées en charge : ici les mécanismes n'ont rien à voir avec ceux mis en
         oeuvre pour un serveur d'application. Il faut pouvoir assurer la transparence de localisation,
         l'instanciation, ... des objets métier ...


Bref, on le voit, on a à faire à des techniques très différentes. Les principaux serveurs d'objets à ce
jour sont les serveurs EJB (Enterprise Java Beans), Corba. Ils ne sont nécessaires à ces
développements que si l'on souhaite utiliser pleinement la logique d'objets métier.
Il est donc important de ne pas mélanger ces notions afin d'éviter de se faire « prendre » comme 80%
des acheteurs de serveurs J2EE (incluant serveur d'application et serveur d'objets) qui n'utilisent que
le moteur de JSP/Servlets dont les coûts sont beaucoup plus limités que l'ensemble J2EE (incluant le
serveur d'objets EJB).
Sur le terrain, on rencontre beaucoup plus de développements sur des serveurs d'application seuls
que d'applications utilisant des serveurs d'objets. En fait, le marché des serveurs d'application s'est
fortement structuré depuis une ou deux années. De plusieurs dizaines de technologies il y a peu,
seules trois technologies émergent aujourd'hui : l'offre Java, l'offre Microsoft et l'offre PHP. Hormis cas
particulier, nous recommandons de ne pas sortir de ces trois choix.
Les points clés d'une architecture sont les capacités transactionnelles du serveur d'application à
délivrer des pages et à intégrer une montée en charge… L'ergonomie au sens large est un autre point
clé. Les choix de design doivent prendre en compte les contraintes du Web (taille des images, ...).
Le marché offre trois familles de solutions de développement pour les serveurs d'application.

    •    Les solutions de scripting peuvent être simples et productives mais plutôt orientées vers les
         sites jetables, de type évènementiel. Un site en ASP, PHP 3 ou ColdFusion peut être
         développé très rapidement ; par contre, sa maintenance est compliquée voire quasi-
         impossible.
    •    Les solutions orientées objets techniques permettent de factoriser le code sans rentrer dans la
         complexité des objets métier. Il est important d'imposer des règles de développement précises
         à ses équipes et prestataires. Les développements JSP/Servlets/JavaBeans, PHP4/5,
         ASP/DCOM (et ASP.Net/DCOM) permettent de tels développements.
    •    Les solutions orientées métier sont plus complexes et plus coûteuses à mettre en oeuvre.
         Elles nécessitent la mise en place de serveur d'objets. On retrouve principalement sur ce
         marché les serveurs d'EJB libres et propriétaires.

Pour ces trois familles de solutions, des produits Open Source existent et sont de plus en plus
adoptés dans les administrations et entreprises (TomCat, JBoss, JonAS…).


3.J2EE en pratique

3.1.J2EE contre ses concurrents

Chaque plateforme de développement a ses avantages et ses inconvénients. Le premier avantage de
cette plateforme est qu’elle a été adoptée par les plus grands groupes dans le monde entier. Nous
parlons, bien entendu d’IBM, Oracle, BEA …
Celle-ci est également stable et fiable, en effet, elle existe depuis 1998 et évolue constamment. Même
si c’est Sun Microsystems qui organise les spécifications de cette plateforme, elle a depuis le début su
écouter les retours de développeurs. Sun a mis en place un système de spécifications pour lesquelles
les développeurs peuvent indiquer leurs besoins, solutions ou mécontentement.
De plus, la plateforme s’appuyant sur Java, permet d’avoir des applications totalement indépendante
de la plateforme système utilisée (aussi bien Windows que Linux ou Mac OS).
Son « concurrent » principal est la plateforme .Net (développé par Microsoft). Cette plateforme, bien
que plus facile à prendre en main, manque de maturité et même si elle séduit certaines entreprises,
elle est plutôt utilisée pour les projets beaucoup moins importants, complexes que ceux utilisant J2EE.
.Net n’a bien sûr rien à envier à J2EE et réciproquement. De plus, l’interopérabilité étant de plus en
plus exploitée, J2EE et .Net peuvent communiquer ensemble de façon transparente.


3.2.Composants standards contre framework

Nous vous avons présenté le standard J2EE avec l’ensemble de ses composants. Cependant il est
parfois lourd d’utiliser un composant conçu pour de grande architecture alors que notre application est
restreinte.
L’ensemble de la communauté OpenSource (principalement) s’est occupé (et s’occupe) de lancer sur
le marché des frameworks servant à simplifier l’utilisation de telle ou telle technologie.
Souvent plus limité que le standard, les framework sont plus simple d’utilisation et plus performant
dans certains cas.

3.2.1.EJB vs Hibernate & Spring
Les EJB peuvent parfois être la bête noire des développeurs J2EE. En effet, ils sont pas évident à
mettre en place et sont souvent lourds à l’utilisation. Ils s’intègrent le plus souvent dans les projets de
grandes envergures.
Pour les projes plus courants et plus petits, les framework Hibernate et Spring peuvent très largement
remplacer ces EJB.

3.2.1.1.Présentation
Les EJB gèrent l’accès et le traitement des données (persistantes ou non). Pour faire de même avec
l’utilisation de framework externe, il faut en utiliser deux en général.

    •   Hibernate : c’est un framework qui permet de « mapper » une base de données relationnelle
        en objets (POJO : Plain Old Java Object). Il permet donc d’abstraire totalement l’accès à la
        base de données et propose donc un accès totalement orienté objet aux données.

    •   Spring : c’est un framework qui permet de « remplacer » la lourdeur des serveurs d’application
        lourds. En effet, on parle de « conteneur léger ». Il prend en charge la création et la mise en
        relation d’objets par l’intermédiaire d’un fichier de configuration décrivant l’ensemble de ces
        relations. L’ un des avantages principal est qu’il n’impose pas d’hériter ou d’implémenter une
        quelconque interface ou classe contrairement aux EJB.

3.2.1.2.Utilisation
L’utilisation de ces deux frameworks dans une même application est recommandée, en effet Hibernate
vous permet d’accéder aux données alors que Spring vous servira de « fabrique automatisée » !
De plus ces deux framework s’intègrent très facilement et ne nécessite qu’un moteur de servlet.
Contrairement aux EJB qui nécessitent un serveur d’application les gérants (beaucoup plus lourd !).

3.2.2.Servlet & JSP vs Struts
Avec l’arrivée des JSP après celle des Servlets, les développeurs ont pu commencer à séparer de
façon remarquable la couche présentation de la couche application / traitement. Cependant la
maintenance du code et la lourdeur d’utilisation des servlets ont montré leurs limites…
Le modèle MVC que chaque développeur pensait de son côté était à chaque fois trop limité et peu
évolutif.
L’arrivée de Struts à permis d’avoir une base solide répondant à un modèle MVC bien cadré.

3.2.2.1.Présentation
Struts est un framework qui permet de construire des applications web. Il se base sur la technologie
Servlet / JSP en ajoutant la prise en charge du Modèle MVC2 (Modèle Vue Contrôleur). Il fournit la
charpente d’une application web et évite aux développeurs d’avoir à gérer de façon fastidieuse la
séparation Modèle Vue Contrôleur.

3.2.2.2.Utilisation
L’utilisation de ce framework est quasiment omniprésente dans le développement d’application web.
Cependant Struts n’est pas la seule technologie aidant au développement de la couche application /
présentation web. En effet, on peut également retrouver JSF (Java Server Faces) ou Cocoon …
3.3.Serveurs d’application : utilisations et limites

Les serveurs d’application se sont développés depuis la création de J2EE. On peut distinguer
principalement 2 grandes catégories de serveurs :

    •   Open Source : évolue grâce à la communauté
    •   Propriétaire : évolue selon l’éditeur

Chaque catégorie a ses avantages et ses inconvénients. Nous allons décrire les serveurs les plus
connus afin d’avoir une vision globale des solutions disponibles.

3.3.1.Open Source

3.3.2.Tomcat : Apache
Tomcat est un conteneur de servlet qui implémente la référence officielle pour les Servlet Java et les
JSP. Ce serveur est très répondu pour les applications web.
Source : http://jakarta.apache.org/tomcat/index.html
Technologies implémentées :

    •   JSP
    •   Servlet
    •   JDBC
    •   JNDI

3.3.3.Jonas : ObjectWeb
Jonas est un serveur d’application implémentant la référence officielle pour les EJB. Il intègre un lien
avec Tomcat afin d’intégrer les fonctionnalités pour les applications web.
Source : http://jonas.objectweb.org/
Technologies implémentées :

    •   JSP
    •   Servlet
    •   EJB
    •   JCA
    •   JDBC
    •   JTA
    •   JMS
    •   JMX
    •   JNDI
    •   JAAS
    •   JavaMail
Architecture :




3.3.4.JBoss : JBoss
JBoss accumule les mêmes fonctionnalités que Jonas. Cependant son architecture est assez
différente et repose principalement sur un « BUS ».
Des projets gravitent autour de ce serveur tels que des plug-in pour Eclipse, des modules pour l’AOP
(Aspect Oriented Programming), Hibernate …
JBoss est l’un des serveurs d’application les plus populaires dans l’Open Source (avec Jonas). Il est
également de plus en plus utilisé en milieu professionnel.

Source : http://www.jboss.org/products/jbossas

Architecture :
3.3.5.Propriétaire
Les serveurs d’application propriétaires se démarquent grâce à des outils facilitant le développement
et/ou la configuration des applications au sein du serveur d’application.
En contrepartie, ils font payer les licences d’utilisation de leur serveur.
Voici un ensemble de serveurs les plus utilisés :

    •   WebSphere : IBM
    •   WebLogic : BEA
    •   WebObject : Apple
    •   Oracle Application Server : Oracle




3.4.IDE (Integrated Development Environment)

Pour développer des applications complexes, il faut impérativement un IDE (Integrated Environnement
Development).
De même qu’avec les serveurs d’application, il existe les IDE Open Source et ceux qui sont
propriétaires.
Voici une présentation de quelques IDE les plus connus.
3.4.1.Open Source

3.4.2.Eclipse : IBM
Screenshot :




Un outil très bien créé car il est simple à utilisé, et il permet d’apprendre java. Cependant, beaucoup
de plugins sont présents, et ils vous aideront à aller de plus en plus loin au fur et à mesure de votre
apprentissage.

Lien : http://www.eclipse.org

3.4.3.Sun : NetBeans




Un autre outil très bien réalisé pour apprendre. Comme c’est un outil réalisé par Sun, il intègre à
merveille des outils comme Sun Application Server, de plus, il intègre des plugins de très bonne
qualité, comme le profiler, qui permet de monitorer vos applications. On regrète qu’il n’est pas autant
de plugins que eclipse même si il est globalement d’aussi bonne voir de meilleur qualité.

Lien : http://www.netbeans.org
3.4.4.Propriétaire

3.4.4.1.Rational Architect (avec WebSphere) : IBM




Un outil taillé pour les professionnels car il permet des fonctionnalités très puissantes. La génération
de code et l’utilisation d’outil de modélisation sont très poussées. Cependant il est perméable aux
débutants, pour qui Eclipse est mieux adapté, et il reste très cher. Il est d’ailleur basé sur Eclipse.
Lien : http://www-306.ibm.com/software/fr/awdtools/

3.4.4.2.XCode & WebObject : Apple
Apple propose une solution très puissante, très basée sur l’aspect WYSIWYG, tout en ayant une
programmation très propre et efficace. Cependant, cette solution ne fonctionne que sous Mac.

3.4.4.3.JDev: Oracle




Oracle JDeveloper est un EDI complet et gratuit permettant de modéliser et développer des
applications Java pour les plateformes J2SE, J2EE ou J2ME. Il est très bien intégré aux base de
données Oracle, avec notamment un débuggueur PL/SQL.


4.Le future de J2EE

J2EE est une plateforme en constante évolution. Nous sommes actuellement à la version 1.4 mais la
version 1.5 est en cours de développement.
Cette version corrigera des spécifications parfois trop complexes. Elle ajoutera également de
nouveaux composants à la plateforme afin de faciliter toujours plus le développement d’application
d’entreprise.
Voici la nouvelle architecture prévue pour cette future version :




Le JDK 1.5 intègre de nouvelles fonctionnalités, c’est donc pour cela que la version J2EE 5 les
utilisera afin d’avoir une plateforme bien plus abordable que la précédente et beaucoup plus optimisée
Architecture J2EE - Comment organiser son application J2EE




1.Introduction

L’élaboration d’une application d’entreprise n’est généralement pas une mince affaire. En effet, de
nombreux composants doivent êtres utilisés et l’interconnexion entre ces composants est très souvent
difficile à mettre en place.
Une application d’entreprise travaille également sur des données qui peuvent être de différents
formats et provenir de différentes applications extérieures.
Le développement de nouvelle application doit prendre en compte les anciennes applications mais
également les futures évolutions probables (changement de base de données, autres types de clients,
changement de la logique métier …)

1.1.Objectifs

L’objectif de ce cours est de présenter les concepts à utiliser lors de la création d’une nouvelle
application de type « entreprise ». C’est-à-dire le travail préalable et surtout la mise en place d’une
architecture, en couches, basée sur J2EE.
La diversité des composants et l’accroissement du nombre de ces composants posent souvent un
problème aux développeurs ou architectes :

    •   Savoir que tel ou tel composant / framework existe
    •   Savoir utiliser le composant efficacement

Cet essentiel vous permettra d’avoir une vision assez générale des composants de base, et vous
donnera d’autres pistes sur lesquelles vous pourrez étudier d’autres nouveaux composants.


1.2.Type d’application

Lorsque nous parlons d’application d’entreprise et plus particulièrement de J2EE, nous parlons des
applications distantes (type client/serveur) à contrario des applications basées sur J2SE qui sont
orientées clients « lourds ».
Nous étudierons donc l’architecture des applications au sein des serveurs d’application qui sont
orientés clients « légers ».


2.Généralités – Architecture complexe

2.1.Qu’est ce qu’une architecture complexe ?

Nous parlons d’architecture complexe lorsque les applications qui doivent être développées sont de
type client/serveur.
Cela signifie qu’il existe des « tiers », contrairement aux architectures « simples » qui sont d’une seule
pièce (comme celles utilisées pour les programmes lourds : logiciel de conception 3D …).
Bien entendu certaine application utilise les deux types d’architectures (simple pour la partie client),
complexe au niveau du serveur.
Finalement, ces deux types d’architectures se rejoignent en ce qui concerne l’organisation (en
couches) mais diffèrent aux niveaux des composants utilisées.


2.2.Organisation

L’organisation des couches ne diffère pas de l’architecture simple (cf. Cours Architecture J2SE).
Cependant la différence est visible au niveau des tiers (qui seront bien plus nombreux), c’est donc
pour cela qu’on appelle également cela des architectures « n-tiers ».
Du fait que ce type d’architecture met en place des connexions client/serveur, les différents serveurs
vont devoir communiquer entre eux.
Voici un diagramme présentant l’architecture J2EE avec la vue « tiers » :
L’architecture, ici, est de type 3-tiers. Celle-ci est la plus utilisée dans les petits et moyens projets. Elle
permet de séparer le client, le serveur d’application et le(s) serveur(s) de données.
Dans le cas d’application beaucoup plus importante (applications d’intégration, gestion métier
poussée, communication entre différentes applications existantes …), l’architecture précédente
poserait certaines limites :

    •   Évolution difficile
    •   Composants non adaptés d’où une perte de temps lors du développement

Voici un autre exemple d’architecture de type « n-tiers » :




De nombreux serveurs peuvent intervenir dans un processus (logique métier, serveur de messagerie
inter-application …).
De même, les types de clients peuvent êtres très variés, voici une représentation physique de
l’architecture :
Nous retrouvons, dans l’API J2EE, un ensemble de composant permettant d’interconnecter ces
différents types de technologie. Nous verrons en détail les différents composants et leur utilisation.



3.Implémentation en J2EE

L’implémentation d’une architecture complexe via J2EE est très modulable et très spécifique. D’une
application à une autre, les outils et les technologies utilisés varient.
Nous allons étudier, ici, les différents outils et technologies que l’on peut utiliser pour chacune des
couches d’une application J2EE.

3.1.Conception architecturale

La conception architecturale n’est pas une mince affaire. Elle fait appel à de nombreux concepts,
souvent méconnus des débutants ou novices. Cette conception se base sur les principes
essentiels suivants (d’autres existent également mais ne sont pas vus ici) :

    •   Faible Couplage
    •   Forte Cohésion
    •   Protection des variations (interfaces, indirection, consultation de services …)

La granularité des composants est très importante, en effet il s’agit de faible couplage entre
applications, sous-systèmes ou processus et non entre petits objets. La granularité est définie par le
niveau de détails contenus dans une unité d’information. Plus il y a de détails, plus bas est le niveau
de granularité ; à contrario, une forte granularité est liée à un niveau faible de détails.
Voici un exemple d’utilisation de la granularité au sein d’un programme :

// forte granularité
public void methodForteGranularite(int id, String name, String email …);

// faible granularité
Public void methodFaibleGranularite(PeopleVo people);


Ceci est un exemple au niveau des paramètres de méthodes, cependant la granularité peut s’évaluer
sur des parties de programmes (combien d’entités avez-vous ?) ou au niveau global du programme
(combien de modules avez-vous ?)…
3.1.1.Faible Couplage
Le couplage est une mesure de degré auquel un élément est lié à un autre, en a connaissance ou en
dépend. S’il y a couplage ou dépendance, l’objet dépendant peut être affecté par les modifications de
celui dont il dépend. Evidemment une sous-classe est fortement dépendante à sa super-classe. Un
objet A faisant appel aux méthodes d’un objet B a un couplage aux services de B.
Le problème est : Comment réduire l’impact des modifications ?
La solution est : Affecter les responsabilités de sorte à éviter tout couplage inutile.
En résumé, le faible couplage permet de séparer au maximum l’implémentation de l’aspect fonctionnel
afin d’avoir à effectuer que très peu de modifications (voir aucune) lors d’un changement de
l’implémentation.
Un exemple très courant est celui des « drivers » ou pilotes. Le système principale définit une
structure commune à tous les pilotes (ou à un type de pilote) ; chaque pilote se doit d’implémenter
l’ensemble de la structure afin d’être utilisé par le système. Dans ce cas là, le système n’a pas
connaissance de l’implémentation (cas également des API …)
En image : (ce qu’il ne faut pas faire !!!)




3.1.2.Forte Cohésion
L’un des problèmes du « débutant » est qu’il ne sait pas exactement où effectuer les traitements et
finit très souvent par aboutir à des classes comportant des milliers de lignes (ingérables et pu
évolutives). Tout comme un bon chef de projet, votre application se doit se bien savoir déléguer les
tâches à effectuer.
Cependant, une mauvaise cohésion (faible cohésion) n’implique pas qu’un objet exécute tout le travail
seul : un objet faiblement cohésif de deux mille lignes de code collabore certainement avec beaucoup
d’autres objets ! Le point essentiel est que toutes ses interactions tendent également à créer un
mauvais couplage (fort). Il faut donc bien penser la liaison entre vos objets (ce qui n’est pas toujours
évident).
Préférez toujours une solution qui présente le plus de cohésion.
Le problème est : Comment s’assurer que les objets restent compréhensibles et faciles à gérer, et,
contribuent au Faible Couplage ?
La solution est : Affecter les responsabilités pour que la cohésion demeure élevée.

3.1.3.Protection des variations
La protection des variations est le concept générique. Il permet d’identifier les points de variations ou
d’instabilité prévisibles. Mais également d’affecter les responsabilités pour créer une interface stable
autour d’eux.
Certaines modifications du système sont prévisibles. Le pattern protection des variations permet de
protéger la structure et les classes contre une variation entraînant un effort très important. Le principe
le plus connu est le mécanisme d’abstraction qui permet de créer une interface stable autour des
objets du système.
Si vous interdisez l’accès direct des clients aux attributs alors le système est protégé contre des
changements de traitement à l’intérieur des classes (exemple d’utilisation des getters / setters). Un
très bon exemple d’utilisation de ce pattern est l’utilisation de machine virtuelle qui permet de protéger
des variations de la plateforme.
La protection des variations est donc à mettre au dessus des autres patterns, car c’est un concept
(pouvant être mis en place avec plusieurs solutions).


3.2.Exemple d’architecture




Nous allons détailler ces différentes couches dans les sections suivantes. Le schéma ci-dessus n’est
qu’un exemple d’architecture basique, une architecture n’est pas générique, il vous faut la
personnaliser en fonction du projet que vous souhaitez développer.
Cependant on retrouve des ensembles génériques dans un grand nombre d’application, c’est ces
ensembles que nous allons présenter ici.
Le nom donné au « couches » est très subjectif et peut varier suivant les personnes, on peut
également trouver :

    •   Présentation (couche gérant le visuel)
    •   Domaine (couche métier)
    •   Services techniques (accès physiques, sous-système générique …)

3.3.Couches : Avantages / Inconvénients

Il est intéressant d’étudier une méthode de développement d’application, cependant il est d’autant plus
difficile de l’appréhender lorsque l’on ne connaît pas les avantages de cette méthode et lorsque l’on
n’a jamais été confronté directement aux problèmes soulevés par une application sans réelle
architecture.
Voici un ensemble d’avantages (non exhaustif) que procure l’élaboration d’une architecture
« complexe » et pensée :

    •   Structure de l’application propre
    •   Modularité de l’application
    •   Evolution de l’application facilitée
    •   Factorisation de code et utilisation de framework ou composants génériques (gain de temps et
        performances)

L’inconvénient principal est la difficulté à mettre en place une architecture correcte et stable. Cette
phase demande une très bonne connaissance des outils disponibles sur le marché mais également
une très bonne aptitude à utiliser l’abstraction et les concepts objets pour le développeur.

3.3.1.Pattern Couches
L’utilisation de couches est expliquée par le pattern « Couches ». Deux questions de conception sont
à se poser :

    •   Quelles sont les grandes parties ?
    •   Comment sont-elles connectées ?
Même si ce pattern guide la définition des grandes parties, ce sont des patterns de conception moins
importants (on parle de conception microarchitecturaux cf. Rubrique Patterns Principaux) qui sont
employés couramment pour la connexions entre les couches.
Une couche est un ensemble pouvant être comparé à un sous-système (possédant un comportement
et des interfaces) et non un groupement d’entités.
Voici quelques exemples de couches :

    •   Persistance
    •   Moteur de règles
    •   Services
    •   …

Cependant vous pouvez avoir un service de « Gestion des articles» ou un groupe « Fondation »
(contenant un ensemble d’outils de gestion des dates, des chaînes de caractères … comme java.util),
sans pour autant les représenter en tant que couche.

3.3.2.Règles
Nous retrouvons bien entendu les principes essentiels au niveau des couches (avec principalement le
couplage entre celles-ci).
Dans la plupart des architectures en couches, celles-ci ne sont pas couplées dans le même sens
limité que dans un protocole réseau fondé sur le modèle OSI. Ce dernier comprend une restriction
stricte : les éléments de la couche N n’ont accès qu’aux éléments de la couche immédiatement
inférieure, N-1.
Cette règle est rarement appliquée dans les architectures de systèmes d’information. La norme est
plutôt une architecture lâche ou transparente dans laquelle les éléments d’une collaborent avec
plusieurs autres couches ou sont couplés à plusieurs autres couches.
Attention : cela est bien entendu possible dans les cas où les principes essentiels sont toujours
respectés !
Voici donc quelques règles génériques sur les couplages entre couches :

    •   Les couches supérieures ont des liens de dépendance avec la couche Services, Techniques
        (physiques) ou Fondation (bases)
    •   C’est avant tout la couche Domaine (ou Services) qui a des liens de dépendance avec la
        couche Infrastructure métier.
    •   La couche Présentation émet des appels vers la couche Application qui envoie des appels de
        services à la couche Domaine. La couche Présentation ne fait pas d’appel directement à cette
        dernière (sauf s’il la couche Application n’existe pas).
    •   S’il s’agit d’une application « de bureau » monoprocessus, les objets logiciels de la couche
        Domaine (Business Object) sont directement visibles depuis les couches Présentation,
        Application et Services techniques.
    •   En revanche, s’il s’agit d’un système distribuée, des copies sérialisables (Value Object) des
        objets de la couche Domaine sont généralement utilisés pour le transfert entre la couche
        présentation et la couche Domaine.


Bien d’autres règles pourraient être établies, cependant beaucoup dépendent du type d’application et
des contraintes techniques ou financières.
Le couplage est donc un principe à prendre très au sérieux dans vos applications. Cependant il est
inutile de passer du temps pour des couplages inutiles. Est-il justifié de gaspiller du temps à tenter
d’abstraire ou de masquer un élément qui a peu de risques de changer ou dont le changement aurait
un impact négligeable ? Si vous construisez une application Java, vous n’allez pas passer à votre
temps à cacher les accès aux bibliothèques Java !

3.4.Couche métier

3.4.1.Explications
C’est la couche principale de toute application. Elle s’occupe aussi bien des accès aux différentes
données qu’aux traitements de celles-ci au niveau métier.
Le traitement métier est :

    •   La vérification de la cohésion entre les données
    •   L’implémentation de la logique métier de l’entreprise au niveau de l’application
    •   La gestion du « workflow » (processus de traitement)
Il est souvent pratique de séparer toute la partie « Accès aux données » de la partie « Traitement de
la logique métier » afin, bien entendu, de pouvoir changer le mode d’accès aux données sans avoir à
toucher à la logique métier et réciproquement.
Nous parlons alors de couche (ou sous-couche) « DA » (Data Access) et de la couche « Service »
(Traitement logique).

3.4.2.Technologies utilisées
Dans le cas d’application importante, l’utilisation des EJB (Entreprise JavaBean) peut s’avérer utile.
Plus particulièrement si vous devez connecter un ensemble de clients très différents des un des
autres (web / client riche / client d’un autre langage …).
Cependant si votre application n’a besoin que d’un seul type de client, comme dans une application
web, alors il vous sera possible d’étudier d’autres solutions moins « gourmandes » en performances.
Hibernate, Spring, iBatis pour en citer que quelques-unes !


3.5.Couche application & présentation

La couche application est très liée au type de client utilisé. Si vous souhaitez travailler avec
un client riche, vous devrez utiliser l’ensemble des outils & librairies mis à disposition pour ce
type de client (J2SE plus particulièrement).

3.5.1.Explications
Dans une architecture J2EE, on utilise principalement des applications web pour les utilisateurs. En
effet, lorsque votre application n’a pas besoin d’utiliser des composants très spécifiques
(fonctionnalités spéciale n’existant par chez les clients légers), il est beaucoup plus pratique d’utiliser
le HTML pour générer l’interface de celle-ci.
Même si dans certains cas les flux réseaux peuvent être beaucoup plus important qu’avec un client
riche, la compensation est réalisée par l’utilisation du haut et très haut débit (au niveau des réseaux).
La couche application (si elle existe) sert de médiateur entre la couche présentation et la couche
domaine (ou métier) et contrôle l’enchaînement des tâches. Elle est chargée de connaître l’état des
sessions des clients.

3.5.2.Technologies utilisées

Vous allez pouvoir utiliser, de base, les composants : Servlet / JSP. Les servlets vous serviront de
« Contrôleur », c’est-à-dire qu’elles devront exécuter l’ensemble des traitements liés aux requêtes
utilisateurs mais également les appels aux modèles métiers.
Les JSP, quant à elles, seront utiles pour toute la partie présentation (génération du HTML à envoyer
au client). Elles ne doivent pas êtres utilisés à la place du contrôleur. Par exemple, si vous souhaitez
traiter un formulaire, vous utiliserez une servlet plutôt qu’une page JSP. Les JSP doivent avoir qu’un
minimum de code Java afin d’être optimale.
Cependant les Servlet/JSP étant des composants de bas niveau, elles n’offrent que des
fonctionnalités de base. Cela est souvent déroutant pour les débutants car ils ne s’occupent pas
d’architecture et finalement terminent la gestion de l’ensemble des requêtes depuis une JSP ou une
Servlet.
Une des solutions est d’utiliser des framework. Le plus connu est sans doute Struts qui met en avant
l’ensemble de l’architecture MVC (Model View Controller). Ce framework utilise le concept des Servlet
/ JSP mais ajoute l’ensemble de l’architecture MVC afin de simplifier la vie du développeur.
Suite à Struts, les JSF (Java Server Faces) ont vu le jour et risquent de s’afficher comme un standard
d’ici peu de temps et donc de remplacer Struts. En effet, JSF est intégré de base dans la plate-forme
J2EE.


3.6.Connexion des couches

L’utilisation de couches s’avère très utile pour la modularité et l’évolutivité d’une application.
Cependant la connexion de ces couches entres-elles n’est pas une mince affaire. En effet, pour rester
dans le principe : « évolution, modularité », il faut que les couches soient indépendantes ; c’est donc à
ce niveau que les problèmes apparaissent.

3.6.1.Solutions directes

3.6.1.1.Communications inter applications
Les couches peuvent se trouver au sein d’une stricte partie d’application mais également au sein de
l’application globale.
C’est-à-dire que la couche métiers peut très bien se trouver sur un serveur (un tiers), la couche
application sur un autre et la présentation sur un autre également. Ceci est la répartition des couches
par tiers.
Le schéma ci-dessous représente les tiers (métiers et application) et montre les types de connexions
entre les serveurs.




Au sein d’un tiers précis, l’application doit également être divisée en couches afin de facilité l’évolution
et la modularité.
L’interconnexion entre ces couches est plus complexe. C’est donc, ici, que doivent intervenir les
patterns.

3.6.1.2.Patterns

Les patterns sont des modèles de programmation qui répondent à un problème particulier. Lorsqu’on
développe une application complexe qui se veut modulable, on est obligé d’utiliser des « astuces »
pour connecter les différentes classes de notre application.
Ces astuces sont, bien entendu, les patterns. Un ensemble très important de pattern existent, vous
trouverez très généralement des solutions à des problèmes critiques dans les patterns. Cependant il
est impératif de les comprendre afin de ne pas les utiliser dans de mauvaises conditions.
Si vous souhaitez plus d’informations sur les patterns, vous pourrez vous référer au cours « Design
Pattern ».


                                      1. Façade
Une façade est un objet frontal constituant le point d’entrée unique des services d’un sous-système.
L’implémentation et les autres composants du sous-système sont privés et invisibles des composants
externes. La façade assure la protection des variations dans l’implémentation du sous-système.
Ce pattern est très utilisé. En effet, il permet de séparer la définition d’un système et son
implémentation. On retrouve ce pattern aussi bien au niveau des drivers (JDBC par exemple),
provider JNDI (CORBA, RMI, DNS …) mais également au sein de vos applications. Vous pouvez
l’utiliser dans celles-ci pour cacher l’implémentation d’accès aux données, aux services …
Voici un schéma récapitulant ce pattern :
                                     2. DAO (Data Access Object)

L’accès aux données peut se faire de plusieurs manières. Le pattern DAO définit la manière la plus
optimale en matière d’évolution et d’architecture objet.
Ce pattern se base sur l’utilisation de classe « Data ». On parle de POJO (Plain Old Java Object) en
Java. Le qualificateur « Old » n’est pas du tout négatif, en effet il indique que la compatibilité avec
l’ensemble des versions de Java est préservée (donc n’utilise pas d’outils spéciaux en dehors du
langage Java).
Votre DAO fournit les méthodes d’ajout d’objet (sauvegarde en base de données), lecture d’objet
(récupération de données depuis la base de données), modification d’objet, suppression d’objet. Il
cache la connexion à la base de données ou tout autre outil d’accès aux données (XML, réseau …).
Le schéma suivant présente le pattern. Il décrit un DAO utilisant JDBC (DataSource et ResultSet).




                                     3. MVC (Model View Controller) ou
                                        Controller
MVC peut être vu comme une structure d’application plus qu’un pattern en lui-même. Le problème
est : « Comment bien séparer le traitement de la requête client, le modèle de données et le choix de la
vue à retourner au client ? ».
MVC définit, comme son nom l’indique, trois composants :

    •   Model : modèle de données (accès aux services …)
    •   Vue : vue retournée à l’utilisateur
    •   Controller : contrôleur traite la demande client, accède aux modèles et sélectionne la vue à
        retourner au client.

Vous pouvez retrouver ce pattern dans SWING, Servlet/JSP pour les plus connus. Si vous souhaitez
en savoir plus, vous pouvez vous reporter à l’essentiel concernant « Struts » (framework utilisant le
concept MVC).
                                     4. Fabrique (Factory)
Un problème s’impose très souvent en terme de création d’objets : « Qui doit être responsable de la
création des objets lorsque ceux-ci correspondent à des responsabilités particulières, telle une logique
de création complexe ou une volonté de séparer les responsabilités de création pour une meilleure
cohésion, etc… ? »
L’utilisation d’une fabrique est sans doute l’une des principales solutions.
Imaginons que vous souhaitiez utiliser des classes de services dans une application web. Comment
voulez vous créer l’objet de service tout en gardant une abstraction totale de l’implémentation ?
En java, vous utilisez l’opérateur « new » pour créer une nouvelle instance d’objet en mémoire comme
ci-dessous :

MonService service = new MonService();

Cependant cette solution est trop restrictive, si vous souhaitez utiliser une autre classe de service,
vous devrez modifier l’ensemble des codes faisant l’appel précédent.
Dans le cadre d’une Fabrique, c’est elle qui s’occupera de choisir la bonne classe de service à
instancier (via une variable d’environnement, fichier de properties …). De plus les méthodes de votre
fabrique permettant de récupérer un service devront retourner le type d’interface de haut niveau de
votre service et NON LE TYPE DE LA CLASSE D’IMPLEMENTATION (cela anéantirait le concept de
Fabrique en lui-même !).

IMonService service = MaFabrique.getMonService(); // retourne un objet de type
// IMonService (interface) et non MonService qui serait la classe d’implémentation

Vous pourriez également utiliser une Fabrique dans les cas suivants :

    •   Une classe ne peut anticiper quel type d’objet elle doit utiliser
    •   Vous voulez centraliser la connaissance des types de classe qui seront créées


Il existe cependant plusieurs types de fabriques (concrète, abstraite …). Ces différents types ont le
point commun de construire des objets, mais la façon dont ils gèrent cette création diffère suivant le
type.

Voici un schéma présentant ce pattern (type abstrait) :
                                    5. Adaptateur (adapter)
Ce pattern est né d’un problème simple : « Comment concilier des interfaces incompatibles ou fournir
une interface stable à des composants similaires possédant différentes interfaces ? ».
La solution est tout aussi simple : « Convertir l’interface d’origine d’un composant en une autre
interface, via un objet adaptateur intermédiaire.
Prenons l’exemple d’un système voulant accéder à des stocks distants de différents fournisseurs.
Chaque fournisseur fournit un moyen particulier d’accéder à ses informations. Cependant chaque
fournisseur fournit la même information (quantité disponible liée à une référence produit par exemple).
Vous pouvez utiliser le pattern adaptateur afin d’uniformiser les accès à ces informations.


Voici un schéma récapitulant ce pattern :




                                    6. Observateur
Lorsque vous connectez deux couches ensembles et que vous voulez qu’une couche de plus bas
niveau appelle ou envoie un message à la couche de plus haut niveau il n’est pas toujours évident de
trouver une solution qui respecte le principe de séparation Modèle - Vue.
Le principe de Faible couplage indique qu’il doit être possible de remplacer une couche (par exemple :
présentation) sans en affecter les autres (par exemple : domaine). Vous ne pouvez pas directement
appeler une méthode d’affichage d’une fenêtre à partir de la couche domaine.
La solution est bien entendu l’utilisation du pattern Observateur.
Problème : Différents types d’objets souscripteurs sont concernés par les changements d’état et les
événements d’un objet diffuseur (publisher) et veulent réagir à leur manière lorsque le diffuseur
génère un évènement. De plus le diffuseur veut maintenir un faible couplage avec les souscripteurs.
Solution : Définit une interface « souscripteur » ou « auditeur ». Les souscripteurs implémentent cette
interface. Le diffuseur peut enregistrer dynamiquement les souscripteurs intéressés par un événement
et le leur signaler.
                                     7. Autres patterns intéressant
Voici une liste d’autres patterns très intéressant en terme de développement :

    •   Singleton
    •   Bridge
    •   Composite
    •   Proxy
    •   Decorator
    •   Iterator
    •   Prototype
    •   State
    •   Strategy

N’hésitez pas à consulter l’essentiel dédié à ces patterns (Design Pattern) afin d’avoir plus
d’informations et de détails.

3.6.1.3.Utilisation de JNDI (Java Naming and Directory Interface)
Nous avons pu voir que certains patterns sont particulièrement utiles lorsqu’ils travaillent avec des
propriétés externes (valeurs externes, hors du code). C’est le cas pour les Fabriques, Adaptateur et
bien d’autres encore. L’une des possibilités est d’utiliser un fichier externe (properties par exemple),
ceci est le cas pour les applications clientes, ou utiliser JNDI dans le cas d’application contenue (dans
un Container).
Vous pouvez facilement mapper une valeur à une clé et donc spécifier la classe à utiliser pour tel ou
tel service sans avoir à l’intégrer à votre code directement.
Voici un exemple de configuration JNDI pour une application web (dans le web.xml) :

<env-entry>
<env-entry-name>contactDaoImpl</env-entry-name>
<env-entry-value>com.society.contactbook.dao.impl.ContactDaoXml</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>

Ces lignes définissent une entrée « contactDaoImpl » dans l’environnement de l’application contenant
la valeur « com.society.contactbook.dao.impl.ContactDaoXml », c'est-à-dire le nom absolue de la
classe DAO à utiliser pour l’implémentation du DAO lié au Contact.
Vous pouvez alors récupérer facilement la valeur dans votre Fabrique :

Context ctx = new InitialContext();
String contactDaoImpl = (String) ctx.lookup("java:comp/env/contactDaoImpl");
IContactDao dao = (IContactDao) Class.forName(contactDaoImpl).newInstance();

Ces quelques lignes permettent d’instancier de façon dynamiquement la classe indiquée dans l’entrée
d’environnement : « contactDaoImpl ».
L’avantage de cette méthode est évidente : on peut changer la classe d’implémentation de
« contactDao » sans avoir à recompiler l’ensemble de l’application !
Nous vous avons présenté une utilisation de JNDI, cependant il existe un grand nombre d’autres
utilisations (particulièrement intégrer à la connexion entre les composants : EJB / JMS / Datasource
…)

3.6.2.Solutions à base de framework

3.6.2.1.Spring

Spring est un conteneur léger, c'est-à-dire qu’il « remplace » un conteneur lourd (utilisé par les EJB
dans un serveur d’application) au sein d’un simple conteneur web. Bien entendu l’utilisation de Spring
est une approche différente comparée à l’utilisation d’un conteneur « lourd ».
Spring est une couche transversale intégrant (liste non exhaustive) :

    •   La gestion des transactions de façon transversale et centralisé
    •   Couche d’abstraction à JDBC (réduit le nombre de ligne de code à écrire pour l’accès JDBC,
        met en place une hiérarchie d’exception plus intuitive …)
    •   Intégration avec (mapping objet/relationel) :
            o Toplink
            o iBatis
            o Hibernate
            o JDO
    •    AOP (Aspect Oriented Programming)
    •    Framework MVC (Model View Controller)

De nombreuses fonctionnalités sont disponibles dans ce framework. Même s’il n’est pas toujours
évident de comprendre rapidement l’utilisation de Spring, il est souvent plus difficile d’utiliser les
standards J2EE …

3.6.2.2.Struts

Struts pourrait être assimilé à une surcouche de Servlet/JSP. Ce framework comble une bonne partie
des lacunes du Model : Servlet/JSP. Ce framework met a disposition un ensemble d’outil afin
d’organiser de façon harmonieuse le modèle MVC au sein d’une application web.
D’une part vous avez les « ACTION » de l’autre la VUE (page jsp). Le framework permet de
centraliser la carte de navigation dans votre site.
Contrairement à Spring, ce framework n’est pas transversale à votre application mais se situe
principalement dans les couches application et présentation.



4.Cas pratique (cahier technique)

4.1.Sujet

Nous allons développer une application web qui permet de gérer un contact book personnel. Cette
application peut sembler simpliste dans le cadre d’un développement non ordonné, cependant elle va
nous permettre d’appliquer différentes techniques vues dans les chapitres précédents.


4.2.Analyse

Avant de se lancer dans l’architecture d’une application (ou d’une partie d’application), il faut, avant
tout, définir l’ensemble des entités de l’application et mettre à plat les différentes fonctionnalités de
celle-ci.
Chaque fonctionnalité doit être détaillé dans les moindres détails (protocole à utiliser, problèmes qui
peuvent être rencontrés, les différentes solutions à ces problèmes). Ce travail s’appelle : l’écriture des
cas d’utilisation. Si nous devions développer un programme de type « caisse enregistreuse », un cas
d’utilisation possible serait d’établir une vente.

4.2.1.Entités
Il n’est pas toujours évident de voir directement l’ensemble des entités d’une application
(principalement dans le cas d’applications importantes et complexes), ceci est tout à fait normal et doit
donc être pris en compte dans le développement.
Dans le cas de notre application, nous n’avons qu’une seule entité : Contact.
Cette entité représente un contact dans notre adresse book. Nous aurions pu avoir une entité
AddressBook dans le cas d’une application pouvant gérer plusieurs addressbook à la fois.

4.2.2.Cas d’utilisation
Voici l’ensemble des cas d’utilisation devant être pris en compte lors du développement de
l’application :

    •    Cas d’utilisation « Ajouter un contact » :

    1.   L’utilisateur appelle la page du formulaire d’ajout de contact
    2.   Celui-ci rentre les informations du contact
    3.   Clic sur le bouton d’ajout du contact
    4.   Le programme doit alors vérifier que le contact n’existe pas déjà dans la base de données :
              1. Si le contact existe déjà, alors on ne l’ajoute pas et on retourne une erreur à
                   l’utilisateur lui indiquant le problème de doublon
        2. Si le contact n’existe pas :
               i.   Le programme doit vérifier la conformité des informations (nom, prénom,
                    adresse, email existants et valides).
                        1. Si les données sont conformes, le programme ajoute le contact :
                                  1. Si l’ajout s’est bien passé, le programme indique un message
                                       positif au client et affiche un formulaire vide à l’utilisateur
                                  2. Si l’ajout s’est mal passé, le programme renvoie l’utilisateur
                                       vers le formulaire remplit et lui indique un problème logiciel.
                        2. Si les données ne sont pas conformes, le programme renvoie
                            l’utilisateur vers le formulaire remplit et lui indique les champs qui ne
                            sont pas conformes

•   Cas d’utilisation « Lister les contacts » :

1. L’utilisateur appelle la page pour lister l’ensemble des contacts
2. Le programme récupère l’ensemble des informations de la base de données
        1. Si la récupération s’est bien passée
                 i.   S’il existe au moins un contact dans la base de données, le programme
                      affiche un tableau récapitulatif des contacts (nom, prénom, adresse, email…)
                ii.   S’il n’existe aucun contact dans la base de données, le programme affiche un
                      message indiquant qu’aucun contact n’a été trouvé et indique un lien vers le
                      formulaire d’ajout de contact
        2. Si la récupération s’est mal passée, le programme renvoie au client un message
             d’erreur logiciel

•   Cas d’utilisation « Modifier un contact » :

1. L’utilisateur appelle la page qui liste les contacts
2. L’utilisateur sélectionne un contact et clique sur « modifier »
3. Le programme charge les informations du contact depuis la base de données
        1. Si le chargement s’est bien passé, le programme renvoie l’utilisateur vers la page du
             formulaire de modification d’un contact
                 i.  L’utilisateur effectue les modifications nécessaires et clic sur le bouton de
                     modification
                ii.  Le programme doit alors vérifier que le contact existe déjà dans la base de
                     données :

1. Si le contact n’existe pas, le programme renvoie un message d’erreur logiciel à l’utilisateur
2. Si le contact existe bien :

                                  1. Le programme doit vérifier la conformité des informations
                                     (nom, prénom, adresse, email existants et valides).
                                  2. Si les données sont conformes, le programme ajoute le
                                     contact :
                                         i.     Si la modification s’est bien passée, le programme
                                                indique un message positif au client et retourne vers
                                                la liste des contacts
                                        ii.     Si l’ajout s’est mal passé, le programme renvoie
                                                l’utilisateur vers le formulaire remplit et lui indique un
                                                problème logiciel.
                                  3. Si les données ne sont pas conformes, le programme renvoie
                                     l’utilisateur vers le formulaire remplit et lui indique les champs
                                     qui ne sont pas conformes

        2. Si le chargement s’est mal passé, le programme retourne un message d’erreur
           logiciel à l’utilisateur

•   Cas d’utilisation « Supprimer un contact » :

1. L’utilisateur appelle la page qui liste les contacts
2. L’utilisateur sélectionne un contact et clique sur « supprimer »
3. Le programme vérifie que le contact existe bien en base de données :
        1. Si le contact existe bien, le programme tente de supprimer le contact demandé :
                i.   Si la suppression a échoué, le programme renvoie à l’utilisateur une erreur lui
                     indiquant une erreur logicielle.
               ii.   Si la suppression a réussi, le programme renvoie à l’utilisateur un message
                     de confirmation de suppression du contact
            2. Si le contact n’existe pas, le programme renvoie un message indiquant l’absence de
                 ce contact dans la base de données.
    4. L’utilisateur est renvoyé vers la liste des contacts


Maintenant que nous avons décrit la logique de l’application, nous allons pouvoir nous orienter vers
l’organisation des éléments de cette application au niveau logiciel et conception.



4.3.Modélisation

La modélisation est une phase importante dans le développement d’un projet (simple ou complexe).
Nous allons expliquer en détail chaque point de l’architecture de notre application.

4.3.1.Architecture
Pour avoir une architecture robuste, modulable et évolutive, il nous faut utiliser le principe de
« couche » (comme vu précédemment).
Nous allons donc séparer au maximum les différents types de traitement de l’application
(Dao, Service, Application, Présentation).


Voici un schéma global de notre architecture :




4.3.2.Patterns utilisés
L’organisation de l’application étant établie, il faut maintenant savoir comment connecter les couches
entre elles. Cette phase est sans doute l’une des plus dure.En effet, il faut penser « objet » et avoir un
degré d’abstraction qui prenne en compte les contraintes d’évolution et de modularité de l’application.
L’utilisation de patterns est donc, ici, indispensable. Voici le détail (du pourquoi) de chacun des
patterns utilisés dans notre application.

4.3.2.1.Façade
Nous utilisons le pattern Façade pour l’ensemble des DAO et des Services. Cela permet de cacher
l’implémentation de ceux-là. Pour chacun des DAO et des Services nous établissons une interface
définissant les différents comportements possibles pour ce composant. Ensuite il suffit de créer
l’implémentation suivant les besoins et les ressources disponibles pour l’application.
4.3.2.2.Fabrique
Le travail de fabrication des services pour la couche application ou encore la création des DAO pour la
couche service est assuré par des Fabriques.
Celles-ci ont la charge de créer les objets demandés de façon complètement autonome. Elles nous
permettrons de créer des instances dynamiquement suivant des entrées spécifiées (via JNDI) dans le
context de l’application courante.

4.3.2.3.MVC (Model View Controller)
Le pattern MVC, est utilisé dans les couches Application et Présentation. Il permet d’interconnecter
ces deux couches de façon remarquable.
Dans ce pattern, il faut définir les éléments : Model, View et Controller. Dans le cas d’une application
web :

    •   Models : les différents services de l’application
    •   View : les pages JSP générant le HTML
    •   Controller : les servlets servant à traiter les requêtes utilisateurs




Voici la description des différentes étapes :

    1. Le client envoie une requête à l’application web
    2. La servlet (mappée sur l’url demandée par le client) doit traiter la demande client
           1. La servlet demande au(x) modèle(s) les informations (ou les supprime ou les met à
                jour)
    3. La servlet sélectionne la bonne vue à afficher au client




4.3.2.4.Les packages
Dans une application Java, il faut également penser à l’organisation de nos différents packages.
Le package racine sera : « com.society.contactbook ». Nous retrouverons les sous-packages :

    •   com.society.contactbook.bo : regroupe l’ensemble des classes décrivant les entités de
        l’application
    •   com.society.contactbook.dao : ensemble des interfaces des DAO
    •   com.society.contactbook.dao.jdbc : ensemble des classes d’implémentation DAO utilisant
        JDBC
    •   com.society.contactbook.service : ensemble des interfaces des services
    •   com.society.contactbook.service.impl : ensemble des classes d’implémentation Service
    •   com.society.contactbook.application : ensemble des classes pour la couche application
        (servlet principalement)
    •   com.society.contactbook.presentation : ensemble des classes à utiliser pour la présentation
        (très peu, décorateurs pour les tag-lib par exemple …)
Struts - Un framework MVC pour vos applications J2EE



1.Introduction

1.1.Présentation de la plateforme J2EE

J2EE (Java 2 Enterprise Edition) est un ensemble d'APIs visant le développement d'applications
orientées entreprise.
Parmis ces APIs, certaines existent déjà dans la version "standard" de Java (Java 2 Standart Edition),
tandis que d’autres ne sont présentes que dans la version orientée entreprise. Ainsi on a les APIs
EJBs (Enterprise Java Beans) ou JMS (Java Message Service) qui sont spécifiques au jdk J2EE, à
l’inverse des APIs JDBC ou RMI qui existent aussi dans le jdk J2SE.
La plateforme J2EE présente une solution optimale pour développer des applications robustes,
sécurisées et évolutives. En effet, choisir cette technologie, c’est suivre un certain nombre de règles.
Le but est en effet de séparer au maximum l’application en couches.
Ce que l’on va justement détailler dans ce cours est de quelle manière on va découper les différentes
couches, quel modèle utiliser, et avec quels outils le faire.

1.2.Quelques rappels

Avant de commencer ce cours, quelques rappels sur les JSP/Servlets, les JavaBeans ainsi que les
TagLib :



1.2.1.Des Servlets aux JSP
Lorsque l’on va développer un site Web à l’aide des technologies J2EE, on peut difficilement passer à
côté des Servlets.
Une Servlet est une classe java chargée une seule et première fois par le moteur de Servlet
(exemple : Tomcat) lors de son premier appel. Par la suite, on fera toujours appel à la même instance
de cette Servlet.
Chaque fois que l’on va créer une Servlet, on créera une classe implémentant l’interface Servlet et
donc redéfinir quelques méthodes dont les méthodes

    •   public void init(ServletConfig cfg) qui sera appelée lors du premier appel de la Servlet, lors
        de son instanciation.
    •   public void service(ServletRequest req, ServletResponse res) qui sera appelée à chaque
        appel de la Servlet instanciée.
    •   public void destroy() qui sera appelée juste avant la destruction de la Servlet.


Ainsi, c’est dans la méthode service() que va se trouver le code java dont le résultat, envoyé au
browser, permettra l’affichage sur une page Web.

Le problème est que si l’on suit ce principe, la partie traitement de notre code est mêlée à la partie
présentation alors que, comme il l’a été précisé précédemment, les bases même d’une application
utilisant les technologies J2EE est le découpage en couches.

Pour séparer clairement la partie traitement de la partie présentation de notre application, on va donc
utiliser les pages JSP.

Une page JSP ressemble à une page HTML pouvant contenir des balises JSP et/ou du code Java.
Elle va donc nous permettre d’afficher les résultats des traitements effectués en arrière plan dans des
classes Java. Dans ce cas, la page JSP va être convertie en Servlet puis le processus va être le
même que pour une Servlet classique.
1.2.2.Dans la JSP : JavaBeans et TagLib

Dans notre page JSP, on va pouvoir utiliser différents composants pour afficher les différents résultats
des traitements effectués dans les couches inférieures.
Les JavaBeans sont des composants qui vont permettre d’instancier et manipuler différents objets
Java (à portée de la page, de requête, de session ou de l’application) à l’aide de balises JSP.
L’avantage est que lorsqu’il va rencontrer une balise, le serveur va automatiquement détecter si
l’instance de l’objet existe déjà ou non. Si c’est le cas, il reprend l’instance existante sinon il en crée
une nouvelle. Cela va permettre d’utiliser un même objet sur plusieurs pages, voire tout le long de
l’application.
Les TagLibs, tout comme les JavaBeans sont des composants qui vont permettre d’instancier et
manipuler différents objets Java à l’aide de balises JSP. Cependant, ils ne pourront avoir qu’une
portée limitée à la page courante. On ne pourra donc pas réutiliser une même instance d’un objet sur
plusieurs pages. Pour chaque page de l’application, on aura une instance différente.
Les TagLibs, balises personnalisées, vont permettre, entre autres de créer ses propres balises de la
plus simple à la plus complexe et ainsi d’afficher les résultats des différents traitements en quelques
lignes.

Pour plus d’informations sur ces composants, n’hésitez pas à consulter le cours sur les Servlets/JSP.

1.2.3.Définition et intérêts d’un framework de présentation

On a donc pu voir que les JSP offrent une solution idéale pour afficher les résultats des traitements
effectués en amont. Ils représentent la partie présentation de notre application. On a aussi vu que l’on
pouvait utiliser les JavaBeans ou les TagLibs pour éviter d’avoir du code Java dans nos pages JSP.
Cependant, l’objectif premier d’un développeur est d’éviter de refaire chaque fois la même chose.

Pour cela, on va utiliser un framework qui impose une méthode de développement simple et efficace.
On va alors obtenir une application respectant les objectifs de réutilisabilité que vise la programmation
orientée objet.

Concrètement, un framework est le squelette d’une application. Cela va permettre la création rapide
d’actions/événements classiques, par exemple, un formulaire (tenant compte de la sécurité, de la
validité, etc…).
Quels que soient les outils utilisés dans les couches inférieures, le framework de présentation choisi
est complètement indépendant. Il nous permet donc d’atteindre l’objectif attendu ; des couches
complètement indépendantes les unes des autres. On peut alors changer l’une des d’elles sans
modifier les autres.

2.Design pattern MVC

2.1.Modèle MVC (Model View Controller)
Le pattern Modèle-Vue-Contrôleur est l’un des modèles les plus connus et le plus célèbre de tous. Il a
été tout d’abord élaboré par Xerox lors de leur premier système fenêtré et plus particulièrement pour
gérer les interactions avec l’utilisateur.
Le problème que résout le modèle MVC est celui de la simplification de trois grandes fonctions
communes à de nombreuses applications :

    •   La maintenance des données dans un stockage de back-end ou sur un système distant.
    •   La construction de la couche présentation destinée à l’utilisateur final.
    •   La gestion de la logique conditionnelle qui décide des écrans qui sont présentés à l’utilisateur,
        de ce qui se passe lorsqu’une erreur survient et de la façon et du moment exacts où les
        systèmes distants sont mis à jour.

Il est donc possible de combiner l’ensemble de ces modules et d’avoir au final un système qui
fonctionne. Cependant les problèmes se posent lorsque vous souhaitez maintenir le code. En effet
dans le cas des JSP, les concepteurs HTML chargés du look end feel et ceux qui maintiennent le
code de traitement sont différents.
MVC traite ce problème en séparant le code en trois parties distinctes :

    •   Les composants Modèle, qui maintiennent les données dans le stockage.
    •   Les composants Vue, qui constituent la couche présentation destinée à l’utilisateur final.
    •   Les composants Contrôleur, qui gèrent la logique conditionnelle. C’est elle qui décide des
        écrans qui sont présentés à l’utilisateur, qui gère les erreurs et la mise à jour des systèmes
        distants.

MVC simplifie donc la maintenance de l’application et empêchent les trois types de logiques de se
mélanger. Il permet également de masquer les détails d’implémentation de chaque domaine aux deux
autres et réduit ainsi les dépendances de codage entre eux. De ce fait, MVC permet de définir une
frontière naturelle entre les concepteurs web qui maintiennent le code html et la couche présentation.
Un des avantages considérable dans le développement d’application web (jsp, servlet) est que le
modèle MVC facilite grandement la gestion des exceptions (car elles sont toutes gérées via le
contrôleur).
D’autres avantages s’appliquent à toutes les formes de traitement conditionnel. Voici quelques
exemples :

    •   Si différentes Vues sont nécessaires en fonction des données qui sont extraites d’une base de
        données ou d’un système distant, le Contrôleur peut décider de la page à présenter.
    •   Si votre application change en fonction de l’heure et/ou la date de la journée, le Contrôleur
        peut gérer cela facilement.
    •   Si un processus de saisie de données nécessite plusieurs pages, dont certaines sont
        facultatives.

Cependant le pattern MVC est clairement définit, pourquoi parle-t-on parfois de MVC1, MVC2 ?


2.2.Modèle MVC2

Le modèle MVC2 n’a rien de plus que le modèle MVC. L’origine de ce nom vient du fait que Sun, lors
de la sortie des spécifications JSP, indiquait dans une section « Page Access Model » (ou modèle
d’accès JSP) un modèle1 et un modèle2.
Le modèle1 décrivait le traitement JSP de la façon dont il était couramment utilisé à l’époque. C'est-à-
dire la requête HTTP est directement envoyée vers un fichier jsp. Tout le traitement se faisait alors
dans la page JSP, et la réponse venait de ce même fichier.
Le modèle2 était bien différent. Il indiquait que c’était la servlet qui devait recevoir la requête HTTP.
Celle-ci pouvait alors effectuer les tâches nécessaires au traitement de cette requête (mémoriser des
informations dans le bean transmis) et transmettre les informations à une page jsp afin que celle-ci
génère la réponse HTTP.
Aucun nom de type « MVC » n’intervient dans ces modèles, cependant le modèle2 s’en inspire
étroitement.
3.Qu’est ce que Struts ?

3.1.Présentation

Struts est un framework pour les applications web.
Un framework est « une structure permettant de soutenir ou de contenir quelque chose, en particulier
un squelette pouvant servir de base à une construction » (cf. dictionnaire anglais).
Struts est en une collection de programmes Java conçue pour vous aider à construire des applications
robustes en gagnant du temps. Il fournit la charpente et le gros œuvre de base. Il vous reste alors plus
qu’à vous consacrer à l’agencement et à la décoration des pièces !
Outre que Struts vous permet de vous faire gagner du temps, il vous permet de voir des applications
complexes comme une suite de composants de base : Vues, Actions, Modèles.
Il vous fournit un ensemble de bonnes pratiques et une base solidement réfléchie. De plus rien ne sert
de réinventer la roue, Struts vous permet de partir d’une base solide, idéal pour les débutants !
Cependant Struts est principalement voué à être utilisé pour les applications web mais est
complètement conforme à la spécification Servlet (actuellement 2.3) qui fait partie de la spécification
plus large de J2EE.
L’un des autres avantages et que Struts est une solution Open-Source dont un grand nombre de
développeurs contribuent activement.



3.2.Architecture

3.2.1.Cycle de vie d’une requête http




    1. Le client envoie une requête à l’application
    2. Le composant Contrôleur reçoit la requête. Il commence alors à prendre des décisions sur la
       façon de continuer, en fonction des règles de gestion de l’application.
    3. Les composants Modèle agissent avec les stockages de données persistant.
    4. Suivant les données retournées par le Modèle, le contrôleur détermine la Vue à utiliser pour
       l’affichage des données et lui transfert les données.
    5. Le composant Vue choisi génère la réponse à la demande du client.
    6.

3.2.2.Vue d’ensemble d’une application struts
Trois parties principales constituent une application utilisant Struts :

    •   La librairie Struts et ses dépendances
    •   Les classes Java : actions, formulaires, exceptions …
    •   Le fichier de configuration struts-config.xml : assemblage de l’application (liaisons url,
        classes, formulaires, sources de données …)
3.2.3.Packages principaux
Voici une description générale des principaux packages de l’application :

    •   org.apache.struts : package root
    •   org.apache.struts.action : package noyau des classes de bas niveau pour les Contrôleur
    •   org.apache.struts.config : package représentant les éléments de configuration (que l’on
        retrouve également dans struts-config.xml)
    •   org.apache.struts.taglib : package root de l’ensemble des tag-lib Struts
    •   org.apache.struts.taglib.bean : package qui regroupe les classes de tag de la librairie Bean
    •   org.apache.struts.taglib.html : package qui regroupe les classes de tag de la librairie Html
    •   org.apache.struts.taglib.logic : package qui regroupe les classes de tag de la librairie Logic
    •   org.apache.struts.taglib.nested : package qui regroupe les classes de tag de la librairie
        Nested
    •   org.apache.struts.tiles : package du plug-in tiles
    •   org.apache.struts.upload : package des classes simplifiant l’upload avec Struts
    •   org.apache.struts.util : package des classes utilitaires de Struts
    •   org.apache.struts.validator : package du plug-in validator

3.2.4.Librairies dépendantes
Voici l’ensemble des librairies qu’il faut intégrer à votre application web afin de pouvoir utiliser Struts :

    •   struts.jar : jar contenant le framework Struts (Actions, forms …)
    •   commons-beanutils.jar ; outils pour l’utilisation des Beans
    •   commons-digester.jar : mapping xml -> objet java
    •   commons-validator.jar : utilisé pour la gestion de la validation (Validator plugin)
    •   commons-logging.jar : permet de faire des logs dans vos applications
    •   commons-fileupload.jar : utilisé pour facilité l’upload de fichier
    •   struts-(bean / logic / nested / html).tld : fichier de description des librairies de tag-lib Struts




3.3.Les modèles : modélisation métier

Un composant modèle représente généralement un objet métier ou un système de back-end
physique. Par exemple, un système qui permet à des utilisateurs de se connecter et de gérer des
informations personnelles peut comprendre un composant Modèle qui représente un Utilisateur.

3.3.1.Classe métier
La classe métier fournit des méthodes qui permettent d’accéder aux informations lié à l’objet (par
exemple pour l’utilisateur : nom, prénom, mot de passe …). Cependant les concepts de base sont les
suivants :

    •   Les classes modèles servent à accéder aux informations mémorisées dans des bases de
        données ou sur des systèmes distants pour les présenter aux utilisateurs.
    •   Elles doivent être conçues de manière à cacher les détails d’implémentation de l’accès à
        l’information (utilisation d’interface).

3.3.2.Exemple de composant Modèle

Dans le cas d’une application construite sur un conteneur de servlet (Tomcat), le composant Modèle
peut se limiter à un simple bean Java fournissant une vue métier à une logique JDBC qui maintient les
informations dans la base de données. Le reste de l’application interagit avec le Modèle pour lire et
écrire les informations et seul le Modèle interagit avec la base de données.
Dans une application construite avec un système frontal utilisant JSP et un système dorsal auquel on
accède via des Web-Services, les composants Modèles servent à fournir une représentation métier du
système dorsal. Le reste de l’application continue à accéder aux informations par l’intermédiaire du
Modèle, mais seul celui-ci accède aux Web Services.
Cela est de même avec un système utilisant les EJB (cf. Entreprise Java Bean). Le Modèle assure
l’accès aux EJB.
Cela permet donc une grande flexibilité pour l’évolution de l’application mais également une extrême
puissance et souplesse pour les développeurs.
3.4.Les vues

Dans le pattern MVC, le composant Vue se concerne sur une seule chose : la création de la couche
présentation que voit l’utilisateur. Ce composant ne doit contenir qu’un strict minimum de logique
métier et d’analyse complexe.
Les composants Vues sont le plus souvent ceux associés à la réponse de la requête HTTP. Dans le
cas de Struts ce sont les JSP qui représente le plus souvent cette vue. En effet, ces fichiers génèrent
le HTML qui sera envoyé au client.
Un des avantages des JSP est que l’on peut tout aussi bien intégrer du HTML, des balises JSP ou
encore des scriptlets Java dans un même fichier. Cependant l’inconvénient est que la tâche des
concepteurs web en est alors complexifiée pour le maintient du look and feel ! De plus un développeur
Java est trop facilement attiré par la tendance à abuser des scriplets et à inclure la logique métier (ou
instructions conditionnelles) directement dans la JSP !
Avec le pattern MVC, ce problème est résolut car il isole le modèle, les traitements et la/les vues. De
plus Struts fournit un ensemble de balises JSP personnalisées servant à construire les composants de
la Vue. On retrouve des balises tels que : <html:text>, <logic:iterate> qui donnent aux développeurs
Java et aux concepteurs web la possibilité de construire des fonctionnalités sans utiliser de scriplet
Java.

3.4.1.Les Tag-lib Struts
Les balises personnalisées du framework Struts constituent une part importante. En effet, elle simplifie
grandement le développement de page JSP (composant Vue) et les relient aux autres composants.
Ces balises vous permettront de débugger plus rapidement vos applications car leur nom et
description sont un avantage considérable contrairement aux scriplets.
Les balises Struts sont regroupées en quatre groupes :

       •   Html : gère l’ensemble des balises html (champs texte, file …)
       •   Logic : gère la logique de vos Vue (itération, condition …)
       •   Bean : gère l’accès aux bean (et à leur propriétés) de votre application (page, request,
           session …)
       •   Nested : regroupe html, logic, bean en ajoutant une arborescence pour l’accès au bean

Tous les descripteurs de ces bibliothèques sont inclus dans le framework sous le nom de : struts-
<<type>>.tld. Par exemple : struts-html.tld représente le descripteur de la bibliothèque html.

3.4.1.1.Struts-Html
Cette bibliothèque est principalement utilisée pour la gestion des formulaires dans Struts. Cependant
elle couvre la majorité des balises de base du HTML.
Voici le tableau récapitulatif des tags disponibles :
Tag Name

Description
base             Affiche la balise HTML <Base>
button           Affiche un bouton
cancel           Affiche un bouton de cancel
checkbox         Affiche une checkbox
errors           Affiche l’ensemble des errors de traitement de la requête (champs non valides …)
file             Affiche un champ fichier
form             Définit une balise <form>
frame            Affiche une frame HTML
hidden           Affiche un champ caché
html             Affiche un tag html
image            Affiche un champ image (formulaire)
img              Affiche une balise img
                 Affiche le contenu javascript permettant d’effectuer une validation côté client d’un
javascript
                 formulaire (utilisation avec le plugin Validator)
link             Affiche une ancre HTML
messages         Affiche l’ensemble des messages accumulés lors de l’exécution de la requête
multibox         Affiche une checkbox
option           Affiche un champ Option
options          Affiche une collection d’options dans un tag Select
optionsCollectio
n
                   Affiche une collection d’options dans un tag Select
password           Affiche un champ mot de passe
radio              Affiche un bouton radio
reset              Affiche un bouton de reset
rewrite            Affiche une url lié au context en cours
select             Affiche un champ Select
submit             Affiche un bouton envoyer
text               Affiche un champ texte
textarea           Affiche un champ textarea (zone de texte)
xhtml              Affiche un tag XHTML


3.4.1.2.Struts-Logic
Cette bibliothèque contient un ensemble de tags utilisés dans la gestion de condition, itération suivant
des objets, et gestion de flux de l’application.

Tag Name

Description
empty                 Evalue le contenu du corps de la balise si la variable est null ou vide.
                      Evalue si le contenu du corps de la balise si la variable est égale à la valeur
equal
                      spécifiée.
forward               Transfert le contrôle à la page spécifiée dans l’entrée ActionForward.
                      Evalue le contenu du corps de la balise si la variable est plus grande ou égale à la
greaterEqual
                      valeur spécifiée.
                      Evalue le contenu du corps de la balise si la variable est strictement plus grande
greaterThan
                      que la valeur spécifiée.
iterate               Répète le contenu du corps de la balise suivant la collection spécifiée.
                      Evalue le contenu du corps de la balise si la variable est inférieure ou égale à la
lessEqual
                      valeur spécifiée.
                      Evalue le contenu du corps de la balise si la variable est strictement inférieure à la
lessThan
                      valeur spécifiée.
                      Evalue le contenu du corps de la balise si la valeur spécifiée est une sous chaine
match
                      appropriée de la variable.
messagesNotPresen     Génère le contenu du corps de la balise le message spécifique n’est pas présent
t                     dans la requête.
                      Génère le contenu du corps de la balise le message spécifique est présent dans la
messagesPresent
                      requête.
                      Evalue le contenu du corps de la balise si la variable est ni null ou ni vide ni une
notEmpty
                      collection vide (testée par isEmpty() de l’interface Collection).
                      Evalue si le contenu du corps de la balise si la variable n’est pas égale à la valeur
notEqual
                      spécifiée.
                      Evalue le contenu du corps de la balise si la valeur spécifiée n’est pas une sous
notMatch
                      chaine appropriée de la variable.
                      Génère le contenu du corps de la balise la valeur spécifique n’est pas présente
notPresent
                      dans la requête.
                      Génère le contenu du corps de la balise la valeur spécifique est présente dans la
present
                      requête.
redirect              Ecrit un header HTTP Redirect dans la réponse.


3.4.1.3.Struts-Bean
Cette bibliothèque est utilisée pour lire et écrire les données contenues dans des beans, voir même
les bean eux-mêmes, ou encore définir de nouveaux beans.

Tag Name

Description
cookie         Définit une variable de script basée sur la valeur du cookie spécifié.
define         Définit une variable de script basée sur la valeur du bean spécifié.
header         Définit une variable de script basée sur la valeur du header spécifié.
include        Charge la réponse depuis une application dynamique et inclut celle-ci dans un bean (type
               String).
message        Affiche le contenu d’un message internationnalisé) dans la réponse.
page           Expose un spécifique objet du context de la page à un bean.
parameter      Définit une variable de script basée sur la valeur du paramètre (requête) spécifié.
               Charge une ressource demandée et inclut celle-ci dans un bean (type String ou
resource
               InputStream).
size           Définit une variable de script basée sur la taille d’une Collection ou Map spécifié.
struts         Expose l’objet de configuration struts à un bean.
write          Affiche le contenu d’une propriété d’un bean dans la page (out).


3.4.1.4.Struts-Nested
Cette bibliothèque est un peu spéciale car elle regroupe les trois précédentes. Cependant elle permet
de structurer vos tags suivant l’arborescence de vos beans.
Voici les trois tags supplémentaires :

Tag Name

Description
nest           Définit un nouveau niveau que les tag fils pourront utiliser.
writeNesting   Définit une variable de script basée sur le niveau courant.
root           Démarre une racine d’une hiérarchie.




3.5.Le contrôleur

Ce sont les composants qui mènent l’action ! Chaque fois qu’un utilisateur soumet un formulaire, c’est
le contrôleur qui le traite. Si l’utilisateur demande une autre page, c’est lui qui décide de ce qui sera
affiché. C’est également lui qui collecte les informations auprès des composants Modèle pour que les
composants Vues aient les informations à afficher.
Voici un récapitulatif des actions effectuées par le Contrôleur :

       •   Valider les données saisies par l’utilisateur.
       •   Prendre des décisions concernant les composants Modèle auxquels il faut accéder ou mettre
           à jour.
       •   Récupérer les données pour le composant Vue.
       •   Prendre des décisions concernant la façon de traiter telle ou telle erreur survenue durant le
           traitement de la réponse
       •   Décider de la Vue à afficher

3.5.1.Servlet Générique : Classes d’action Struts
Le contrôleur est implémenté en deux parties :

       •   Java : classe d’action
       •   Struts : configuration xml

L’action reçoit les saisies utilisateur, coordonne l’accès au stockage persistant ou systèmes distants
met en œuvre la logique de l’application et décide du composant Vue qui sera affiché ensuite à
l’utilisateur.
Une classe d’action doit :

       •   Etendre la classe : org.apache.struts.action.Action ou une classe dérivée
       •   Surcharger la méthode : public ActionForward execute(…) afin d’effectuer le traitement
           spécifique à l’action
       •   Ne comporter aucun lien avec une page JSP directement (ce lien est effectué dans le fichier
           de configuration)

Cette classe d’action peut être très simple comme très complexe. Par exemple vous pouvez créer un
lien qui dirige vers une classe d’action affichant un formulaire de création d’un nouveau compte
utilisateur. La classe d’action s’apparentera à cela :
package com.labosun.lessons.struts.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class NewAccountAction extends Action {
public ActionForward execute(ActionMapping mappings, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return mappings.findForward("newUserForm");
}
}
Aucune référence au fichier jsp n’est faite. La configuration de cette action ressemblera à (cf. §3.8) :

<action path="/addUser"
type="com.labosun.struts.action.NewAccountAction"
input="/home.jsp"
scope="request" >
<forward name="home" path="/adduser.jsp"></forward>
</action>

Cependant la majorité des actions est bien plus complexe que celle-ci. Elles font des traitements
d’accès aux données, mise à jour …
Voici l’ensemble des méthodes les plus utilisées de la classe Action :

    •   addErrors(javax.servlet.http.HttpServletRequest request, ActionMessages errors) : ajoute une
        erreur à la requête. L’ensemble des erreurs ajoutées pourra être affiché par l’intermédiaire de
        la balise : <html:errors/>
    •   addMessages(javax.servlet.http.HttpServletRequest request, ActionMessages messages) :
        ajoute un message à la requête. L’ensemble des messages ajoutés pourra être affiché par
        l’intermédiaire de la balise : <html:messages/>
    •   execute(ActionMapping mapping, ActionForm form, javax.servlet.http.HttpServletRequest
        request, javax.servlet.http.HttpServletResponse response) : méthode à surcharger afin
        d’effectuer le traitement spécifique pour l’action en cours. Retourne un objet de type
        ActionForward indiquant la vue (ou autre action) à laquelle on souhaite passer la requête.
    •   setLocale(javax.servlet.http.HttpServletRequest request, java.util.Locale locale) : permet de
        récupérer la locale de l’utilisateur (utile pour savoir le pays/langue de celui-ci).




3.6.L’internationalisation

Struts met à disposition un ensemble d’outils permettant de gérer facilement l’internationalisation dans
vos applications web.
Vous pouvez tout aussi bien utiliser l’internationalisation pour gérer les messages d’erreur de vos
formulaires que pour vos messages d’une page JSP.
Dans Struts, on parle de bundle de messages pour indiquer un groupe de message dynamique. Un
bundle est représenté par un fichier Properties (bien connu dans le monde Java).

3.6.1.Les fichiers « Properties »
Qu’est ce qu’un fichier Properties ? En java, il existe une classe : java.util.Properties qui permet de
lire un fichier standardisé très simplement (un fichier properties).
Ce fichier prend généralement l’extension .properties et regroupe des groupes binaires (clé => valeur).
Pour chaque valeur dynamique on associe une clé unique indépendante du langage ce qui permet de
relier plusieurs valeurs à une même clé selon le langage en cours.
Voici un exemple de fichier Properties :
cle1=valeur1
cle2=valeur2
cle3=valeur3
3.6.2.Outils Struts

Struts utilise un bundle par défaut qui est lié au fichier : ApplicationRessources.properties. Cependant
vous pouvez tout à fait personnaliser l’accès à ce fichier dans le fichier de configuration de Struts.
Vous pouvez également scinder vos propriétés en plusieurs fichiers et donc plusieurs bundle.
L’utilisation principale se fera dans vos JSP par l’intermédiaire de la balise :
<bean:message key=”cle0” />
Ou
<bean:message key=”cle0” bundle=”nomDuBundle” />

Vous pouvez également récupérer la valeur d’un message directement dans une action :
getResources(request).getMessage("cle");




3.7.Le traitement des erreurs

Struts vous permet de centraliser la gestion des exceptions liées au Contrôleur. Vous pouvez définir
des liaisons entre le type d’exception et une page jsp (pour afficher un message). Vous pouvez
également ajouter une classe handler qui est automatiquement appelée lorsqu’une exception est
jetée.
Voici un schéma représentant le fonctionnement en globalité :




    1.   Une classe d’action jette une exception de type : « Type »Exception
    2.   Struts détecte une configuration pour cette exception dans le fichier de configuration
    3.   Suivant cette configuration, Struts appelle la classe Handler pour cette exception
    4.   Struts redirige vers la page jsp d’erreur configurée

3.8.Fichier de configuration

Le fichier de configuration est le point central de toute application Struts. En effet, il regroupe
l’ensemble des objets et de leur configuration. On peut connaître l’ensemble de la logique de
l’application (navigation) avec ce fichier. Malgré la lourdeur d’utilisation de xml dans une application
web complexe (suivant le nombre de fonctionnalités), les outils qui ont été mis en œuvre pour aider le
développeur dans le paramétrage de ce fichier sont maintenant stables et robustes.

3.8.1.Assemblage de l'application
Le fichier de configuration : struts-config.xml (le plus souvent nommé ainsi) est chargé d’indiquer à
Struts quels sont les formulaires, les actions disponibles et permet également d’ajouter un certain
nombre de modèles « enfichables » au package Struts de base.
Nous étudierons ici l’ensemble des balises de bases utilisées dans une application Struts grâce à la
DTD fournit par Struts à l’adresse suivante : http://jakarta.apache.org/struts/dtds/struts_config_1_2.dtd (DTD
de la version 1.2).
La première balise que l’on retrouve dans le fichier de configuration est : « struts-config ». La dtd
correspondante est celle-ci :
<!ELEMENT struts-config (display-name?, description?, data-sources?, form-beans?, global-
exceptions?, global-forwards?, action-mappings?, controller?, message-resources*, plug-in*)>
<!ATTLIST struts-config id ID #IMPLIED>
On l’appelle la balise « root », c'est-à-dire la racine de toutes les autres. On ne peut trouver qu’une
seule de cette balise dans un même fichier. Il est aisé de voir qu’elle accepte (dans son corps) les
balises :

    •   display-name : nom de l’application liée au fichier de configuration en cours
    •   description : description du fichier de configuration
    •   data-sources : l’ensemble des sources de données (1 instance acceptée)
    •   form-beans : l’ensemble des « form-bean » (1 instance acceptée)
    •   global-exceptions : l’ensemble des handler d’exception (1 instance acceptée)
    •   global-forwards : l’ensemble des points de retour de type « global » (1 instance acceptée)
    •   action-mappings : l’ensemble des mappings d’action (1 instance acceptée)
    •   controller : paramétrage d’un contrôleur spécifique (dit RequestController) (1 instance
        acceptée)
    •   message-resources : définition d’un bundle de messages (multiples instances acceptées)
    •   plug-in : définition d’un package (extérieur) connecté : plug’in (multiples instances acceptées)
    •

3.8.2.Datasources
Vos objets modèles ont peut être besoin d’établir une connexion avec une base de données. Dans ce
cas, vous pouvez utilisez Struts pour initialiser un pool de connexion plutôt que de passer directement
par votre serveur d’application. L’intérêt premier est que vous n’aurez pas à « toucher » à la
configuration de votre serveur d’application lors du déploiement de votre application. Cela simplifie
donc grandement le déploiement de celle-ci.
Voici la partie de DTD correspondante aux DataSource :
<!ELEMENT data-sources (data-source*)>
<!ATTLIST data-sources id ID #IMPLIED>
<!ELEMENT data-source (set-property*)>
<!ATTLIST data-source id ID #IMPLIED>
<!ATTLIST data-source className %ClassName; #IMPLIED>
<!ATTLIST data-source key %AttributeName; #IMPLIED>
<!ATTLIST data-source type %ClassName; #REQUIRED>

La balise data-sources regroupe l’ensemble des data-source configuré pour l’application. Elle contient
donc de 0 à n balises data-source.

Chaque balise data-source définit précisément les paramètres de configuration des connexions à une
source de données (et plus précisément à des objets JDBC 2.0 DataSource). Voici les différentes
balises qu’elle peut contenir :

    •   className : cette balise est optionnelle. Elle définit la classe servant à configurer la source de
        données et doit donc contenir toutes les données nécessaires pour instancier une DataSource
        du type requis. Autrement dit, elle doit gérer l’ensemble des propriétés spécifiées dans les
        clauses set-property (cf. plus bas) de la balise. Si cette balise est omise, Struts utilise la
        classe par défaut : org.apache.struts.config.Config pour gérer les propriétés.
    •   key : est une clé qui permet à l’application d’accéder à l’objet datasource créé par Struts. Par
        exemple dans le cas d’une clé : « stockdb », l’application pourra y accéder via :
        « org.apache.struts.action.DATA_SOURCE/stockdb » (si aucun préfixe de module n’existe :
        préfixe = "")
    •   type : spécifie le nom de la classe de la DataSource qui doit implémenter java.sql.DataSource.
        Cette classe est utilisée par Struts pour créer une instance de la datasource, elle configuré via
        la classe définit dans l’attribute « className ».
    •   set-property : cette balise permet d’attribuer une clé et une valeur. Elles servent
        principalement à configurer les paramètres de connexion à la source de données (login,
        password, paramètres de la base…)

Voici la DTD correspondante :

<!ELEMENT set-property EMPTY>
<!ATTLIST set-property id ID #IMPLIED>
<!ATTLIST set-property property %PropName; #REQUIRED>
<!ATTLIST set-property value CDATA #REQUIRED>

    •   property : représente la clé (propriété)
    •   value : représente la valeur liée à la clé
3.8.2.1.Exemple
Voici un exemple XML de la clause data-sources :

<data-source>
<set-property property="autoCommit" value="true"/>
<set-property property="decription" value="DataSource - MysqlLocal"/>
<set-property property="driverClass" value="com.mysql.jdbc.Driver"/>
<set-property property="maxCount" value="4"/>
<set-property property="minCount" value="1"/>
<set-property property="user" value="root"/>
<set-property property="password" value=""/>
<set-property property="url" value="jdbc:mysql://localhost/contacts"/>
</data-source>

3.8.2.2.Utilisation dans l’application
Vous pouvez accéder à l’objet DataSource depuis une Action via le code suivant :

DataSource ds =
servlet.getServletContext().getAttribute(“org.apache.struts.action.DATA_SOURCE/stockdb”);

A partir de là, vous pourrez récupérer une connexion à votre source de données et effectuer tous les
traitements que vous désirer.

3.8.3.FormBeans
Les balises form-beans et form-bean indiquent à Struts à quel identifiant unique est associé une
classe ActionForm. Elles servent également à initialiser les DynaForms (cf. §5.3).
Voici la partie de DTD correspondante :
<!ELEMENT form-beans (form-bean*)>
<!ATTLIST form-beans id ID #IMPLIED>
<!ATTLIST form-beans type %ClassName; #IMPLIED>

<!ATTLIST form-bean id ID #IMPLIED>
<!ATTLIST form-bean className %ClassName; #IMPLIED>
<!ATTLIST form-bean dynamic %Boolean; #IMPLIED>
<!ATTLIST form-bean name %BeanName; #REQUIRED>
<!ATTLIST form-bean type %ClassName; #REQUIRED>

<!ELEMENT form-property (set-property*)>
<!ATTLIST form-property className %ClassName; #IMPLIED>
<!ATTLIST form-property initial CDATA #IMPLIED>
<!ATTLIST form-property name %PropName; #REQUIRED>
<!ATTLIST form-property size %Integer; #IMPLIED>
<!ATTLIST form-property type %ClassName; #REQUIRED>

Voici une description de chacune des balises :

    •   form-beans : encapsule la liste des beans formulaires définis. L’attribut type a été déclassé.
    •   form-bean : définit un formulaire unique.
            o className : sert à définir un bean de configuration pour ce formulaire (comme pour
               les DataSource) cependant cela est n’est pas commun.
            o dynamic : a été déclassé
            o name : représente l’identifiant unique qui permet d’accéder au formulaire et d’associé
               ultérieurement le formulaire à une action. Remarque importante : si la portée de votre
               formulaire est celle de la session, vous pouvez accéder au formulaire via la méthode
               getAttribute() de l’objet session.
            o type : doit indiquer une classe ActionForm valide. C’est cette classe qui sera
               instanciée pour créer le formulaire.

Si le type dérive (ou est) : org.apache.struts.action.DynaActionForm, les balises form-property sont
également inspectées par Struts. Chacune d’elles définit une valeur initiale, un nom et un type (par
exemple java.lang.String) pour chaque propriété du formulaire (cf. §5.3)
3.8.3.1.Exemple
<form-bean name="contactForm"
type="com.society.contactbook.application.forms.ContactForm" />

3.8.4.Exceptions
La balise global-exceptions regroupe l’ensemble des exceptions que votre application gère au plus
bas niveau durant le traitement d’une requête. Vous pouvez gérer autant de type d’exception que vous
le souhaitez. Nous pouvons assimiler cette partie de configuration au try … catch dans le langage
Java. En effet vous déclarez un bloc « catch » via une balise exception.
Voici une description des attributs qu’admet cette balise :

    •   bundle : spécifie le nom du bundle de ressources qui contiendra les messages pour cette
        exception.
    •   handler : spécifie la classe appelée lorsque l’exception se produit. Elle doit impérativement
        dérivée de la classe org.apache.struts.action.ExceptionHandler ou d’une classe dérivée.
    •   key : désigne la clé du message d’erreur qui sera généré et qui sera recherché dans le bundle
        de messages.
    •   path : définit l’URL à laquelle le contrôle est transféré lorsque l’exception survient.
    •   scope : indique à Struts s’il doit mémoriser les ActionError créées par la gestion de l’exception
        dans la portée de la session ou la requête.
    •   type : nomme l’exception qui sera interceptée par ce gestionnaire.

3.8.4.1.Exemple
<global-exceptions>
<exception key=”database.error” path=”/errors/generalErrors.jsp”
scope=”request” type=”java.sql.SQLException” />
</global-exceptions>
Si une exception est lancée durant le traitement, le modèle database.error est recherché dans le
bundle de ressources par défaut (ApplicationResources.properties), une nouvelle ActionError est
créée à l’aide du modèle, puis elle est placée dans les attributs de la requête et le contrôle est
transmis à la page d’erreur standard.

3.8.5.Global Forwards
Les transferts globaux servent à définir des chemins d’accès utilisables par toutes les actions définies
dans la configuration. Si par exemple un certains nombre d’actions doivent transférer le contrôle à un
écran de connexion, vous pouvez définir ce transfert ici au lieu de le faire dans chaque action.
Voici la partie de la DTD correspondante :
<!ELEMENT global-forwards (forward*)>
<!ATTLIST global-forwards id ID #IMPLIED>
<!ATTLIST global-forwards type %ClassName; #IMPLIED>

<!ELEMENT forward (icon?, display-name?, description?, set-property*)>
<!ATTLIST forward id ID #IMPLIED>
<!ATTLIST forward className %ClassName; #IMPLIED>
<!ATTLIST forward contextRelative %Boolean; #IMPLIED>
<!ATTLIST forward module %RequestPath; #IMPLIED>
<!ATTLIST forward name CDATA #REQUIRED>
<!ATTLIST forward path %RequestPath; #REQUIRED>
<!ATTLIST forward redirect %Boolean; #IMPLIED>
Voici la description de l’ensemble des attributs :

    •   contextRelative : indique si « path » est relatif au module d’une application modulaire (si faux)
        ou de l’application web (si vraie)
    •   name : représente l’identifiant unique du forward. Il permet de retrouver ce forward dans une
        action avec la méthode findForward()
    •   redirect : si positionné à true le contrôle est transmis à la page avec une redirection plutôt
        qu’un transfert. Une nouvelle requête est donc créée

Nous n’utilisons généralement pas les balises set-property pour les forward. Cependant celles-ci
peuvent vous être utiles si vous souhaitez créer vos propres classes de forward (dérivant de la classe
de base). Dans ce cas là vous devrez également indiquer le nom complet de la classe dans l’attribut :
className.
3.8.5.1.Exemple
Voici un exemple simple d’utilisation de global-forwards :
<global-forwards>
<forward name=”home” path=”/pages/home.jsp” />
</global-forwards>

3.8.6.Action mappings
Cette partie est sans doute la plus complexe. Elle relie les actions, les formulaires et les transferts.
Voici la partie de la DTD correspondante :
<!ELEMENT action-mappings (action*)>
<!ATTLIST action-mappings id ID #IMPLIED>
<!ATTLIST action-mappings type %ClassName; #IMPLIED>

<!ELEMENT action (icon?, display-name?, description?, set-property*, exception*, forward*)>
<!ATTLIST action id ID #IMPLIED>
<!ATTLIST action attribute %BeanName; #IMPLIED>
<!ATTLIST action className %ClassName; #IMPLIED>
<!ATTLIST action forward %RequestPath; #IMPLIED>
<!ATTLIST action include %RequestPath; #IMPLIED>
<!ATTLIST action input %RequestPath; #IMPLIED>
<!ATTLIST action name %BeanName; #IMPLIED>
<!ATTLIST action parameter CDATA #IMPLIED>
<!ATTLIST action path %RequestPath; #REQUIRED>
<!ATTLIST action prefix CDATA #IMPLIED>
<!ATTLIST action roles CDATA #IMPLIED>
<!ATTLIST action scope %RequestScope; #IMPLIED>
<!ATTLIST action suffix CDATA #IMPLIED>
<!ATTLIST action type %ClassName; #IMPLIED>
<!ATTLIST action unknown %Boolean; #IMPLIED>
<!ATTLIST action validate %Boolean; #IMPLIED>

Voici une description de chacune des balises :

    •   attribute : spécifie un ID unique permettant de mémoriser l’ActionForm dans la portée de la
        requête ou la session sinon name est utilisé
    •   forward / include : permettent de transférer le contrôle à un nouveau chemin au lieu de traiter
        l’action
    •   input : indique la page ou action qui a généré le formulaire servant à entrer les données
    •   parameter : permet de transmettre un autre paramètre à l’action, cependant il est alors
        préférable d’utiliser la balise plus générale : set-property
    •   path : sert à relier la requête à l’action – ce doit être le chemin de l’action sans aucun suffixe.
        Si par exemple vous avez spécifié « /admin/add.do » dans l’action de votre formulaire alors
        vous indiquerez « /admin/add » dans le path
    •   prefix / suffixe : spécifient respectivement un préfixe et un suffixe qui seront ajoutés aux noms
        de propriétés du bean ActionForm avant de les associer aux noms de paramètre de la requête
    •   roles : permet de restreindre l’accès à l’action en indiquant un ensemble de rôle séparée par
        une virgule.
    •   type : spécifie la classe de l’Action.
    •   validate : si cette attribut est positionné à false, le contrôleur n’exécute pas la méthode
        validate() de l’ActionForm




3.8.6.1.Exemple

<action path="/Welcome"
scope="request"
validate=”false”
type="com.society.contactbook.application.actions.WelcomeAction">
<forward name="success" path="home" />
</action>
3.8.7.Controller
Cette balise n’est pas souvent utilisée. Seules les personnes souhaitant modifier le noyau de Struts
s’en serviront !
<!ELEMENT controller (set-property*)>
<!ATTLIST controller id ID #IMPLIED>
<!ATTLIST controller bufferSize %Integer; #IMPLIED>
<!ATTLIST controller className %ClassName; #IMPLIED>
<!ATTLIST controller contentType CDATA #IMPLIED>
<!ATTLIST controller forwardPattern CDATA #IMPLIED>
<!ATTLIST controller inputForward %Boolean; #IMPLIED>
<!ATTLIST controller locale %Boolean; #IMPLIED>
<!ATTLIST controller maxFileSize CDATA #IMPLIED>
<!ATTLIST controller memFileSize CDATA #IMPLIED>
<!ATTLIST controller multipartClass %ClassName; #IMPLIED>
<!ATTLIST controller nocache %Boolean; #IMPLIED>
<!ATTLIST controller pagePattern CDATA #IMPLIED>
<!ATTLIST controller processorClass %ClassName; #IMPLIED>
<!ATTLIST controller tempDir CDATA #IMPLIED>
Voici la description de chacun des attributs :

    •   bufferSize : permet de modifier la taille du tampon d’entrée lors du traitement de chargement
        de fichiers
    •   maxFileSize : définit la taille maximale du fichier pouvant être chargé et peut être suivi d’un K
        (kilo), d’un M (méga) ou d’un G (giga)
    •   contenType : permet de définir un autre type de contenu par défaut que « text/html »
    •   debug : active le débuggage si la valeur est supérieure à 0
    •   forwardPattern : permet de modifier la façon dont le chemin d’une application est lié à une
        URL relative au contexte à l’aide de forwardPattern. La valeur de $A est expansée en préfixe
        de l’application (/applicationContext par exemple) et celle de $P dans le chemin requis. Par
        défaut la valeur est $A$P
    •   inputForward : positionné à true, les paramètres des attributs input des balises action sont
        traités comme des transferts et non comme des chemins. C'est-à-dire qu’ils sont comparés
        aux balises forward définies globalement et localement au lieur d’être traités comme des URI
    •   locale : mémorise un locale dans la session de l’utilisateur si ce n’est pas déjà fait. (lorsque
        cet attribut est positionné à true)
    •   nocache : une requête de désactivation de la mise en cache des contenus est envoyée au
        navigateur client avec chaque réponse HTTP
    •   pagePattern : indique à Struts comment relier les pages à l’URL sous-jacente, d’une façon
        assez semblable à forwardPattern
    •   processorClass : remplace la classe « processor de requête » par défaut de Struts par une
        personnalisée.

3.8.7.1.Exemple
Voici un exemple de balise pouvant être utilisée :
<controller nocache=”true” contentType=”image/jsp”/>

3.8.8.Message-resources
Cette balise vous permet de définir un bundle de ressources pour votre application. Vous pouvez, bien
entendu, en définir plusieurs si besoin est.
<!ELEMENT message-resources (set-property*)>
<!ATTLIST message-resources id ID #IMPLIED>
<!ATTLIST message-resources className %ClassName; #IMPLIED>
<!ATTLIST message-resources factory %ClassName; #IMPLIED>
<!ATTLIST message-resources key %AttributeName; #IMPLIED>
<!ATTLIST message-resources null %Boolean; #IMPLIED>
<!ATTLIST message-resources parameter CDATA #REQUIRED>
Voici une description de chacun des attributs :

    •   factory : permet de spécifier d’où proviennent les données de la ressource de messages. Par
        défaut, il est configuré pour lire des fichiers « properties »
    •   key : définit une clé pour accéder au bundle.
    •   null : permet de spécifier ce qu’il faut faire lorsqu’un message n’est pas trouvé. Si l’attribut est
        positionné à true, il retourne null. Positionné à false, il retourne un message d’erreur avec la
        clé érronée.
    •   parameter : permet de transmettre un paramètre à la fabrique lorsque le bundle est créé. Pour
        les fabriques à base de fichiers de propriétés, c’est le chemin du fichier.

3.8.8.1.Exemple
Voici un exemple de configuration d’un bundle :
<message-resources
key=”com.labosun.MAIN_MESSAGES”
parameter=”com.labosun.MainMessages” />
Vous devez avoir un fichier MainMessages.properties dans votre package : com.labosun

3.8.9.Plug-in
Ce dernier élément vous permet de charger dynamiquement des fonctionnalités supplémentaires dans
le framework Struts.
<!ELEMENT plug-in (set-property*)>
<!ATTLIST plug-in id ID #IMPLIED>
<!ATTLIST plug-in className %ClassName; #REQUIRED>

Voici une description de chacun des attributs :

    •   className : spécifie la classe qui charge le plug-in.
    •   set-property : permet de transférer au plug-in les arguments nécessaires à son
        fonctionnement

3.8.9.1.Exemple
Voici un exemple de configuration du plug-in « validator » :
<plug-in className=”org.apache.struts.validator.ValidatorPlugIn”>
<set-property property=”pathnames”
value=”/WEB-INF/validator-rules.xml”,
”/WEB-INF/validation.xml” />
</plug-in>

3.9.Intégration avec l’application Web

Nous avons vu, jusqu’à présent, le fonctionnement de Struts indépendamment d’une application web
basée sur un moteur de servlet.
Si l’on ne peut pas intégrer Struts avec notre application web, alors ce framework est inutilisable ! Bien
entendu une solution existe, nous allons rediriger un ensemble d’url (grâce au mapping par un pattern)
à une servlet du framework Struts.
Nous utiliserons : org.apache.struts.action.ActionServlet. Cette servlet va nous permettre
d’indiquer le fichier de configuration à utiliser, le type de debuggage à prendre en compte …
Nous n’avons plus, ensuite, qu’à mapping un type d’url à cette servlet.
Voici un exemple de code à inclure dans le fichier de description de votre application web : web.xml :


<!-- Standard Action Servlet Configuration (with debugging) -->
<servlet>
<servlet-name>actionstruts</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>

<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>actionstruts</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>




4.Les tiles

4.1.Présentation

Lorsqu’on développe une application Web, on remarque généralement que l’ensemble des pages
possède un élément central variable entouré d’éléments plus ou moins statiques (barre de navigation,
en-tête, pied de page …)
La bibliothèque Tiles, qui est un plug-in intégrable à Struts, permet de créer des modèles de page
réutilisables pour l’ensemble de votre application à l’aide d’un document XML central.
Cette librairie peut s’utiliser aussi bien indépendamment de Struts ou en interne. Nous verrons ici
l’utilisation intégrée à Struts (ce qui nous permet d’avoir toute la puissance de Struts combinée avec
celle des tiles).

.




4.2 Architecture

Voici un schéma récapitulatif de l’architecture et utilisation de Tiles :

Cette architecture préserve la séparation : structure des pages - contenus des pages. En effet, vous
pouvez créer autant de layouts que vous le souhaitiez ; pour affecter un layout à une page il suffit que
la définition de cette page (dans le fichier de configuration) hérite du layout concerné. Nous pouvons
donc remarquer que la couche application n’intervient pas directement sur les pages jsps mais passe
par la couche de structuration (les tiles).

4.3.Installation

Afin de pouvoir utiliser la bibliothèque Tiles, vous devez la déclarer auprès de la configuration Struts.
Tiles étant un plugin struts vous avez juste à insérer ces quelques lignes dans votre fichier de
configuration Struts :
<plug-in className="org.apache.struts.tiles.TilesPlugin">
<set-property property="moduleAware" value="true" />

<set-property property="definitions-parser-validate"
value="true" />
</plug-in>
Tiles étant un plugin vous devez avoir les librairies dans votre classpath (tout comme vous avez les
libraries de Struts dedans). Depuis la version 1.2 de Struts, Tiles est intégré dans Struts (struts.jar). Si
vous utilisez une version antérieure (ce qui n’est pas recommandé) vous devrez ajouter la librairie
Tiles manuellement (vous trouverez cette librairie ici : http:// …). Par défaut, le plug-in initialise
l’application avec le fichier : tiles-def.xml du dossier WEB-INF.

4.4.Fichier de configuration

Le fichier de configuration, comme pour Struts, est le point central de gestion de Tiles dans votre
application. C’est dans ce fichier que vous devrez définir l’ensemble des définitions de votre
application. Voici une description de l’ensemble des balises pouvant êtres contenus dans ce fichier.
Comme pour tout fichier xml bien formé, il suffit de se baser sur la DTD pour connaître l’ensemble de
la structure à appliquer à notre fichier xml. Voici le lien vers la DTD du fichier de configuration des
Tiles : http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd.

4.4.1.Balise racine
La première balise rencontrée est la balise « parente ». En effet, c’est elle qui va contenir l’ensemble
des balises enfantes de configuration des Tiles.
<!ELEMENT tiles-definitions (definition+)>

Cette balise ne peut contenir que des balises de type : definition. Elle ne sert qu’à regrouper
l’ensemble des définitions.

4.4.2.Balise definition
C’est la balise la plus important. En effet, elle permet de définir une définition de tile, c’est-à-dire une
structure de page pouvant être inséré dans une page ou directement utilisée en tant que forward dans
la configuration Struts. Cette balise contient bien plus d’attribut que la précédente car elle permet de
gérer l’ensemble des cas de structure d’une page (layout ou contenu de page). Voici la partie de DTD
correspondante :

<!ELEMENT definition (icon?, display-name?, description?, put*, putList*)>
<!ATTLIST definition id ID #IMPLIED>
<!ATTLIST definition controllerClass %ClassName; #IMPLIED>
<!ATTLIST definition controllerUrl %RequestPath; #IMPLIED>
<!ATTLIST definition extends %DefinitionName; #IMPLIED>
<!ATTLIST definition name %DefinitionName; #REQUIRED>
<!ATTLIST definition page %RequestPath; #IMPLIED>
<!ATTLIST definition path %RequestPath; #IMPLIED>
<!ATTLIST definition role CDATA #IMPLIED>
<!ATTLIST definition template %RequestPath; #IMPLIED>

    •   controllerClass : le nom complet de la classe spécifique à utiliser en tant que contrôleur (si on
        ne souhaite pas utiliser celle par défaut)
    •   extends : utiliser pour faire dériver la définition en cours d’une définition mère (cela permet de
        surdéfinir les valeurs des attributs de la définition mère)
    •   name : nom unique de la définition
    •   path (ou page) : définit la page à utiliser pour la structure de cette tile (layout). La ressource
        est alors chargée (ou incluse en cas de page jsp)
    •   role : définit un role pour la gestion de la sécurité (définition du type d’acc§s à la définition)
    •   template : même chose que path. Utilisé pour la compatibilité avec la librairie : Template

Une définition peut également contenir des balises put et putList qui permette de définir des attributs
(put) et des listes (putList).
4.4.3.Balise put
La balise put permet de définir un attribut (clé => valeur) dans une définition.
Voici la DTD correspondate :
<!ELEMENT put (#PCDATA)>
<!ATTLIST put id ID #IMPLIED>
<!ATTLIST put content CDATA #IMPLIED>
<!ATTLIST put direct %Boolean; #IMPLIED>
<!ATTLIST put name CDATA #REQUIRED>
<!ATTLIST put type %ContentType; #IMPLIED>
<!ATTLIST put value CDATA #IMPLIED>

    •   name : l’identifiant unique pour l’attribut (la clé)
    •   type : définit le type de la valeur (string, page, template, definition)
    •   direct : même chose que : type="string" (compatibilité avec Template)
    •   value (ou content) : la valeur associé à la clé (définit avec l’attribut name)
    •   content : même chose que value (compatiblité avec Template)

4.4.4.Balise putList
La balise putList permet de définir une liste dans une définition.
Voici la DTD correspondante :
<!ELEMENT putList ( (add* | item* | bean* | putList*)+) >
<!ATTLIST putList id ID #IMPLIED>
<!ATTLIST putList name CDATA #REQUIRED>
Le seul attribut est le nom de la liste (name). Cependant la liste peut contenir différents éléments :

    •   add : même chose qu’un élément put
    •   item : élément spécifique de la liste (cf. ci-dessous)
    •   bean : élément de type JavaBean (cf. ci-dessous)
    •   putList : autre liste

4.4.5.Balise item
La balise item est utilisée pour définir un élément dans une liste. Cet élément peut contenir différentes
propriétés (valeur, icône, lien, message d’information).
Voici la partie DTD correspondante :
<!ELEMENT item (#PCDATA)>
<!ATTLIST item id ID #IMPLIED>
<!ATTLIST item classtype %ClassName; #IMPLIED>
<!ATTLIST item icon CDATA #IMPLIED>
<!ATTLIST item link CDATA #REQUIRED>
<!ATTLIST item tooltip CDATA #IMPLIED>
<!ATTLIST item value CDATA #REQUIRED>

    •   classtype : nom complet de la classe à utiliser (par défaut :
        org.apache.struts.tiles.beans.MenuItem)
    •   icon : valeur de la propriété icon
    •   link : valeur de la propriété link
    •   tooltip : valeur de la propriété tooltip
    •   value : valeur de la propriété value

4.4.6.Balise bean
La balise bean permet de définir un élément de type : JavaBean dans une liste. Cela permet de
personnalité l’ensemble des propriétés disponibles. Chacune de ses propriétés étant initialisée via une
balise : set-property (description ci-dessous).
Voici la partie de DTD correspondante :

<!ELEMENT bean (set-property*)>
<!ATTLIST bean id ID #IMPLIED>
<!ATTLIST bean classtype %ClassName; #REQUIRED>
Vous pouvez remarquer que l’attribut principal est : classtype qui permet de définir le nom complet de
la classe à utiliser pour le JavaBean.




4.4.7.Balise set-property
La balise set-property se retrouve très souvent dans les fichiers de configuration (cf. balise
Datasource fichier de configuration Struts). Elle permet d’initialiser une propriété d’un Bean en
indiquant le nom de celle-ci et la valeur associée.
Voici la DTD correspondante :
<!ELEMENT set-property EMPTY>
<!ATTLIST set-property id ID #IMPLIED>
<!ATTLIST set-property property %PropName; #REQUIRED>
<!ATTLIST set-property value CDATA #REQUIRED>

    •   property : désigne le nom de propriété à initialiser
    •   value : désigne la valeur à associer à la propriété


Voici la majorité des balises / attributs utilisés dans le développement avec le plugin Tiles.
Vous pouvez remarquer qu’il n’y a aucun moyen de différencier vos templates (ou layout) de vos
pages réellement utilisables. En effet Tiles vous fournit le simple fait de créer des définitions et de faire
de l’héritage entre elles. C’est donc au développeur de gérer (via une norme, convention …) la
différenciation entre layout et pages.


4.5.Layouts

Les layout définissent (un peu comme dans la librairie SWING) la structure d’une page, c’est à dire les
différents éléments d’un modèle et le positionnement de ceux-là.
Vous pouvez créer des modèles pour :

    •   les pages
    •   des composants (liste, formulaire …)
    •   des vues spéciales


En théorie, vous n’utiliserez jamais directement une définition « type layout » dans une page jsp (sauf
cas spécifiques) mais vous les utiliserez comme définitions parentes.
Voici un exemple de définition qui représente un modèle de page :
<definition name="template.main" path="/tiles-layouts/mainTemplate.jsp" >
<put name="title" value="No title" />
<put name="header" value="/tiles-components/default/header.jsp" />
<put name="hmenu" value="pub.hmenu.default" />
<put name="vmenu" value="pub.vmenu.default" />
<put name="cbody" value="/tiles-components/default/cbody.jsp" />
<put name="rbody" value="/tiles-components/default/rbody.jsp" />
<put name="footer" value="/tiles-components/default/footer.jsp" />
</definition>

Cette définition déclare un « modèle » de page qui contient :

    •   un titre : title
    •   un bandeau entête : header
    •   un menu horizontal : hmenu
    •   un menu vertical : vmenu
    •   un contenu principale : cbody
    •   un contenu secondaire : rbody
    •   un pied de page : footer


Une page jsp de structure est associée à cette définition (via path="") afin qu’on puisse définir le
positionnement de chacun des éléments.
Dans cette définition les éléments sont déclarés et initialisés par des valeurs « par défaut ».
Cette définition a été écrite afin d’être utilisé par des définitions de page réelle.




4.5.1.Le fichier de structure
Nous venons de voir comment déclarer une définition, cependant dans le cas d’un modèle (layout)
nous devons utiliser une page JSP afin d’indiquer comment placer les éléments.
Dans votre page JSP, vous pouvez alors utiliser les valeurs de chacun des attributs pour les insérer
dans votre page modèle.
Votre attribut peut pointer vers différente chose :

    •      Une chaîne de caractère
    •      Une définition
    •      Une page JSP ou autre


Il vous faudra utiliser la librairie tiles (tag-lib) dans votre page modèle afin d’insérer les différents
éléments.
Voici une description de l’ensemble de cette librairie :
// TODO
// TABLEAU

Voici un exemple de page modele de base :

<%@ taglib uri="/tags/struts-tiles" prefix="tiles" %>

<html>
<title><tiles:getAsString name="title" /></title>

<body>
<table width="100%" height="100%" >

<tr height="100" align="center" bgcolor="#001188" ><td>
<tiles:insert attribute="header" />
</td></tr>

<tr bgcolor="#FFFFFF" ><td valign="top" >
<TABLE>
<tr>
<td width="250" valign="top" >
<tiles:insert attribute="menu" />
<td>
<td>
<tiles:insert attribute="body" />
</td>
</tr>
</TABLE>
</td></tr>

<tr height="100" align="center" bgcolor="#001166" ><td>
<tiles:insert attribute="footer" />
</td></tr>

</table>

</body>
</html>

Ce fichier permet de définir l’emplacement des composants. Chaque définition de page qui héritera du
layout utilisant cette page verra ses composants placés de cette manière :
4.6.Tiles

Les Tiles sont également des définitions. Cependant elles sont utilisables réellement car elle
définissent des pages « finales ». Une tile est le plus souvent une définition héritant d’une autre (de
type layout) et qui surdéfinit l’ensemble des attributs spécifique à la tile.
Par exemple, si vous utilisez le modèle décrit précédemment vous pouvez juste avoir besoin de
redéfinir le titre de la page et le contenu principale (voir également secondaire). Dans ce cas là, vous
gardez le bandeau, les menus, le footer par défauts.
Cela évite la redondance d’information et si vous souhaitez changer de bandeau, il vous suffira de
modifier la définition du modèle principal.


4.7.Exemple d’architecture de définitions

Voici un schéma récapitulatif d’une utilisation de la librairie Tiles :




Nous avons donc 3 layouts :

    •   Page : structure globale d’une page
    •   Form : modèle pour les formulaires
    •   List : modèle pour les listes


Ensuite les définitions (tiles) héritent du layout à utiliser et redéfinissent les éléments à personnaliser

5.2.Fichiers de configuration

5.2.1.Dans le fichier struts-config.xml
Ce framework de validation nécessite donc l’utilisation de deux fichiers de configuration que l’on va
« déclarer », tels des plugins, dans le fichier struts-config.xml. Pour cela on va utiliser la balise <plug-
in> et lui indiquer les chemins vers nos deux fichiers XML:
Définition de la balise <plug-in> :


             Élément racine de la définition. On a une balise plu-gin pour chaque "plugin" ajouté.
             className : attribut de la balise plug-in, il permet de définir le chemin de la classe à
plug-in
             instancier,interne ou externe à Struts. Cela signifie que l’on peut parfaitement créer son
             propre framework de validation voire un framework d’une toute autre utilité.

set-         Si la classe instanciée (className) prend des arguments en paramètre, ils seront définis
property     dans cette balise. On a donc une balise set-property pour chacun des arguments.
             property : attribut de la balise set-property, il permet de spécifier le nom de l’argument.
             values : attribut de la balise set-property, il permet de spécifier la valeur de l’argument
             défini dans property.


Nous venons donc de spécifier une nouvelle balise plug-in, tout comme on l’a fait pour les Tiles
précédemment, et deux nouveaux fichiers.
Voyons donc plus en détail ce que sont ces fichiers et à quoi ils vont nous servir.

5.2.2.Le fichier validator-rules.xml :

Fourni avec le plugin, ce fichier XML défini les règles que l’on pourra utiliser pour tester les valeurs
des champs. Il permet d’effectuer le lien entre le type de validation (champ requis, champ email …) et
le chemin vers la méthode et la classe correspondante qui permettront d’effectuer l’opération de
vérification des données (valeur du champs).
De base, Struts Validator gère les champs vides, les formats d’email ou de date invalides, les
longueurs de champs minimum ou maximum, et bien d’autres encore.
Vous trouverez la description de toutes ces validations sur le site de la fondation Apache :
http://struts.apache.org/userGuide/dev_validator.html dans la section "Standard Built In Validations".
Définition des balises du fichier validator-rules.xml :



form-
                 Élément racine du fichier.
validation

global           Tous les éléments inclus dans la balise global seront accessibles par l’ensemble des
                 formulaires (sorte de constantes)

validator        Définit chaque type de validation et le lie la méthode correspondante.
                 Attributs de la balise validator :
                 name : définit le nom logique de la validation.
                 classname : définit le chemin vers la classe contenant la méthode de validation
                 correspondante.
                 method : définit le nom de la méthode de validation correspondante.
                 methodParams : type des arguments passés en paramètre.
                 msg : définit le message d’erreur à afficher en cas de non validation du champ.
                 depends : définit si la validation est obligatoire ou non.
                 jsFunctionName : définit le nom de la fonction javascript correspondante qui
                 s’executera côté client

javascript       Définit le code de la fonction javascript qui s’exécutera côté client.


Nous allons voir une explication de l’un des "validator" fourni par défaut dans le fichier validator-rules
de base.




Il s’agit ici du validator vérifiant qu’un champ est requis, c’est-à-dire qu’il vérifie que le champ n’est pas
vide. On peut donc voir qu’il a été nommé "required", qu’il est rattaché à la méthode validateRequired
de la classe FieldChecks. En cas de « non validation » du champ mappé à ce validator, le message
d’erreur se trouvant un peu plus haut dans le fichier validator-rules.xml sera affiché :


Dans le cas présent, on aurait le message arg0 is required.

5.2.3.Le fichier validation.xml :
Une fois les différents types de validation définis, c’est dans le fichier validation.xml qui va nous servir
a mapper les champs de nos formulaires aux validations définies dans validator-rules.xml.
Définition des balises du fichier validation.xml :



form-
               Élément racine du fichier.
validation

global         Entre ces balises vont être définies des informations qui seront accessibles, visibles par
               l’ensemble des formulaires de notre fichier (constantes).

constant       A chaque constante correspond une balise constant.

constant-      Cette balise permet de définir le nom de la constante
name

constant-      Cette balise permet de définir la valeur de la constante. On va pouvoir par exemple
value          définir l’expression régulière définissant la valeur de cette constante

formset        Cette balise va englober tous les mappings de nos différents formulaires.

form           À chaque formulaire correspond une balise form.
               name : attribut de la balise form, il permet de définir le nom du formulaire concerné. Ce
               nom correspond à celui défini dans le fichier struts-config.xml (attribut name de la balise
               form-bean).

field          À chaque champ du formulaire nécessitant une validation correspond une balise field.
               property : attribut de la balise field, c’est ici que l’on va spécifier le nom de la propriété
               (champ) que l’on va lier à la ou les validations définie(s) dans validator-rules.xml. Ce
               nom de propriété est celui défini dans le Form Bean.
               depends : attribut de la balise field, il permet de spécifier les noms des validations
               associées au champ property.

                    argn : balise interne à la balise field, elle permet de spécifier l’argument à donner
                        au message d’erreur correspondant.

                    var : balise interne de la balise field, elle va permettre de préciser(si besoin est)
                        une contrainte d’intégrité pour les validations spécifiées dans l’attribut depends.

                            var-name : balise interne de la balise var, on va spécifier le nom de la
                                validation dont on veut donner la contrainte d’intégrité.

                            var-value : balise interne de la balise var, on va spécifier la contrainte
                                d’intégrité (exemple : expression régulière,...).


Jusqu’à maintenant nous avons un fichier validator-rules.xml qui définit une liste de validations dont
les classes (et donc méthodes) se trouvent dans les fichiers commons-validator.jar et jakarta-oro.jar.
On a par ailleurs un formulaire d’inscription(InscripForm) avec les champs nom(obligatoire),
password(obligatoire, taille minimum de 4 caractères) et email(obligatoire, format d’email).
Nous souhaitons donc vérifier la validité de ces champs. Ceci va être fait dans le fichier validation.xml.
C’est en effet ici que l’on va lier chacun de nos champs aux types de validations déclarées dans
validator-rules.xml.
D’après les règles de la validation définies ci-dessus pour le formulaire InscripForm, on peut voir que
l’on va utiliser les validators suivants (présents dans le fichier validator-rules.xml de base) : required,
email et minlength.


Voici donc comment se présentera notre fichier validation.xml pour la partie concernant InscripForm :




Voyons l’explication du fichier ligne par ligne :

    Toutes nos définitions sont englobées dans la balise racine form-validation.

Dans un premier temps, nous définissons une constante minPass. Elle définit la longueur minimum
du mot de passe. On l’englobe dans une balise <global>, ce qui signifie qu’elle sera visible par toutes
les définitions de formulaire qui vont suivre.
Nous avons besoin du champ password dans le formulaire InscripForm, et étant susceptible d’être
réutilisé dans un autre formulaire, on a choisi de déclarer cette constante minPass avec une portée
globale. Sachant que le champ password devra avoir une longueur supérieure à 4, la valeur de
minPass est initialisée à 4.




    •   La balise <formset> contient l’ensemble des validations des différents formulaires. Cela
        signifie que l’on aura une balise <form> pour chaque définition de formulaire. Dans notre cas,
        nous n’avons que InscripForm, nous avons donc une seule balise <form>.

Nous ouvrons donc la balise <form> et indiquons dans l’attribut name, l’identifiant de notre formulaire
indiqué dans le fichier struts-config.xml, "inscripForm".
Le premier champ dont nous devons vérifier la validité est le champ nom, il est en effet obligatoire.
L’objet que nous allons créer ici est un objet client. Dans la balise <field> est donc indiqué le nom de
la propriété concernée, c’est-à-dire nom. Étant un champ obligatoire, on ajoute le validator
required(cf :valitor-rules.xml) pour l’attribut depends.
On vient donc de lier le champ nom du formulaire au validator required.
En cas d’erreur de validation, le message arg0 is required sera affiché (chapitre sur validator-rules.xml
). Afin de spécifier justement le nom qui sera affiché pour arg0, on utilise la balise <argn> avec 0 pour
la valeur de n. La valeur de arg0 est défini dans l’attribut key, il s’agit de la valeur de
client.form.require.nom du fichier de properties.
On vient maintenant de définir plus précisément le message d’erreur.




    Nous allons agir de la même manière que pour le champ nom avec le champ password. Ce
       champ est obligatoire et doit être d’une longueur minimum de 4 caractères. L’attribut depends
       a donc pour valeurs required et minlength.

Afin de spécifier la valeur de minlength, on utilise la balise <var>. <var-name> nous permet de
spécifier le nom du validator concerné et <var-value>, la valeur, justement que l’on va affecter à ce
validator.

Précédemment, nous avons spécifier que la constante minPass a été initialisée en début de fichier,
avec une portée globale. Nous réutilisons donc cette constante afin de l’affecter à minlength.




    •   Les validators pour les champs nom et password viennent donc d’être définis. Il ne reste que
        le champ email dont la démarche est la même. Simplement les validators concernés sont
        required et email (toujours du fichier validator-rules.xml).




Nous venons donc de :

    •   initialiser le plug-in Validator dans le fichier struts-config.xml et fait les liens avec les fichiers
        validation.xml et validator-rules.xml.
    •   définir les différentes validations dans le fichier validator-rules.xml en faisant les liens avec les
        classes concernées.
    •   définir les validations à faire sur les champs de notre formulaire en faisant les liens entre ces
        champs et les validators (définis dans validator-rules.xml), dans le fichier validation.xml.

Cependant, nous n’avons pas parlé des Form beans ; en effet, la classe associée à notre formulaire
InscripForm doit étendre la classe DynaActionForm, classe fille de la classe ValidatorForm.



5.3.DynaForms

On a vu jusque maintenant comment lier les champs de nos formulaires, mais à aucun moment la
définition de la classe Form Bean correspondante.
En effet, si l’on utilise Struts, il faut nécessairement lier chaque formulaire à un bean ; c’est-à-dire à
une classe Java respectant certaines règles :

    un attribut pour chaque propriété du formulaire avec le même nom

    un getter et un setter pour chaque attribut du bean




Par ailleurs cette classe doit étendre ActionForm. La classe ActionForm possède en effet une
méthode validate() qui va justement permettre de définir le code validant ou non les champs du
formulaire.
Si l’on suit cette indication, notre classe InscripForm est définie comme suit :
Cette classe contient des attributs correspondants aux propriétés de notre formulaire InscripForm ainsi
que des getters et des setters pour chacun d’eux. Elle surdéfinit par ailleurs la méthode validate() de
la classe ActionForm afin de faire les validations des différents champs du formulaire.
Rappelons que client.form.require.nom fait référence à la clé se trouvant dans le fichier de
properties(cf.3.6.1) qu’on aura défini en amont. Le lien avec ce fichier se fait dans le fichier de
configuration struts-config.xml à l’aide de la balise <message-resources> (cf.3.8.1).
Après avoir créé notre Form Bean, revoyons le fichier struts-config.xml :



Le fait est que, si nous avons choisi d’utiliser le framework Validator, c’est justement dans le but
d’éviter d’écrire les codes de validations pour chaque formulaire puisqu’ils risquent d’être redondants.
C’est pourquoi un ensemble de validations standards sont regroupés dans le fichier struts.jar. De plus,
c’est pour cela que nous avons regroupé toutes les définitions des validations de champs dans le
fichier validation.xml.
Ce que l’on veut, c’est se servir de ces définitions et les mapper à notre Form Bean.
Nous allons donc étendre la classe ValidatorForm, sous-classe de la classe ActionForm. Cette
classe est incluse dans le framework Validator (on pourra le trouver dans le fichier struts.jar, dans le
package org.apache.struts.validator).Voyons la nouvelle classe InscripForm :
Sa définition dans le fichier struts-config.xml ne change pas :



Simplement, les validations des champs vont se faire par rapport au fichier validation.xml. Le nom
défini dans l’attribut name est l’identifiant qui permettra, de lier les champs du formulaire InscripForm
aux validators requis.
Nous avons déjà gagné un temps non négligeable, puisque pour chaque formulaire, il nous suffit de
créer un Form Bean contenant des attributs ainsi que leurs accesseurs. Mais nous allons encore
simplifier la chose. En effet, chaque fois redéfinir toutes ces méthodes pour chaque champ est assez
fastidieux.
Pour cela, nous n’allons plus créer de Form Bean pour chaque formulaire mais utiliser les DynaForms,
c’est-à-dire définir nos balises <form bean>de la manière suivante :




Avec cette nouvelle définition, Struts va s’occuper lui-même de créer le Form Bean. Nous ne devons
que spécifier les propriétés du formulaire ainsi que leurs types.
On a donc deux propriétés à valider pour ce formulaire inscripForm, de types String et initialisées à
vide



5.4.Résumé de la configuration des Validators

Résumons les différentes étapes à réaliser pour gérer la validation d’un formulaire :

    Instancier la classe ValidatorPlugin à l’aide de la balise <plug-in> afin de donner l’accès aux
        fonctionnalités du framework Validator à Struts.

    Configurer les deux fichiers de configuration validator-rules.xml(dont la configuration par défaut et
       amplement suffisante) et validation.xml.

    Créer un Form Bean qui étend la classe ActionForm.
Il est donc assez rapide de gérer la validation des formulaires à l’aide du framework Validator.



6.Intégration avec XDoclet

xDoclet est un outil très intéressant pour la génération automatique de code ou descripteurs de
déploiement ou autres fichiers utiles.
Nous allons étudier, dans cette partie, l’intégration de XDoclet pour la génération des fichiers de
configuration struts (struts-config.xml, validation.xml …).
Afin de bien comprendre le principe XDoclet, il faut savoir l’utiliser et connaître Ant (cf. Essentiel Ant).

6.1.Struts-config.xml

La génération du fichier struts-config.xml est automatisable avec XDoclet.
Grâce à XDoclet vous allez pouvoir définir directement dans votre code java :

    •   Les action-mapping
    •   Les action-exception
    •   Les action-forward
    •   Les action-form
    •   Les action-dynaform


Ces définitions vont se faire à partir des tags XDoclet « @Struts » que vous pouvez retrouver à
l’adresse : http://xdoclet.sourceforge.net/xdoclet/tags/apache-tags.html
Nous allons détailler les plus importants dans cette section.

6.1.1.Tags @Struts

Voici une description non exhaustive des tags les plus utilisés avec Struts :

    •   @struts.action : définit un mapping pour une classe d’action et les différents attributs liés
             o name : nom de l’action (doit être unique dans l’ensemble de l’application struts)
             o type : type de la classe à utiliser (par défaut, la classe en cours). Il est possible de
                 surcharger cette valeur dans certains cas (par exemple avec l’utilisation de framework
                 externe tel que Spring)
             o path : chemin de l’action (url)
             o scope : domaine dans lequel l’action va exister (request, session, application ; request
                 par défaut)
             o input : chemin d’input pour l’action
             o roles : rôle(s) autorisé(s) à accéder à cette action
             o validate : indique si struts doit valider les entrés du formulaire (true | false, true par
                 défaut)
             o parameter : paramètre d’option de l’action (il faut mettre « action »).
    •   struts.action-exception : définit un handler sur un type d’exception (liée à l’action)
             o key : clé de la propriété à utiliser pour retrouver le texte de l’erreur
             o type : nom absolu de la classe d’exception qui sera intercepté
             o handler : nom absolu de la classe à utiliser comme Handler de l’exception
             o path : chemin vers la ressource qui permettra de terminer la requête
             o scope : domaine dans lequel l’objet ActionError sera utilisé.
    •   struts.action-forward : définit un « forward » local pour l’action
             o name : nom du forward qui sera utilisé dans l’action
             o path : chemin vers la ressource à utiliser
             o redirect : indique s’il faut effectuer une redirection ou non (true | false, false par
                 défaut)
    •   struts-form : définit un « form bean » et ses attributs
             o name : nom du « form bean » qui permettra de le référencer

On retrouve également les tags liés au plugin Validator :

    struts.validator : définit la validation à effectuer sur un champ du formulaire

             type : définit le type de validation à effectuer sur le champ (« required » par exemple)
    struts.validator-args : définit un argument à appliquer à la validation

             arg0value

             arg1value

             arg2value

             arg3value

    struts.validator-var : déclare une variable pour le validator

             name : nom de la variable

             value : valeur de la variable


D’autres attributs peuvent être utilisés dans certains cas. Nous ne les avons pas détaillé afin de
simplifier cette présentation.

6.1.2.Exemples
Voici quelques exemples d’utilisation de ces tags dans le code java.


Exemple de class Action :

/**
*
* @struts.action path="/admin/comment/add/display" name="commentForm"
* scope="request" validate="false" parameter="action"
*
* @struts.action-forward name="success" path="admin.comment.add.display"
*/
public class DisplayCommentFormAction extends Action {


protected ActionForward delegateExecute(ActionMapping mappings,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws ApplicationException {
....

return mappings.findForward("success");
}
}

Utilisation avec les ActionForm :

/**
* LoginForm class.
*
* @struts.form name="loginForm"
*/
public class LoginForm extends BaseForm {

// Membres
private String loginTxt;

private String password;


/**
* Default constructor.
*
*/
public LoginForm() {
}
/**
* @return Returns the login.
* @struts.validator type="required"
* @struts.validator-args arg0value="key.form.login.required.login"
*/
public String getLoginTxt() {
return loginTxt;
}

/**
* @param login
* The login to set.
*/
public void setLoginTxt(String login) {
this.loginTxt = login;
}

/**
* @return Returns the password.
* @struts.validator type="required"
* @struts.validator-args arg0value="key.form.login.required.password"
*/
public String getPassword() {
return password;
}

/**
* @param password
* The password to set.
*/
public void setPassword(String password) {
this.password = password;
}

}

6.1.3.Intégration avec Ant
XDoclet s’intègre avec Ant afin de pouvoir automatiser son exécution au sein d’un script.

<target name="app-webdoclet" depends="app-compile">

<taskdef name="webdoclet" classname="xdoclet.modules.web.WebDocletTask">
<classpath>
<path refid="xdoclet.classpath" />
<path refid="web.compile.classpath" />
</classpath>
</taskdef>

<!-- xDoclet pour le web (struts et web.xml) -->
<webdoclet force="${xdoclet.force}"
destdir="${web.gen.target.dir}"
mergedir="${web.merge.dir}"
excludedtags="@version,@author"
verbose="true">

<fileset dir="${struts.source.dir}">
<include name="**/*.java" />
</fileset>

<!-- struts-config.xml -->
<strutsconfigxml
validateXML="true"
version="1.2">
</strutsconfigxml>

</webdoclet>
</target>
Il faut :

     •      Déclarer la tâche « webdoclet » via « taskdef », en lui spécifiant la classe :
            xdoclet.modules.web.WebDocletTask à utiliser. Vous devez donc spécifier un classpath
            valide contenant la classe précédente (incluse dans les jars XDoclet).
     •      Appeler la tâche webdoclet en lui spécifiant le répertoire de destination (destdir), les fichiers à
            parser (via un fileset), et la sous-tâche « strutsconfigxml » qui permet de générer le fichier
            struts-config.xml.




Vous pouvez également utiliser les sous tâches suivantes :

     •      strutsdynaformvalidationxml : pour la génération des paramétrages de vos « dynaform ».
     •      strutsvalidationxml : pour la génération du fichier validation.xml


Vous pouvez avoir plus de détails sur l’utilisation de chacune de ces tâches à l’adresse suivante :
http://xdoclet.sourceforge.net/xdoclet/ant/xdoclet/modules/web/WebDocletTask.html.




7.L’avenir de Struts

7.1.Intégration avec JSTL

En adoptant JSP 1.2, les différents serveurs d’applications peuvent exploiter toute l’efficacité de JSTL
(Java Standard Tag Library). Le principal effet sur Struts sera de fournir une solution de rechange à la
bibliothèque de balises de Struts beaucoup plus puissante et capable d’interopérabilité avec les
plates-formes non Struts.
En conséquence, on peut s’attendre progressivement à une dévalorisation des balises Struts.



7.2.Intégration avec JSF

L’auteur de Struts : Craig McClanahan est fortement impliqué dans les spécifications des JSF (Java
Server Faces). Struts et JSF seront donc probablement bientôt étroitement liés.
Une fois de plus l’intérêt pour les balises Struts s’estompera pour celle de JSF (toujours dans le but
d’avoir un standard).


7.3.Adoption généralisée de Struts

Struts est devenu rapidement très populaire dans la communauté de développement Java J2EE. IBM,
Oracle sont de bons exemples de leader ayant choisit Struts dans leur outil de développement
d’application Web.

Fin du document
Hibernate - Persistance objet - relationnel


1.Présentation

Travailler dans les deux univers que sont l'orienté objet et la base de données relationnelle peut être
lourd et consommateur en temps. Hibernate se propose de joindre ces deux univers, à travers le
mapping objet/relationnel. Le terme mapping objet/relationnel (ORM) décrit la technique consistant
à faire le lien entre la représentation objet des données et sa représentation relationnelle, basé sur un
schéma SQL.

Hibernate s'occupe du transfert des classes Java dans les tables de la base de données (et des
types de données Java dans les types de données SQL). Il permet également de requêter les
données et propose des moyens de les récupérer.

On peut voir Hibernate comme une fine surcouche de JDBC qui lui ajouterait une dimension objet.




Schéma simplifié du fonctionnement d'Hibernate


Dans ce cours, nous allons considérer que le cours JDBC est acquis. Veuillez vous référer à ce cours
si vous ne disposez pas de toutes les connaissances nécessaires en JDBC tel que les
PreparedStatement.
Nous allons voir, dans un premier temps, l'architecture d'Hibernate, à travers ses fichiers de
configuration, et son framework. Dans une deuxième partie, nous mettrons en pratique ce que nous
avons appris à travers Eclipse et le plugin Hibernate Synchronizer1. Nous générerons les objets et la
structure de la base de données automatiquement, grâce aux fichiers de mapping.


2.Avantages et inconvénients

2.1.Avantages

Gain de temps si on utilise des outils pour générer automatiquement la base de données et le code.
Ce n'est pas la seule possibilité !

    •   Java vers mapping : XDoclet
    •   Mapping vers java : xmlSchema (inclus dans Hibernate, utilisé par le plugin Hibernate-
        Synchronizer)
    •   Mapping vers ddl : hbm2ddl (inclus dans Hibernate, utilisé par le plugin Hibernate-
        Synchronizer)
    •   DDL vers mapping : middlegen

Les objets métiers sont plus faciles à manipuler.
Peu de dépendance envers une base de données précise. Théoriquement, il n'y a que le fichier de
configuration à changer si on passe d'une base de données comme Oracle vers PostgreSQL.


2.2.Inconvénients

Nécessite d'apprendre à l'utiliser.
3.Installation d'Hibernate

Téléchargez Hibernate sur son site web http://www.hibernate.org/, décompactez le fichier sur votre
disque. Nous utiliserons ici la version 2.1.8 d'Hibernate.
Hibernate est une librairie. Elle doit donc se situer dans votre variable d'environnement CLASSPATH,
ainsi que son répertoire lib.
Les derniers exemples utiliseront Eclipse (http://download.eclipse.org/).

Le plugin Hibernate Synchroniser sera également utilisé.
Nous verrons au moment venu la façon de l'installer.
Une base de données est nécessaire. Nous utiliserons ici PostgreSQL.
Nous aurons également besoin du driver JDBC de votre base de données. Voici quelques liens :

4.Lier Hibernate avec une base de données

Nous verrons ici deux façons de configurer l'accès à la base de données.
Ces fichiers définissent les propriétés de la connexion. Voici les propriétés revenant régulièrement :

hibernate.connection.driver_class Classe du driver JDBC

hibernate.connection.url           URL JDBC

hibernate.connection.username      utilisateur de la base de données

hibernate.connection.password      mot de passe de la base de données

hibernate.dialect                  Le nom de la classe du dialect Hibernate - active
                                   l'utilisation de certaines fonctionnalités spécifiques à la plateforme.
                                   Quelques exemples parmi les SGBDR les plus utilisés :
                                   net.sf.hibernate.dialect.PostgreSQLDialect (PostgreSQL)
                                   net.sf.hibernate.dialect.MySQLDialect (MySQL)
                                   net.sf.hibernate.dialect.OracleDialect (Oracle)
                                   net.sf.hibernate.dialect.SQLServerDialect(SQL Server)
                                   ...

hibernate.default_schema           Positionne dans le SQL généré un schéma/tablespace
                                   par défaut pour les noms de table ne l'ayant pas
                                   surchargé (optionnel).

hibernate.show_sql                 Mis sur " true ", ce paramètre permet d'afficher dans la console les
                                   requêtes sql générées par l'architecture Hibernate. (optionnel)

hibernate.connection.datasource    Nom JNDI de la source de données. Utilisé notamment quand le
                                   serveur d'application héberge un pool de connexion JDBC.

hibernate.query.substitutions      Vous pouvez définir de nouveaux tokens dans les requêtes
                                   Hibernate en utilisant la propriété.
                                   exemple :

                                   hibernate.query.substitutions vrai=1, faux=0
                                   remplacerait les tokens vrai et faux par des entiers dans le SQL
                                   généré.
                                   hibernate.query.substitutions toLowercase=LOWER
                                   permettrait de renommer la fonction SQL LOWER en toLowercase



4.1.Le fichier hibernate.properties

Il s'agit d'un fichier texte placé dans le CLASSPATH de votre projet (WEB-INF/classes pour un projet
web). Il est au format "clé=valeur".
Voici un exemple, adapté à une base de données PostgreSQL :
hibernate.dialect net.sf.hibernate.dialect.PostgreSQLDialect
hibernate.connection.driver_class org.postgresql.Driver
hibernate.connection.url jdbc:postgresql://192.168.0.1/quickstart
hibernate.connection.username monLogingin
hibernate.connection.password monMotDePasse
hibernate.query.substitutions yes 'Y', no 'N'
4.2.Le fichier hibernate.cfg.xml

Ce fichier a exactement la même utilité que hibernate.properties.
Sa syntaxe est bien sûr en XML. Ses valeurs surchargent celles du fichier hibernate.properties si les
deux fichiers sont présents.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- local connection properties -->
<property name="hibernate.connection.url">
jdbc:postgresql://localhost/quickstart
</property>
<property name="hibernate.connection.driver_class">
org.postgresql.Driver
</property>
<property name="hibernate.connection.username">monLogin</property>
<property name="hibernate.connection.password">monMotDePasse</property>
<!-- property name="hibernate.connection.pool_size"></property -->
<!-- dialect for PostgreSQL -->
<property name="dialect">
net.sf.hibernate.dialect.PostgreSQLDialect
</property>
<property name="hibernate.show_sql">false</property>
<property name="hibernate.transaction.factory_class">
net.sf.hibernate.transaction.JDBCTransactionFactory
</property>
<mapping resource="com/supinfo/labosun/Contact.hbm" />
</session-factory>
</hibernate-configuration>

Les éléments property permettent donc de renseigne une paire " clé-valeur ".
Dans la deuxième partie du fichier, l’élement mapping permet de renseigner les fichiers de mapping
entre la base de donnée et les objets persistants. Nous les verrons plus tard.

5.Ecrire un fichier de mapping

Le fichier de mapping permet de lier la base de données avec les objets persistants.
Il est écrit en XML. Voici ses éléments :

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>
<class name="net.sf.hibernate.examples.quickstart.Cat" table="CAT">
<id name="id" type="string" unsaved-value="null" >
<column name="id_cat" sql-type="char(32)" not-null="true"/>
<generator > </id>
<property name="name">
<column name="name_cat" length="16" not-null="true"/>
</property>
<property name="birthdate" type="date">
<column name=birthdate_cat" />
</property>
</class>
</hibernate-mapping>


On peut voir que les éléments de l'objet Cat que nous avons créé juste avant sont liés aux colonnes
de la base de données.
Nous allons expliquer le rôle de chacun des éléments que nous venons de rencontrer.
5.1.L’élément class

Vous pouvez déclarer une classe persistante en utilisant l'élément class. "name" déclare la classe
persistante et "table" déclare la table à mapper.


5.2.L’élément id

Les classes mappées doivent déclarer la colonne clé primaire de la table.
L'élément <id> définit le mapping entre cette propriété et cette colonne clé primaire. "name" définit la
variable servant d'identifiant dans l'objet persistant (optionnel) et "type" définit le type de la variable
utilisée (optionnel).
Type peut être :
1. Le nom d'un type Hibernate basique (ex. integer, string, character, date, timestamp, float, binary,
serializable, object, blob).
2. Le nom d'une classe Java avec un type basique par défaut (eg. int, float, char, java.lang.String,
java.util.Date, java.lang.Integer, java.sql.Clob).
3. Le nom d'une classe Java serialisable.
4. Le nom d'une classe Java implémentant un type personnalisé.


5.3.L’élément column

L'élément fils optionnel "column" définit le champ utilisé dans la base de donnée. Il peut être associé
aux balises id et property.
L'attribut " sql-type " est utilisé pour définir le type du champ dans la base de donnée (optionnel,
Hibernate fait la conversion automatiquement).


5.4.L’élément generator

L'élément fils de l'id définit la classe Java utilisée pour générer l'identifiant unique des instances d'une
classe persistante.
Class permet de définir la façon de créer cet identifiant. Voici les plus généralement utilisés :

    •   incrementGénère des identifiants du type long, short ou int qui sont uniques seulement
        lorsqu'aucun autre process n'insère de données dans la même table.
    •   sequenceUtilise une séquence dans DB2, PostgreSQL, Oracle, SAP DB, McKoi ou un
        générateur dans Interbase. L'identifiant retourné est de type long, short ou int.
    •   uuid.hexUtilise l'algorithme à 128-bit UUID pour générer les identifiants de type string,
        uniques sur un réseau donné (l'adresse IP est utilisée). L'UUID est encodée comme une
        chaîne de 32 chiffres hexadécimaux.
    •   nativeUtilise la méthode la plus adaptée à la base de données précisée en tant que dialect.

5.5.L’élément property

L'élément property déclare une propriété persistance de la classe, respectant la convention
JavaBean.
L'élément optionnel name définit la variable servant d'identifiant dans l'objet persistant
L'élément optionnel type définit le type de la variable utilisée.
Il existe de nombreuses autres balises, notamment pour établir les relations entre les tables. Nous en
verrons certaines plus tard, lors de l'utilisation du plugin Eclipse.
5.6. Ecrire une classe persistante

Les objets persistants sont tout simplement des objets respectant les caractéristiques des JavaBeans
: l'utilisation des getter/setter.

Voici un exemple :
package eg;
import java.util.Set;
import java.util.Date;
public class Cat {

private Long id;
private String name;
private Date birthdate;

private void setId(Long id) {
this.id=id;
}
public Long getId() {
return id;
}
void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
void setBirthdate(Date date) {
birthdate = date;
}
public Date getBirthdate() {
return birthdate;
}

public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof Cat))
return false;
final Cat cat = (Cat) other;
if (!getName().equals(cat.getName()))
return false;
if (!getBirthday().equals(cat.getBirthday()))
return false;
return true;
}

public int hashCode() {
int result;
result = getName().hashCode();
result = 29 * result + getBirthday().hashCode();
return result;
}
}
Remarques:
la visibilité des méthodes est indifférente, donc elles peuvent être private, default, public, ...
il faut laisser un constructeur par défaut afin qu'Hibernate puisse l'instancier.
implémenter l'identifiant utilisé dans la base de donnée est optionnel mais conseillé pour permettre
des fonctionnalités du cycle de vie (que nous verrons plus tard). Il est également conseillé d'utiliser un
type non primitif.
Certaines fonctionnalités internes d'Hibernate nécessitent que la classe ne soit pas finale.
Il est conseillé de surdéfinir les méthodes hashCode() et equals(). Ce n'est pas toujours utile, mais
c'est une bonne habitude à prendre.

6.Manipuler des objets persistants
L'architecture Hibernate possède différentes classes :

6.1.Configuration (net.sf.hibernate.Configuration)

Représente un ensemble de mappings des classes Java d'une application vers la base de données
SQL.
Configuration cfg = new Configuration();
Si vous avez écrit un fichier de configuration de type « hibernate.properties », il faudra ajouter les
fichiers de mapping (il n'est pas possible de les ajouter à partir de ce fichier) :
cfg.addFile("Cat.cfg.xml");

Si vous avez utilisé la configuration de type " hibernate.cfg.xml ", il faudra ajouter le fichier :
File configFile = new File("hibernate.cfg.xml");
cfg.configure(configFile);

On voit de cette façon que l'on peut nommer ce dernier fichier de configuration comme on le souhaite.


6.2.SessionFactory (net.sf.hibernate.SessionFactory)

Nous sommes maintenant prêts à utiliser la Session Hibernate. C'est l'interface du gestionnaire de
persistance, on l'utilise pour sauver et récupérer les objets respectivement dans et à partir de la base
de données. Mais d'abord, nous devons récupérer une Session (l'unité de travail Hibernate) à partir
de la SessionFactory :

SessionFactory sessionFactory = cfg.buildSessionFactory();

En général, une SessionFactory n'est construite qu'une seule fois, c'est-à-dire au démarrage (avec
une servlet de type load-on-startup). Cela veut donc dire que l'on ne doit pas la garder dans une
variable d'instance des servlets. Il faut un support de type Singleton pour pouvoir y accéder
facilement. L'approche montrée ci-dessous résout les deux problèmes : celui de configuration et celui
de la facilité d'accès à SessionFactory.

Nous implémentons HibernateUtil, une classe utilitaire :

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
public class HibernateUtil {
private static final SessionFactory sessionFactory;

static {
try {
// Crée la SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (HibernateException ex) {
throw new RuntimeException("Problème de config:"+ex.getMessage(), ex); } }

public static final ThreadLocal session = new ThreadLocal();

public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// Ouvre une nouvelle Session, si ce Thread n'en a aucune
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}

public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
}



L'utilisation de ThreadLocale permet de rattacher chaque thread à un objet Session.

Les Sessions sont ouvertes par la SessionFactory et sont fermées quand le travail est terminé :
Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
session.save(princess);
tx.commit();
HibernateUtil.closeSession();

6.3.L'accès aux méta-données

Hibernate expose les métadonnées au travers des interfaces ClassMetadata et CollectionMetadata
et la hiérarchie de Type. Les instances des interfaces de métadonnées peuvent être obtenues depuis
la SessionFactory.

Cat fritz = new Cat();
ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
Long id = (Long) catMeta.getIdentifier(fritz);
Object[] propertyValues = catMeta.getPropertyValues(fritz);
String[] propertyNames = catMeta.getPropertyNames();
Type[] propertyTypes = catMeta.getPropertyTypes();

// retourne une Map de toutes les propriétés qui ne sont pas des collections ou des associations

Map namedValues = new HashMap();
for ( int i=0; i<propertyNames.length; i++ ) {
if (!propertyTypes[i].isEntityType()
&& !propertyTypes[i].isCollectionType()) {
namedValues.put( propertyNames[i], propertyValues[i] );
}
}

6.4.Transaction (net.sf.hibernate.Transaction)

Dans une Session, chaque opération sur la base de données se fait dans une transaction qui isole les
opérations de la base de données (c'est également le cas pour les lectures seules). Nous utilisons
l'API Transaction pour s'abstraire de la stratégie transactionnelle utilisée (dans notre cas, les
transactions JDBC).

On obtient un objet Transaction en appelant la méthode de votre objet session nommée
beginTransaction().

On pourra manipuler la transaction avec les méthodes commit(), pour valider la transaction.

Session session = HibernateUtil.currentSession();
Transaction tx= session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
session.save(princess);
tx.commit();
HibernateUtil.closeSession();

Si vous décidez de ne pas committer vos modifications :
tx.rollback();


6.5.Session (net.sf.hibernate.Session)

C’est un objet à durée de vie courte qui représente une conversation entre l'application et l'entrepôt de
persistance. Il encapsule une connexion JDBC.

Session session = HibernateUtil.currentSession();
Transaction tx= session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
session.save(princess);
tx.commit();
HibernateUtil.closeSession();

save() avec un seul argument, génère et assigne un identifiant unique.

Il est possible de recharger un objet et toutes ses collections à n'importe quel moment en utilisant la
méthode refresh().
Vous devez invoquer Session.flush() pour vous assurer que les changements sont synchronisés
avec la base de données. Cependant, si vous utilisez une Transaction, l'appel de la méthode
commit() l'appellera automatiquement.
La méthode load() de Session vous permet de récupérer une instance persistante si vous connaissez
son identifiant.

Cat cat = (Cat) sess.get(Cat.class, id);
if (cat == null) {
cat = new Cat();
sess.save(cat, id);
}
return cat;

Si vous ne connaissez pas les identifiants des objets que vous recherchez, utilisez la méthode
iterate() offerte par la Session, retournant un objet java.util.Iterator.

// itération sur les ids
Iterator iter = sess.iterate("select mate from Cat as cat join cat.mate as mate where cat.name = ?",
name, Hibernate.STRING);

while (iter.hasNext()) {
Qux qux = (Qux) iter.next(); // récupération de l'objet
// ...
}

Le second argument de iterate() est un objet ou un tableau d'objets.
Le troisième argument est un type Hibernate ou un tableau de types Hibernate.

Ces types passés en argument sont utilisés pour lier les objets passés en argument au ? de la requête
(ce qui correspond aux paramètres d'un PreparedStatement JDBC).

Si vous avez besoin de définir des limites sur le résultat d'une requête (nombre maximum
d'enregistrements et / ou l'indice du premier résultat que vous souhaitez récupérer), utilisez une
instance de net.sf.hibernate.Query :
//paramètre nommé (préféré)
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
q.setString("name", "Fritz", Hibernate.STRING);
q.setFirstResult(20);
q.setMaxResults(10);
Iterator cats = q.iterate();

Ces deux derniers exemples ont introduit la notion de HQL. Il s'agit du langage d'interrogation utilisé
par Hibernate. Il est très proche de SQL. Nous en reparlerons d'ici peu.


6.6.Mettre à jour un objet

La méthode saveOrUpdate()de l'objet session vous permettra d'ajouter ou modifier les données
automatiquement, en fonction du contexte dans lequel il s'applique.

// dans la première session
Cat cat = (Cat) firstSession.load(Cat.class, catID);

// dans une couche supérieure de l'application
Cat mate = new Cat();
cat.setName("cesar");

// plus tard, dans une nouvelle session
secondSession.saveOrUpdate(cat); // mise à jour de l'état existant (cat a un id non null)
secondSession.saveOrUpdate(mate); // sauvegarde d'une nouvelle instance (mate a un id null)

Il est nécessaire de l'utiliser quand l'objet est sauvegardé dans un objet session différent de celui qui
l'a créé (très souvent le cas lorsque l'objet passe par une interface utilisateur).

Si l'objet persistant reste dans la juridiction de l'objet session qui l'a créé, un simple flush() ou un
commit() sur la transaction est suffisant.
6.7.Supprimer l'objet de la source de données

Session.delete() supprimera l'état d'un objet de la base de données. Evidemment, votre application
peut toujours contenir une référence à cet objet.
La meilleure façon de l'apréhender est donc de se dire que delete() transforme une instance
persistante en instance transiante (non persistante).
session.delete(cat);

6.8.Les exceptions

Si un problème intervient lors de la communication avec la source de donnée (SQLException inclus),
il faut appeler un rollback() de la session, fermer la session et ne plus l'utiliser.
Session sess = HibernateUtil.currentSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// traitement...
tx.commit();
} catch (Exception e) {
if (tx != null) tx.rollback();
throw e;
} finally {
sess.closeSession();
}

7.HQL : les requêtes Hibernate

Hibernate fournit un langage d'interrogation extrêmement puissant qui ressemble au SQL. HQL
(Hibernate Query Language) est totalement orienté objet, comprenant des notions d'héritage, de
polymorphisme et d'association.
Ce langage est insensible à la casse, sauf au niveau des noms des objets Java.
Les éléments que l'ont spécifie font parti de l'objet persistant, et non de la base de donnée. A aucun
moment, on ne spécifie d'élément faisant partie de la base de donnée (c'est le travail des fichiers
de mapping).
Pour bien comprendre Hibernate, il faut raisonner en Objet et pas en terme de SGBD relationnel.

7.1.La clause from

La requête Hibernate la plus simple est de la forme :
from eg.Cat

Elle retourne simplement toutes les instances de la classe eg.Cat.

La plupart du temps, vous devrez assigner un alias puisque vous voudrez faire référence à Cat dans
d'autres parties de la requête.
from eg.Cat as cat


7.2.La clause where

La clause where vous permet de réduire la liste des instances retournées.

from eg.Cat as cat where cat.name='Fritz'

Cette requête retourne les instances de Cat dont name est égale à 'Fritz'.

select foo
from eg.Foo foo, eg.Bar bar
where foo.startDate = bar.date

Cela permet de retourner les instances de Foo pour lesquelles il existe une instance de bar avec la
propriété date égale à la propriété startDate de Foo. Les expressions utilisant la navigation rendent la
clause where extrêmement puissante. Soit :

from eg.Cat cat where cat.mate.name is not null


7.3.La clause select

La clause select sélectionne les objets et propriétés qui doivent être retournés dans le résultat de la
requête.
Soit :

select mate
from eg.Cat as cat

8.XDoclet

8.1.Présentation d’XDoclet

Comme vous avez pu le constater, il y a de nombreuses étapes à respecter lorsque l’on veut utiliser
l’API Hibernate dans une application.
De plus certaines étapes sont redondantes, telle que la génération du fichier de mapping qui reprend
en grande partie les informations présentes dans les classes persistantes.
De plus, la moindre modification au niveau de la base de donnée tel que l’ajout d’une colonne, va
nécessiter en plus de la modification au niveau de la table, de répercuter les modifications d’une part
sur le fichier de mapping mais aussi sur les objets persistants.
XDoclet va nous permettre de gagner du temps en simplifiant cette étape. Sa tache principale consiste
à générer automatiquement le fichier de mapping. Il va aussi nous permettre de créer/modifier la table.
Les seuls fichiers que nous allons avoir à écrire seront les classes persistantes, nos fameux
Javabeans qui contiennent les informations de la base de donnée sous forme objet.

8.2.Ant

Le programme Ant a pour mission de compiler et de regrouper automatiquement divers composants
d'un projet Java en tenant compte des dépendances entre ces derniers. Il remplit à ce titre le même
rôle que la commande make, bien connue des programmeurs C et C++.
Nous n’allons pas voir dans ce cours la syntaxe exacte d’un script Ant puisque ce n’est pas l’objet de
ce cours. Tout ce qu’il faut retenir c’est que Ant va nous permettre d’exécuter un script afin
d’automatiser l’appel à certaines fonctions pour générer nos fichiers de mapping et nos tables dans la
base de donnée.


8.3.Installation de l’API XDoclet

XDoclet est une API, c'est-à-dire un ensemble de classes fournies dans des fichiers JAR.
Ces classes seront appelées à partir d’un script Ant.
Ant étant géré par Eclipse sans l’ajout de modules supplémentaires, nous n’allons nous intéresser
qu’à l’installation de l’API XDoclet.
Tout d’abord, il faut télécharger l’API sur le site officiel : http://xdoclet.sourceforge.net.
Puis ajouter l’ensemble des fichiers JAR téléchargés dans le répertoire lib et dans le buildpath de
votre application.

8.4.Le fichier build.xml

C’est le fichier qui contient le script Ant à exécuter. Ce fichier permettra de faire appel à XDoclet pour
la génération automatique du code.
Notez que les « classpath » d’un script Ant sont indépendants de ceux définis dans Eclipse. Il sera
donc nécessaire de les spécifier à nouveau à l’aide de balises <classpath>.


<!-- this file uses Apache Ant 1.5.3 beta 1 -->
<project name="addressBook" basedir=".">
<!-- The location where your xdoclet jar files reside -->
<!--<property name="xdoclet.lib.home" value="c:/java_api/xdoclet-1.2b3/lib"/>-->
<property name="lib.dir" value="./lib/"/>
<property name="build.dir" value="./"/>
<target name="clean" depends="init" description="removes all directories related to this build">
<!-- <delete dir=""/>-->
</target>

<target name="init" description="Initializes properties that are used by other targets.">
<property name="dist" value="dist"/>
</target>

<target name="prepare" depends="init,clean" description="creates dist directory">
<echo message="Creating required directories..."/>
<mkdir dir=""/>
</target>

<target name="hibernate" depends="prepare"
description="Generates Hibernate class descriptor files.">

<taskdef name="hibernatedoclet"
classname="xdoclet.modules.hibernate.HibernateDocletTask">
<classpath>
<fileset dir="./lib/xdoclet/">
<include name="*.jar"/>
</fileset>
</classpath>
</taskdef>

<!-- Execute the hibernatedoclet task -->
<hibernatedoclet
destdir="."
excludedtags="@version,@author,@todo"
force="true"
verbose="true"
mergedir="">

<fileset dir=".">
<include name="*.java"/>
</fileset>

<hibernate version="2.0"/>

</hibernatedoclet>
</target>

<path id="hibernate.schemaexport.classpath">
<!-- <path>
<pathelement location="${lib.dir}/hibernate2.jar" /> -->
<fileset dir="./lib/xdoclet/" >
<include name="*.jar" />
</fileset >
<fileset dir="./lib/hibernate/" >
<include name="*.jar" />
</fileset >
<!-- <pathelement path="${build.dir}" />
</path>
<path>-->
<pathelement location="./lib/jdbc/mysql-connector-java-3.0.14-production-bin.jar" />
<pathelement location="./lib/hibernate/hibernate2.jar" />
<pathelement location="./" />
<!-- pour chercher les fichier hbm à la racine -->
<pathelement location="./JavaSource/" />
</path>

<target name="schemaexport" >

<taskdef name="schemaexport"
classpathref="hibernate.schemaexport.classpath"
classname="net.sf.hibernate.tool.hbm2ddl.SchemaExportTask"
>
</taskdef >

<schemaexport config="./hibernate.cfg.xml">
</schemaexport>

</target>
</project>

Pour commencer ajouter le fichier build.xml suivant à la racine de votre projet.
Il faut ensuite faire apparaître la fenêtre de Ant dans Eclipse si celle-ci n’est pas déjà visible :
Menu Window => show view => ant




Puis glisser/déposer le fichier build.xml dans le fenêtre de Ant :




8.5.Exécution du script ANT

Dans la fenêtre Ant on peut visualiser l’ensemble des taches disponibles en développant
l’arborescence (cliquez pour cela sur le « + »).




En double cliquant sur la tache hibernate, le script va parcourir l’ensemble des fichiers sources à la
recherche de commentaire XDocLet, puis générer le fichier de mapping correspondant (tous les
chemins vers les fichiers sources doivent évidemment être spécifiés dans le fichier build.xml).

De même la tache schemaexport permettra de générer la table dans la base de données en fonction
des Javabeans et de leurs commentaires XDocLet.

8.6.Les tags XDoclet pour Hibernate
Les tags XDoclet sont placés dans le Javabean, c'est-à-dire la classe qui contient les informations
mappées de la base de données.
Nous allons dans un premier temps spécifier la table qui stockera les informations du Javabean avant
la déclaration de la classe comme cela :

/**
* @hibernate.class table="Contact"
*/
public class Contact implements Serializable {
…
}

Ensuite pour chaque variable, nous allons devoir spécifier le type de donnée stockée en base et le
nom de la colonne qui recevra la donnée.
Par exemple :

/**
* @hibernate.id generator-> * column="id"
* @return Integer
*/
public int getId() {
…
}

Dans cet exemple nous avons mappé la variable id dans la colonne id. et nous avons aussi spécifié
que cette variable est de type integer, et correspond à un ID qui s’incrémente automatiquement.

Voici un autre exemple qui mappe la variable nom de type string vers la colonne nom :
/**
* @hibernate.property column="nom" type="string"
* @return String
*/
public String getNom() {
return nom;
}

Evidemment nous ne pouvons pas expliquer en détail l’ensemble des tags XDoclet appliqués à
Hibernate dans cette partie. Je vous invite à consulter la documentation officielle pour une description
exhaustive de tous les tags sur http://xdoclet.sourceforge.net/xdoclet/tags/hibernate-tags.html .
Vous y trouverez l’ensemble des types de donnée que l’on peut mapper, ainsi que diverses options de
configuration.


9.Hibernate 3

La version 3 fait d'Hibernate un outil encore plus performant pour gérer les problèmes de persistance
d'objets en Java. Cette nouvelle version permet le traitement et la transformation de documents XML,
rendant leurs données plus facilement persistantes.
Hibernate 3 gère aussi les tags dans les objets persistants au lieu de l’utilisation d’un fichier de
mapping. Il permet donc d’éviter la redondance des informations à écrire entre les objets persistants et
le fichier de mapping.
Par ailleurs le package HBM2DDL permet de générer automatiquement les tables dans la base de
donnée. L’utilisation de XDoclet en est par conséquent inutile.
Il reste pour le moment nécessaire de connaître Hibernate 2 dans la mesure où Hibernate 3 est très
récent et peu utilisé en entreprise pour le moment. Cependant il est important de s’y intéresser de très
près.


10.Conclusion

Nous avons vu dans ce cours le principe du mapping objet/relationnel. Cela permet de bénéficier des
avantages de la programmation objet pour l’utilisation de données stockées en base.
L’API Hibernate permet de faciliter ce type de programmation. Par ailleurs, nous avons aussi appris à
utiliser de nombreux outils afin de faciliter la programmation et de générer automatiquement certaines
lignes de code.
1 Depuis la version 3.0 d’Hibernate, le plug-in à utiliser est Hibernate Tools fourni directement par les
équipes de développement d’Hibernate
Ant - L'automatisation des tâches du programmeur



1.Présentation

1.1.Automatisation des tâches

Le programme Ant a pour mission de compiler et de regrouper automatiquement divers composants
d'un projet Java en tenant compte des dépendances entre ces derniers. Il remplit à ce titre le même
rôle que la commande make, bien connue des programmeurs C et C++.




La commande Make est la solution idéale pour compiler des projets en C ou C++, mais elle s'avère
inadaptée à la programmation Java. Elle reste en effet très dépendante du shell et il s'avère difficile de
créer des scripts complexes qui s'exécutent sur toutes les plates-formes. Le programme Ant,
développé par la fondation Apache, a pour vocation de reprendre les avantages de cet outil tout en
apportant une meilleure portabilité. Il utilise dans ce but des fichiers de configuration en XML.

Comme avec make, un projet Ant est constitué d'une collection de cibles correspondant aux
différentes étapes de la construction du programme et pour lesquelles il est possible de définir des
dépendances afin de contrôler l'ordre dans lequel elles s'exécuteront. Les différentes tâches ne sont
toutefois pas indiquées sous la forme de commandes mais par le biais de balises XML prédéfinies.
C’est Ant qui choisira la commande à invoquer pour chacune d'entre elles. Ces dernières offrent les
fonctions classiques de compilation et de gestion des fichiers, mais donnent également le moyen de
générer automatique de la documentation ou des paquetages. Bien évidemment, ce système est
multi-plateforme.
Ant est une application écrite Java, elle nécessite donc d’avoir une machine virtuelle installée sur
votre ordinateur.




Construction d’un fichier de construction


1.2.Installation

Nous partirons du principe que vous disposez déjà sur votre système d'un kit de développement Java
correctement configuré (la variable JAVA_HOME pointant vers le répertoire où se trouve la machine
virtuelle).
1.2.1. Téléchargement
Pour installer Ant, il suffit de le récupérer à l'adresse http://ant.apache.org. Il existe une version en code
source, ainsi que d’une autre pré compilée, comme pour la plupart des projets Apache.
Récupérez l’archive pré compilée et, décompressez-la.
Vous devez ensuite copier le dossier obtenu dans le répertoire de votre choix (par exemple
/usr/local/ant) et régler les variables $ANT_HOME et $PATH.

Pour un système Linux
export ANT_HOME=/usr/local/ant
export PATH=$PATH:$ANT_HOME/bin
Nous vous conseillons de placer ces lignes dans le fichier .bashrc afin d'éviter d'avoir à les taper à
chaque début de session.

Pour un système Windows
Il faut aller dans Démarrer -> Panneau de configuration -> Système -> Avancé -> Variables
d’environnement ...
Vous pouvez y redéfinir les variables système/ou utilisateur :




Si vous créez/modifiez une variable utilisateur, les changements seront pris en compte lors du
redémarrage des consoles en ligne de commande.
Cependant, la modification des variables système nécessitera le redémarrage de la session, dans la
plupart des cas.


2.Utiliser Ant dans un projet

Pour illustrer l'utilisation de Ant, nous prendrons l'exemple d'un programme Java dont la fonction est
d'afficher un carré magique (tableau dont la somme des nombres présents sur une ligne horizontale,
verticale, ou diagonale, est toujours la même).
Cette petite application contient deux classes réunies au sein du paquetage
com.example.MagicalSquare :

             o   Square qui gère le carré magique a proprement dit.
             o   TestSquare qui créé une instance de celui-ci, et déclenche son affichage.
            o

L'illustration suivante montre l'organisation de cette petite application à travers la perspective
« ressource » (voir dans le menu Windows -> Open perspective -> Others -> Ressources).

La plupart des projets Ant reposent sur cette structure, et nous vous conseillons de vous en inspirer
pour vos futures applications.

Le répertoire src renferme tous les fichiers source (extension .java), build accueillera nos classes
compilées (.class) et dist contiendra notre distribution (les fichiers jar). On trouvera dans ce dernier un
sous-répertoire lib contenant l'archive jar de notre application prête à être déployée. Et pour finir, le
répertoire doc servira à stocker la documentation de notre classe.

Certains projets peuvent également contenir un sous-répertoire conf pour les fichiers de configuration,
res contenant les différentes ressources nécessaires au programme (images, traductions, sons, ...), et
un dossier etc pour les fichiers qui n'appartiennent à aucune des catégories précitées.

Description des éléments du projet, visibles dans la figure ci-dessus :
Le répertoire src : les sources
JRE System Library : les librairies Java
Le répertoire lib : les librairies
ant.jar : la librairie Ant, on l’utilise ici pour écrire nos propres tâches
Le répertoire build: contient les fichiers sous forme de bytecode (fichiers compilés)
Le répertoire dist: contient les fichiers jar
Le répertoire doc: contient la documentation
build.xml : le fichier de construction de Ant
labo.txt : un fichier qui sera utilisé par la tâche que nous allons écrire

2.1.Importer le projet servant d’exemple

Pour suivre le cours, vous aurez besoin d’importer le projet que nous avons décrit précédemment.
Voici les étapes à suivre :

     •    Commencer par dézipper le fichier suivant : http://www.labo-sun.com/data/File/project_td12.zip
     •    Démarrer Eclipse
     •    Sélectionner File -> Import ...
     •    Sélectionner "Existing Project into Workspace"




     •    Vous pourrez sélectionner le répertoire contenant le projet
     •    Le projet sera disponible, et il se nomme "magicalSquare"

Pour plus d’informations, vous pouvez revoir le cours sur Eclipse proposé par le laboratoire Sun.

2.2.Le fichier « build.xml »

Les différentes règles de construction de notre programme doivent être enregistrées dans un fichier
« build.xml » situé dans le répertoire racine du projet. Vous en trouverez le contenu pour notre
programme dans le répertoire racine du projet.

Un fichier Ant est généralement constitué de deux sections :

     •    la déclaration des variables de configuration globales
     •    une liste de cibles comprenant les différentes opérations à effectuer pour satisfaire chacune
          d'elles.


Voici son contenu :

<project name="magical square" default="run" basedir=".">
<property name="debug.dir" value="false"/>
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="doc.dir " value="doc"/>
<property name="dist.dir " value="dist"/>
<property name="lib.dir " value="lib"/>

<target name="init" description="Initialize the project">
<tstamp/> <!--declare and initialize the DSTAMP variable-->
<mkdir dir="${build.dir}"/>
<mkdir dir="${dist.dir }"/>
<mkdir dir="${doc.dir}"/>
<mkdir dir="${lib.dir}"/>
</target>

<target name="compile" depends="init" description="compile the sources">
<javac
srcdir="${src.dir}"
destdir="${build.dir }"/>
</target>

<target name="doc" description="generate the documentation">
<javadoc
packagenames="com.example.magicalSquare"
sourcepath="${src.dir }"
destdir="${doc.dir }"/>
</target>

<target name="dist" depends="compile" description="Generate a jar file">
<jar
jarfile="${dist.dir }/magical_Square-${DSTAMP}.jar"
basedir="${build.dir}"
update="true">
<manifest>
<attribute
name="Main-Class"
value="com.example.magicalSquare.TestSquare"/>
</manifest>
</jar>
</target>

<target
name="run"
depends="init,compile"
description="Start up application">

<java dir="${build.dir}"
classname="com.example.magicalSquare.TestSquare"
fork="true" >

<classpath>
<pathelement path="${build.dir}" />
<fileset dir="${lib.dir}">
<include name="**/*.jar"/>
</fileset>
</classpath>
</java>
</target>

<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${doc.dir}"/>
<delete dir="${dist.dir}"/>
</target>

<!-- target available only if the "myFile" variable exists -->
<target name="main" depends="TesteAcces" if="myFile">
<replace
File="labo.txt"
Token="test"
Value="java" summary="true"/>
</target>
<target name="TesteAcces">
<taskdef name="accesOk"
classname="TesteAcces"
classpath="${build.dir}" />
<accesOk filename=" ${test.file.path}" />
<echo message="The file ${test.file.path} is writeable=${myFile}!"/>
</target>
</project>



2.4.La balise <property>

<property name="debug.dir" value="false"/>
<property name="src.dir " value="src" />
<property name="build.dir " value="build"/>
<property name="doc.dir " value="doc" />
<property name="dist.dir " value="dist" />
Les commandes property qui suivent servent à définir des variables qui peuvent ensuite se voir
insérées ou testées par le biais de d'expressions de type ${variable}. Si le projet se trouve dans le
répertoire projet placé à la racine du compte toto, la deuxième clause property attribuera donc la
valeur '/home/labosun/projet/src” à la variable ${src.dir}.
2.5.La balise <target>

<target name="init" description="Initialize the project">
...
</target>
<target name="compile" depends="init" description="compile the sources">
...
</target>
<target name="documentation">
...
</target>
<target name="dist" depends="compile" description="Generate a jar file">
...
</target>
<target name="clean">
...
</target>

Le reste du fichier est constitué d'une succession de cibles correspondant chacune à une étape de la
construction de l'application. Elles se voient incarnées par une paire de balises target qui renferment
une liste de tâches à exécuter. Ces dernières doivent bien sûr se voir également insérées sous la
forme de balises XML. Le nom de chacune d'elles correspond à une fonction précise et différents
paramètres peuvent être spécifiés par le biais d'attributs ou de nouvelles balises insérées à l'intérieur
de ces dernières.

2.5.1.Ecrire la cible “init”
<target name="init" description="Initialize the project ">
<tstamp/>
<mkdir dir="${build.dir}" />
<mkdir dir="${doc.dir}" />
<mkdir dir="${dist.dir}/lib"/>
</target>

Comme l'indique l'attribut name de la balise target, notre première cible est nommée init. L'attribut
description permet d'afficher un message d'aide sur la cible lorsque l'utilisateur tape la commande
suivante :

ant -projecthelp

Bien qu'il soit optionnel, nous vous conseillons donc de la renseigner systématiquement pour toutes
les cibles que vous souhaitez rendre accessibles. Cette première cible aura pour effet d'exécuter les
tâches tstamp et mkdir qu'elle contient. tstamp a pour effet d'assigner la date et l'heure actuelle aux
variables DSTAMP, TSTAMP et TODAY, respectivement sous la forme “aaaammjj”, “hhmm” et “mois
jour année”. La tâche mkdir acceptent comme unique attribut le chemin du répertoire à créer. Si ce
dernier comprend des dossiers inexistants, ces derniers sont créés automatiquement. Dans notre cas,
le répertoire dist sera ainsi créé implicitement par la commande suivante avant l'ajout du répertoire
lib :

<mkdir dir=”${dist.dir}/lib” />

2.5.2.Ecrire la cible “compile”
Observons à présent la cible compile.

<target name="compile" depends="init" description="compile the sources">
<javac srcdir="${src.dir}" destdir="${build.dir}"/>
</target>

L'attribut « depends », comme son nom l'indique, a pour fonction de spécifier les étapes devant être
effectuées avant d'exécuter les clauses de cette cible. Celui-ci doit contenir le nom de ces dernières
séparées par des virgules.
2.5.2.1.la tâche javac
<javac srcdir="${src.dir}" destdir="${build.dir}"/>

Notre cible ne compte ici qu'un seul élément appelant la tâche javac, dont le but est de compiler tous
les fichiers sources Java présents dans le répertoire indiqué dans l'attribut srcdir et de placer les
classes compilées ainsi obtenues dans le dossier dont le nom est fourni par destdir.

Cette opération est récursive. Le fichier présent dans « ./src/com/example/magicalSquare/ » sera
donc bien compilé. A l'instar de make, Ant compilera chaque classe uniquement s'il n'existe pas déjà
un fichier compilé dans le répertoire de destination ou si la date d'enregistrement d'un fichier
éventuellement déjà présent dans ce dernier est plus ancienne que le fichier source. Ainsi, si nous
lançons Ant, modifions le fichier Square.java, puis relançons immédiatement la compilation, seul la
classe Square sera à nouveau générée.

2.5.3.Ecrire la cible “run”
La cible run nous permet d'exécuter le logiciel, avec, si besoin, des arguments.

2.5.3.1.la tâche java
<java
dir="${build.dir}"
classname="com.example.magicalSquare.TestSquare"
fork="true" />

Nous l'utilisons afin de démarrer notre application, tout comme nous l'aurions fait avec la commande
“java” dans la console du système. Trois arguments vont être définis :

    •   classname permet de définir la classe de démarrage de l'application (celle contenant la
        méthode public void main(String[] args))
    •   dir, permettant de désigner le répertoire racine de l'application. On y définit le répertoire build.
    •   fork indique qu'une nouvelle machine virtuelle sera utilisée. Cette option est nécessaire afin
        de changer le répertoire d'exécution (attribut dir).

2.5.3.2.le paramètre classpath
Elle est imbriquée dans la tâche <java> afin de définir la variable classpath. Celle-ci contient le
chemin dans le système de fichier vers les librairies jar nécessaire à l'application (les drivers d'une
base de données par exemple).
Le contenu de l’attribut name nécessite une explication : **/*.jar. Cela signifie que l’on charge tous les
fichiers terminant par « .jar », dans tous les sous-répertoires de ${lib.dir}.
<java …>
<classpath>
<fileset dir="${lib.dir}">
<include name="**/*.jar"/>
</fileset>
</classpath>
</java>

Le paramètre fileset permet de lire l'ensemble dans fichier jar dans le répertoire lib.
Le paramètre include permet d’inclure dans le classpath les fichiers correspondant à l’expression
régulière donnés par l’attribut name.

2.5.4.Ecrire la cible documentation

2.5.4.1.La tâche javadoc
<javadoc
packagenames="com.example.magicalSquare"
sourcepath="${src.dir}"
destdir="${doc.dir}"/>

La tâche javadoc de la cible documentation appelle le programme homonyme qui permet de générer
automatiquement des pages d'aide en HTML à partir du code source de vos classes. Contrairement à
javac, cette tâche génère systématiquement les fichiers de documentation sans vérifier si ces derniers
existent déjà ou sont à jour. Il est donc conseillé de ne l'appeler que de manière ponctuelle.
Nous fournissons ici trois attributs :

             o   destdir indique le dossier dans lequel seront stockées les pages HTML,
             o   sourcepath le répertoire racine de vos sources
             o   packagenames indique la liste des paquetages qui devront être documentés

Vous pouvez compiler uniquement certains fichiers en omettant ces deux derniers paramètres et en
précisant leur chemin à l'aide de l'attribut sourcefiles.

2.5.5.Ecrire la cible dist

2.5.5.1.La tâche jar
<target name="dist" depends="compile" description="Generate a jar file">
<jar
jarfile="${dist.dir}/magical_Square-${DSTAMP}.jar" basedir="${build.dir}"/>
</target>

L'avant-dernière tâche sert à créer une archive jar qui permettra de distribuer facilement vos classes
compilées. Vous pouvez remarquer dans l'attribut jarfile que nous utiliserons ici la variable
${DSTAMP} (créé par l’appel de la tâche <tstamp/>) afin d'inclure la date de compilation dans le nom
d'archive. Un nouveau fichier apparaîtra ainsi chaque jour, permettant de disposer automatiquement
d'un historique de notre projet. Cette pratique est extrêmement courante dans le contexte de projets
OpenSource, où l'on procède quotidiennement à des “nightly builds”, des compilations systématiques
du code source d'un projet.

2.5.6.Ecrire la cible “clean”
La cible clean est similaire à celle utilisée dans make et a pour fonction de nettoyer notre projet de
tous les répertoires créés par Ant.

2.5.6.1.La tâche delete
<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${doc.dir}"/>
<delete dir="${dist.dir}"/>
</target>

Nous utilisons pour cela la tâche delete et son attribut dir qui efface récursivement un répertoire.
Vous pouvez également effacer une liste de fichiers grâce à l'attribut file.


2.6.Lancement de Ant

Vous pouvez à présent tester votre fichier de configuration. Pour cela, vous devez vous placer dans le
répertoire racine du projet et taper la commande Ant suivie de la liste des cibles à construire. Par
exemple :

ant init

Si vous lancez Ant sans paramètre, le programme tentera de satisfaire la cible par défaut indiquée
dans la balise project.

Dans notre exemple, Ant essayera donc d’exécuter la cible run, ce qui entraînera l'exécution dans
l'ordre des cibles init, compile et dist par le jeu des dépendances.

La commande suivante aura pour effet de détruire les répertoires ${build.dir}, ${dist.dir} et
${doc.dir} ainsi que leur contenu :

ant clean
3.Intégration à Eclipse

Que ce soit dans NetBeans ou dans Eclipse, la présence de Ant est très marquée. Nous verrons ici
son utilisation dans Eclipse.

3.1.Création des fichiers de compilation

L'éditeur inclut les fonctionnalités suivantes :

    •   Syntaxe colorée
    •   Assistance d'écrire du code

3.2.Visualisation du projet




Dans la perspective « java » du projet, il y a une vision particulière des librairies, des packages, des
classes, …

Par rapport à la perspective « ressource », que nous avons détaillé précédemment :

    •   La librairie « ant.jar » apparaît au sein du projet, et non dans le répertoire « lib »
    •   Les librairies « JRE System Library », qui sont celles de la machine virtuelle, sont listées
    •   La navigation dans le projet est plus agréable

3.3.Édition et exécution des fichiers de compilation

Chaque fichier ayant une extension de type .xml peut être exécuté comme un fichier de construction
Ant. Cependant, tous les fichiers XML ne sont pas des fichiers compatibles avec Ant.
Dans cette section, l'exécution de fichier Ant à travers Eclipse sera pris en compte en utilisant le
fichier build.xml du projet magicalSquare.

    1. Sélectionnez build.xml dans le navigateur et sélectionnez Run > External Tools à partir du
        clic droit.
    2. La fenêtre External Tools apparaît.
    3. Cette fenêtre permet de configurer de nombreux aspect du fichier de construction Ant, mais
        concentrons-nous sur l'onglet “Targets”.
    4. Appuyez sur le bouton Run.
    5. Le fichier de configuration Ant est démarré, et la sortie est envoyée à la vue Console.
Vous pourrez ensuite utiliser le fichier Ant directement depuis la barre d’outil, avec le bouton External

Tools (           ).


3.4. Utilisation de la vue Ant


La vue Ant permet d'avoir un aperçu visuel du fichier de construction.
Le menu contextuel de cette vue ouvre un éditeur, permettant d'exécuter des cibles, et de le
configurer.
Voici à quoi ressemble la vue Ant:




Comme les autres vues dans l'espace de travail, la vue Ant a sa propre barre d'outils pour ajouter des
fichiers de construction, et les exécuter.

3.5.La barre d'outils

3.5.1.Add Buildfiles
Cette commande vous propose de sélectionner le fichier de construction dans les “workspace”s.
Voici l'icône :
3.5.2.Add Buildfiles with Search
Cette commande recherche les fichiers de construction et les ajoute à la vue Ant. Vous pouvez
spécifier un nom à rechercher et la zone de recherche.
Voici l'icône :

3.5.3.Hide Internal Targets
Cette commande filtre les vues Ant afin que les cibles interne (internal targets) ne soient pas
affichées. Les cibles internes sont les cibles sans description.

Voici l'icône :
Remarque : les cibles internes n’apparaissent pas lorsque l’on entre la commande « ant –
projecthelp ».

3.5.4.Run the Default Target of the Selected Buildfile
Cette commande exécute la cible du fichier de construction sélectionné ou de la cible sélectionnée. Le
fichier de construction est exécuté en utilisant la configuration associée.
Voici l'icône :

3.5.5.Remove Selected Buildfile
Cette commande supprime, dans la vue, le fichier de construction sélectionné.
Voici l'icône :

3.5.6.Remove All Buildfiles
Cette commande supprime, dans la vue, tous les fichiers de construction
Voici l'icône :

3.6.Le menu contextuel

Le menu contextuel vous permettra d'accéder à peut prêt aux même fonctionnalités.




Un clic droit, … et le menu contextuel apparaît !

Les variables d’environnements dans Eclipse

Il est possible que votre environnement d’exécution ne fonctionne pas correctement en raison d’une
mauvaise configuration des variables d’environnement « JAVA_HOME », « CLASSPATH » ou encore
« PATH ».
Pour cette raison, nous pouvons redéfinir les variables d’environnement, afin, par exemple, de
démarrer un logiciel tiers non référencé dans la variable PATH, à partir d’un script Ant
Nous allons ici démarrer « rmiregistry ». Ce logiciel est fourni avec le kit de développement Java, et il
vous permet d’établir, grâce au protocole RMI, un annuaire d’objets partagés.
Voici notre script Ant :

<project name="Magical Square" default="run" basedir=".">
<target name="run" description="start up the rmi registry">
<echo message="start up the rmi registry" />
<exec executable="rmiregistry" dir="build.dir">
</exec>
</target>
</project>
Dans ce script, la tâche « exec » n’a pas encore été expliquée. Il s’agit de démarrer une application,
dont le nom de l’exécutable est spécifié dans l’attribut executable. Il ne faut pas oublier de spécifier le
répertoire dans lequel l’application est démarrée, ici c’est le répertoire ${build.dir}, car rmiregistry a
besoin d’avoir accès aux classes de l’application (cela permet d’avoir les classes de l’application dans
son CLASSPATH).
Si vos variables d’environnements sont bien configurées, rmiregistry sera trouvé automatiquement,
sinon il faudra les redéfinir. Faites pour cela un clic droit sur le fichier de script, et sélectionnez Run
As -> Ant Script ...




Puis, l’onglet «Environment»




Sélectionnez le bouton « Replace native environment with specified environment » si vous
souhaitez n’utiliser que les variables définies ici.
Il faut donc redéfinir certaines valeurs correspondant à votre système.
Enfin, redémarrez le script, et « rmiregistry » sera trouvé.
Cette fonctionnalité d’Eclipse vous permettra donc de remédier rapidement à une mauvaise
configuration du système.
4.Création d’une tâche personnalisée

L’écriture d’une nouvelle tâche est illustrée par l’exemple suivant : nous en développerons une qui
testera si un fichier est éditable. Cela peut se justifier par le fait que des commandes comme replace
ne s’exécutent que si un fichier est autorisé en écriture. Il est donc judicieux de développer une tâche,
qui définisse un attribut myFile, associé à canWrite lorsque le fichier spécifié avec l’attribut filename
est autorisé en écriture :
/**
* this task test if the destination file is writeable
*/
import java.io.File ;
import org.apache.tools.ant.BuildException ;
import org.apache.tools.ant.Task ;
public class TesteAcces extends Task {
// the file which will be used
private String filename ;
// this setter define the filename variable,
// it is called automatically by the xml attribut
public void setFilename(String filename){
this.filename = filename;
}

/**
* Override the execute() method
* @exception BuildException, if something wrong happens,
* it allows us to throw the problem.
* @todo refactor !
*/
public void execute() throws BuildException {
// retrieves a file
File myFile = new File(filename);
// checks if the file is writeable
if(moyFile.canWrite()){
project.setUserProperty("myFile", "canWrite");
}
}
}

Dans le code Java, il faut prendre en considération ceci :

    •   La nouvelle tâche doit dériver de la classe abstraite org.apache.tools.ant.Task.
    •   Il faut implémenter la méthode execute(), appelée à l’exécution de la tâche. La méthode
        execute() doit pouvoir lever une exception BuildException.
    •   Pour chaque attribut (uniquement filename ici), un accesseur doit être écrit.

D’autre part, on pourra réaliser les initialisations en implémentant la méthode init().
Deux champs publics : project et location permettent de modifier l’environnement dans lequel la
tâche s’exécute (voir l’API).
Pour utiliser une tâche, il faut qu’elle soit compilée, qu’elle soit déclarée dans taskdef et qu’elle se
trouve dans le CLASSPATH :

<taskdef name="accesOk" classname="TestAccess" classpath="${build.dir}" />

<target name="main" depends="TestAccess" if="myFile">
<replace
File="labo.txt"
Token="test"
Value="java" summary="true"/>
</target>
<target name="TestAccess">
<accesOk filename="${test.file.path}" />
<echo message="The file ${test.file.path} is writeable = ${myFile}" />
</target>
A la deuxième ligne, la classe (ici dans le package par défaut) est déclarée avec le nom accesOk.
Ensuite, dans la cible main, une dépendance de la cible TestAcces est reconnue et exécutée. Si la
permission en écriture est à vrai, la propriété myFile est définie et envoyé à la sortie avec un echo.
Sans droit d’écriture, la propriété n’est pas définie et l’attribut if de la cible main veille à ce que les
tâches de la cible main ne soient pas exécutées. Mais si l’attribut est défini et qu’il existe des
permissions en écriture, un remplacement de texte, par exemple dans la cible main, peut être effectué
sur le fichier.
Notre fichier est labo.txt, et il est placé à la racine du projet. Il contient le texte « text », qui sera
remplacé par « java ».

5.XDoclet : une utilisation avancée des tags

5.1.Présentation générale

XDoclet est un moteur de génération de code. C'est un projet open-source hébergé sur le site
sourceforge.net, sous licence BSD.Le but de ce projet est d’améliorer la productivité des
programmeurs en leur évitant de taper du code redondant et d’avoir à s’occuper des mises à jour des
documents liés à un projet. Ainsi, tout le travail fastidieux de création de fichiers XML de déploiement
ou de paramétrage et la génération de code source commun (interfaces pour les EJB par exemple…)
est supprimé.Les développeurs n'auront alors plus qu'à se concentrer sur l'édition d'un seul fichier
source Java par composant. Cela s’appelle l’intégration continue.
XDoclet permet de générer du code. Pour cela, on doit écrire des balises spécifiques de type :
/** @commentaire */

Les principaux bénéfices de l'intégration continue :

    1. Vous n'avez plus à vous occuper de la mise à jour des fichiers de déploiement (meta-data) à
       chaque fois vous modifiez du code. Le déploiement de ces meta-data est continuellement
       intégré.
    2. Travailler avec un seul fichier par composant vous permet d'avoir une vue plus globale de ce
       que vous êtes entrain de faire. Si votre composant est constitué de plusieurs fichiers, comme
       les Entreprise Java Bean (EJB), il est facile de s'y perdre. En effet, un seul EJB est
       traditionnellement constitué de 7 fichiers voire plus. Avec XDoclet, vous n'aurez plus qu'à
       maintenir un seul d'entre eux et le reste sera généré.
    3. Vous allez considérablement réduire votre temps de développement, et pourrez ainsi vous
       concentrer sur d'autres aspects puisque XDoclet va générer beaucoup de code pour vous.

5.2.Comment fonctionne XDoclet ?

XDoclet permet le développement orienté attribut (Attiribute-Oriented Programming) pour Java. En
d'autres termes, cela signifie que vous allez ajouter plus d'information dans votre code en y ajoutant
des meta données (attributs). Cela est réalisé grâce aux commentaires situés dans le code.
Pour rappel, une balise JavaDoc se présente sous la forme :
/**
* @author CASIMIR
**/
Une balise spécifique XDoclet se présente sous la forme :
/**
* @namespace:tag_name name="value" name2="value2" ...
**/
Chaque balise XDoclet est identifiée par un nom (tag_name) qui appartient à un espace de noms
(namespace). Les propriétés sont passées par le format name="value". Il est possible d'associer
plusieurs propriétés à un élément en cumulant les couples name="value".
Voici différents espaces de noms utilisés par XDoclet :

    •   ejb: informations standard sur les EJB (non spécifiques).
    •   jboss: informations spécifiques au serveur d'application JBoss.
    •   weblogic: informations spécifiques au serveur d'application BEA Weblogic.
    •   webSphere: informations spécifiques au serveur d'application IBM WebSphere.
    •   orion: informations spécifiques au serveur d'application Orion (Oracle).
    •   castor: génère les informations de mapping pour le framework Castor.
    •   soap: génère les descripteurs SOAP.
    •   struts: génère les fichiers struts-config.xml à partir de Form et Action.
    •   web : génère le fichier de configuration web.xml pour les Web Applications.
    •   hibernate : fichier de mapping objet/relationnel
XDoclet va parcourir (effectuer un parsing) des fichiers sources et va générer des fichiers particuliers
comme des descripteurs XML et/ou du code source. Ces fichiers sont générés à partir de templates
qui utilisent les informations fournies dans le code source et les balises JavaDoc spécifiques.
Actuellement, XDoclet ne peut être utilisé que comme une étape de construction de programme (build
process) en utilisant Jakarta Ant. C’est d’ailleurs pour cela que nous vous en parlons lors du cours sur
Ant.
Bien que XDoclet fût à l'origine conçu comme un outil de création d'EJBs, il a évolué en un moteur de
génération de code générique. XDoclet est constitué d'un noyau et de modules toujours plus
nombreux. Ces nouveaux modules permettent de générer d'autres fichiers pour différents
composants.
XDoclet a été développé avec une collection de modules pour la génération de différents types de
fichiers. Les utilisateurs et autres développeurs peuvent écrire leurs propres modules (ou en modifier
d'existants) s'ils veulent étendre les fonctionnalités d'XDoclet.

5.3.L’installation

L’installation de XDoclet suit 4 étapes :

Télécharger XDoclet : Pour télécharger XDoclet, visitez le site http://xdoclet.sourceforge.net et suivez le
lien vers la catégorie « Download / Install ». Vous devez choisir parmi trois types de distribution :


Distribution    Description

xdoclet-lib-    Inclut seulement le fichier JAR. Le fichier compressé contient toutes les librairies
1.23            requises, elle convient seulement si vous voulez télécharger les derniers binaires, et
                que vous n’avez ni besoin de la documentation et ni des exemples.

xdoclet-bin-    Inclut les fichiers JAR, la documentation, et des exemples.
1.23

xdoclet-src-    Inclut les scripts et les sources nécessaires pour construire XDoclet (cette méthode est
1.23            recommandée uniquement si vous êtes vraiment aventureux et que vous voulez
                regarder sous le capos comment XDoclet fonctionne).



Dépaquetez les binaires : Choisissez un chemin approprié pour XDoclet et dépaquetez le fichier
dans ce répertoire.

Ajoutez le chemin de XDoclet dans votre fichier “build.xml” : En utilisant la tâches de Ant
nommée <path>, ajouter le chemin vers XDoclet vers votre fichier de construction. Par exemple :

<property name="lib.dir" value="lib"/>
<property name="xdoclet.lib.dir" value="xdocletlib"/>
<path id="xdoclet.lib.path">
<fileset dir="${lib.dir}" includes="*.jar"/>
<fileset dir="${xdoclet.lib.dir}" includes="*.jar"/>
</path>

Ajoutez les modules de XDoclet vers votre fichier de construction. Ajoutez la balise <taskdef>
pour préciser à Ant d’inclure un module XDoclet. Par exemple, la suivante ajoute le support pour les
<ejbdoclet> à votre fichier de construction:

<taskdef name="ejbdoclet"
classname="xdoclet.modules.ejb.EjbDocletTask"
classpathref="xdoclet.lib.path"/>

5.4.Exemple

Nous allons utiliser une fonctionnalité de XDoclet qui permet de créer des pages html avec les
annotations @todo situés dans votre code. Cela permet d’avoir une vue d’ensemble de l’avancement
du projet.
Récapitulatif des étapes pour la génération de code avec XDoclet :
    1. Créer son propre template de déploiement .xdt (facultatif)Habituellement on utilise les
        modules déjà développés pour XDoclet (qui sont très puissants) plutôt que de développer ses
        propres modules (à moins que les fonctionnalités désirées ne soient pas déjà implémentées).

Ici nous n’avons rien à faire car nous utilisons un module déjà existant.

    2. Commenter son code avec des balises spécifiques XDoclet.

On place par exemple des balises @todo avec les méthodes.
/**
* @tag a ré-écrire d’urgence
*/

    3. Créer un fichier de déploiement build.xml pour Ant intégrant des demandes de génération de
        code XDoclet.

<target name="todo"
description="generate the todo web site"
depends="clean,init">
<property name="xdoclet.lib.dir" value="xdocletlib" />
<path id="xdoclet.lib.path">
<fileset dir="${xdoclet.lib.dir}" includes="*.jar" />
</path>


<taskdef name="documentdoclet"
classname="xdoclet.modules.doc.DocumentDocletTask"
classpathref="xdoclet.lib.path" />
<documentdoclet destdir="todo">
<fileset dir="${src.dir}">
<include name="**/*.java" />
</fileset>
<info />
</documentdoclet>
</target>

    4. Lancer Ant sur le fichier build.xml, et les fichiers désirés sont générés.

Consulter le fichier index.html situé dans le répertoire « todo ».
Note : nous avons remarqué une erreur dans le fonctionnement de XDoclet. En effet, lors du
lancement du target « todo », il manque le fichier « class.gif ». Pour résoudre ce problème, nous
avons copié ce fichier à partir de la racine du projet vers le répertoire « todo ». Cette tâche est
réalisée dans la cible « init ».
Voici la page que vous devriez obtenir :




XDoclet génèrera, de la même manière, de nombreux fichiers de configuration.
6.Introduction à Maven

Maven est un outil « open-source » de Apache Jakarta. Il permet de faciliter et d'automatiser la
gestion et la construction d'un projet Java. Le site officiel est : http://maven.apache.org.

Il permet notamment :

    •   d'automatiser la compilation, les tests unitaires et le déploiement des applications du projet
        (jar, war)
    •   de gérer les dépendances des bibliothèques nécessaires au projet
    •   de générer des documentations du projet : rapport de compilation et des tests unitaires,
        javadoc,…etc.
    •   de générer un site web complet du projet
    •   d’utiliser une large bibliothèques de goals prédéfinis (les plugins)
    •   de faciliter la création d’extensions grâce au langage Jelly (à base de XML)


Ce cours ne peut cependant se substituer à Ant car il permet d’avoir des réglages plus fins, même si
le temps passé à réécrire le script est généralement plus long. De plus, le projet est en cours de
refonte avec une réécriture du code entre la version 1 et 2 (qui ne sont pas compatibles ensembles). Il
faut donc attendre quelques mois pour qu’il devienne un véritable standard.

Pour vous donner un aperçu des caractéristiques des deux logiciels, voici un tableau faisant office de
résumé :


                                    Ant                                    Maven

 Installation                       Très simple                            Très simple (similaire à Ant)

 Temps de démarrage d’un            5 minutes                              15 minutes
 projet

 Temps pour ajouter une             10 minutes pour ajouter une            2 minutes pour utiliser un
 fonctionnalité                     cible                                  nouveau « goal »

 Temps d’apprentissage              30 minutes. Très simple à              2 heure, car il peut être difficile
                                    comprendre, et très bien               à appréhender
                                    documenté

 Standardisation des projets        Non (ce qui est plutôt bien, on        Oui (ce qui est bien, car tous
                                    peut faire ce que l’on veut)           les projets se ressemblent)

 Génération de documentation        Aucun standard, mais il y a            Oui
                                    plusieurs outils disponibles

 Intégration dans les               Oui                                    Encore basique
 environnements de
 développement


Nous allons traiter de la version 1.0.2 de Maven.

6.1.Pourquoi avoir développé Maven ?

La structure de développement et le déploiement (EARs, WARs et JARs) des projets J2EE sont
aujourd'hui beaucoup plus standardisés. Ainsi, Maven propose des fonctionnalités de ANT mais aussi
de nombreux autres qui répondent plus aux standards et aux besoins des développeurs d'aujourd'hui.
Maven est aussi plus flexible car il permet de créer plus facilement ses plugins (en Jelly) à la
différence de Ant qui reste relativement statique. Enfin, les scripts Ant ne sont pas réutilisables entre
projets, alors que le but de Maven est justement de fournir des fonctionnalités réutilisables.

6.2.Composition du fichier project.xml

En général, trois principales parties composent le fichier project.xml :
      •     La partie « gestion du projet » inclue les informations telles que l'organisation du projet, la liste
            des développeurs, la localisation du code source, et l'URL du système pour déceler les bugs.
      •     La partie « dépendance » du projet inclut les informations concernant les dépendance du
            projet.
      •     La partie de « build et de documentation » (rapports) contient les informations du build
            telles que le répertoire du code source, des tests unitaires, et les rapports à générer définis
            dans le build.

6.2.1.Exemple de fichier project.xml
Voici un exemple d'un fichier project.xml :

<?xml version="1.0" encoding="ISO-8859-1"?>

<!-- l'élément racine d'un fichier projet -->
<project>

<!------------------------------------------------------------------ -->
<!-- Partie gestion de projet -->
<!------------------------------------------------------------------ -->

<!-- groupe id du projet. -->
<!-- Si présent, l'id est utilisé comme nom de dossier-->
<!-- dans le repository -->
<!-- pour regrouper tous les jars du projet -->
<groupId>labosun.maven_session</groupId>

<!-- Unique id du projet. -->
<!-- Il est aussi utilisé pour générer des noms de fichier -->
<!-- i.e, le fichier JAR est nommé : <id>-<version> -->
<id>maven_session</id>

<!-- Le nom court du projet -->
<name>maven_session</name>

<!-- numero de version du projet. (Pas de norme à suivre) -->
<currentVersion>1.2</currentVersion>

<!------------------------------------------------------------------ -->
<!-- Partie dépendances du projet -->
<!------------------------------------------------------------------ -->
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>

<!------------------------------------------------------------------ -->
<!-- Partie build et documentation du projet -->
<!------------------------------------------------------------------ -->
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<unitTestSourceDirectory>src/test/java</unitTestSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<unitTest>
<includes>
<include>**/*Test.java</include>
</includes>
</unitTest>
</build>
</project>


6.3.Les fonctionnalités

Les tâches exécutées par Maven reposent sur des plugins qui sont des fichiers jar. Chaque plugin
permet d'effectuer des tâches particulières prédéfinies appelées « goals ». Les goals sont
configurables dans le fichier de configuration de Maven : « maven.xml ».

Maven s'utilise en ligne de commande sous la forme suivante : Maven plugin:goal.Exemple :
Maven java:compile
Si le goal n'est pas spécifié le goal par défaut sera exécuté, s'il est définit.
Il faut exécuter Maven dans le répertoire qui contient le fichier « project.xml ». Si les paramètres
fournis ne sont pas corrects, une exception est levée.
Pour obtenir une liste complète des plugins à disposition de Maven, il suffit d'utiliser la commande :
maven –g

Voici une liste non exhaustive de divers plugins et de leurs goals :
Catégorie

Plugins avec goals
Package,
                   jar : deploy, installwar : clean, deploy, init, installear : ear, deploy
déploiement
Construction       java : compile, jartest : matchjalopy : formatclean
Documentation      site : generate, deployjavadoc : deploy, installpdf checkstyle : report
                   Eclipse : add-maven-repo, generate-classpath, generate-project jbuilder :
Intégration IDE
                   generate-library, generate-project
                   Jhibernate Jdiff Junit Junit (Produit un rapport des changements sur un CVS ou
Autres
                   SVN)

6.4.Générer un projet

Pour débuter un projet, Maven propose un plugin qui permet de générer la structure du projet :

$ maven genapp
| \/ |__ _Apache__ ___
| |\/| / _` \ V / -_) ' \ ~ intelligent projects ~
|_| |_\__,_|\_/\___|_||_| v. 1.0
Enter a project template to use: [default]
defaut
Please specify an id for your application: [app]
myApp
Please specify a name for your application: [Example Application]
My Appplication Test
Please specify the package for your application: [example.app]
fr.example

Il y a plusieurs types de template prédéfinie :

    •   default – Génère un simple projet Jar.
    •   Ejb – Génère un simple EJB.
    •   Struts - Génère une simple application web Struts.
    •   Struts-jstl - Génère une simple application web Struts et JSTL.
    •   Web - Génère une simple application web Struts.
    •   Web-jstl - Génère une simple application web Struts avec JSTL.
    •   Complex - Génère un projet très complexe avec ear, wars, ejbs, struts, xdoclet.

Voici la structure du « template » par défaut :

6.5. Générer un site Web

Maven propose une fonctionnalité qui permet de générer automatiquement un site web pour le projet
regroupant un certain nombre d'informations utiles le concernant.
Pour demander la génération du site, il suffit de saisir la commande :
maven site:generate

Lors de l'exécution de cette commande, un répertoire target/docs est créé contenant les différents
éléments du site.Le site généré est composé de deux grandes parties :
La partie Project Info qui regroupe trois pages : la mailing list, la liste des développeurs et les
dépendances du projet.
La partie Project report qui permet d'avoir accès à des comptes rendus d'exécution de certaines
tâches : javadoc, tests unitaires, ... Certaines de ces pages ne sont générées qu'en fonction des
différents éléments générés par Maven.
Le contenu du site pourra donc être réactualisé facilement en fonction des différents traitements
réalisés par Maven sur le projet.
Ce plugins génère donc la structure de l'application selon les paramètres saisis et génère aussi le
squelette des fichiers project.properties et project.xml. Ce plugin permet de démarrer rapidement
sur le développement d'une application.

6.6.Quelques plugins utiles et intéressants

6.6.1.Les Plugins Java et clean
Ce sont les principaux plugins qui permettent la compilation et le nettoyage des fichiers générés par la
compilation.
Goal

Description
java:prepare-
                      Créer les répertoires nécessaires à la compilation
filesystem
                      Compile les fichiers codes sources Java du projet. Le répertoire source est défini
java:compile
                      entres les tags <build> dans project.xml.
clean:clean           Supprime ${maven.build.dir} et ${basedir}/velocity.log.

6.6.2.Le plugin Jar
Les fichiers d'archives Java sont des fichiers .jar qui regroupent un ensemble de ressources. Ces
archives Java comportent essentiellement des classes java compilées, mais elles peuvent également
contenir toutes sortes de ressources comme des images et des fichiers de configuration.
Ces fichiers permettent de déployer et de distribuer très facilement des bibliothèques de classes Java.
L'adjonction à l’archive d'un fichier de configuration particulier, appelée manifeste, permet de rendre
l’archive exécutable. Le manifeste doit alors contenir les informations concernant la classe à exécuter
lors du lancement de l’archive.

Goal

Descriptio
n
              Créer un fichier jar dans le repertoire build du projet sous la forme ${project.id}-
jar:jar
              ${project.currentVersion}.jar où id and currentVersion proviennent de project.xml.


6.6.3.Le plugin Jalopy
Jalopy est un utilitaire open source très pratique qui permet de formater du code source Java et
même de vérifier l'application de normes de codage.
Il permet notamment :
    •   d'indenter le code
    •   de générer des modèles de commentaires javadoc dynamique en fonction des l'éléments du
        code à documenter (par exemple générer un tag @param pour chaque paramètre d'une
        méthode)
    •   d'organiser l'ordre des clauses import
    •   d'organiser l'ordre des membres d'une classe selon leur modificateur
    •   de vérifier l'application de normes de codage


              Goal

              Description
              jalopy:format Reformate tous les fichiers sources selon un code de convention


6.6.4.Le plugin Site
Le plugin site permet de générer un site très complet du projet
Goal

Description
             Génère le site d'information du projet. Il exécute les goals suivants pour créer la
             documentation du site :
             jdepend : analyse la qualité du code (calcul des métriques)
             checkstyle : analyse si les conventions de codage ont été respectées
             changelog : Résume tous les changelog d'un CVS
Site:generat
             activity : rapport sur l'activité des développeurs et des fichiers
e
             javadoc : génération de la javadoc du projet
             jxr
             junit-report : rapport des tests unitaires
             tasklist : résume une liste a faire
             xdoc

6.7.Le repository

Afin de bien gérer les dépendances, Maven utilise un système qui s'appuie sur des repositories pour
télécharger automatiquement les composants qu'il a besoin. Mais pour éviter que les fichiers se
téléchargent à chaque reconstruction, Maven stocke automatiquement les dépendances nécessaires
dans le repository local, situé dans $HOME/.maven/repository

6.8.Résumé

Les grands avantages de Maven sont :

    •   Les nombreux plugins déjà existants qui répondent à nos besoins
    •   La diffusion de l'état actuel du projet via la génération de site


Maven est un outil récent mais il est déjà utilisé par de nombreux projet Jarkarta. Il a donc déjà fait ses
preuves et on peut avoir confiance sur la maturité de cet outil.

Maven est plus simple et plus facile à prendre en main que Ant. De plus mevenide (le plugin Eclipse)
permet de l'intégrer dans la plupart des outils de développement.
La version 2 est attendue avec impatience.

7. Conclusion

Cette initiation devrait être largement suffisante pour permettre de créer des fichiers de configuration :
compilation, déplacement de fichiers, exécuter une application, générer une documentation ... etc.
Sachez toutefois qu’Ant dispose d'autres fonctions très intéressantes telles que la récupération
automatique de fichiers CVS sur un serveur, ou la signature des paquetages.
Il représente donc un outil indispensable pour créer des applications Java de taille importante, vous
devriez gagner du temps à mesure que vos projets vont grandir.
Nous avons également introduit XDoclet, qui sera largement étudié lors de l’utilisation d’Hibernate,
des ejb, ... etc.
Pour la partie concernant Maven, il s’agissait seulement de donner un aperçu de cet outil qui est en
phase de devenir un standard.

Liens :

    •     ANT (avec pléthore de liens externes) : http://jakarta.apache.org/ant
    •     XDOCLET : http://xdoclet.sourceforge.net
    •     Maven : http://maven.apache.org
    •     Antidote (interface graphique pour ant) :
          http://jakarta.apache.org/ant/manual/Integration/Antidote.html
    •     AntDoc (Javadoc pour Ant) : http://mapage.noos.fr/antdoc/example_top.html
    •     Nant (Ant version .NET) : http://nant.sourceforge.net/
    •     AntContrib (tâches C++ pour ANT) : http://sourceforge.net/projects/ant-contrib/
    •     CruiseControl (outil d’intégration continue basé sur ANT) : http://cruisecontrol.sourceforge.net/
              o Cette technique permet par exemple de monitorer le code source d'une application
                 pour automatiser la construction d'un projet en cas de changement. Il envoie ainsi un
                 mail avec la liste des modifications effectuées depuis la dernière construction ou
                 génère des rapports en HTML.

8.Lexique des balises

8.1.Eléments de base de Ant

project
L’exemple propose dans ce cours ne contient, comme tout fichier build, qu’un seul élément project.
Ce projet peut recevoir un nom de votre choix (attribut name) et doit pointer vers un répertoire de
travail pour Ant (attribut basedir). Dans l’exemple, c’est le répertoire courant qui est utilisé. Il est
possible et judicieux d’indiquer une cible standard, appelée par défaut lorsque la commande shell ant
est passée sans paramètre (attribut default). Ceci permet un appel n’utilisant que la commande ant
sans argument.
target
Les « cibles » regroupent les phases de travail individualisées. L’attribut depends permet de définir
des dépendances, c’est-à-dire, l’ordre dans lequel les cibles seront exécutées. Les cibles peuvent
dépendre de plusieurs autres cibles, séparées par des virgules dans la liste associée à l’attribut
(depends = « B, C, D »).
property
Dans l’exemple du cours, les propriétés, c'est-à-dire les variables de Ant, sont d’abord définies. Par
exemple, des répertoires ou des versions peuvent, ici, être spécifiées. Ces variables sont accessibles,
dans la mesure où elles sont référencées avec ${}.
Notez qu’une fois que les variables ont été définies, elles ne peuvent plus être modifiées.
En plus des propriétés définies par l’utilisateur, vous disposez de toutes les propriétés système (voir la
description de la méthode getProperties() de la classe java.lang.System dans la documentation Java
API) et de quelques propriétés spécifiques à Ant (basedir, ant.file, ant.version, project.name, et
ant.java.version).
Les variables DSTAMP et TSTAMP sont également intéressantes afin d’insérer la date dans le nom
du fichier jar créé, par exemple.

8.2.Jeux de fichiers

Un grand nombre de tâches prédéfinies ont besoin des chemins d’accès à des classes, des fichiers,
etc. Les jeux de fichiers (marqueurs <fileset>) sont utilisés pour spécifier des ensembles de fichiers.
Ces marqueurs sont normalement des marqueurs internes. Ils peuvent être définis, le cas échéant, à
l’aide des éléments include, exclude, etc., par l’intermédiaire d’un jeu de modèles ou de références.
Certaines tâches prédéfinies offrent ces marqueurs directement sous forme d’attribut (mais avec un
« s » à la fin, comme ceci : includes).
Cette balise s’applique à : apply, chmod, copy, delete, dependset, ear, exec, filter, javac, javadoc,
move, touch, uptodate, war, zip, …etc.
Dans l’exemple suivant, la tâche copy est utilisée pour copier récursivement, à partir du répertoire
défini par « src » tous les fichiers source Java, dans le nom desquels ne figure pas « 42 », et cela
vers un répertoire « dest ». L’expression **/ englobe tous les sous-répertoires.
<copy todir="dest">
<fileset dir="src" includes="**/*.java" excludes="**/*42*"/>
</copy>
La tâche « fileset » peut comporter les attributs suivants :


Attribut          Description

Dir               Répertoire d’origine pour la définition des fichiers

Includes          Liste de noms de fichiers, séparés par des virgules, si omis, tous les fichiers sont
                  ajoutés.

Excludes          Liste des fichiers qui sont à exclure

casesensitive     Indique si le système de fichier en sensible à la classe.


Exemple :
<fileset dir="src">
<patternset refid="sources"/>
</fileset>

8.3.Tâches

Voici une brève liste des commandes les plus généralement utilisées.

8.3.1.copy
Permet de copier plusieurs fichiers dans un répertoire. Les fichiers du répertoire source ne sont copiés
que s’ils sont plus récents que ceux du répertoire de destination ou lorsqu’ils n’existent pas déjà dans
le répertoire de destination. Cependant, les fichiers plus récents peuvent toujours être écrasés par
overwrite.
Attribut     Description

file         Le fichier à copier, au cas où aucun fileset n’est défini, file est un attribut obligatoire.

tofile       Fichier de destination, est utilisé si l’attribut file est défini

todir        Répertoire de destination. A spécifier lors de la définition de l’attribut file ou d’un jeu de
             fichiers

overwrite Défini à yes, cet attribut permet d’écraser les fichiers récents. La valeur par défaut « no »
          protège les fichiers cible les plus récents.



8.3.2.delete
La tâche delete permet de supprimer plusieurs fichiers ou répertoires. Des jeux de fichiers peuvent
être spécifiés à l’aide des includes, excludes, etc, … ou encore dans un élément fileset emboîté.
Attribut Description

file       Supprimer le fichier indiqué. L’un ou l’autre des attributs file ou dir doit être déclaré.

dir        Supprime le répertoire indiqué et, récursivement, tous ses sous-répertoires. Les répertoires
           vides ne sont pas supprimés par défaut. L’un des deux attributs file ou dir doit être
           nécessairement spécifié.

quiet      True entraîne la suppression des messages d’erreur, lorsque des fichiers/répertoires
           n’existent pas. False (valeur par défaut) affiche les messages d’erreur.

On peut utiliser le marqueur interne « fileset ». La section Jeux de fichiers décrit les spécifications
d’un ensemble de fichiers, qui peuvent être spécifiés dans un marqueur interne.
Exemple :
Le deuxième delete de l’exemple suivant efface le répertoire build.
<target name="clean">
<delete file="/lib/chess.jar" />
<delete dir="build" />
</target>
Tous les fichiers avec l’extension .class sont supprimés de l’arborescence à partir du répertoire
« sub/tmp » :

<delete>
<fileset dir="sub/tmp" includes="**/*.class" />
</delete>

8.3.3.echo
Affiche un texte arbitraire dans la fenêtre du shell ou dans un fichier.

Attribut    Description

Message Spécifie le message de sortie. Obligatoire si le message ne se trouve pas dans le corps.

File        Fichier dans lequel le message doit être écrit

Append      Indique si le message doit être ajouté à la fin du fichier. Si la valeur de cet attribut est false,
            le fichier de destination est écrasé. True attache le message.

Exemples :

L’exemple retourne une simple information :
<echo
message="Statut : tous les fichier *.lib ont été créés avec succès. " />
Une chaîne plus longue et une ligne vide sont retournées ici :
<echo>
Informaton de statut : les 42 fichiers *.lib ont été créés avec succès et ont été copées dans le
répertoire de production.
</echo>
Le dernier exemple qui suit ajoute une information de log à un fichier log.txt existant, et utilise la
propriété DSTAMP produite pas l’intermédiaire de TSTAMP :
<echo
file="log.txt"
append="true"
message="LOG: conversion réussie le ${DSTAMP}."/>

8.3.4.jar
Permet la création d’une archive jar.
Les fichiers peuvent être spécifiés à l’aide de fileset et aussi comme éléments imbriqués dans
l’élément jar. On peut aussi spécifier includes, excludes, etc. comme attributs de jar pour une
définition plus compacte.



Attribut     Description

Jarfile      Nom du fichier jar à créer.

Basedir      Répertoire source,à partir duquel les fichiers à archiver dans le fichier jar doivent être lus.

Compress Compress est par défaut true, autrement dit, le fichier est compressé. False empêche la
         compression.

Filesonly    Avec true, seuls les fichiers sont archivés. Cependant le paramètre par défaut est false,
             car on a besoin aussi en règle générale d’archiver aussi les répertoires.

Includes     Liste de fichiers séparés par des virgules, qui doivent être utilisés.

Excludes     Liste de fichiers séparés par des virgules, qui ne doivent pas être utilisés.

Manifest     Permet la spécification du fichier manifest. Ce fichier permet de définir la classe contenant
             la fonciton main, entre autre.

Update       Dans le cas où le fichier existe déjà, spécifie si ce fichier doit être remplacé.

Exemple :

L’exemple suivant suppose que tous les fichiers source de la tâche javac dans build ont été créés.
Ce répertoire source est rassemblé dans une archive jar et écrit dans lib :
<jar jarfile="${lib.dir}/ejbs.jar" basedir="${build.dir}"/>
Dans l’exemple suivant, sont pris en compte dans le marqueur jar tous les fichiers « class », et les
fichiers dont le nom contient la chaîne test, sont explicitement exclus.
<jar jarfile="lib/ejbs.jar">
<fileset dir="${build.dir}/classes"
excludes="**/*test*"/>
</jar>

8.3.5.java
Exécute une classe Java.
Cette tâche comporte un attribut fork permettant de démarrer une machine virtuelle Java, de sorte
que le programme à la fin de l’exécution du code Java met un terme à la machine virtuelle VM avec
System.exit(0), mais non à la machine virtuelle du build.

Attribut           Description

Classname          Le nom de la classe qui doit être démarrée. On doit indiquer comme paramètre,
                   soit classname, soit jar.

Jar                Par opposition à classname, jar permet de spécifier ici un fichier jar. L’attribut jar
                   doit posséder une entrée Main-Class dans le fichier manifest, et fork doit être
                   défini sur true.

Classpath          Le chemin de classe. En présence de plusieurs chemins, il est recommandé
                   d’utiliser class-path comme élément interne de Java (voir l’exemple ci-dessous).

Fork               Autorise le démarrage d’une classe dans une autre machine virtuelle Java.
                   Désactivé par défaut.

Jvm                Dans le cas où fork est défini, jvm donne l’ordre permettant de démarrer la
                   machine virtuelle.

Jvmarg (élément    Dans le cas où fork est défini, les arguments de la classe dans la nouvelle
imbriqué)          machine virtuelle sont définis ici.

Dir                Si fork est défini, permet de spécifier le répertoire pour l’exécution de la nouvelle
                   machine virtuelle.

Output             Les sorties sont écrites dans un fichier.

Marqueurs internes
Arg et jvmarg peuvent être utilisés dans Java comme arguments de la ligne de commande. Par
exemple :
<arg line="arg1 arg2 arg3"/>
Exemples
Cet exemple exécute une classe Java en lui passant des arguments avec l’élément arg. Une ligne est
passée comme argument avec line. Les noms de fichiers, contenant des espaces, doivent être
déclarés avec l’attribut value.
<java classname="foo.bar.Main">
<arg line="-verbose ${src.dir}/*.java"/>
</java>
L’exemple suivant appelle une classe qui doit être exécutée dans une nouvelle machine virtuelle. Le
chemin de classe est défini comme élément interne classpath de Java et contient deux références
aux fichiers jar et à un élément de chemin, pointant vers un chemin de classe prédéfini

<java classname="org.labo.computer.Main" fork="yes">
<classpath>
<pathelement location="lib/${jarname}"/>
<pathelement location="ext/tools.jar"/>
<pathelement path="${classpath}"/>
</classpath>
</java>

8.3.6.javac
Compile une arborescence de répertoires qui contient le code source Java.
Cette tâche permet de parcourir les répertoires de manière récursive et de compiler tous les fichiers
Java n’ayant pas de fichier de classe correspondant ou dont le fichier de classe est plus ancien qu’ils
ne le sont.




Attribut        description

Srcdir          Répertoire où se trouvent les fichiers Java. Attribut obligatoire, lorsque aucun élément
                src n’est spécifié dans javac.

Destdir         Répertoire dans lequel les fichiers class sont stockés.

Includes        Liste de fichiers ou de modèles, séparés par des virgules, qui peuvent être utilisés.

Excludes        Liste de fichiers ou de modèles séparés par des virgules, qui ne doivent pas être
                utilisés.

Classpath       Le chemin de classe à utiliser

Bootclasspath Chemin des classes nécessaires au lancement de la machine virtuelle.

Encoding        Encodage des fichiers source.

Debug           Indique au compilateur si la compilation doit inclure les informations de débogage. La
                valeur par défaut est off ; autrement dit, l’information de débogage n’est pas utilisée.

Optimize        Indique si le code doit être optimisé. La valeur par défaut est off, ce signifie sans
                optimisation.

Target          Permet de spécifier la version Java des fichier de classe (par exemple 1.2 ou 1.4)

Fork            Yes permet d’exécuter javac dans une autre machine virutelle. La valeur par défaut
                est no.

Source          Assure la compatibilité des sources avec une version du jdk. Peut prendre les valeurs
                1.3 ou 1.4 et ne peut être utilisé qu’avec des compilateurs Sun supèrieure à 1.3.



Marqueurs internes
Les fichiers source peuvent être spécifiés à l’aide de fileset, includes, excludes, etc (voir la section
Jeux de fichier) utilisés comme élément imbriqués dans la tâche javac. Comme solution alternative,
on peut spécifier includes, excludes, etc… comme attributs de la tâche javac pour des définitions de
fichiers plus compactes.
Par ailleurs, srcdir, classpath, bootclasspath et extdir peuvent être utilisés comme éléments
internes de javac et comme structures de chemins.

Exemples :
Le premier exemple compile toutes les sources du répertoire actuel et écrit l’ensemble de
l’arborescence avec tous les fichiers de classe dans WEB-INF/classes.

<javac srcdir="." Destdir="WEB-INF/classes" />
Dans le second exemple, l’option verbose est active et le texte source de même que includes et
excludes sont spécifiés comme marqueurs internes :

<javac destdir="${build.dir}" verbose="true">
<src path="${brain}"/>
<src path="${graphics}"/>
<include name="${package1}/**"/>
<exclude name="${package1}/*test*"/>
</javac>

8.3.7.javadoc
Permet la création d’une documentation javadoc.
La documentation de l’API est générée à partir des sources Java qui sont recherchées dans un ou
plusieurs répertoires que l’on renseignera dans un attribut « sourcepath » ou un élément imbriqué.


Attribut      Description
Sourcepath    Spécifie l’emplacement des sources. L’un des attributs sourcepath ou sourcepathref
              doit être défini. Sourcepath n’est pas un attribut requis dans la mesure où le marqueur
              emboîté <sourcepath> est spécifié.

Destdir       Définit le répertoire cible dans lequel seront écrits les fichiers javadoc. Attribut
              obligatoire.

Sourcefiles   On peut spécifier directement le code source à l’aide d’une liste de fichiers source,
              séparés par des virgules. L’un des attributs sourcefiles ou packagenames doit être
              défini. N’est pas obligatoire dans le cas où les marqueurs <source> ou <package> sont
              spécifiés.


Exemple :
Cet exemple génère une documentation dans le répertoire javadoc. Private est une option javadoc qui
permet d’englober dans la documentation les classes et les champs privés, ainsi que les méthodes
privées des classes.

A l’aide des attributs packagenames et excludepackagenames, des classes peuvent être inclues ou
exclues
<javadoc packagenames="org.labosun.project"
sourcepath="."
destdir="."
private="true" />

8.3.8.taskdef
« taskdef » sert à définir une nouvelle tâche dans le projet actuel. « taskdef » est appelée soit avec
les attributs « name » et « classname », soit avec uniquement « name » et « classname », soit avec
uniquement l’attribut « file » ou l’attribut « resource ».


Attribut      Description

Name          Le nom de l’élément XML par lequel la tâche est invoquée. Attribut obligatoire, si file ou
              resource ne sont pas spécifiés.

Classname     Nom de la classe qui implémente la nouvelle tâche. Attribut obligatoire si file ou
              resource ne sont pas spécifiés.

Resource      Nom d’un fichier de propriétés comportant des paires nom/classe.

Classpath     CLASSPATH dans lequel se trouvent les classes Java implémentant les tâches.


Exemple:
<taskdef name="myGenerator"
classname="org.labosun.anttask.generator" />
Servlet & JSP - Développement Web


1.Introduction

1.1.Présentation

Pour une programmation optimisée, une application Web doit être fondée sur une architecture
MVC(Model-View-Controller).




La couche présentation (View) correspond aux pages Web affichées au niveau du client (browser,
applet, application propriétaire …).

Ces pages peuvent être statiques ou dynamiques. Dans le dernier cas, elles sont le résultat d’un code
exécuté côté serveur. La programmation de cette couche peut être réalisée par un serveur
d’application J2EE à l’aide des composants suivants :

    •   Les Servlets Java
    •   Les Java Server Pages
    •   Les JavaBeans
    •   Les Tag Librairies

2.Les serveurs d’applications J2EE

2.1.Présentation
Il existe de nombreux serveurs d’applications mais chacun d’eux présente des avantages et des
inconvénients. Ils sont, en effet, adaptés à différentes utilisations.

Certains sont exclusivement spécialisés dans le développement de servlet/jsp (Tomcat, Resin, Jetty,
WebSphere Express et Weblogic Express) tandis que d’autres englobent l’ensemble des
spécifications J2EE(JBoss, Jonas, Oracle 9iAS, WebSphere Application Server, Weblogic Platform).

Lors du choix d’un serveur applicatif, on va évidemment prendre en compte son prix. Si la majorité
sont payants, il en existe tout de même qui sont tout à fait compétitifs en open source ; Tomcat, Jetty,
JBoss, Jonas ou Orion.

Robuste et stable, Tomcat est le serveur standard pour le développement servlet/JSP. Nous l’avons
donc choisi pour les différents exemples du cours


2.2.Fonctionnement

2.2.1.Les requêtes

2.2.1.1.Une requête HTTP de base
Une requête HTTP est effectuée par un navigateur Web (appelé aussi client) auprès d’un serveur
Web afin de récupérer le contenu d’une page Web.

Les différentes étapes du processus sont les suivantes :

    •   Le navigateur se connecte au serveur
    •   Le navigateur envoie sa requête au serveur
    •   Le serveur cherche la page demandée (la réponse)
    •   Le serveur commence à envoyer le contenu de la page « réponse » (ou une page d’erreur)
    •   Le navigateur affiche la page au fur et à mesure qu’il la reçoit
    •   Une fois le transfert terminé, le navigateur ferme la connexion

Remarque : Dans le cas d’une requête simple (vers une page html par exemple) aucun moteur de
servlet n’intervient pour la génération du résultat.

2.2.1.2.Une requête sur un serveur Java
Le serveur Web doit s’accompagner d’un module supplémentaire qui gèrera les servlets. Celui-ci est
appelé « moteur de servlets » ou « conteneur de servlets ». Ce module est soit intégré directement au
serveur Web soit sous forme de moteur indépendant. Nous expliquerons ci-après la différence entre
ces deux technologies. Dans notre exemple, nous allons prendre le cas où notre moteur de servlets
est indépendant (afin de mieux comprendre la distribution des tâches entre les deux composants).

Les différentes étapes du processus sont les suivantes :

    •   Le navigateur se connecte au serveur et envoie sa requête
    •   Le serveur détecte que l’on veut un accès à une servlet et transfert donc la requête au moteur
        de servlets
    •   Le moteur vérifie que la servlet est instanciée
    •   Si c’est le premier appel, le moteur crée une instance de la servlet et appelle la méthode init()
        de celle-ci
    •   Le moteur appelle ensuite la méthode service() de la servlet. Cette méthode reçoit deux
        objets en paramètres :
             o Le premier représentant la requête du client
             o Le deuxième représentant la réponse à donner à ce client
    •   Le moteur retourne le résultat généré au serveur web qui le renvoie au client.
    •   Le client affiche la page et ferme la connexion

Lors des appels suivants aucune instance ne sera créée puisque le moteur utilise toujours la même.
Dans le cas de plusieurs appels synchrones (et d’une servlet générique) le moteur créé pour chaque
appel un thread particulier qui générera le résultat pour l’appel associé.

2.2.1.3.Moteur de Servlets

                                     1. Mode Autonome
Un moteur de servlet autonome est un moteur qui est totalement indépendant, c'est-à-dire qu’il
contient également un serveur Web. Toutes les requêtes passent par le moteur de servlet, qu’elles
appellent une page statique basique ou une servlet.
Ce mode est surtout utilisé lorsque l’on utilise uniquement des servlets ou que l’on souhaite les tester
(lors de développement en local par exemple).
2.2.1.4.Mode Lié au serveur web
Le moteur de servlet est le plus souvent lié au serveur Web. En effet, dans la majorité des cas, les
servlets sont couplées avec les JSP ou tout autre type de pages accessibles via ce serveur Web. Le
fait que le moteur soit indépendant du serveur Web permet d’optimiser les temps de réponses car le
moteur n’est sollicité que pour le traitement des servlets (et non pour toutes les requêtes).
Cependant la plupart des moteurs de servlet peuvent être utilisés aussi bien en mode autonome qu’en
mode lié.

2.2.2.Configuration

2.2.2.1.Apache Tomcat
Pour indiquer quels dossiers sont partagés sur le serveur vous devez créer un « Context Path » sur le
fichier de configuration du serveur : « server.xml ».
Ce fichier permet de spécifier une multitude d’informations concernant le paramétrage du serveur,
cependant nous nous attarderons que sur les spécifications de base pour l’utilisation de servlet et
JSP.
Il vous permet notamment de configurer différents noms d’hôtes (« host ») afin de spécifier différents
paramètres pour chacun d’eux.

Voici le contenu d’une balise host de base pour la configuration de localhost (hôte par défaut).

<Host name="localhost" debug="0" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">

<Logger className="org.apache.catalina.logger.FileLogger"
directory="logs" prefix="localhost_log." suffix=".txt"
timestamp="true"/>

<Context path="/localhost" reloadable="true"
docBase="C:\www_root\localhost\"
workDir=" C:\www_root\localhost\work" />

<Context path="/localhost2" reloadable="true"
docBase="C:\www_root\localhost2\"
workDir="C:\www_root\localhost2\work" />

</Host>

L’élément principal à retenir ici est la balise : « Context ». En effet c’est elle qui va permettre de définir
des « Context Path » et donc de partager des dossiers de votre disque dur.
Dans l’exemple ci-dessus nous définissons 2 « Context » qui sont mappés sur des dossiers locaux.
Le premier : localhost est mappé sur : « C:\www_root\localhost\ ».
Le second : localhost2 est mappé sur : « C:\www_root\localhost2\ ».

Vous pourrez accéder à ces deux « context » depuis votre navigateur web via les adresses :
http://localhost:port/localhost/ et http://localhost:port/localhost2/.
Le port est défini dans le fichier de configuration du serveur (par défault il est égal à : 8080).

2.2.2.2.Sun Application Server
Une fois installé, le serveur vous crée un premier serveur virtuel. Un dossier de paramétrage est alors
créé ; il porte le nom suivant: https-[NomDevotreMachine.Utilisateur.com]. Cette structure peut
changer en fonction des paramètres que vous avez choisi lors de votre installation. Nous utiliserons
supinfo.labosun.com pour nos exemples. Nous allons nous attacher au dossier config.
Dans celui-ci vous avez le fichier : default-web.xml qui correspond au fichier par défaut utilisé pour
les applications Web de votre serveur virtuel. Vous avez également un fichier server.xml qui va nous
être utile pour configurer l’équivalent des « context path » vus précédemment.

Pour indiquer au serveur un mappage de dossier, il faut ajouter la balise suivante dans la balise <VS
…></VS> :

<WEBAPP uri="/pathMap" path="[Lecteur]:[Chemin]"enabled="true"/>

Voici ce que cela donne pour notre exemple :
<VSCLASS id="vsclass1" objectfile="obj.conf" rootobject="default" acceptlanguage="off">
<PROPERTY name="docroot" value="d:/Sun/WebServer6.1/docs"/>
<VS id="https-supinfo.labosun.com" connections="ls1" mime="mime1" aclids="acl1"
urlhosts="supinfo.labosun.com" state="on">
<PROPERTY name="docroot" value="d:/Sun/WebServer6.1/docs"/>
<USERDB id="default"/>
<SEARCH>
<WEBAPP uri="/search" path="d:/Sun/WebServer6.1/bin/https/webapps/search" enabled="true"/>
</SEARCH>
<WEBAPP uri="/localhost" path="C:\www_root\localhost" enabled="true"/>
<WEBAPP uri="/localhost2" path="C:\www_root\localhost2" enabled="true"/>
</VS>
</VSCLASS>

Nous avons, comme pour l’exemple précédent, mappé localhost et localhost2 aux deux dossiers
respectifs : « C:\www_root\localhost » et « C:\www_root\localhost2 ».

2.2.3.Compilation de servlets et tests
Nous allons dans cette partie utiliser une servlet la plus basique qui puisse exister. Elle affichera
simplement un « Hello World » lors de son appel par le client.

Voici son code :

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.GenericServlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public final class HelloServlet extends GenericServlet {

public void service (ServletRequest req, ServletResponse res)
throws IOException
{
res.setContentType (“text/html”);
PrintWriter pageWriter = res.getWriter();
pageWriter.println(“<html>”);
pageWriter.println(“<body>”);
pageWriter.println(“Hello World”);
pageWriter.println(“</body>”);
pageWriter.println(“</html>”);
}
}

2.2.3.1.Compilation
Si vous souhaitez exécuter la compilation à partir d’une ligne de commande et du compilateur javac
du JDK vous devrez vérifier que votre variable d’environnement : CLASSPATH contient bien un
chemin pointant vers les fichiers JAR des servlets standard (librairies).

Ensuite vous n’avez qu’à exécuter la commande :

javac HelloServlet.java

Le compilateur vous crée un fichier de classe qu’il faudra mettre à un endroit précis en fonction du
moteur de servlets que vous utilisez.

2.2.3.2.Déploiement
Pour utiliser votre servlet il faut maintenant la déployer.

                                       2. Apache Tomcat et Sun Application
                                          Server
Nous allons déployer la servlet basique pour l’exemple.
Tout d’abord, il faut que vous ayez au moins un « context path » sur votre serveur (nous utiliserons le
context : localhost).

Voici l’arborescence que l’on doit utiliser pour faire fonctionner les servlets :

/ => Racine du context
/.classpath => Fichier de définissions des chemins aux librairies
/WEB-INF/classes => Dossier qui contient les packages et servlets
/WEB-INF/lib => Dossier qui peut contenir des librairies externes
/WEB-INF/web.xml => Fichier de configuration de l’application
/work/ => Dossier de travail pour le moteur de servlet

Nous plaçons donc notre fichier : HelloServlet.class dans le dossier : /WEB-INF/classes/.
Nous devons maintenant indiquer au moteur de servlet que nous souhaitons utiliser ce context en tant
que « web-application ». Cela se fait via le fichier web.xml.

Voici ce que doit contenir au minimum notre fichier pour que notre servlet puisse fonctionner :


<?xml version="1.0" encoding="ISO-8859-1"?>

<!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>

<display-name>Web.xml de base</display-name>
<description>
Fichier xml de définition d’une application Web-apps de base.
</description>

<!-- Définition des servlets présentes dans l’application -->

<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>

<!-- Mappage de noms pour les servlets -->

<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/servlet/Hello</url-pattern>
</servlet-mapping>

</web-app>


Nous pouvons remarquer que nous spécifions que c’est une web-app par le biais de la balise : <web-
app></web-app>

Nous indiquons alors que cette web-app contient la servlet HelloServlet qui correspond à la classe
HelloServlet(.class).

Et finalement nous « mappons » le path : servlet/Hello à la servlet. Cela nous permet alors d’accéder
au résultat de la servlet via l’adresse : http://localhost:8080/localhost/servlet/Hello

Remarque : il faut bien respecter l’ordre des balises, cela signifie que vous ne pouvez pas mettre une
balise <servlet> suivie d’une balise <servlet-mapping> puis à nouveau une balise<servlet>. En
effet, il n’est pas certain que tous les serveurs parsent correctement ce genre de fichier.


3.Les Servlets
3.1.Présentation
Les servlets représentent l’alternative dans le monde Java à la programmation des CGI (ou Common
Gateway Interface). C’est une classe java qui, chargée dynamiquement, permet d’accroître les
capacités d’un serveur Web et de répondre à des requêtes dynamiquement. Le fait qu’elles
s’exécutent dans une machine virtuelle Java (JVM) sur le serveur, leur assure une portabilité complète
et une sécurité accrue.
Contrairement aux applets, les servlets n’imposent pas au client d’avoir le support de Java car elles
s’exécutent côté serveur.

3.1.1.Avantages
Les servlets ont énormément d’avantages comparés aux simples CGI. En effet, les servlets peuvent
être toutes gérées par des threads séparés au sein du même processus ou par des threads dans
plusieurs processus répartis sur différents serveurs.
L’autre gros avantage des servlets est qu’elles sont portables d’un système d’exploitation à l’autre
mais aussi d’un serveur Web à l’autre.

Voici une liste non exhaustive des avantages :

    •   Efficacité
             o Semi compilées
             o Résidentes
             o Multithreads
             o Gestion du cache
             o Connexions persistantes (pour les bases de données par exemple)

    •   Pratique
            o Gestion des cookies
            o Suivi de session
            o Simplification pour la manipulation du protocole HTTP
            o Portables (sur tous les systèmes d’exploitations supportant java)
            o Il existe un grand nombre de solutions gratuites

    •   Puissance
            o Communication bidirectionnelle avec le serveur Web
            o Échange de données via URI
            o Partage de données entre servlets
            o Chaînage de servlet (inclusion de servlet dans une autre …)


3.1.2.Inconvénients
Le seul inconvénient des servlets est qu’elles sont limitées en matière d’interface graphique car elles
s’exécutent côté serveur. Cependant il est tout à fait possible de les « coupler » à des technologies
telles que Flash, Applet … Afin de rendre le contenu et les données beaucoup plus interactives.


3.2.Implémentation de base

3.2.1.Structure fondamentale
Nous avons vu précédemment comment installer et configurer un serveur Web et un moteur de
servlets. Nous allons maintenant nous attarder sur le concept même des servlets avec leur conception
proprement dite.

3.2.1.1.Interface Servlet
Pour qu’une classe représente une servlet, il faut impérativement qu’elle implémente l’interface Servlet
(du package javax.servlet) directement ou indirectement. Cette interface oblige à implémenter les
méthodes suivantes :

    •   La méthode public void init( ServletConfig cfg )
    •   La méthide public void service ( ServletRequest req, ServletResponse res)
    •   La méthode public void destroy ()
    •   La méthode public ServletConfig getServletConfig ()
    •   La méthode public String getServletInfo ()
Nous étudierons ces méthodes en détail ci-dessous.
Remarque : les trois premières méthodes représentent le cycle de vie d’une servlet.
L’utilisation de l’interface Servlet n’est pas très courante. En effet différentes classes abstraites ont été
développées afin de simplifier la tâche au développeur.




3.2.1.2.Classe GenericServlet
Cette première classe est la plus basique. Elle appartient elle aussi au package java.servlet. Il vous
suffit tout simplement, dans une classe fille, d’implémenter la méthode : public void service
( ServletRequest req, ServletResponse res).

Voici un exemple de servlet implémentant cette classe :

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.GenericServlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public final class HelloServlet extends GenericServlet {

public void service (ServletRequest req, ServletResponse res)
throws IOException
{
res.setContentType (“text/html”);
PrintWriter pageWriter = res.getWriter();
pageWriter.println(“<html>”);
pageWriter.println(“<body>”);
pageWriter.println(“Coucou”);
pageWriter.println(“</body>”);
pageWriter.println(“</html>”);
}
}

3.2.1.3.Classe HttpServlet
Cette classe est un peu plus évoluée et davantage orientée développement Web. Elle est cependant
inclue dans le package : javax.servlet.http. De même que pour la classe précédente, nous pouvons
simplement implémenter la méthode : public void service ( ServletRequest req, ServletResponse res)
cependant une alternative s’ouvre à nous. Comme le protocole HTTP permet de transmettre des
données de différentes manières (GET ou POST), cette classe possède les méthodes s’y rapportant.
Cela permet d’implémenter les méthodes indépendamment des méthodes que l’on souhaite utiliser.

    •   La méthode public doGet ( ServletRequest req, ServletResponse res) est appelée lors
        d’une requête de type GET.

    •   La méthode public doPost ( ServletRequest req, ServletResponse res) est appelée lors
        d’une requête de type POST.


Vous pouvez également trouver d’autres méthodes public doXxx (ServletRequest req,
ServletResponse res) avec Xxx = ( Put, Delete, Options …)

Remarque : si vous ne souhaitez pas différencier le type de méthode utilisée pour la requête, vous
pouvez soit utiliser la classe GenericServlet, soit n’implémenter que la méthode service() dans votre
servlet.

3.2.2.Cycle de vie
Nous avons pu voir que le moteur de servlet n’utilise qu’une seule instance par servlet et que chaque
requête cliente a pour résultat un nouveau thread qui est transmis à doGet() ou à doPost() ou bien
service() (selon les cas). Nous allons ici examiner comment les servlets sont initialisées, utilisées et
détruites. Ces étapes s’appellent le « Cycle de vie » d’une servlet.




3.2.2.1.La méthode init()
La méthode init() est appelée uniquement lors du premier appel à la servlet (soit via un appel client
soit lors du démarrage du serveur en fonction de votre configuration serveur). Par conséquent, elle est
employée pour effectuer les opérations de paramétrage et d’initialisation de la servlet.
Il existe deux prototypes de la méthode init().

    •   Le premier ne prend aucun argument et est utilisé lorsque la servlet n’a pas à lire de
        paramètre variant d’un serveur à l’autre.

La définition de celle-ci ressemble à ceci :

public void init () throws ServletException {
// Initialisation Code
}

    •   La seconde version de init est utilisée lorsque le servlet a besoin d’accéder aux paramètres de
        configuration du serveur afin de s’adapter au serveur sur laquelle elle est lancée. On peut très
        bien penser récupérer les paramètres de connexion à une base de données par exemple, un
        fichier de mots de passes ou bien d’autres choses encore.

Le second prototype ressemble à ceci :

public void init (ServletConfig cfg) throws ServletException {
super.init(cfg);
// Initialisation Code
}

Cette méthode admet un argument de type ServletConfig qui permet de récupérer les valeurs des
différents paramètres de configuration grâce à la méthode getInitParameter() qui demande en entrée
le nom du paramètre et retourne, en sortie, sa valeur.
La première ligne du corps de la méthode fait appel à super.init(). Cette ligne ne doit pas être oubliée,
en effet la méthode init() de la classe parent enregistre l’objet ServletConfig à un endroit précis dans
la servlet qui est utilisé par la suite.

Remarque : si vous décidez de surcharger la méthode init(), n’oubliez surtout pas l’appel à
super.init() car vous risquez d’avoir des problèmes …

3.2.2.2.La méthode service
Cette méthode est appelée pour chaque requête reçue. Cette méthode vérifie le type de requête et
appelle automatiquement soit doGet(), doPost(), doTrace() … Dans une servlet, qui doit traiter les
requêtes POST et GET de la même manière, vous pourriez être tenté de surcharger service() mais ce
n’est pas la bonne solution ! En effet, cela risquerait de nuire à l’évolution de votre servlet. Il vaut
mieux dans ce cas appeler doPost() via doGet() ou inversement.
Grâce à cette méthode, vous pourrez, par la suite, surcharger les méthodes doPut(), doTrace(),
doOptions() soit directement dans la servlet soit dans les « servlets filles ».

Voici le prototype de cette méthode :

public void service (ServletRequest req, ServletResponse res)throws IOException

3.2.2.3.La méthode destroy
Le serveur peut demander la suppression de l’instance de la servlet, soit par demande explicite de
l’administrateur (redémarrage du serveur par exemple), soit parce que la servlet demeure inactif
pendant une trop longue période. Le serveur appelle alors la méthode destroy de la servlet afin que
celle-ci effectue toutes les opérations de destruction. Vous pouvez retrouver des opérations de
fermeture de connexion à une base de données, d’interruption de threads lancés en tâches de fond,
fermer un fichier…
Remarque : un serveur Web peut très bien s’interrompre à cause d’un bug ou autre problème
technique (une personne peut très bien débrancher la prise de courant !) c’est pour cela qu’il ne faut
pas faire confiance à 100% à l’appel automatique de cette méthode et faire des sauvegardes
d’éléments principaux durant l’exécution de la servlet.
Voici le prototype de cette méthode :

public void destroy()

3.2.3.Lire la requête
La lecture des données de la requête se fait via l’instance de l’objet : HttpServletRequest passée en
paramètre des méthodes doGet, doPost …

Depuis la requête, il est possible de récupérer tous les paramètres passés au ServeurWeb par le
client :
Il existe quatre méthodes pour cela :

    •   La méthode getParameter ( String name ) : retourne la valeur d’un parameter à partir de son
        nom

    •   La méthode getParameterValues ( String name ) : retourne une liste de valeurs d’un
        parameter à partir de son nom (utilisé par exemple dans une liste à choix multiples)

    •   La méthode getParameterMap ( String name ) : retourne une liste d’associations de
        paramètres et de valeurs

    •   La méthode getParameterNames ( String name ) : retourne la liste de tous les noms des
        paramètres passés au serveur web

Cet objet permet également de récupérer des informations concernant le client :

    •   La méthode getRemoteAddr () : retourne l’IP du client
    •   La méthode getRemoteHost () : retourne le nom complet de l’hôte client

De la même façon, nous pouvons récupérer des informations concernant le serveur :

    •   La méthode getServerName () : retourne le nom complet du serveur
    •   La méthode getServerPort () : retourne le numéro du port utilisé par le serveur


Vous pouvez également accéder à l’ensemble des variables d’environnement du serveur (comme
pour les scripts CGI).
Voici un tableau récapitulatif des méthodes à utiliser :


              Variable d’environnement CGI        Méthodes d’une servlet HTTP



              SERVER_NAME                         getServerName()

              SERVER_PROTOCOLE                    getProtocole()

              SERVER_PORT                         getServerPort()

              REQUEST_METHOD                      getMethod()

              PATH_INFO                           getPathInfo()

              PATH_TRANSLATED                     getPathTranslated()

              SCRIPT_NAME                         getServletPath()

              DOCUMENT_ROOT                       getServletContext().getRealPath(« / »)
              QUERY_STRING                        getQueryString()

              REMOTE_HOST                         getRemoteHost()

              REMOTE_ADDR                         getRemoteAddr()

              AUTH_TYPE                           getAuthType()

              REMOTE_USER                         getRemoteUser()

              CONTENT_TYPE                        getContentType()

              CONTENT_LENGTH                      getContentLength()

              HTTP_ACCEPT                         getHeader(« Accept » )

              HTTP_USER_AGENT                     getHeader(« User-Agent » )

              HTTP_REFERER                        getHeader(« Referer » )



3.2.4.Écrire la réponse
La réponse à la requête se fait via l’instance de l’objet : HttpServletResponse passée en paramètre
des méthodes doGet(), doPost() … (Nous noterons l’instance de cet objet : « res »).

3.2.4.1.Type de contenu
La première chose à faire est d’indiquer à la servlet le type de données qu’elle va renvoyer. En effet
vous pouvez très bien retourner du html, une image générée …
La méthode : setContentType() vous permettra d’effectuer cela. Elle prend en paramètre le type de
contenu. Voici quelques exemples d’utilisation de cette méthode :

res.setContentType (“text/html”) // page html
res.setContentType (“text/xml”) // page xml
res.setContentType (“image/jpeg”) // image jpeg

Il est possible de renvoyer tout type de contenu en spécifiant exactement le bon type mime.

3.2.4.2.Écriture
L’écriture des données de type texte dans la réponse se fait par l’objet : PrintWriter que l’on peut
récupérer par la méthode : getWriter().

PrintWriter out = res.getWriter();
out.println(“<HTML>”);
out.println(“<BODY>”);
out.println(“Bonjour le monde”);
out.println(“</BODY>”);
out.println(“</HTML>”);
out.close();

Si votre réponse renvoie des données binaires, il est préférable d’utiliser l’objet ServletOutputStream
dont une instance est retournée par la méthode : getOutputStream()

Voici un exemple pour retourner une image à partir d’une image locale :

res.setContentType (“image/gif”);
ServletOutputStream out = res.getOutputStream();
FileInputStream fis = new FileInputStream (new File (“./logo.gif”));
while (fis.available() > 0)
{
fis.read (buf,0,buf.length);
out.write(buf);
out.flush();
}
fis.close();
out.close();

3.3.Implémentation avancée

Nous avons vu, dans la partie précédente, les bases pour le développement de servlet. Nous allons à
présent nous attarder sur des concepts plus avancés.

3.3.1.Les cookies

3.3.1.1.Principe
Les cookies sont de petites informations textuelles envoyées par le serveur et renvoyé par le client,
sans modification de sa part, à chaque visite ultérieure. Cette technique peut être très intéressante
pour garder une trace des visiteurs (savoir s’il est déjà venu sur le site ou non …).

3.3.1.2.Avantages
Les cookies permettre de garder un lien entre le client et sa session sur un serveur par le biais d’un
identifiant de session par exemple.
L’utilisation des cookies est très simple à mettre en place et permet de gérer un ensemble de
fonctionnalités intéressantes pour le visiteur.

3.3.1.3.Inconvénients
Les cookies sont transmis à chaque requête, cela peut alourdir les demandes et réponses du client si
ces cookies sont utilisés abusivement.
L’utilisation de cookies n’est pas à préconiser pour une utilisation sécurisée, en effet les valeurs de
chaque paramètre sont passées en clair dans la requête et peuvent être récupérées par un pirate
assez facilement.
Les cookies ne sont pas toujours acceptés par le client, cela peut poser un énorme problème
lorsqu’un système n’a pas de moyen en parallèle mis en place.

3.3.1.4.Création de cookies
Les servlets ont leur propre API de cookies. Les cookies sont représentés par l’objet Cookie. Voici
une description de cette classe :

    •   Le constructeur Cookie (String name, String value) : constructeur qui prend en paramètre le
        nom du cookie et sa valeur assignée.

Une fois l’objet cookie instancié, vous pouvez spécifier certains attributs de celui-ci (ce sont des
« setters ») ou récupérer une information d’un cookie via le « getter » correspondant :

    •   La méthode String getComment(): retourne le commentaire
    •   La méthode setComment (String): assigne un commentaire

    •   La méthode String getDomain(): retourne le domaine
    •   La méthode setDomain (String domainPattern) : assigne un domaine

    •   La méthode int getMaxAge(): retourne la durée en secondes avant que le cookie soit périmé
    •   La méthode setDomain (int lifetime) : indique la durée en secondes avant que le cookie soit
        périmée, la valeur 0 indique que le cookie doit être supprimé.

    •   La méthode String getName() : retourne le nom du cookie
    •   La méthode setName (String name) : assigne un nom au cookie

    •   La méthode String getPath () : retourne le chemin d’un cookie
    •   La méthode setPath (String path) : assigne un chemin au cookie (permet d’indiquer que le
        cookie n’est utilisé que pour certains niveaux dans le site)
     •    La méthode boolean getSecure () : retourne true si le cookie est transmis que pour les
          connexions sécurisées
     •    La méthode setSecure (boolean secureFlag) : positionne le drapeau d’indication de
          transmission.

     •    La méthode String getValue () : retourne la valeur du cookie
     •    La méthode setValue (String cookieValue) : assigne ou modifie la valeur du cookie

Une fois le cookie paramétré il faut le rajouter à l’en-tête http : Set-Cookie. Pour cela vous avez juste à
appeler la méthode addCookie de l’objet de réponse de votre servlet.

Voici un exemple de création de cookie :

Cookie cookieDejaVenu = new Cookie(“dejaVenu”, “1”);
cookieDejaVenu.setMaxAge(60 * 60 * 24 * 365); // 1 an
res.addCookie(cookieDejaVenu);

3.3.1.5.Lecture des cookies
Les cookies reçus sont stockés dans un tableau que l’on peut récupérer par l’instance de l’objet
HttpServletResquest via la méthode getCookies(). Celle-ci renvoie un tableau dont la longueur
représente le nombre de cookie envoyé (si la longueur est égale à nulle, aucun cookie n’a été
envoyé). Pour obtenir un cookie en particuliers à partir de son nom vous devez parcourir le tableau en
appelant pour chaque élément la méthode getName() jusqu’à temps d’arriver sur le cookie qui vous
intéresse.
Une fois récupérer vous avez juste à appeler la méthode getValue() pour récupérer son contenu.

Voici un exemple de création de cookie :


public void doGet (HttpServletRequest req,HttpServletResponse res )
throws ServletException, IOException
{
// Initialisation
PrintWriter out = res.getWriter();
res.setContentType (“text/html”);
Cookie [] tabCookies = req.getCookies();
Cookie cookie;

// Boucle qui affiche tous les cookies
for (int i = 0; i < tabCookies.length; i++)
{
cookie = tabCookies [i];
out.println(“Cookie : ” + i + “<br>” +
“ - Nom : ” + cookie.getName() + “<br>” +
“ - Valeur : ” + cookie.getValue() + “<br>”);

}
out.flush();
out.close();
}

Si vous voulez supprimer un cookie, il suffit d’attribut une durée de vie à 0 à votre objet cookie :
c1.setMaxAge(0);

3.3.2.Les sessions

3.3.2.1.Présentation
Nous avons vu précédemment comment garder des informations chez le client par le biais de cookie.
Cependant ce mécanisme est très limité, c’est donc pour cela que le système de session a été mis en
place. Lorsque l’on parle de session, l’activité se passe côté Serveur et non Client (contrairement aux
cookies). Les sessions sont très utilisées dans les applications web et plus particulièrement dans les
sites e-business … Elles permettent de stocker des informations pour chaque client côté serveur.

3.3.2.2.Avantages
Les sessions sont exécutées côté serveur ce qui leur permet d’être beaucoup plus sécurisées que les
cookies.
Un seul identifiant est nécessaire pour lier le serveur et le client car les données sont stockées sur le
serveur. Les sessions sont simples d’utilisation grâce à l’API de suivi de session des servlets.

3.3.2.3.Inconvénients
Un lien doit être mis en place entre le client et le serveur (identifiant de session) soit par cookie soit
par le mécanisme de réécriture d’URL.

3.3.2.4.API de suivi de session
Les servlets possèdent une API pour la gestion des sessions. L’utilisation de sessions dans les
servlets est très simple car il suffit de récupérer l’objet session lié à la requête actuelle. Si aucun objet
de session n’a encore été créé, on peut décider de le créer automatiquement.
La classe décrivant une session est : HttpSession.

Voici une description de chacun des méthodes de celle-ci :

    •   La méthode Object getValue () ou Objet getAttribute (String name) : retourne la valeur d’un
        attribut de la session précédemment ajouté.

Remarque : la méthode getValue() est à remplacer par getAttribute() depuis la version 2.2 de l’API
des servlets.

    •   La méthode putValue (String name, Object value) ou setAttribute (String name, Object
        value) : associe un objet à un nom d’attribut de la session.

Remarque : la méthode putValue est à remplacer par setAttribute depuis la version 2.2 de l’API des
servlets.

    •   La méthode removeValue (String name) ou removeAttribute (String name) : supprime un
        attribut à partir de son nom.

Remarque : la méthode removeValue est à remplacer par removeAttribute depuis la version 2.2 de
l’API des servlets.

    •   La méthode String [] getValueNames() ou Enumeration getAttributeNames () : retourne
        les noms de tous les attributs de la session.

Remarque : la méthode getValueNames est à remplacer par getAttributeNames depuis la version
2.2 de l’API des servlets.

    •   La méthode String getId() : retourne l’identifiant unique de la session (il peut être utilisé
        comme une clé ).

    •   La méthode boolean isNew() : retourne true si la session vient d’être créées (nouvelle
        session) ou false dans le cas d’une session déjà existante.

    •   La méthode invalidate() : invalide l’ensemble de la session et libère tous les objets qui y sont
        associés.

3.3.2.5.Gestion de la session
Tout d’abord il vous faut récupérer l’objet session associé à la requête cliente, pour appeler la
méthode : getSession de l’objet HttpServletRequest.

HttpSession session = req.getSession (true);

Pour que la session soit créée automatiquement (dans le cas où elle n’existerait pas), nous passons
true en paramètre à la méthode getSession.
Remarque : Si on passait false en paramètre, la méthode getSession nous retournerait un objet null.

Avec cet objet session, vous allez pouvoir lui ajouter des objets (méthode setAttribute), en retirer
(méthode removeAttribute) ou en lire (méthode getAttribute).
3.3.3.L’interface SingleThreadModel
Nous avons vu qu’une servlet n’a qu’une seule instance qui est ‘attaquée’ par plusieurs threads à la
fois (1 thread par client). Ceci peut parfois être gênant lorsque l’on veut, par exemple, qu’un objet de
connexion à une base de données ne soit pas partagé parmi les threads, la modification d’un fichier
précis …
Une première solution serait d’affecter l’attribut synchronized à la méthode service de la servlet.

Cependant il existe un meilleur moyen de gérer cela. En effet, il existe l’interface SingleThreadModel
qui permet d’indiquer au moteur de servlets que l’on souhaite qu’une instance de la servlet ne soit
« attaquée » que par un Thread à la fois.

Cette interface ne comporte aucune méthode à implémenter, elle ne représente qu’un drapeau pour le
moteur de servlet.

Finalement cette interface est pratique car elle permet en quelques sortes de sécuriser les accès
concurrents, cependant ce n’est pas la meilleure solution. L’idéal est de créer dans le cas d’une
connexion à la base de données, une gestion de pool de connexions multithreadées. En effet, il se
peut que la base de données n’accepte qu’un nombre restreint de connexions (qui peut être dépassé
par le nombre de clients accédant à la servlet).

3.3.4.Utilisation du ServletContext
On appelle contexte associé à la servlet (ServletContext) l’environnement au sein duquel la servlet
s’exécute. L’objet ServletContext est créé par le containeur de servlet et permet d’accéder à des
informations rattachées à l’environnement.
L’objet créé est partagé par toutes les servlets du containeur. Cela permet donc de partager des
informations entre les servlets.

3.3.4.1.Description de l’objet ServletContext
Voici une description des méthodes principales de l’objet ServletContext :

    •   La méthode Object getAttribute(String name) : retourne l’attribut associé à name

    •   La méthode setAttribute(String name, Object o) : assigne un objet à un nom d’attribut

    •   La méthode removeAttribute(String name) : supprime un attribut du context

    •   La méthode Enumeration getAttributeNames() : retourne la liste des attributs du contexte

    •   La méthode int getMajorVersion() : retourne la dernière version supportée par
        l’environnement

    •   La méthode int getMinorVersion() : retourne la première version minimale supportée

3.3.4.2.Utilisation l’objet ServletContext
L’accès au context se fait par le biais de la méthode getServletContext() de la servlet. L’objet
récupéré est du type ServletContext.

4.Les Java Server Pages

4.1.Présentation

Nous venons donc de voir les diverses utilisations d’une servlet avec leurs avantages et leurs
inconvénients.
Les servlets, classes java étendant la classe javax.servlet.http.HttpServlet, mettent l’accent sur le
code java. Les pages JSP sont, elles, plutôt proche du code html.
En effet, une page JSP ressemble beaucoup à une page html à laquelle on a ajouté du code java
encapsulé dans des balises (<% %>). Elle va cependant réutiliser des fonctionnalités de la classe
servlet telles que la variable request de la classe HttpSerlvetRequest ou la variable out de la classe
javax.servlet.ServletOutputStream.
Au contraire des servlets une page JSP va nous permettre de séparer clairement deux types de code
(le traitement de la requête et la génération du flux HTML).

Il est très aisé de programmer à l’aide d’une page JSP ; en effet, il n’est pas nécessaire d’avoir une
grande connaissance de l’API sous-jacente.
Par ailleurs, contrairement aux servlets, le webmaster va pouvoir modifier le code html sans avoir à
toucher au code java.

Comme nous venons de le voir, une page JSP peut à la fois contenir du code Java et du code html.
Le html va composer la structure statique de la page tandis que le code JSP composera les éléments
dynamiques de la page. Ces éléments peuvent être de 3 types :

    •    Eléments de scripting
    •    Directives
    •    Eléments d’action

4.2.Eléments de base du scripting

Il existe 4 types d’éléments de scripting dans une page JSP : les scriplets, les commentaires, les
déclarations et les expressions.

Les scriplets, <% mon code java %>, sont des blocs de code java intégrés au sein d’une page html.
Ces caractères vont permettre d’indiquer le langage à utiliser pour traiter le code encapsulé.

Exemple :

<%String s = "Hello!"; %>

Les commentaires, <%-- mes commentaires --%>, comme leur nom l’indique vont vous permettre
d’insérer des commentaires dans votre code. Vous pouvez évidemment utiliser les balises de
commentaire html, mais l’avantage des balises jsp est que les commentaires ne seront pas visibles
des utilisateurs.

Les déclarations, <% ! %>, vont permettre de déclarer des méthodes et des variables d’instance,
connues de toute la page jsp, afin qu’elles soient intégrées dans la servlet résultante. Elles peuvent
être placées puis utilisées n’importe où dans la page jsp.

Les expressions, <%= %>, sont évaluées lorsqu’une page JSP est demandée et leurs résultats sont
convertis en une chaîne transmise à l’objet out implicite. Aucun point-virgule n’est requis à la fin d’une
expression.

Exemple:

Message :<%= s%>

Affichage:

Hello!

4.3.Les objets implicites en JSP

Lorsque vous allez coder en java à l’intérieur de vos scriplets, vous aurez accès à une liste
d’éléments, appelés objets implicites, qui vous permettront d’interagir avec l’environnement de la
servlet d’exécution.

    •    L’élément request fait référence à l’objet javax.servlet.http.HttpServletRequest associé à
         la requête. Il va permettre d’accéder aux paramètres POST, GET, ou les en-têtes des
         requêtes http tels que les cookies.

    •    L’élément response fait référence à l’objet javax.servlet.http.HttpServletResponse envoyé
         avec la réponse du client. Il n’est pas souvent utilisé, puisqu’on lui préfère le flux de sortie out.

    •    L’élément out fait référence à l’objet javax.servlet.jsp.JspWriter est un flux de sortie
         permettant d’envoyer la réponse au client.

    •    L’élément config fait référence à l’objet javax.servlet.ServletConfig de la page.
    •   L’élément pageContext fait référence au contexte de la page, représenté par l’objet
        javax.servlet.jsp.PageContext. Il va permettre d’accéder à différents attributs de la page.

    •   L’élément application fait référence à l’objet javax.servlet.ServletContext. Il va permettre
        d’enregistrer des données globales tout comme les variables d’instance. La différence est que
        ces données vont être partagés par toutes les servlets du moteur de servlets.

    •   L’élément page fait référence à une instance de la page elle-même tout comme le fait this
        dans une classe java.

    •   L’élément session fait référence à l’objet javax.servlet.http.HttpSession.

    •   L’élément exception fait référence à l’objet java.lang.Throwable. Il permet d’attraper les
        exceptions.

4.4.Exemple d’utilisation avec un formulaire

On souhaite créer un formulaire très simple permettant d’entrer des données dans un champ de texte
puis de les afficher dans une page suivante que l’on accèdera à l’aide d’un bouton.




Figure 1 : Formulaire.html




Figure 2 : ResultForm.jsp

Voici le code correspondant aux pages JSP Formulaire.html et ResultForm.jsp :

FirstJSP.html :

Nous ne attarderons pas sur cette partie de code puisque ce fichier ne contient que du code html.

<html>
<head>
<title>Mon Formulaire</title>
</head>
<BODY>
<H3>Mon Formulaire</H3>
<table><tbody>
<tr><td>
<form action="ResultForm.jsp" method="post" >
Votre nom <input type=text name="nom"></td></tr>
<tr><td> Votre prenom <input type=text name="prenom"></td></tr>
<tr><td><input type="submit" name=submit value="Valider">
<input type="submit" name=submit value="Reset"></td></tr>
</form>
</tbody>
</table>
<br><br>
</BODY>
</html>

Result .jsp :

Cette partie de code est plus intéressante ; on peut voir ici comment récupérer des données envoyées par un
formulaire.

Ainsi, grâce à la méthode getParameter(String s) de la variable request, on peut récupérer la valeur des
variables passées en paramètre.

<html>
<head>
<title>Résultats de mon formulaire</title>
</head>
<body>
<H3>Mes informations</H3>

<%
if(request.getParameter("submit").equals("Valider"))
{
%>
<TABLE>
<TBODY>
<TR> <TD><b>Nom: </b><%out.println(request.getParameter("nom"));%></TD></TR>
<TR><TD><b>Prenom: </b><%out.println(request.getParameter("prenom"));%></TD></TR>
<TR><TD><form action="Formulaire.html"><input type=submit value="Retour"></form></TD></TR>
</TBODY>
</TABLE>
<%
}
else
{%> <jsp:forward page="Formulaire.html"/><%
}
%>
</body>
</html>


4.5.Eléments d’action : Inclusions de scripts et balises « jsp: »

Un élément d’action est constitué de balises que l’on peut intégrer dans une page JSP.
Voici ci-dessous la description des actions JSP standard.

    •    jsp :useBean


La balise useBean va permettre de séparer la partie traitement du code de la partie présentation. En
effet, on va avoir des classes java, qui vont nous permettre de créer les JavaBeans. Ceux-ci vont
récupérer différentes données de la page JSP, les traiter et éventuellement retourner des données
que l’on pourra afficher dans la page JSP.

Un JavaBean est un objet représenté une classe JAVA qui doit respecter les règles suivantes :

                o   Elle doit comporter au moins un constructeur sans arguments
                o   Aucuns des attributs ne doivent être publics
                o   Chaque attribut (property) doit avoir un getter et un setter.
                o   Pour les attributs booléens, on doit remplacer getXXX() par isXXX().


//SES ATTRIBUTS….
La balise useBean permet d’instancier un composant JavaBean qui pourra être appelé dans la page
JSP.

MonBean.java

public class MonBean {
private int nb ;
private boolean empty ;

public MonBean(){
}
public int increm(int i){return nb+10;}
public int getNb(){return nb ;}
public void setNb(int nb){this.nb=nb ;
public boolean isNb(){return empty ;} ;
}

TestBean.jsp

<jsp :useBean id=« testB » >

       •   Les balises jsp :setProperty et getProperty


Ces balises vont permettre de récupérer ou modifier les valeurs d’une instance de JavaBean.
Il y a 3 manières de faire :

Une étoile pour property permet d’attribuer automatiquement à chaque attribut de l’objet bean les
valeurs récupérer d’un formulaire par exemple.
<jsp :setProperty name = « testB » property = « * »/>

On peut indiquer directement le nom de la propriété à laquelle on veut attribuer une valeur.
<jsp :setProperty name = « testB » property = « nomProp »/>

On peut indiquer directement le nom de la propriété à laquelle on veut attribuer une valeur et, de plus
indiquer la valeur grâce à l’attribut value.

<jsp :setProperty name = « testB » property = « nomProp » value = « Jean » />

       •   Les balises jsp : include et param


4.6.Directives

Les directives sont des messages envoyés au container JSP qui vont indiquer au container la façon
dont il doit transcrire une page en servlet. Ces directives sont repérables grâce aux caractères <%@
%>.
Il existe 3 types de directives ; les directives de pages, les directives d’inclusions et les balises
personnalisés.

4.6.1.Directives de pages
Ces directives se présentent sous la forme suivante : <%@page... %>. Elles prennent en paramètre
un certain nombre de paramètres listés ci-dessous:


Paramètre         Rôle du paramètre                                            Valeurs possibles

langage           Indique le langage utilisé                                   « java »

info              Le texte contenu dans ce paramètre, contenant des            dépend du container de la
                  informations sur la page sera récupérable grâce à la         jsp
                  méthode servlet.getServletInfo()

contentType       Indique le type MIME(Multipurpose Internet Mail              text/html;charset=ISO-
                  Extension) de la page ainsi que le jeu de caractères         8859-1
                  utilisés

extends           Indique la classe mère de la servlet générée. Cet attribut   « class »
                  est à manipuler avec précaution.

import            Indique les inclusions de package ou de classes.             Aucune
                  Plusieurs localisations peuvent être spécifiées, séparées
                 à l’aide de virgule.
                 <%@page import="java.util.*"%>
                 <%@page import="java.util.*,java.io.*"%>
                 Il correspond à l’import dans une classe java.


buffer           Par défaut, le contenu d’une page JSP est bufferisé pour        8192b
                 accroître les performances.

autoFlush        Indique si le tampon doit être vidé lorsqu’il est plein.        « true »/« false »

session          Indique si la page courante peut accéder aux données            « true »/« false »
                 stockées dans la session.

isThreadSafe     Indique si le modèle SingleThreadModel (vu au point             « true »/« false »
                 3.3.3) est utilisé ou non.

errorPage        Toute exception non gérée dans le code sera remontée            « url »
                 à la classe mère de la JSP.

isErrorPage      Indique au conteneur J2EE que la servlet générée sert           « false »
                 de page d’erreur.
* les valeurs en gras sont les valeurs par défaut

4.6.2.Directives d’inclusions




Ces directives permettent d’inclure le contenu d’un autre fichier dans la page JSP courante à l’aide de
la syntaxe suivante, « <%@include file= ‘‘UrlRelative’’%>.
Ce type d’inclusion est réalisé avant la compilation de la JSP en servlet


4.6.3.Directives des tag-libs
Ces directives définissent l'adresse et le préfixe d'une librairie de balises (tags) pouvant être utilisés
dans la page.
<%@ taglib uri="tagLibraryURL" prefix="tagPrefix" %>
Elles permettent d'aller créer de nouveaux tags de la forme :
<tagPrefix:NomTag attribut1="valeur" ... %> ... </tagPrefix:NomTag>

Nous étudierons plus précisément ces directives dans le chapitre suivant (4.7).
La balise include est utilisée pour intégrer des ressources statiques ou dynamiques dans une page
JSP. Elle va permettre d’inclure des fichiers au moment de la requête.

Il est aussi possible d’inclure un fichier au moment de la traduction de la page, juste avant la
compilation (4.4.2).
Afin de passer des informations à la ressource à inclure il suffit d’ajouter la ligne suivante :

<jsp:include page=“Chemin relatif de la page” flush=“true”/>
<jsp :param name= “PARAM1” value= ‘‘VALUE1’’ />
<jsp :param name= “PARAM2” value= ‘‘VALUE2’’ />(...)
</jsp :include>

    •   jsp:forward

Cette balise est utilisée pour passer le contrôle de la requête à une autre ressource dynamique ou
statique.
Afin de passer des informations à la ressource à inclure, il suffit de procéder de la même manière que
la balise include, avec la balise param.

Exemple :

<jsp:forward page="Redirect.jsp">
<jsp:param name="redir" value="ca marche"/>
</jsp:forward>

Ainsi, dans la page Redirect.jsp on va pouvoir récupérer la valeur de « redir » à l’aide de la variable
request( request.getParameter(« redir » )).

    •   Les balises jsp:plugin et jsp:fallback


La balise plugin est utilisée pour générer des tags HTML <object> ou <embed> permettant d’indiquer
au navigateur client le chargement du plugin correspondant à la balise.
La balise fallback va permettre de spécifier le message à montrer si le client ne supporte pas
l’affichage d’applet.


4.7.Les balises personnalisées (Taglib)

4.7.1.Présentation
Comme nous l’avons expliqué précédemment, une page JSP est une page de code qui peut contenir
du HTML et du code JAVA. Malgré la puissance offerte par le langage Java, il arrive souvent que l’on
réécrive plusieurs fois la même chose au sein d'une même page JSP (boucles, tests, récupération
d'objets en session).

Afin d’éviter ce type de perte de temps, un mécanisme permettant d'effectuer ces tâches répétitives à
l'aide de balises personnalisées a été mis en place. Ce mécanisme qui définit le fonctionnement de
nouvelles balises s'appelle taglib.

Permettant d'isoler le code Java dans un fichier .java plutôt que dans un fichier .jsp, ces taglib ont
pour but de faciliter l’organisation d’une réalisation d’un site Web en JSP. Ceci est en effet
fondamental pour le développement et surtout la maintenance puisqu’on va permettre d'isoler le travail
des Web designers de celui des développeurs.

Ainsi, on va avoir un fichier TLD qui va décrire les attributs de la balise et un fichier JAVA qui va
décrire les actions de la balise.

4.7.1.1.Rôle du descripteur de déploiement
Le descripteur de déploiement est un fichier XML qui va permettre au container de JSP l’accès au
fichier TLD (en lui indiquant le nom, l’emplacement dans le répertoire...). Il est possible d’éliminer le
rôle du descripteur de déploiement en ajoutant des informations sur le nom et l’emplacement du fichier
TLD directement dans la page JSP. Toutefois cela diminue la flexibilité, car lors de changements, il
convient de modifier tous les fichiers JSP utilisant les customs Tags.
4.7.1.2.Rôle et format du fichier TLD(Tag Librairy Descriptor)
Le fichier TLD est un fichier XML qui va permettre de définir les différentes balises personnalisées.

Syntaxe d’un fichier TLD :


Balise            rôle de la balise

<taglib>          Constitue l’élément racine du fichier


<tlibversion>     version de la librairie de balise


<jspversion>      version des spécifications JSP dont dépend la librairie de balise

<shortname>       nom court pour la librairie

<info>            informations pour la documentation

<uri>             lien vers une source additionnelle de documentation identifiant de manière unique la
                  librairie

<tag>             englobe la définition d’une balise personnalisée


                                          3. L’élément tag

L’élément tag va permettre de définir les différentes caractéristiques de notre balise personnalisée à
l’aide des éléments suivants :


Nom de l’élément                                                         Rôle de d’élément

name(obligatoire)                                                        identifiant pour le tag

tagclass(obligatoire)                                                    Nom de la classe
                                                                         correspondant à la balise

teiclass                                                                 Nom de la classe d’info
                                                                         concernant la tagclass

bodycontent                                                              type de contenu

    •    Empty :pas de contenu pour le tag
    •    JSP : le corps peut être constitué d’éléments JSP

    •    tagdependent : le corps doit être interprété par ce tag

info                                                                      descriptions, infos

attribute

    •    name(obligatoire) :identifiant
    •    required : (true,false)

    •    rtextprvalue:la valeur de cet attribut peut être déterminé au
         moment de la requête(true,false)

                                          4. Exemple de fichier TLD
Dans cet exemple, nous avons une classe JAVA que UpperTag.java qui va permettre de mettre un
texte en majuscule. Nous souhaitons donc définir la balise correspondante dans notre fichier Desc.tld.
Fichier Desc.tld :

<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
<taglib>
<tlibversion>1.0</tlibversion>
<jsp-version>1.2</jsp-version>
<short-name>mesTags</short-name>
<description>Mes premiers Tags</description>
<tag>
<name>Majuscule</name>
<tag-class>UpperTag</tag-class>
</tag>
</taglib>

4.7.2.L’API JSP Custom Tag
La balise personnalisée doit être reliée à un tag handler qui va définir ses actions. Un tag handler est
une classe Java appelée chaque fois que le container de JSP rencontre une balise personnalisée.

Cette classe doit implémenter une interface du package javax.servlet.jsp.tagext où dériver de l’une
des classes de ce package.

Les 3 interfaces les plus importantes sont Tag, IterationTag et BodyTag.

4.7.2.1.L’interface Tag
public interface Tag {

int doEndTag();
int doStartTag();
Tag getParent();
void release();
void setPageContext(PageContext pc);
void setParent(Tag t);

}

4.7.2.2.L’interface IterationTag
L’interface IterationTag étend l’interface Tag permet d’afficher plusieurs fois le contenu d’un tag.

public interface IterationTag {

int doAfterBody();
}

La méthode doAfterBody() est appelée après la méthode doStartTag() et peut retourner
Tag.SKIP_BODY ou Tag.EVAL_BODYAGAIN.

4.7.2.3.L’interface BodyTag
public interface BodyTag {

void doInitBody();
void setBodyContent(BodyContent b)
}

L’interface BodyTag étend l’interface IterationTag.

4.7.2.4.Le cycle de vie d’un Tag Handler
Le cycle de vie d’un tag handler est contrôlé par le container de JSP :

    1. Dans un premier temps, le container de JSP obtient une instance du tag handler à partir du
       pool, ou en crée une nouvelle.
    2. Il appelle ensuite la méthode setPageContext() à laquelle il passe un objet PageContext
        représentant la page JSP qui contient le custom tag.

    3. Le container appelle ensuite la méthode setParent() à laquelle il passe un objet Tag (tag du
        parent) ou un objet null.

    4. Le container de JSP définit ensuite tous les attributs du custom tag. Les attributs sont traités
        comme les propriétés dans un javabean (getters, setters)

    5. Le container appelle ensuite la méthode doStartTag(). Celle-ci peut retourner les valeurs :
           1. Tag.SKIP_BODY : Le tag ne renvoie rien, (Ex : Offre promotionnelle affichée
                seulement pour les personnes abonnées)
            2. Tag.EVAL_BODY_INCLUDE : Le container traite le corps du tag en tant que JSP et il
                est affiché dans la page JSP (ce corps peut être constitué de JSP, de HTML, et
                même d’autres tags)

    6. Quelle que soit la valeur retournée précédemment, le container appelle ensuite la méthode
        doEndTag().Cette méthode retourne la valeur
           1. Tag.SKIP_PAGE : pas de traitement pour le reste de la page JSP
           2. Tag.EVAL_PAGE : reste de la page JSP traitée normalement.

    7. La dernière méthode appelée est la méthode release(). C’est au sein de cette méthode qu’il
        est possible d’écrire du code de libération de la mémoire (fermeture des connexions …).

    8. Enfin, le container de JSP retourne une instance du tag handler dans le pool.

4.7.2.5.Création d’un custom tag
On peut diviser les customs Tags en deux catégories ;

    •   Les balises sans traitement de corps

Il s’agit des balises comme les sauts de ligne (balise <br> en html) qui ne vont pas nécessiter de
modification de contenu.

Pour créer une balise ne nécessitant pas de modification de contenu, on va créer une classe étendant
la classe TagSupport qui elle-même implémente l’interface IterationTag:

public class TagSupport {

int doEndTag();
int doStartTag();
static Tag findAncestorWithClass(Tag from, Class classe);
String getId();
Tag getParent();
Object getValue(String k);
Enumeration getValues();
void removeValue(String k);
void setId(String id);
void setPageContext(PageContext pageContext);
void setParent(Tag t) ;
void setValue(String k, Object o);
}


    •   Les balises avec traitement de corps

Il s’agit des balises comme des mises en gras ou en couleur (balises <b></b> ou bien <font
color></font> en html) qui vont modifier un contenu.

Pour ce type de balises, on va créer une classe étendant la classe BodyTagSupport qui elle-même
étend la classe TagSupport et implémente l’interface BodyTag :

public class BodyTagSupport {
int doAfterBody();
int doEndTag();
void doInitBody();
int doStartTag();
BodyContent getBodyContent();
JspWriter getPreviousOut() ;
void release() ;
void setBodyContent(BodyContent b);
}

4.7.2.6. Syntaxe et utilisation d’un custom tag
Lorsque vous voudrez intégrer votre balise personnalisée dans une page JSP, il suffira de tout d’abord
de ‘déclarer’ la balise en début de page comme suit :


<%@ taglib uri= ‘‘CheminBalisePerso’’ prefix= ‘‘PrefixeDuTag’’ %>
L’attribut uri permet de spécifier le chemin où se trouve le fichier TLD qui décrit la syntaxe de la
balise, tandis que l’attribut prefix permet de définir une instance du custom tag.

Pour utiliser votre balise personnalisée, il suffit alors d’utiliser les expressions suivantes :
< PrefixeDuTag:NomTag attribut1= ‘‘valeur’’/>
< PrefixeDuTag:NomTag> body </tagprefix :Nomtag>

4.7.2.7.La classe BodyContent
Cette classe hérite de javax.servlet.jsp.JspWriter. Elle représente le corps du custom tag (lorsqu’il
existe).
Le BodyContent d’un custom tag peut être récupéré à partir de la méthode
setBodyContent(BodyContent b) de l’interface bodytag à partir du paramètre en argument de la
méthode.

La récupération du body se fait à l’aide de la méthode getBodyContent()
String content = bodyContent.getString() ;

Enfin, l’écriture vers la page JSP se fait à l’aide d’un PrintWriter joint au corps du tag
JspWriter out = bodyContent.getEnclosingWriter() ;

4.7.3. La richesse des CustomTag
Les CustomTag présentent une grande richesse dans la programmation JSP. Là où l’on va pouvoir
réellement profiter de leur puissance, c’est en réutilisant des taglibs prédéfinies, très complètes et
parfaitement réutilisables.

Les JSTL (JavaServer Pages Standard Tag Library) sont des librairies de référence. Elles contiennent
toute une série de balises personnalisées qui vont permettre, par exemple, d’itérer, exécuter des
requêtes SQL, utiliser des documents XML.

Il existe, par ailleurs de nombreuses autres librairies toutes aussi puissantes les unes que les autres.
Voici quelques liens où vous trouverez les plus connues :

http://jakarta.apache.org/taglibs/index.html
http://displaytag.sourceforge.net/install.html
http://struts.apache.org/userGuide/index.html


4.7.3.1. Les JavaServer Pages Server TagLib
Les JSTL présentent donc de nombreuses fonctionnalités. Nous allons présenter quelques exemples
d’utilisation.

Tout d’abord, pour utiliser des balises de cette librairie, il faut utiliser un conteneur d’application
implémentant au moins l’API servlet 2.3 et l’API JSP 1.2.

Tous les fichiers nécessaires à leur utilisation se trouvent dans le pack Java Web Services Developer
Pack v1.4.
Une fois le dossier concerné(/jstl/) récupéré, il faudra placer :
-les TLD (présents dans /jstl/tld/) dans le répertoire /WEB-INF/tlds/
-les fichiers jstl.jar et standard.jar (présents dans /jstl/tld) dans le répertoire /WEB-INF/lib

Les JSTL présentent 4 librairies différentes :

    •   La librairie Core représente les fonctions de base et est associée au fichier TLD c.tld.

    •   La librairie XML représente les traitements XML et est associée au fichier TLD x.tld

    •   La librairie I18n représente les fonctions d’internationalisation de la page JSP et est associée
        au fichier TLD fmt.tld

    •   La librairie Database représente les traitements SQL est associée au fichier TLD sql.tld

La librairie Core

Cette librairie concerne les balises de base telles que des balises de condition, de gestion d’url ou
d’itération.

    •   Les balises d’expression de langage : out, set, remove et catch.

La balise out permet d’envoyer un flux de sortie à la page JSP contenu dans l’attribut value. Elle
prend, de plus pour attributs default(valeur par défaut si value est nulle) et escapeXml(booléen
indiquant si les caractères particuliers -<,>,&,é…- doivent être convertis en leur équivalent HTML -&lt,
&gt,…-, true par défaut).
On notera que cette balise doit contenir au moins l’attribut value et ne doit pas contenir de corps.

Exemple :
<coreTag : out value = « Hello ! » default= « default value » escapeXml= « true » />

La balise set permet de stocker une variable dont on va définir la portée de visibilité.
Elle permet d’ « instancier » une variable, à l’aide des attributs var(nom de la variable) et
value(valeur à stocker) ou bien de modifier les valeurs d’un objet bean, à l’aide des attributs
target(nom de la variable contenant le bean), property(nom de la propriété du bean) et value. C’est
ensuite l’attribut scope qui va permettre de spécifier la portée de visibilité de la variable. Il peut
prendre en paramètre les valeurs suivantes : page, request, session et application.

Exemple 1:
<coreTag : set value = « Hello ! » var = « mess » scope= « page »/>

Exemple 2:
<coreTag : set value = « Hello ! » target= « monBean » property= « nomB » scope= « page »/>

Pour récupérer la valeur de la variable, on va pouvoir utiliser la balise out vue précédemment en
utilisant la syntaxe suivante ${nomvariable}:

Exemple :
<coreTag : out value = « ${pageScope.mess} » default= « default value » es capeXml= « true » />
Si on ne précise pas la portée de la variable dans l’attribut value (value= « ${mess} »), elle sera , par
défaut recherchée dans la page, puis dans la requête, la session , et enfin dans l’application.

La balise remove permet de supprimer une variable. Il suffit pour cela de spécifier le nom de la
variable dans l’attribut var et la portée de cette variable dans l’attribut scope.

La balise catch, comme son nom l’indique, permet d’attraper les exceptions. Le code risquant de lever
une exception doit être encapsulé entre les balises catch. L’attribut var qui contient les informations
concernant l’exception levée possède une propriété message qui va permettre d’afficher un message
d’erreur.
Exemple :
<coreTag : catch var = « Erreur » >
<%--code risquant de lever une exception ◊
</coreTag :catch>
<coreTag : out value= « ${Erreur.message} »/>

    •   Les balises de condition et d’itération : if, choose, forEach, forTokens.
    •   Les balises de gestion d’url : import, url, redirect.
5.Interactions entre JSP et Servlet

5.1.Pourquoi faire des interactions entre les JSP et Servlet

Bien qu’elles présentent des avantages énormes (génération d’image en temps réel, compression de
pages …) lorsque l’application nécessite beaucoup de programmation, les servlets ne sont pas
vraiment adaptées à la génération de code HTML, c’est également pour cela que les JSP ont fait leur
apparition.
Même si les technologies comme les JavaBeans, les Tag-Libs, les scriptlets ont fait leur apparition et
ont montré leur puissance en ce qui concerne l’intégration de code java propre dans les pages de
résultat, il reste toujours un problème.

Les JSP sont fournies pour ne fournir qu’un seul type de présentation. Si l’on souhaite créer une
application complexe cherchant à présenter les informations de façon différentes en fonction des
demandes du client cela devient de plus en plus complexe.
C’est alors qu’interviennent les transmissions de requêtes.

5.2.A partir d’une Servlet

Pour utiliser la transmission de requête ou l’inclusion de document externe il faut utiliser un
RequestDispatcher. Cet objet s’obtient depuis un ServletContext et la méthode
getRequestDispatcher().

Vous devez fournir une URL absolue du fichier vers lequel vous voulez transmettre votre requête. Si
vous voulez obtenir un RequestDispatcher vers l’adresse : http://host/folder/page.jsp il vous faudra faire
l’appel suivant :

RequestDispatcher dispat = getServletContext().getRequestDispatcher(“/folder/page.jsp”);

Une fois que vous avez eu votre objet il ne vous reste plus qu’à appeler la méthode forward ou
include de celui-ci. Vous devez fournir à celle-ci les objets HttpServletRequest et
HttpServletResponse.

Si vous utilisez include, la ressource est insérée dans la servlet active.
Si vous utilisez forward, le flux est complètement redirigé vers la nouvelle ressource (l’appelant ne
peut plus effectuer de sortie au client, cette tâche est transmise à la nouvelle ressource uniquement).

L’inclusion ou le forward vers une nouvelle ressource (page ou servlet) se fait ainsi :

// Inclusion
void service (HttpServletRequest req,
HttpServletResponse res )
{
RequestDispatcher dispat = getServletContext().getRequestDispatcher(“/folder/page.jsp”);
dispat.include(req,res);
}

// Forward
void service (HttpServletRequest req,HttpServletResponse res )
{
RequestDispatcher dispat = getServletContext().getRequestDispatcher(“/folder/page.jsp”);
dispat.forward(req,res);
}

Remarques :

    •   L’inclusion d’une servlet dans une autre est synchrone, c’est à dire qu’elle ne rend la main
        qu’à la fin de la méthode service de la servlet appelée

    •   La redirection est quand à elle asynchrone

5.3. A partir d’un script JSP

La transmission de requête dans une JSP est quasiment la même que celle pour une servlet. En effet
même si ce concept est beaucoup moins utilisé il peut l’être lorsque la JSP découvre qu’une donnée
transmise n’est pas correcte par exemple.
La méthode classique de redirection peut être utilisé avec les JSP, cependant il est préférable
d’utiliser jsp:forward pour des raisons de commodités.

Voici le code qui permet de rediriger vers une autre page :

// Forward
<jsp:forward page= « /pageForward.jsp » />

                                JDO - Persistance des données


1. Introduction

1.1. Qu’est-ce que JDO (Java Data Object)
JDO offre une approche orientée objet et standardisée pour la persistance des objets Java.
Au contraire de JDBC, orienté base de données relationnelle, JDO cache les difficultés liées aux
différences entre le monde objet et le monde relationnel. Cela permet au support des données d’être
un SGBD relationnel, objet ou XML, ou utiliser toute autre forme d’enregistrement des données. De
plus, les modifications à apporter au code sont minimes pour rendre des objets persistants.
Les « classes du domaine » (celles qui correspondent aux entités qui seront persistantes) peuvent
être écrites exactement comme si elles n’étaient pas persistantes ; la persistance des applications est
le plus souvent transparente (aucune ligne de code ne l’indique).

1.2. Composants principaux
Une application nécessitant des données persistantes comprend plusieurs composants principaux et
essentiels à son bon fonctionnement :

    •   Une « base de données » dans laquelle sont enregistrés des objets de 2 types :
            o persistants (correspondent à des données de la base),
            o éphémères (transients), non persistants,
    •   Un gestionnaire de persistance gère la persistance des objets persistants,
    •   Des méta-données, enregistrées dans un fichier XML, décrivent les classes des objets
        persistants.

2.Instance JDO

La classe d’une instance persistante, appelée instance JDO, doit implémenter l’interface
javax.jdo.spi.PersistenceCapable.Une instance d’une telle classe peut ne pas être persistante. Elle
le sera seulement si elle est passée en paramètre de la méthode makePersistent() de la classe
PersistenceManager ou si elle est référencée par une instance persistante.

2.1.Persistance par référence
JDO utilise la persistance par référence (by reachability) : tout objet référencé par un objet persistant
devient persistant. Ce mécanisme est récursif : un objet qui est rendu persistant par référence rend
persistant toutes les instances dont il a une référence. Cela participe à la transparence de la
persistance : seule la racine d’une arborescence d’objets doit être rendue explicitement persistante.
On utilise pour cela la méthode suivante de la classe PersistentManager :

     void makePersistent(Object object)



2.2.Modification des données
Pour modifier des données de la base il suffit de modifier les instances JDO associées et de valider la
transaction

2.3.Suppression des données
Pour supprimer des données persistantes, on utilise la méthode suivante de la classe
PersistentManager :

     void deletePersistent(Object object)

On peut aussi supprimer toute une collection ou un tableau d’instances persistantes avec les
méthodes surchargées :
     void deletePersistentAll(Object[] objects)
     void deletePersistentAll(Collection array)

Si l’instance que l’on veut supprimer est référencée par une autre instance persistante, il ne faut pas
oublier d’enlever cette référence, sinon l’instance redeviendra persistante par référence.
En particulier, pour les relations 1-N ou M-N, il ne faut pas oublier de retirer l’instance que l’on veut
supprimer des collections qui correspondent à ces relations..

3.Classes et interfaces de l’API JDO

3.1.Packages de JDO
L’API est composée de 2 packages :
javax.jdo : les classes et interfaces utilisées par les développeurs,
javax.jdo.spi : les classes et interfaces utilisées par JDO pour son fonctionnement interne


3.2.Classes et interfaces principales
Interfaces :

    •   PersistentManager : interlocuteur principal de gestion de la persistance des instances,
    •   Transaction : permet de démarrer, valider et invalider des transactions,
    •   Extent : collection de données,
    •   Query : permet sélectionner les données récupérées dans la base via des flitres,
    •   PersistentManagerFactory : permet de récupérer un PersistentManager,

Classe :

    •   JDOHelper : classe utilitaire pour, par exemple, récupérer une PersistentManagerFactory

3.3. Gestionnaire de persistance (GP)
Le gestionnaire de persistance est l’interlocuteur principal du développeur pour utiliser JDO. Il doit
répondre aux caractéristiques suivantes :

    •   Une instance JDO est associée à un seul GP,
    •   Un GP est responsable de la gestion de l’identité et de la persistance des instances JDO dont
        il a la charge,
    •   Il gère les transactions et les recherches dans la base,
    •   Un GP est une instance d’une classe qui implémente l’interface
        javax.jdo.PersistenceManager,
    •   L’implémentation de JDO fournit les GP,
    •   Un GP ne peut gérer qu’une seule connexion à une base à un moment donné,
    •   Plusieurs GP peuvent être utilisés si l’application a besoin de plusieurs
        connexions/transactions en parallèle vers des sources de données (distinctes ou non).

3.4.Classes d’exception JDO
Ce sont des sous-classes de RuntimeException (donc non contrôlées) placées dans le paquetage
javax.jdo.
Il existe un grand nombre d’exception, mais elles se répartissents en 2 groupes de classes
correspondant à des sous-classes de :

    •   JDOFatalException : problèmes impossibles à traiter ; il faut arrêter le traitement en cours,
    •   JDOCanRetryException : on peut essayer de relancer le traitement qui a lancé l’exception,
        ou lancer un traitement alternatif.

4.Méta-données

4.1.Les méta-données
Utilisées par l’application et les outils JDO pendant l’enrichissement des classes et l’exécution de
l’application, elles donnent le point de vue des classes Java – un point de vue « objet » – sur les
données persistantes ; par exemple, quels champs de la classe seront persistants (enregistrés dans la
base) les relations entre les classes persistantes : 1-1, 1-N, M-N.
Il n’y a cependant aucune information sur la base qui va contenir les données ; c’est l’implémentation
de JDO qui va utiliser d’autres informations internes, ou éventuellement données dans d’autres
fichiers, pour faire le « mapping » (liaison) entre ce point de vue « objet » et le point de vue de la base
de données (par exemple « relationnel »).
Des extensions permettent cependant de donner à l’implémentation JDO des indications sur la façon
d’effectuer ce mapping.
Ces méta-données sont rangées dans un (ou plusieurs) fichier XML.


4.2.Exemple simple de méta-données
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jdo SYSTEM "jdo.dtd">
<jdo>
       <package name="fr.labosun.jdoProject">
<class name="Chat">
<field name="nom"/>
<field name="age"/>
<field name="couleur"/>

       </package>
     </jdo>



4.3.Valeurs par défaut pour les méta-données
Les valeurs par défaut des méta-données sont définies par la spécification JDO et permettent de
simplifier les méta-données. Il n’est donc pas nécessaire de spécifier le tag field lorsque le type du
champ correspond à un type de persistance par defaut.
<jdo>
         <package name="fr.labosun.jdoProject">
<class name="Chat">
</class>
</package>
     </jdo>



4.4.Types persistants
Tous les champs ne peuvent pas être déclarés persistants ; ils doivent être d’un type accepté par
l’implémentation JDO, la spécification JDO imposant que certains types soient acceptés.
Si un type n’est pas persistant pour l’implémentation utilisée, on peut encore souvent s’en sortir en
utilisant des observateurs d’événements JDO.

4.5.Types persistants par défaut
Les types persistants par défaut sont :

    •   Types primitifs
    •   String
    •   BigDecimal et BigInteger
    •   Date
    •   Locale
    •   Classes PersistenceCapable
    •   Tableaux de types primitifs ou de type précédemment cités
    •   Classes de collections (ArrayList,…)

4.6.Types des champs persistants
Les champs persistants peuvent être

    •   de type primitif
    •   les classes enveloppantes des types primitifs
    •   java.util.Locale
    •   java.math.BigDecimal et BigInteger
    •   java.util.Date
    •   java.util.HashSet (optionnellement les autres classes collections)
    •   d’une classe « capable de persistance »
    •   de la classe Object, mais l’implémentation JDO peut restreindre les affectations à ce champ
        en lançant une ClassCastException
    •   des interfaces « collections » Collection et Set (Map et List sont optionnels)
    •   de n’importe quelle interface, mais l’implémentation JDO peut restreindre les affectations à ce
        champ en lançant une ClassCastException

4.7.Valeurs par défaut pour la clé primaire

Par défaut l’identité des instances JDO est définie par la base (et non par l’application). Dans les
exemples précédents, l’identité d’une adresse est définie par la base.

4.8.Nom de classe

Si la classe appartient aux paquetages java.lang, java.util ou java.math, ou au package indiqué par le
tag package dans lequel elle est indiquée, on peut ne donner que son nom « terminal ». Sinon, il faut
donner le nom complet (préfixé par le nom du paquetage). Les noms des classes internes sont écrits
sous la forme « ClasseEnglobante$ClasseInterne »

5.Développement d’une application JDO

5.1.Enrichissement des classes persistantes

Les classes des objets persistants doivent implémenter l’interface javax.jdo.spi.PersistenceCapable.
Le code lié à cette interface est automatiquement ajouté par l’outil JDO Enhancer (enrichissement des
classes persistantes). Selon le produit JDO, cet enrichissement peut être effectué en modifiant le code
source ou en modifiant directement le bytecode (sans doute le cas le plus fréquent). Cependant,
l’enrichissement est portable entre différentes implémentations de JDO.
Aucune permission spéciale n’est requise pour travailler avec les champs privés des classes.

5.2.Interface PersistenceCapable
Elle est composée de quelques constantes et de méthodes qui permettent :

    •   de gérer la persistance des instances,
    •   d’interroger une instance sur son état au moment de l’exécution.

Toutes les méthodes ont des noms qui commencent par « jdo » ; ces méthodes sont utilisées par JDO
pour son fonctionnement interne.

5.3.Classpath pendant l’exécution
Pour l’enrichissement des classes persistantes, les classes de l’implémentation de JDO (dans des
fichiers jar le plus souvent) doivent être dans le classpath ainsi que le(s) fichier(s) de méta-données.
On doit, de plus, y ajouter le driver JDBC du logiciel utilisé pour gérer les objets persistants (un SGBD
le plus souvent)

5.4.Processus de développement
Le développement standard d’une application JDO s’organise comme suit :

    •   Écrire les classes sans se préoccuper de la persistance,
    •   Repérer les classes correspondant aux objets persistants (classes « capables de
        persistance »),
    •   Écrire les méta-données qui décrivent le schéma de la base de données et les informations
        pour rendre les objets persistants,
    •   Enrichir les classes persistantes avec les outils JDO,
    •   Utiliser les objets persistants pour écrire et tester l’application.

L’intérêt de JDO est que l’on peut souvent compiler toutes les classes de l’application avant même
d’avoir enrichi celles qui sont capables de persistance. En effet, les appels aux méthodes de
l’interface PersistenceCapable sont effectués par l’implémentation de JDO ; elles ne sont donc pas
écrites dans les classes de l’application et ne provoquent donc pas d’erreurs de compilation.

5.5.Outils de développement
Les implémentations de JDO fournissent des outils pour :
    •    enrichir les classes persistantes (indispensable),
    •    générer les ordres SQL pour créer le schéma de la base de données. à partir des méta-
         données (optionnel),
    •    générer les méta-données à partir d’une base de données existante (optionnel).

6.Exemple simple de code

6.1.Le logiciel nécessaire

    •    Une implémentation de JDO
    •    Un SGBD
    •    Un driver JDBC pour ce SGBD
    •    Pour ce cours, on utilisera les logiciels gratuits en open source suivants :
    •    l’implémentation de JDO LiDO (http://www.libelis.com)
    •    le SGBD MySQL (http://www.mysql.org)
    •    le driver JDBC MM.MySQL (http://mmmysql.sourceforge.net)

6.1.1.LiDO Community Edition
Cette version est gratuite et réservée aux utilisations non commerciales et comporte une limitation :
pas d’outil pour travailler avec une base de données existante. Les SGBD commerciaux (Oracle en
particulier) ne sont pas supportés. La version commerciale de LiDO contient toutes ces
fonctionnalités. Les développeurs LiDO sont très réactifs et coopérants avec la communauté des
développeurs

6.1.2.Problème éventuel avec les IDE
La plupart des IDEs compilent automatiquement les classes en arrière-plan. La version « enrichie »
des .class peut donc être écrasée par une version non enrichie obtenue automatiquement à partir du
fichier source. Pour éviter ce problème, il faut une version de l’IDE qui prendront en compte JDO

6.2.Le code
On doit importer le paquetage javax.jdo qui contient les classes distribuées par Sun. Le plus souvent
JDO n’apparaît que par l’intermédiaire du gestionnaire de persistance

6.2.1.Classe de base

        fr.labosun.jdoProject;

        Chat {
          String nom;
          String couleur;
          String age;

          Chat(String nom, String couleur, String age){
           .nom = nom;
           .couleur = couleur;
           .age = age;
         }

          String getNom() {
            nom;
         }
          setNom(String nom) {
           .nom = nom;
         }
         ...
           String toString(){
             nom+" - "+couleur+" "+age;
         }
     }
6.2.2.Enrichissement de la classe
On utilise l’outil pour enrichir la classe Chat ; dans l’implémentation de JDO LiDO, c’est la classe Java
Enhance qui fait office de cet outil.
On doit lui passer :

    •   une classe Java à enrichir
    •   des méta-données qui décrivent les classes persistantes

6.2.3.Méta-données
<jdo>
        <package name="fr.labosun.jdoProject">

<class name="Chat">
<field name="nom"/>
<field name="couleur"/>
<field name="age"/>
          </class>
        </package>
      </jdo>


6.2.4.Enrichissement de la classe : exécution avec LiDO
Les fichiers jar du répertoire lib de LiDO et le répertoire bin de LiDO doivent être dans le classpath. On
lance l’exécution de la classe LiDO :

    set LIDO_HOME=C:\Program Files\LiDo
    java –cp "%LIDO_HOME%\lib\lido-rt.jar;..."
    –metadata


6.2.5.Tester les classes persistantes
Il reste à créer la base de données qui va contenir les objets persistants. On utilisera ici le SGBD
MySQL. On peut ensuite écrire du code qui va utiliser les classes persistantes enrichies (ici la classe
Chat.class)

                             1. Outil pour construire le schéma de la base de
                                données
Les implémentations de JDO offrent d’autres outils que l’enrichisseur de classes. Un des outils permet
de générer le code pour construire la base de données qui va contenir les objets persistants.
Pour LiDO la classe DefineSchema a cette fonction. Le paramètre -sql permet d’enregistrer dans un
fichier les ordres SQL qui vont créer la base, sans les faire exécuter (utile pour information et en cas
de problème)

                             2. Construire le schéma de la base de données
                                avec LiDO
On doit indiquer le driver JDBC, l’URL « JDBC » de la base et le fichier qui contient les méta-
données : Le classpath doit contenir les classes enrichies de l’application, les fichiers jar de
l’implémentation de JDO et le driver JDBC java com.libelis.lido.ds.jdbc.DefineSchema :

    •   driver : com.mysql.jdbc.Driver
    •   database : jdbc:mysql://localhost/chats
    •   metadata : metadata.jdo

     set LIDO_HOME=C:\Program Files\LiDo

     java -cp "%LIDO_HOME%\lib\lido-rt.jar;..."
6.2.6.Ajouter un Chat à la base


      javax.jdo.Transaction;

      Test1 {
          String DBURL = "jdbc:mysql://localhost/chats";
          String DBDRIVER = "com.mysql.jdbc.Driver";


6.2.7.Fabrique de gestionnaire de persistance
           Exception {
                  // Créer une fabrique de gestionnaire de persistance
                  PersistenceManagerFactory pmf =
                          (PersistenceManagerFactory) Class

          .forName("com.libelis.lido.PersistenceManagerFactory")
                         .newInstance();


6.2.8.Démarrer une transaction
                    pmf.setConnectionURL(DBURL);
                    pmf.setConnectionDriverName(DBDRIVER);
                    // Récupérer le gestionnaire de persistance
                    PersistenceManager pm = pmf.getPersistenceManager();
                    // Démarrer une transaction
                    Transaction t = pm.currentTransaction();
                    t.begin();


6.2.9.Rendre un Chat persistant
                    // Crée un nouveau Chat...
                    Chat chat = Chat("Felix", "noir", "5");
                    // ... et le rend persistant
                    pm.makePersistent(chat);


6.2.10.Enregistrer le Chat dans la base
                    // Commit la transaction.
                    // Le Chat est enregistrée automatiquement dans la base !
                    t.commit();
                    // Fermeture du gestionnaire de persistance
                    pm.close();
          }
    }


6.2.11.Exécuter Test1
Le fichier metadata.jdo est utilisé pendant l’exécution des classes persistantes.
Avec LiDO, on peut indiquer son emplacement avec la propriété Java jdo.metadata :
      java –Djdo.metadata=<filepath> -classpath ...
    fr.labosun.jdoProject.Test1



7.Concepts plus avancés de JDO

7.1.Héritage et classes persistantes

7.1.1.Pré requis
Si on a une hiérarchie d’héritage, une classe peut être persistante ou non, indépendamment des
autres classes de l’arborescence (classes filles ou mères). Pour cela, il faut que :
    •    Toutes les classes persistantes de l’arborescence doivent avoir la même clé primaire (gérée
         par la base ou par l’application), donnée dans la classe la plus haute hiérarchiquement,
    •    Tous les champs persistants le restent dans les classes filles persistantes,
    •    Toutes les classes doivent avoir un constructeur sans paramètre.

7.1.2.Méta-données pour les classes filles persistantes
On peut indiquer la classe mère persistante d’une classe persistante par un attribut du tag class :
    persistence-capable-super
>
Exemple :

     <class name="Salarie"
       "/>
       <field name="salaire"/>
     </class>

On ne doit pas déclarer les champs persistants hérités, ni la clé primaire.
On peut aussi ne pas indiquer cet héritage dans le fichier de metadata si on ne veut pas que JDO
tienne compte d’une classe mère persistante.

7.1.3.Exemple

                             3. Méta-données : classe Personne
<class name="Personne">
<field name="adresse"/>
<field name="prenom"/>
<field name="nom"/>
          </class>


                             4. Méta-données : classe Salarie

            <class name="Salarie"
              persistence-capable-super
             <field name="prime"/>
<field name="salaire"/>
          </class>


                             5. Classe Personne

        fr.labosun.jdoProject;

        Personne {
            String nom, prenom, adresse;

             Personne (String nom, String prenom, String adresse) {
                    .nom = nom;
                    .prenom = prenom;
                    .adresse = adresse;
            }
             String getNom() {
                     nom;
            }
             setNom(String nom) {
                    .nom = nom;
            }
            //... (get/set)
     }
                             6. Classe Salarie

      fr.labosun.jdoProject;

      Personne {
          double prime;
          double salaire;

            Salarie(double prime, double salaire, String nom,

                                  String prenom, String adresse) {
                     (nom, prenom, adresse);
                     .prime = prime;
                     .salaire = salaire;
          }

            String getPrime() {
                    prime;
          }
            setPrime(double d) {
                   prime = d;
          }
          //... (get/set)
     }




7.3.Relations entre classes
Par le code Java et les méta-données on peut décrire des relations entre les classes persistantes.
Ces relations pourront permettre de naviguer d'une classe à l'autre lors des requêtes écrites en
JDOQL (voir section §8).

7.3.1.Relations 1-1 entre classes persistantes
Inclure dans chacune des classes une variable du type de l'autre classe.

    <class name="Salarie">   <field name="poste"/></class>
   <<SPAN
    class="sourceKeyword">class name="Poste">   <field
   name="salarie"/></class>

On peut ne pas faire cette inclusion dans les 2 sens si on interdit la navigation dans un des sens.

7.3.2.Relations 1-n entre classes persistantes
Inclure dans la classe « maîtresse » une variable contenant une collection d'instances de l'autre
classe :

<class name="Societe">
       <field name="salaries">             </field>
     </class>

Et dans l'autre classe une variable du type de l'autre classe :

<class name="Salarie">
       <field name="societe"/>
       ...</class>

On peut ne pas faire cette inclusion si on veut interdire la navigation vers la classe « maîtresse ».

7.3.3.Relations m-n entre classes persistantes
Inclure dans chacune des classes une variable contenant une collection d'instances de l'autre classe.

<class name="Projet">
      <field name="salaries">    <collection element-
   type="fr.labosun.jdoProject.Salarie"/>
     </field>
    </class>

<class name="Salarie">
       <field name="projets">    <collection element-
    type="fr.labosun.jdoProject.Projet"/>
     </field>
     </class>

On peut ne pas faire une des 2 inclusions si on veut interdire la navigation dans un des sens.

7.3.4.Exemple de relation 1-1 et 1-n

                            12.Méta-données : classe Zoo

          <class name="Zoo">
            <field name="singes">
              <collection element-type="fr.labosun.jdoProject.Singe"/>
</field>
<field name="nom"/>
<field name="directeur"/>
</class>

                            13.Méta-données : classe Singe
<class name="Singe">
<field name="nom"/>
<field name="age"/>
<field name="couleur"/>
<field name="zoo"/>
          </class>


                            14.Méta-données : classe Directeur

          <class name="Directeur"
<field name="nom"/>
<field name="salaire"/>
<field name="zoo"/>
          </class>


                            15.Classe Zoo

      fr.labosun.jdoProject;


      java.util.Collection;

      Zoo {
          String nom;
          Collection singes;
          Directeur directeur;

           Zoo(String nom) {
                  singes = ArrayList();
                  .nom = nom;
          }

           addSinge(Singe singe) {
                singes.add(singe);
        }
         removeSinges(Singe singe) {
                singes.remove(singe);
        }

         Collection getSinges() {
                 singes;
        }
         setSinges(Collection singes) {
                .singes = singes;
        }
        //... (get/set)
    }


                       16.Classe Singe

     fr.labosun.jdoProject;

     Singe {
         String nom, age, couleur;
         Zoo zoo;

         Singe(String nom, String age, String couleur) {
                .nom = nom;
                .age = age;
                .couleur = couleur;
        }
         String getNom() {
                 nom;
        }
         setNom(String nom) {
                .nom = nom;
        }
        //... (get/set)
    }


                       17.Classe Directeur

     fr.labosun.jdoProject;

     Directeur{
         String nom;
         salaire;
         Zoo zoo;

         salaire, Zoo zoo) {
                .nom = nom;
                .salaire = salaire;
                .zoo = zoo;
        }

         String getNom() {
                 nom;
        }
         setNom(String nom) {
                nom = nom;
        }
        //... (get/set)
    }



8.Le « Langage » d’interrogation JDOQL (JDO Query Language)
8.1.Introduction
2 parties :

     •   l'API pour faire des requêtes : des méthodes de la classe PersistenceManager,
     •   le langage d'interrogation lui-même.

JDOQL permet de retrouver, modifier et supprimer des données gérées par un gestionnaire de
persistance.


8.2.Interface Extent
JDOQL utilise intensivement l'interface javax.jdo.Extent. Une instance d'une classe qui implémente
Extent est une sorte de collection d'instances JDO qui représente des données de la base ; les
données de la base représentées par les instances contenues dans l'Extent ne sont vraiment
récupérées qu'à la demande, par l'itérateur qui le parcourt.

8.2.1.Obtenir un Extent
On utilise la méthode getExtent de la classe PersistenceManager à laquelle on doit passer en
paramètres pour récupérer une instance d’Extent :

     •   la classe des instances persistantes contenues dans l'Extent (preciser le package complet si
         besoin),
     •   dire si on veut inclure les instances des sous-classes de cette classe.

8.2.2.Exemple simple de recherche
      // Retrouve tous les singes
      Extent extent = pm.getExtent(Singe.);
      Iterator i = extent.iterator();
        (i.hasNext()) {
           Singe singe = (Singe) i.next();
           System.out.println("Singe : "+singe.getNom()+",
     "+singe.getAge());
      }
      extent.closeAll();




8.3.Recherche d'information
La recherche d’information se fait avec la classe javax.jdo.Query qui permet de récupérer des
instances persistantes enregistrées dans la base. Dans la version actuelle de JDO, une recherche ne
donne pas des valeurs de champs mais des instances entières.

8.3.1.Obtenir un Query
Pour obtenir une instance de Query, on utilise la méthode newQuery de la classe
PersistenceManager (qui est la fabrique de Query). Il existe un grand nombre de méthodes Query
surchargées, mais un Query a besoin de :

     •   la classe des instances que l'on veut obtenir,

ou

     •   d'une collection (ou d'un extent) dans laquelle chercher (ils sont appelés les « candidats »),

et optionnellement

     •   d'un filtre pour sélectionner les instances (cf. §8.4).



      Query query = pm.newQuery(extent, "zoo.nom == z");
     Query query = pm.newQuery(Singe., "zoo.nom == z");
8.3.2.Classe Query
La création d’un Query n’est pas fixe dès lors qu’on l’a instancié ; on peut le paramétrer grâce aux
méthodes suivantes :

    •   On peut modifier un Query avec les méthodes setClass(), setFilter(), setCandidate()
    •   On peut fixer un ordre par setOrdering() (syntaxe proche de SQL : salaire descending, nom)
    •   On peut compiler le Query (méthode compile()), ce qui permet de vérifier les éléments du
        Query et de donner une chance à l’implantation JDO d’optimiser le Query pour plusieurs
        utilisations futures.

8.3.3.Exécution du Query et paramètres
Le Query est lancé par la commande execute.
Les recherches sont souvent paramétrables. Par exemple, un paramètre peut indiquer le numéro de
Sécurité Sociale de la Personne dont on veut les informations ; execute est surchargée pour passer
jusqu’à 3 paramètres au Query. Au-delà de 3, il faut choisir comment passer les paramètres : par
tableau (executeWithArray) ou par Map (executeWithMap).

8.3.4.Déclaration des paramètres
Les paramètres utilisés dans un query doivent être déclarés. La syntaxe est proche de celle de Java
(« , » est le séparateur).
Par exemple :

     query.declareParameters("String nomSinge, String ageSinge");


8.3.5.Déclaration des variables
Les variables utilisées dans un query doivent être déclarées. La syntaxe est proche de celle de Java
(« , » est le séparateur).
Par exemple :

     query.declareVariables("Singe singe");


8.3.6.Importation de classes
On peut aussi avoir besoin d'importer des classes de déclaration des paramètres ou des variables
(décrites plus loin) pour enlever une ambiguïté. La syntaxe est celle de Java (« ; » est le séparateur) :

     query.declareImports("import java.util.Date; import
     fr.labosun.AutreCls");


8.3.7.Résultat de l’exécution du Query
La méthode execute renvoie un Object, que l’on doit caster en Collection.

     Collection qryResults = (Collection) query.execute(…);


8.3.8.Analogie d'un Query avec une méthode
On peut considérer un Query comme une méthode placée dans la classe des instances que l'on
recherche :

    •   Les paramètres du Query correspondent aux paramètres de la méthode,
    •   Les variables correspondent aux variables locales de la méthode,
    •   Les imports correspondent aux importations de classes nécessaires à l'exécution de la
        méthode.

8.3.9.Exemple :

     Extent extSinges = pm.getExtent(Singe.class, );
     Query q = pm.newQuery(extSinges, "zoo.nom == z");
     q.declareParameters("String z");
     Collection singes = (Collection) q.execute("Zoo de Vincenne");
8.2.Interface Extent
JDOQL utilise intensivement l'interface javax.jdo.Extent. Une instance d'une classe qui implémente
Extent est une sorte de collection d'instances JDO qui représente des données de la base ; les
données de la base représentées par les instances contenues dans l'Extent ne sont vraiment
récupérées qu'à la demande, par l'itérateur qui le parcourt.

8.2.1.Obtenir un Extent
On utilise la méthode getExtent de la classe PersistenceManager à laquelle on doit passer en
paramètres pour récupérer une instance d’Extent :

     •   la classe des instances persistantes contenues dans l'Extent (preciser le package complet si
         besoin),
     •   dire si on veut inclure les instances des sous-classes de cette classe.

8.2.2.Exemple simple de recherche
      // Retrouve tous les singes
      Extent extent = pm.getExtent(Singe.);
      Iterator i = extent.iterator();
        (i.hasNext()) {
           Singe singe = (Singe) i.next();
           System.out.println("Singe : "+singe.getNom()+",
     "+singe.getAge());
      }
     extent.closeAll();




8.3.Recherche d'information
La recherche d’information se fait avec la classe javax.jdo.Query qui permet de récupérer des
instances persistantes enregistrées dans la base. Dans la version actuelle de JDO, une recherche ne
donne pas des valeurs de champs mais des instances entières.

8.3.1.Obtenir un Query
Pour obtenir une instance de Query, on utilise la méthode newQuery de la classe
PersistenceManager (qui est la fabrique de Query). Il existe un grand nombre de méthodes Query
surchargées, mais un Query a besoin de :

     •   la classe des instances que l'on veut obtenir,

ou

     •   d'une collection (ou d'un extent) dans laquelle chercher (ils sont appelés les « candidats »),

et optionnellement

     •   d'un filtre pour sélectionner les instances (cf. §8.4).



      Query query = pm.newQuery(extent, "zoo.nom == z");
     Query query = pm.newQuery(Singe., "zoo.nom == z");


8.3.2.Classe Query
La création d’un Query n’est pas fixe dès lors qu’on l’a instancié ; on peut le paramétrer grâce aux
méthodes suivantes :

     •   On peut modifier un Query avec les méthodes setClass(), setFilter(), setCandidate()
     •   On peut fixer un ordre par setOrdering() (syntaxe proche de SQL : salaire descending, nom)
    •   On peut compiler le Query (méthode compile()), ce qui permet de vérifier les éléments du
        Query et de donner une chance à l’implantation JDO d’optimiser le Query pour plusieurs
        utilisations futures.

8.3.3.Exécution du Query et paramètres
Le Query est lancé par la commande execute.
Les recherches sont souvent paramétrables. Par exemple, un paramètre peut indiquer le numéro de
Sécurité Sociale de la Personne dont on veut les informations ; execute est surchargée pour passer
jusqu’à 3 paramètres au Query. Au-delà de 3, il faut choisir comment passer les paramètres : par
tableau (executeWithArray) ou par Map (executeWithMap).

8.3.4.Déclaration des paramètres
Les paramètres utilisés dans un query doivent être déclarés. La syntaxe est proche de celle de Java
(« , » est le séparateur).
Par exemple :

     query.declareParameters("String nomSinge, String ageSinge");


8.3.5.Déclaration des variables
Les variables utilisées dans un query doivent être déclarées. La syntaxe est proche de celle de Java
(« , » est le séparateur).
Par exemple :

     query.declareVariables("Singe singe");


8.3.6.Importation de classes
On peut aussi avoir besoin d'importer des classes de déclaration des paramètres ou des variables
(décrites plus loin) pour enlever une ambiguïté. La syntaxe est celle de Java (« ; » est le séparateur) :

     query.declareImports("import java.util.Date; import
     fr.labosun.AutreCls");


8.3.7.Résultat de l’exécution du Query
La méthode execute renvoie un Object, que l’on doit caster en Collection.

     Collection qryResults = (Collection) query.execute(…);


8.3.8.Analogie d'un Query avec une méthode
On peut considérer un Query comme une méthode placée dans la classe des instances que l'on
recherche :

    •   Les paramètres du Query correspondent aux paramètres de la méthode,
    •   Les variables correspondent aux variables locales de la méthode,
    •   Les imports correspondent aux importations de classes nécessaires à l'exécution de la
        méthode.

8.3.9.Exemple :

     Extent extSinges = pm.getExtent(Singe.class, );
     Query q = pm.newQuery(extSinges, "zoo.nom == z");
     q.declareParameters("String z");
     Collection singes = (Collection) q.execute("Zoo de Vincenne");


8.4.Filtres
Lors de la création du Query, on peut filtré dès celle-ci les résultats qui seront retournés grâce a des
filtres. La condition de sélection des filtres peut être complexe et peut comporter des « && », « || »,
« ! » comme en Java.
Elle peut aussi utiliser les noms des champs de la classe des instances recherchées, ainsi que des
méthodes.
Les seules méthodes autorisées dans les filtres sont :

    •   Des méthodes sur les String,
    •   Des méthodes sur les Collection.

8.4.1.Méthodes sur les String
On peut selectionner les résultats en analysant les String retournées grâce aux méthodes suivantes :

    •   startsWith(…)

     String filter = "(singe.age > 10) && (singe.nom.startsWith(\"A\"))"


    •   endsWith(…)

     String filter = "(singe.age > 10) && (singe.nom.endsWith(\".\"))"


8.4.2.Méthodes sur les Collection
On peut selectionner les résultats en testant directement sur la Collection retournée grâce aux
méthodes suivantes :

    •   contains(…)

Cette méthode doit être utilisée à la gauche de l’opérateur & (ou &&). Son but est d’associer une
variable aux éléments de la Collection ; cette variable peut être alors utilisée dans l’expression placée
à droite de l’opérateur &.

Si le champ de la classe dont on cherche les instances correspond à une Collection d'une autre
classe persistante et correspond à une relation déclarée dans les méta-données, on peut naviguer
dans la Collection. Pour cela, il faut associer une variable aux éléments de la Collection et donner
une condition qui devra être remplie par au moins un élément de celle-ci pour que la condition soit
vraie.

     query.declareVariables("Singe singe");
     String filter = "singes.contains(singe) & singe.age > 5"

Ca peut s'interpréter comme « la variable singe est contenue dans la Collection singes et elle vérifie
singe.age > 5 ».

    •   isEmpty() (renvoie true si la collection est vide)

     String filter = "singes.isEmpty()"


8.4.3.Remarque importante
Les noms utilisés dans les filtres sont les noms des classes Java et des variables persistantes. Il ne
faut pas utiliser les noms des tables (ou autres) dans lesquelles les données vont être enregistrées.
Le principe de JDO est que l’on travaille sur des objets sans pour autant connaître les tables
relationnelles ou autres supports de données.

8.4.4.Exemple

     Extent extent = pm.getExtent(Zoo.);
     String filter = "directeur.salaire > min
                   && directeur.nom.startsWith(\"letter\")";
     Query qry = pm.newQuery(extent, filter);
     qry.declareParameters("String min, String letter");
     Collection zoos = (Collection) qry.execute("1200","A");

     Iterator it = zoos.iterator();
      (it.hasNext()) {
           Zoo zoo = (Zoo) it.next();
           System.out.println("Zoo : "+zoo.getNom());
     }

Cet exemple permet de récupérer et d’afficher toutes les instances persistantes du Zoo dont le
directeur a un salaire de plus de 1200 et dont le nom commence par ‘A’.

8.5.Compléments sur PersistenceManager
Le gestionnaire de persistance fourni aussi les méthodes suivantes :

    •    refresh(Object instanceJDO),
    •    refreshAll(Object[] instancesJDO),
    •    refreshAll(Collection instancesJDO),
    •    refresh().

Celles-ci permettent de rafraîchir l’état des instances JDO pour qu’elles représentent les valeurs
actuelles de la base. Elles peuvent être utilisées dans une transaction optimiste pour avoir plus de
chance de succès de la transaction au moment du commit.
Il fourni aussi les méthodes :

    •    retrieve(Object instanceJDO),
    •    retrieveAll(Object[] instancesJDO),
    •    retrieveAll(Collection instancesJDO).

Celles-ci permettent d’informer le gestionnaire de persistance que l’application a l’intention d’utiliser
ces instances JDO. Il aura ainsi l’occasion d’optimiser ces récupérations.


9.Etats des instances JDO

9.1.Définition
Le modèle objet des données manipulées par JDO (les instances JDO) contient 2 types d’instances :

    •    FCO (First Class Object) : instance JDO qui ont une identité JDO,
    •    SCO (Second Class Object) : elle n’a pas d’identité JDO par elle-même ; elle correspond à
         des données qui sont enregistrées comme une partie des données d’un FCO.

9.2.FCO et SCO
Les types qui correspondent aux interfaces qui ne sont pas des collections doivent être implémentés
avec des FCO.
Tous les autres types peuvent être implémentés avec des FCO et des SCO
La spécification indique que les applications JDO ne doivent pas dépendre du fait que ces types sont
implémentés par des FCO et des SCO
Mais, il n’est pas évident de savoir comment faire quand on lit cette spécification…
Design Pattern - Modèles d'architecture


1.Introduction

1.1.Pourquoi Pattern ?
Un pattern (modèle) est une façon de faire, une manière d’arriver à l’objectif fixé. Les patterns vous
permettent d’utiliser des principes basés sur l’habileté des précurseurs en la matière. Les modèles
sont une façon excellente de capturer et transporter l’habileté.
Lorsque votre habileté a employé certaines méthodes communes seront maîtrisées, vous vous
rendrez compte que ces méthodes pourraient être réutilisé dans plusieurs contextes.
Les Patterns, sont apparus bien avant l’apparition de l’informatique, étaient utiliser en architecture. Les
modèles architecturaux peuvent servir et inspirer les personnes qui occuperont les bâtiments.
Le Pattern font référence aux modèles standardisés d’implémentation de certaines taches à réaliser…
Les écrivains documentent ces modèles, en aidant à standardiser ces modèles. Ils assurent aussi que
la sagesse accumulée d'une habileté est disponible aux futures générations des praticiens.

1.2.Pourquoi Design Pattern ?
Un Design Pattern est un modèle de conception, il utilise des classes et leurs méthodes dans un
langage orientée objet, Java dans notre cas. Les Design Patterns permettent de mettre en application
des concepts spécifiques sans pour autant avoir à passer beaucoup de temps sur la méthode de
développement à employer pour arriver à un objectif. Ils permettent donc de simplifier votre réflexion
en réutilisant des principes de base de la conception, cela aura donc pour effet de rendre votre
programme plus lisible lors de phase de maintenance par exemple.
Si vous voulez devenir de puissant développeur Java, vous devriez étudier les Design Patterns.
Les Design Patterns sont tout simplement des architectures de classes permettant d'apporter une
solution à des problèmes fréquemment rencontrés lors des phases d'analyse et de conception
d'applications. Ces solutions sont facilement adaptables (donc réutilisables), elles sont utilisables sans
aucun risques dans la grande majorité des langages de programmation orientée objet.
Les Design Patterns sont fiables. Ils ont une architecture facilement compréhensible et identifiable
pour les développeurs. Cela améliore donc la communication et la compréhension entre
développeurs.


1.3.Pré requis
L’assimilation des concepts des Design Patterns impose de maîtriser les concepts de base et
avancés. Il faut donc maîtriser plus précisément les notions suivantes :

    •   Concepts de POO (Programmation Orientée Objet)
    •   Diagramme des classes UML
1.4.Comment utiliser les Design Patterns ?
Les Design Patterns sont des organisations de classes. La difficulté réside dans le fait de savoir
identifier les moments où il faut les utiliser et les moments où cela n’est pas utile.
Ce cours a uniquement vocation de vous présenter certains des Design Patterns les plus utilisés.
Vous devez donc être apte à répondre aux questions suivantes :

    •   Quand utiliser un Design Pattern ? (savoir identifier les problèmes)
    •   Quel Design Pattern utiliser ? (cerner les problèmes identifiés)
    •   Comment le mettre en oeuvre ? (résoudre le problème)

Les Design Patterns ne sont donc pas vraiment une solution miracle pour tous les problèmes, mais ce
sont plutôt des méthodes de résolutions. Un Design Pattern peut être vu comme une formule
mathématique, c'est la solution mais encore faut-il l'appliquer au bon moment avec les bonnes
variables. Il n’est pas nécessaire d’apprendre par cœur, mais un développeur « architecte » se doit de
savoir qu’ils existent.


1.5.Historique
Les principaux auteurs des Design Patterns (Erich Gamma, Richard Helm, Ralph Johnson et John
Vlissides), en ont dégagés 23 principaux, sont plus connus sous le nom du : « Gang Of Four ».
On retrouve une classification en 3 catégories :


    •   Creational Patterns (Les Patterns de création)

            o   Abstract Factory, Builder, Factory Method, Prototype, Singleton

    •   Structural Patterns (Les Patterns de structure)
            o Adapter, Bridge, Composite, Decorator, Façade, Flyweight, Proxy

    •   Behavioral Patterns (Les patterns de comportement)
           o Chain of Resp., Command, Interpreter, Iterator, Mediator, Memento, Observer,
               State, Strategy, Template Method, Visitor

Pour plus de détails sur un des Design Patterns, vous pouvez consulter le site www.dofactory.com.
Les Design Patterns qu’ils ont créés sont considérés comme la base de tous les autres, à partir
desquelles d’autres patterns pourront ensuite être construits.


2.Les Patterns de création

2.1.Prototype

2.1.1.Théorie
Le Design Pattern Prototype est utilisé lorsqu’un système doit être indépendant de la façon dont ses
produits sont créés, assemblés, représentés ou quand la classe n'est connue qu'à l'exécution et pour
éviter une hiérarchie trop redondante.
Prototype permet donc de créer de nouveau objets en copiant une instance.

2.1.2.Structure
Les classes à utiliser pour le Pattern Prototype sont (également appelées : les participants) :

    •   Prototype (c’est la super classe à partir de laquelle vous appellerez la méthode clone() qui
        remplira les fonctionnalités recherchées)
    •   ConcretePrototype (l’héritage redéfinissant la méthode clone() permettra de retourner la
        copie de l’instance à cloner)
    •   Client (c’est à partir de l’Opération que vous pourrez créer une nouvelle instance)
2.1.3.Pratique
Le langage Java vous permet, grâce à l’interface Clonable, de pouvoir simplifier ce principe de
recopie d’un objet.
      prototype;

      Cloneable {
         String a;
         String b;
           Prototype(String c, String d) {
                  a = c; b = d;
         }

           CloneNotSupportedException {
                   super.clone();
          }
     }
Vous devez gérer l’exception « CloneNotSupportedException » qui peut être levée lors de l’appel de
la méthode clone() :

      prototype;

      Client {

           main(String[] args) {
                  {
                         Prototype p1 = new Prototype("Labo", "Sun");
                         Prototype p2 = (Prototype) p1.clone();
                         System.out.println(p2.a + ":" + p2.b);
                  } (CloneNotSupportedException e) {}
          }
     }

L’instance p2 est bien une copie de p1, cela signifie que lorsque la copie est faite, vous pouvez
modifier p1 sans que cela affecte p2.


2.2.Singleton

2.2.1.Théorie
Le singleton est une classe dont on veut s’assurer qu’une seule et unique instance de celle-ci est
créée pendant toute la durée d’exécution de votre application.
Ce Design Pattern est très facile à utiliser et souvent et souvent utilisé, cependant beaucoup de
personnes l’utilisent mal.
2.2.2.Structure
Pour s’assurer de l’unicité de l’instance du singleton, il faut tout d’abord penser à limiter les accés aux
constructeurs (le plus souvent « private » ou « protected », cependant si vous utilisez ce deuxième
cela ne certifie pas l’unicité de l’instance si vous hérité votre singleton).
Si vous n’avez pas de constructeur, il vous faut donc un autre moyen pour retourner une instance de
votre classe. Pour cela vous devrez implémenter une méthode « static » qui par convention
s’appellera : getInstance().




2.2.3.Pratique
Vous pouvez utiliser le pattern Singleton dans le cas d’un objet de connexion à une base de données

Voici un exemple de code du corps d’un singleton (votre classe ne doit pas obligatoirement porté le
nom « Singleton ») :

     /**
      * Exemple d'implémentation d'un singleton.<p>
      * Cet exemple ne fait rien.
      */

      Singleton {

          /** L'instance statique */
           Singleton instance;

          /**
            * Récupère l'instance unique de la class Singleton.
            * Remarque : le constructeur est rendu inaccessible
            */
            Singleton getInstance() {
                     == instance) { // Premier appel
                            instance = new Singleton();
                   }
                     instance;
          }

          /**
            * Constructeur redéfini comme étant privé pour interdire
            * son appel et forcer à passer par la méthode getInstance()
            */
            Singleton() {
          }
     }



3.Les Patterns de structure

3.1.Adapter

3.1.1.Théorie
Ce pattern est très utilisé pour encapsuler un ensemble de fonctions sous la même implémentation.
L’utilisation de plusieurs APIs pour une question de portabilité est un très bon exemple d’utilisation de
l’adapteur. En effet, comme son nom l’indique il permet d’adapter une implémentation à une autre afin
de généraliser les APIs.
3.1.2.Structure
La structure est assez simple, elle redéfinit les accès aux méthodes de l’API. L’interface « Adapteur »
permet de définir l’ensemble des méthodes qui seront communes à tous les adaptateurs. La classe
« AdapteurImpl » est l’implémentation de l’adaptateur qui permet de faire le lien entre l’API et la
normalisation que vous souhaitez mettre en place.
La/les classe(s) cible(s) représentent l’ensemble des fonctions de l’API utilisé.




3.1.3.Pratique
Ce pattern permet donc de rajouter une couche logiciel afin de normaliser des implémentations par
exemple. Plusieurs raisons s’offrent à nous pour l’utilisation de ce pattern :

    •    Le besoin de ne pas dépendre d’une API particulière ou de pouvoir en changer rapidement.
    •    Une portabilité encore plus forte.

Le seul cas où il est difficile de partir dans ce genre de concept est lorsque le temps pour le
développement de votre application est très limité. Car ceci demande beaucoup de patience lorsque
les APIs à uniformiser sont importantes (exemple API javamail et Lotus pour l’envoi des mails).
Voici un exemple très simple d’utilisation de ce pattern. Dans celui-ci nous « émulons » le fait que
nous ayons nos 2 APIs d’envoi de mail (ApiMail1 et ApiMail2). Notre adapter, ici, nous permettra
d’utiliser l’API que l’on veut, très facilement (nous spécifions une méthode mail() dans notre interface
de normalisation).

    •    Hiérarchie des packages :




Les packages adapter.apimail1 et adapter.apimail2 représentent nos deux apis de mails
complètement différentes l’une de l’autre. Cependant elles ont le point commun de pouvoir envoyer
des mails.

    •    L’API mail1 :

        adapter.apimail1;

        ApiMailBase {
            ApiMailBase() {

           }

            envoiMail() {
                   System.out.println("Envoi mail avec ApiMail1");
                   ;
       }
}



•    L’API mail2 :

    adapter.apimail2;

    Base {
         Base() {
       }

        sendMail() {
               System.out.println("Envoi de mail avec ApiMail2");
               ;
       }
}



•    L’interface ApiMailAdapter permet d’indiquer les fonctionnalités communes que les apis sont
     capables d’exécuter.

    adapter;

    ApiMailAdapter {
       // Méthode commune à toutes les apis
        mail();
}


•    Implémentation de l’adapter pour l’API mail1 :

    adapter;

    adapter.apimail1.ApiMailBase;

    ApiMailAdapter {

        mail() {
                ApiMailBase.envoiMail();
       }
}


•    Implémentation de l’adapter pour l’API mail2 :

    adapter;

    adapter.apimail2.Base;

    ApiMailAdapter {

        ApiMail2AdapterImpl() {
        mail() {
                Base.sendMail();
       }
}


•    Le client (qui permet ici de tester l’application) :

    adapter;

    Client {
        ApiMailAdapter api;
            Client(ApiMailAdapter api) {
                   .api = api;
           }
                      ApiMailAdapter getApi(){
                      api;
           }
         main(String []args){
                Client clientAvecApiMail1 =                 ApiMail1AdapterImpl());
                Client clientAvecApiMail2 =                 Client(new
   ApiMail2AdapterImpl());

                     // Envoi mail (fictif)
                     clientAvecApiMail1.getApi().mail();
                     clientAvecApiMail2.getApi().mail();
           }
     }


3.2.Composite

3.2.1.Théorie
Le design pattern composite compose les objets dans les structures de type arbre ou arborescence.Il
permet alors d’afficher une partie ou l’intégralité d’une hiérarchie.

3.2.2.Structure
La structure est la même que celle d’un arbre. Celle-ci se compose :

    •    Classe abstraite « Component » qui définit les attributs et méthodes communes
         (feuilles/nœuds).
    •    Une classe d’implémentation : « Leaf » qui représente une feuille.
    •    Une classe d’implémentation : « Composite » qui représente le nœud de l’arbre.




3.2.3.Pratique
Ce pattern est surtout utilisé dans les applications utilisant des hiérarchies (arbres). Pour cela nous
allons simplement créer une application qui simule une hiérarchie d’éléments (chaine de caractère) et
les afficher.
Voici le résultat que nous allons produire :

     + Racine
     + RacineElement1
     + SousRoot1
     -+ SousSousRoot
     --+ SSElement1
     --+ SSElement2
     --+ SSElement3
     + RacineElement2
     + SousRoot2
     -+ SElement1
     -+ SElement2
     -+ SElement3
Cette application est composée de 4 classes dont 3 principales (qui représentent le pattern) :

    •    La classe abstract AbstractElement (Component) : permet de définir le type d’élément que
         vous voulez hiérarchiser.

       composite;
     AbstractElement {
            String name;
            AbstractElement(String name) {
                   .name = name;
            add(AbstractElement e);
            remove(AbstractElement e);
            indentation) {
                   StringBuffer strBuf = StringBuffer();
                    i = 0; i < indentation; i++)
                           strBuf.append("-");
                   System.out.println(strBuf.toString() + "+ " + .name);
                    true;
          }
     }



    •    La classe Leaf : représente une feuille dans votre arbre. Elle implémente AbstractElement.



        composite;

      AbstractElement {
           Leaf(String name) {
                  (name);
           add(AbstractElement e) {
                  System.err.println("Impossible de rajouter des éléments
   dans une feuille");
                  ;
           remove(AbstractElement e) {
                  System.err.println("Impossible de supprimer des éléments
   dans une feuille");
                  ;
         }
    }



    •    La classe Node : représente un nœud dans votre arbre. Cette classe implémente
         AbstractElement. On peut y ajouter des feuilles ou d’autres nœuds.

        composite;

        java.util.ArrayList;

        AbstractElement {
           ArrayList elements;

           Node(String name) {
                  (name);
                  elements = ArrayList();
         }

           add(AbstractElement e) {
                  elements.add(e);
                  ;
         }

           remove(AbstractElement e) {
                  elements.remove(e);
                  ;
         }

          indentation) {
                 .display(indentation);
                   i = 0; i < elements.size(); i++)
                          ((AbstractElement)
   elements.get(i)).display(indentation + 1);
                 ;
        }
    }



   •   La classe Client n’est autre que la classe de lancement de l’application et ici, les tests



      composite;
    Client {
           main(String[] args) {
                  Node root = new Node("Racine");
                  Node sousRoot1 = Node("SousRoot1");
                  Node sousSousRoot        = Node("SousSousRoot");
                  Node sousRoot2 = Node("SousRoot2");
                  // sousSousRoot
                  sousSousRoot.add( Leaf("SSElement1"));
                  sousSousRoot.add( Leaf("SSElement2"));
                  sousSousRoot.add( Leaf("SSElement3"));
                  // sousRoot1
                   sousRoot1.add(sousSousRoot);
                  // sousRoot2
                  sousRoot2.add( Leaf("SElement1"));
                  sousRoot2.add( Leaf("SElement2"));
                  sousRoot2.add( Leaf("SElement3"));
                  // Racine
                  root.add( Leaf("RacineElement1"));
                   root.add(sousRoot1);
                  root.add( Leaf("RacineElement2"));
                   root.add(sousRoot2);
                   root.display(-1);
         }
    }


Vous pouvez faire le rapprochement entre un cas d’utilisation du Design Pattern Composite dans le
JDK. Prenons le cas d’un composant Swing, le JPanel. En effet le JPanel vous permet de contenir
d’autres composants comme des JButton, JLabel… mais vous pouvez également faire en sorte que
votre JPanel comporte plusieurs autres JPanel, c’est un cas réel de l’utilisation du composite.
3.3.Pont (Bridge)

3.3.1.Théorie
Ce pattern permet d’éviter d’avoir un lien permanent entre l'abstraction et l'implantation. L'implantation
est choisie à l'exécution par exemple.
L'abstraction et l'implantation sont toutes les deux susceptibles d'être raffinées, étoffées, ….
Cependant, toutes les modifications éventuellement subies par l'implantation ou l'abstraction ne
doivent pas avoir d'impacts sur le client (pas de recompilation)

3.3.2.Structure
Ce diagramme de classe vous montre à la fois les concepts d’héritage multiple ainsi que le lien de
composition entre la classe « Abstraction » et « Implementor ». C’est ce lien qui donne toute la
particularité de ce Design Pattern.




3.3.3.Pratique
    •    La classe Client permet d’instancier et d’appeler la méthode operation().

        bridge;

        Client {

            main(String[] args) {
                   Abstraction abstraction =               RefinedAbstraction();

                     abstraction.implementor =             ConcreteImplementorA();
                     abstraction.operation();

                     abstraction.implementor =             ConcreteImplementorB();
                     abstraction.operation();
           }
     }


    •    La classe Implementor est la super classe qui s’occupe de la partie implémentation, vous
         retrouverez la méthode operation() qui sera rédéfinie dans les sous classes.



        bridge;
        Implementor {
            operation();
     }


    •    La classe ConcreteImplementorA qui hérite de la classe Implementor et redéfini la méthode
         operation().



        bridge;

        Implementor {

            operation() {
                   System.out.println("ConcreteImplementorA Operation");
           }
     }


    •    La classe ConcreteImplementorB qui hérite de la classe Implementor et redéfini la méthode
         operation(). La méthode operation() n’affichera pas la même ligne que la classe précédante.

        bridge;

        Implementor {

            operation() {
                   System.out.println("ConcreteImplementorB Operation");
           }
     }


    •    La classe Abstraction vous permet de simuler la présence de la méthode operation() au
         niveau abstrait.

        bridge;

        Abstraction {
            Implementor implementor;

            operation() {
                   implementor.operation();
           }

            setImplementor(Implementor impl) {
                   .implementor = impl;
           }
     }


    •    La classe RefinedAbstraction hérite de la classe ci–dessus et n’a que pour but de lier la
         partie d’abstraction d’implémentation.

        bridge;

        Abstraction {
             operation() {
                    implementor.operation();
           }
     }


3.4.Décorateur

3.4.1.Théorie
Ce design pattern peut être assimilé à un « emballage ». Il permet l’ajout dynamique de
responsabilités à un objet. En effet nous pouvons très bien utiliser l’héritage pour ce genre de chose,
cependant ce concept est limité car il ne définit que des responsabilités statiques et manque donc
énormément de souplesse. De plus l’héritage n’est pas toujours possible (dans le cas d’une classe
finale par exemple) puis l’héritage peut donner lieu à la prolifération de classes ce qui n’est pas
toujours facile à gérer.
Lorsque l’on parle de proliférations de classes et manque de souplesse de l’héritage, le mieux est de
prendre un exemple. Imaginons que vous ayez une classe qui s’occupe des « logs » (journaux) et que
vous voulez lui ajouter l’heure et la date d’émission d’un message. Vous voulez également faire un
affichage dans la console, un log dans un fichier, un log par mail et un affichage Swing pour
l’administrateur. Dans chaque cas vous voulez afficher ou non l’heure et la date.
Nous voilà donc obligés de surcharger la classe de log (pour un fichier) 3 fois pour les divers types de
logger et 2 fois pour que chacun affiche l'heure et la date, ou non (au total 6 classes).

3.4.2.Structure
La structure de ce pattern se compose d’au moins 4 éléments :

    •    L’interface « Composant » : qui définit les méthodes de bases qui correspondent au type de
         composant.
    •    L’implémentation par défaut du composant : « DefaultComposant ».
    •    Classe abstraite héritant de Composant définissant le décorateur. Le plus simple est d’utiliser
         une classe abstraite (cela peut être fait via une interface) et d’avoir une instance vers un objet
         de type « Composant » afin d’avoir accès à toutes les méthodes de bases implémentées.

3.5.Façade

3.5.1.Théorie
Facade est un Pattern très utilisé, spécialement dans vos développements d’interfaces Swing par
exemple. Ce Pattern permet d’unifier vos éléments communs d’une même interface. Facade permet
de définir des interfaces graphiques de niveau avancé en prévoyant une décomposition et une notion
d’héritage entre vos éléments qui composent votre interface.
Vous pourrez, grâce à ce Pattern, composer des interfaces homogènes plus rapidement.

3.5.2.Structure
Le principe du Pattern Facade réside dans le fait de prévoir une notion de hiérarchie parmi tous les
éléments qui composent votre Interface. Cette notion peut avoir un niveau plus ou moins élevé
d’abstration selon vos besoins.




3.5.3.Pratique
    •    La classe Client vous permet de constituer votre interface, dans l’exemple ci-dessous ce ne
         sera qu’une interface fictive puisque l’on se contentera d’une impression dans la console de
         message. La classe Client constitue son interface par instanciation de la classe Facade.



        facade;

        java.io.IOException;

        Client {
        IOException {
               Facade f = Facade();
               f.methodA();
               f.methodB();

                System.in.read();
       }
}


•    La classe Facade comprend l’ensemble des éléments à construire et deux méthodes
     methodA() et methodB() qui permettent appeler des méthodes des éléments venant d’être
     construit.

    facade;

    Facade {

       SubSystemOne one;
       SubSystemTwo two;
       SubSystemThree three;

        Facade() {
               one = SubSystemOne();
               two = SubSystemTwo();
               three = SubSystemThree();
       }

        methodA() {
               System.out.println("\nmethodA() ---- ");
               one.methodOne();
               three.methodThree();
       }

        methodB() {
               System.out.println("\nmethodB() ---- ");
               two.methodTwo();
               three.methodThree();
       }
}


•    La classe SubSystemOne simule le premier des éléments graphiques.

    facade;

    SubSystemOne {

        methodOne() {
               System.out.println("Methode 1");
       }
}


•    La classe SubSystemTwo simule le second des éléments graphiques.

    facade;

    SubSystemTwo {

        methodTwo() {
               System.out.println("Methode 2");
       }
}


•    La classe SubSystemThree simule le dernier des éléments graphiques.
        facade;

        SubSystemThree {

            methodThree() {
                   System.out.println("Methode 3");
           }
     }




    •    Vous pouvez ensuite créer un ensemble de Logger dérivant du Decorateur (qui est lui-même
         un logger). De ce fait vous allez pouvoir « empiler » les loggers afin que par exemple le
         message de base soit traité plusieurs fois (ajout de la date/heure, mise en forme …)

3.4.3.Pratique
Afin de mieux appréhender ce concept, nous allons étudier notre exemple de « logger ». Nous aurons
un logger par défaut qui permettra simplement d’afficher des messages dans la console ou dans un
stream quelconque. Nous lui rajouterons ensuite les fonctionnalités suivantes :

    •    Ajouter la date pour chaque message
    •    Afficher les messages dans un JTextArea (provenant d’une fenêtre ouverte)

Voici le code complet de l’application :

    •    L’interface Logger :

        decorator;

        interface Logger {
            String log(String msg);
     }


    •    L’implémentation par défault DefaultLogger

        decorator;
        java.io.PrintStream;

        Logger {
            PrintStream stream;
            DefaultLogger() {
            DefaultLogger(PrintStream stream) {
               .stream = stream;
        setStream(PrintStream stream) {
               .stream = stream;
        PrintStream getStream() {
               .stream : System.out;
        String log(String msg) {
               .getStream().println(msg);
                msg;
       }
 }


•    La classe abstraite implémentant Logger : DecoratorLogger

   decorator;
 Logger {
        Logger logger;
        DecoratorLogger(Logger logger) {
               .logger = logger;
        String log(String msg) {
                logger.log(msg);
        Logger getLogger() {
               .logger;
        setLogger(Logger logger) {
               .logger = logger;
      }
 }


•    L’implémentation de nos différents traitements applicables (différents Logger).
         o LoggerWithDate : ajoute la date au message

   decorator;
   java.text.SimpleDateFormat;
   java.util.Date;
 DecoratorLogger {
        LoggerWithDate(Logger logger) {
               (logger);
        String log(String msg) {
               .log(msg);
        String getDate(java.util.Date date) {
               SimpleDateFormat formatter = SimpleDateFormat("dd-MM-
yyyy");
                formatter.format(date);
      }
 }


           o   LoggerSwing : affiche le message dans un JTextArea

   decorator;
   javax.swing.JTextArea;
 DecoratorLogger {
        JTextArea txtFieldLog;
        LoggerSwing(Logger logger, JTextArea txtFieldLog) {
               (logger);
               .txtFieldLog = txtFieldLog;
        String log(String msg) {
               txtFieldLog.setText(txtFieldLog.getText() + "\n" +
.log(msg));
                msg;
      }
 }


•    La classe de « démarrage », Client (qui contient aussi la classe pour la fenêtre Swing)
     decorator;
    javax.swing.JFrame;
     javax.swing.JTextArea;
    Client {
         main(String[] args) {
                // Frame pour le log
                FrameLog frameLog = FrameLog("TestLogger");
                frameLog.show();

                // Crée un logger
                Logger logger = DefaultLogger();
                // Ajoute une fonctionnalité (date)
                LoggerWithDate loggerWithDate = LoggerWithDate(logger);
                // Ajoute la fonctionnalité : Swing
                LoggerSwing loggerSwing = LoggerSwing(loggerWithDate,
   frameLog.getFieldLog());
                // Affiche un log
                logger.log("Test1");

                    // Affiche le log dans le Swing
                    loggerSwing.log("Test2");

                    // Affiche le log dans le swing (sans date)
                    loggerSwing.setLogger(logger);
                    loggerSwing.log("Fin");
    JFrame {
          JTextArea txtConsoleLog;
          FrameLog(String title){
                 .setTitle("Logger : " + title);
                 .setSize(300, 300);
                 txtConsoleLog = JTextArea();
                 .getContentPane().add(txtConsoleLog);
                 .setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
          JTextArea getFieldLog() {
                  this.txtConsoleLog;
        }
    }




4.Les patterns de comportement

4.1.Commande

4.1.1. Théorie
Le principale objectif de ce Design Pattern est d’encapsuler une requête sous forme d’objet. Vous
pouvez utiliser ce pattern lorsque :

    •   vous voulez paramétrer des objets pour réaliser certaines actions avec différentes requêtes
    •   pour spécifier, mettre en file, et exécuter des requêtes à des moments différents
    •   pour implémenter des opérations réversibles
    •   pour structurer un système à l’aide d’opérations de haut niveau construites à partir
        d’opérations primitives.

4.1.2. Structure
Ce design pattern est représenté par :

    •   une interface Command qui définit les méthodes de la commande qui devront être
        implémentées.
    •   une classe ConcreteCommand qui implémente l’interface Command servant à définir la
        jonction entre l’ objet « Receiver » et les actions à effectuer.
    •   une classe « Client » qui génère les « Command »
4.1.3.Pratique
Ce design pattern est très utilisé dans le monde Java et de la programmation objet. En effet il se
retrouve dans les applications utilisant les files d’attentes, les logs, les supports d’opérations
reversibles (“undo”)…
Le meilleure et le plus connu des exemples est sans doute celui de Swing. En effet, cette librairie
l’utilise grandement pour les actions associées aux contrôles (boutons, images …).
Dans le monde J2EE (Java Entreprise) c’est « Struts » qui utilise à merveille ce pattern.
Nous allons donc voir ici un exemple très basique qui permet de gérer les actions afin de pouvoir
revenir en « arrière » dans une application (undo) ou de revenir en avant (redo). L’application ici est
une calculatrice de base faisant les opérations de base.

    •    L’interface Command :

        command;

        Command {
            execute();
            unExecute();
     }


    •    L’implémentation de Command : CalculCommand

      command;
     Command {
           Calculator receiver;
           operator;
           operand;
           operand) {
                  .receiver = receiver;
                  .operator = operator;
                  .operand = operand;
           execute() {
                  receiver.operation(operator, operand);
           unExecute() {
                  receiver.operation(undo(operator), operand);
           undo(char operator) {
                   undo = 0;
                   (operator) {
                   '+':
                          undo = '-';
                          ;
                   '-':
                          undo = '+';
                          ;
                   '*':
                          undo = '/';
                          ;
                   '/':
                          undo = '*';
                          ;
                   undo;
         }
     }


    •    Le « receiver » (classe Calculator) :

      command;
     Calculator {
          total = 0;
          Calculator() {
          operand) {
                  (operator) {
                  '+':
total += operand;

                                ;
                       '-':
total -= operand;

                                ;
                       '*':
total *= operand;

                                ;
                       '/':
total /= operand;

                             ;
                     System.out.println("Total : " + .total);
           }
     }


    •    L’ « invoker » (classe Invoker) :

      command;
     java.util.ArrayList;
     Invoker {
          Calculator calculator;
          ArrayList commands;
          int current;
          Invoker() {
                 calculator = Calculator();
                 commands = ArrayList();
current = 0;
}

         levels) {
                System.out.println("Redo : " + levels + " levels");
                 i = 0; i < levels; i++) {
                        (current < commands.size() - 1) {
                                ((Command)
   commands.get(current++)).execute();
         levels) {
                System.out.println("Undo : " + levels + " levels");
                 i = 0; i < levels; i++) {
                         (current > 0) {
                                ((Command) commands.get(--
   current)).unExecute();
         operand) {
                Command command = CalculCommand(calculator, operator,
   operand);
                command.execute();
                commands.add(command);
                current++;
           }
     }


    •    Le client

        command;

        Client {
            main(String []args) {
                   Invoker ink = Invoker();

                       ink.compute('+',       100);
                       ink.compute('-',       50);
                       ink.compute('*',       10);
                       ink.compute('/',       2);

                       ink.undo(4);
                       ink.redo(3);
           }
     }


4.2.Iterator

4.2.1.Théorie
Ce pattern est très utilisé et permet d’accéder à des éléments d’un agrégat séquentiellement. Il a pour
but premier de donner un moyen de parcourir et d’accéder aux éléments sans pour autant exposer sa
structure interne. Il peut également avoir différents moyens de lister le contenu (ordre
croissant/décroissant, insertion…) et permettre (si on le souhaite) d’accéder simultanément à la liste
avec différents moyens (croissant et décroissant en même temps par exemple).

4.2.2.Structure




4.2.3.Pratique
L’iterator peut être utilisé pour accéder à une liste d’éléments suivant un moyen précis :

    •    Croissant
    •    Décroissant
    •    Insertion
    •    …

Vous pouvez également utiliser un itérator qui vous permettrait d’appliquer un filtre particulier à la liste
(ne prendre que les éléments commençant par ‘a’ par exemple).
Nous allons voir dans l’exemple suivant comment utiliser ce pattern. Pour cela nous allons faire une
application qui permet d’itérer de différentes façons une collection (simplement, en indiquant un pas,
ou encore une condition).
Nous avons donc, dans notre application :
•    L’interface ICollection : (décrit une collection de base pour notre itérateur)

   iterator.abstracts;
 ICollection {
       IIterator createIterator();
       index);
       getIndexMax();
       add(Object item);
       remove(Object item);
 }


•    L’interface IIterator : (décrit un itérateur de base)

   iterator.abstracts;
 IIterator {
       Object first();
       Object next();
       isDone();
       Object current();
 }


•    L’implémentation de ICollection : CollectionSimple

    iterator;

    iterator.abstracts.ICollection;
    iterator.abstracts.IIterator;

    java.util.ArrayList;

    ICollection {
        ArrayList array;

        CollectionSimple() {
               array = ArrayList();
       }

        IIterator createIterator() {
               );
       }

        step) {
               , step);
        IteratorWithCondition createIterator(String conditionStartWith)
{
               , conditionStartWith);
        index) {
               .array.size() > index) {
                       .array.get(index);
               } {
                       ;
        getIndexMax() {
                this.array.size();
        add(Object item) {
               .array.add(item);
        remove(Object item) {
               .array.remove(item);
       }
 }


•    Les implémentations de IIterator :
         o IteratorSimple : (itérateur tout simple)

    iterator;
      iterator.abstracts.ICollection;
      iterator.abstracts.IIterator;

      IIterator {
          ICollection collection;
          current;
          IteratorSimple(ICollection collection){
                 .collection = collection;
                 .current = -1;
          Object first() {
current = 0;
                      .collection.getElement(current);
               Object next() {
current++;
                      .isDone()) {
                              .collection.getElement(current);
                      ;
               isDone() {
                      .collection.getIndexMax());
               Object current() {
                      .collection.getElement(current);
             }
     }


                 o   IteratorWithStep : (itérateur avec un pas)

      iterator;
     iterator.abstracts.ICollection;
      iterator.abstracts.IIterator;
     IIterator {
          ICollection collection;
          current;
          step;
          IteratorWithStep(ICollection collection){
                 .collection = collection;
                 .current = -1;
                 .step = 1;
          step){
                 .collection = collection;
                 .current = -step;
                 .step = step;
          Object first() {
current = 0;
                      .collection.getElement(current);
               Object next() {
current += step;
                       (!this.isDone()) {
                              .collection.getElement(current);
                       null;
               isDone() {
                      .collection.getIndexMax());
               Object current() {
                      .collection.getElement(current);
             }
     }


                 o   IteratorWithCondition : (itérateur qui vérifie si la méthode toString() de chaque objet
                     commence par la condition de l’itérateur

      iterator;
     iterator.abstracts.ICollection;
      iterator.abstracts.IIterator;
     IIterator {
          ICollection collection;
          current;
          String condition;
          IteratorWithCondition(ICollection collection){
                 .collection = collection;
                 .current = -1;
                 .condition = "";
          IteratorWithCondition(ICollection collection, String condition){
                 .collection = collection;
                 .current = -1;
                 .condition = condition;
          Object first() {
current = 0;
                      .collection.getElement(current);
               Object next() {
current++;
                     .isDone()) {

   (this.collection.getElement(current).toString().startsWith(this.condit
   ion))
    {
return this.collection.getElement(current);
                                } {
                                     .next();
                      ;
               isDone() {
                      .collection.getIndexMax());
               Object current() {
                      .collection.getElement(current);
             }
     }


    •    Une classe qui représente les objets :

      iterator;
     Item {
           String texte;
           Item(String texte) {
                  .texte = texte;
         }

               setTexte(String texte) {
                      .texte = texte;
             }
               String getTexte() {
                      .texte;
               String toString() {
                       texte;
             }
     }


    •    La classe de lancement et paramétrage de l’application :

      iterator;
     Client {
          main(String [] args) {
                 CollectionSimple listItems = CollectionSimple();
                 listItems.add( Item("Item1"));
                 listItems.add( Item("AItem2"));
                 listItems.add( Item("Item3"));
                 listItems.add( Item("Item4"));
                 listItems.add( Item("AItem5"));
                 listItems.add( Item("Item6"));
                     listItems.add( Item("Item7"));
                     listItems.add( Item("Item8"));

IteratorSimple iterator = (IteratorSimple) listItems.createIterator();
IteratorWithStep iteratorStep = (IteratorWithStep) listItems.createIterator(2);
                      IteratorWithCondition iteratorCond1 =
    listItems.createIterator("AItem");

Item itemTmp;

                     System.out.println("--- ITERATOR SIMPLE ---");
                     ) {
System.out.println(itemTmp);
}

                     System.out.println("--- ITERATOR STEP 2 ---");
                     ) {
System.out.println(itemTmp);
}

                     System.out.println("--- CONDITION START WITH Item ---");
                     ) {
                             System.out.println(itemTmp);
                     }
          }
     }



4.3.State

4.3.1.Théorie
Le Pattern State permet, lorsqu’un objet est altéré de changer son comportement changé. Le
changement d’état peut parfois poser des problèmes dans leurs gestions, le Pattern State permet de
pallier à ce problème de manière simple et rapide.
Cependant il n’est pas toujours obligatoire de l’utiliser dans toutes les situations. Il vous faudra
identifier s’il sagit réellement d’un changement d’état qui doit être gérer.

4.3.2.Structure
State doit comporter des classes précises participantes à ce Pattern :

    •    Context
    •    State
    •    ConcreteState

On utilise State lorsque :
• Le comportement d'un objet dépend de son état, qui change à l'exécution.
• Les opérations sont constituées de parties conditionnelles de grande taille (case).
4.3.3.Pratique
  •    La classe Client permet d’instancier un objet Context et d’en afficher son état suite à des
       changements liés à l’appel de la méthode request() :

      state;

      Client {

      main(String[] args) {
                 Context c =         ConcreteStateA() );
                 c.show();

                   c.request();
                   c.show();

                   c.request();
                   c.show();
         }
   }


  •    La classe Context comporte un attribut de type State et une méthode Request() qui change
       l’état.

      state;

      Context {

          State state;

          Context(State state) {
                 .state = state;
         }

          request() {
                 state.handle();
         }

          show() {
                 System.out.println("State: " + state );
         }

          setState(State state) {
                 .state = state;
         }
   }
    •    La classe State est abstraite afin de forcer les classes qui la dérive de redéfinir la méthode
         abstraite handle().

        state;

        State {
        handle(Context context);
     }


    •    La classe ConcreteStateA hérite de la classe State et redéfini la méthode handle().

        state;

        State {
             handle(Context context) {
                    context.setState( ConcreteStateB());
           }
     }


    •    La classe ConcreteStateB procède selon le même procédé que la classe précédente mais
         effectue le changement d’état dans le sens inverse.

        state;

        State {
             handle(Context context) {
                    context.setState( ConcreteStateA());
           }
     }




EJB 3 - Les Entreprise Java Bean version 3 (JavaBeans)

1.Introduction

Cet essentiel est la suite de « Entreprise Java Bean 2.1 ». Cependant, nous allons étudier les
nouvelles spécifications 3.0.

1.1.Pré requis

Avant d’entamer les spécifications des EJB 3, il vous faut connaître :

    •    Les nouvelles fonctionnalités du JDK 1.5
    •    EJB 2 & 2.1
    •    Concept de mapping : objet / relationnel

1.1.1.JDK 1.5
Les EJB 3 se basent maintenant sur le JDK 1.5 (contrairement aux EJB 2.1 qui utilisaient le JDK 1.4).
Un ensemble très important de fonctionnalités ont été ajoutées au JDK 1.5, vous pouvez les retrouver
dans l’essentiel : « Nouveautés du JDK 1.5). Parmi celles-ci, les annotations que l’on peut intégrer au
code (un peu de la même façon qu’avec les xDoclet), ou encore les générics (ou template en C++).
                               1. Annotations

Voici un exemple d’utilisation des annotations :

package test.ejb.interfaces;

import javax.ejb.Local;

@Local
public interface Calculator {

public double calculate (int start, int end,
double growthrate, double saving);

}

Nous indiquons, dans cet exemple, que l’interface Calculator est l’interface local pour l’ejb
CalculatorBean et cela juste en utilisant l’annotation : « @Local ». Cette annotation utilise la classe
« javax.ejb.Local » c’est donc pour cela que celle-ci est importée.
Vous verrez dans les exemples prochains d’EJB que l’on peut spécifier des valeurs aux propriétés
liées à l’annotation.

                               2. Génériques (generics)

Les génériques sont également une grande nouveauté. En effet, il permette un typage encore plus
fort. Alors qu’avant, les collections, les iterators … retournaient des objets (car les collections
pouvaient contenir n’importe quel type d’objet), maintenant vous devrez spécifier (comme pour les
tableaux) le type à utiliser. De même vous pourrez créer vos propres génériques …

Voici un exemple d’utilisation de l’interface Collection dans sa nouvelle version :
package com.society.generics;
import java.util.Collection;
import java.util.HashSet;
public class TestGenerics {
public static void main(String[] args) {
Collection<String> myStringCollection = new HashSet<String>();
myStringCollection.add(new String("I'm a String object"));
myStringCollection.add("it's a String object");
// Impossible ici !!!
// Object n’est pas une String !
myStringCollection.add(new Object()); // Erreur lors de la compilation
}
}

Dans notre exemple, nous créons une Collection de String via : « Collection<String> ». Suite à cela,
nous ne pouvons alors n’ajouter que des objets de type String ou héritant de la classe String.

1.1.2.EJB 2 & 2.1
La connaissance des spécifications précédentes vous permettront de comprendre plus vite les
concepts des EJB mais également de voir les évolutions qu’apporte la nouvelle spécification.

Nous retrouvons dans cette spécification :

    •   Les interfaces Home / Reference & Local / Remote pour les EJB Session.
    •   Le code, côté client ne change pas, voir très peu. Utilisation courante de JNDI et RMI…
    •   Les différents type d’EJB : Session / Entity / Message Driven


Il est donc recommandé de voir l’ensemble des concepts et la conception d’EJB 2 même s’il est vrai
que la spécification 3 va simplifier énormément ceux-là.


2.Spécifications 3.0

2.1.Présentation
Les spécifications EJB 3 interviennent dans un context bien précis. En effet, avec la nouvelle version
du JDK (1.5), une nouvelle version du J2EE SDK (1.5) y sera liée. Ces spécifications interviennent
donc dans l’optique du développement avec ce SDK.


2.2.Objectifs de la nouvelle spécification

La spécification 3 des EJB a tout d’abord été élaborée en vue de simplifier la conception d’EJB du
côté développeur.
Voici les principaux points qui ont été simplifiés :

    •   Simplification de la définition des interfaces, suppression d’un bon nombre de points requis
        dans la version 2.1 (plus besoin d’hériter d’une super interface ou classe)
    •   Simplification pour la création de la classe du Bean.
    •   Simplification des API pour l’accès à l’environnement du Bean : définition par simple injection
        dépendante
    •   Introduction de l’utilisation des annotations en Java qui sont utilisées à la place du descripteur
        de déploiement
    •   Simplification concernant la persistance d’objet par la définition par l’utilisation facilité de
        mapping objet/relationnel basée sur l’utilisation direct de classes Java et non de composants
        persistants.

2.2.1.Metadata annotations et descripteur de déploiement
Les annotations sont une nouveauté importante dans l’élaboration d’EJB. En effet, les développeurs
utilisant ce système pourront se passer du descripteur de déploiement.
Cependant pour ceux qui veulent garder leurs habitudes du fichier XML, il sera possible de le garder.

2.2.2.Compatibilité ascendante
La spécification stipule qu’un bean écrit avec l’API EJB3 doit pouvoir être un client à un composant
écrit avec l’API 2.1 (2 & 1 également).
Cela a bien entendu comme objectif de faciliter la migration des applications existantes ou simplement
l’utilisation de celles-ci dans les nouvelles applications.



2.3.Spécifications des différents beans

Les différents composants d’un bean (classe / interface / descripteur de déploiement) se simplifient
avec la version 3. Nous allons voir en détails chacun de ces changements.

2.3.1.Classe bean et interfaces

                              3. Classe bean

Le développeur définit la classe de l’entreprise bean et l’annote en utilisant le concept de metadata en
Java. Ces annotations peuvent être appliquées à la classe du bean pour spécifier les informations
liées au conteneur, pour demander un service ou encore pour fournir différentes informations de
configuration pour le deployer.

L’élément requis par la classe du bean est la définition du type de bean à utiliser (Stateless ?
Statefull ? Entity ?). Il est bien entendu possible de ne pas utiliser les annotations metadata et utiliser
alors un descripteur de déploiement.

                                      1. Interfaces

Dans EJB 3, les interfaces sont des « pures » interfaces java. C'est-à-dire que vous n’avez plus
besoin d’hériter de « EJBObject » ou « EJBLocalObject ».

                                               1. Business (métier)

Seuls les session bean et les message driven bean ont besoin d’une business interface (nous verrons
plus tard comment cela se passe avec les entity bean). Cependant les message driven bean utilise
typiquement l’interface javax.jms.MessageListener (à cause de l’API JMS : Java Message Service).
La classe bean peut implémenter directement l’interface métier. Une classe bean peut cependant
implémenter plusieurs interfaces à la fois à condition de respecter ces quelques règles :

    •   Si le bean implémente qu’une seule interface, alors ça doit être l’interface métier (business).
        Celle-ci doit donc être la local ou la remote (ou bien entendu les deux) et l’interface doit porter
        l’annotation indiquant le type d’interface (Local ? Remote ?)
    •   Un bean peut implémenter plusieurs interfaces. Dans ce cas, chacune des interfaces doit
        explicitement être marquée par les annotations indiquant le type de l’interface métier (Local ou
        Remote).
    •   Une interface métier ne doit pas étendre javax.ejb.EJBObject ou javax.ejb.EjbLocalObject


Les annotations liées aux interfaces sont :

    •   @Local : pour spécifier que l’interface est de type local
    •   @Remote : pour spécifier que l’interface est de type remote




                                              2. Exceptions

Les méthodes des interfaces métiers peuvent déclarer des exceptions liées à l’application. Cependant
vous n’avez plus à spécifier, pour les méthodes de la remote interface, l’exception :
java.rmi.RemoteException.
Si un problème survient au niveau du protocole, une EJBException est jetée par le conteneur.

                                              3. Home

Les home interfaces ont été éliminées.
Les sessions beans n’ont plus besoin d’avoir de home interface. Un client peut acquérire une
référence du session bean directement.
En ce qui concerne les Entity, la création d’entité se fait simplement par l’opérateur new
(d’instantation) et par l’utilisation de l’EntityManager API (vu plus loin dans l’essentiel).

2.3.2.Callbacks et Classe Listener Callback
Une méthode peut être définie comme étant une méthode « callback » afin de recevoir une notification
au niveau du cycle de vie d’un session ou message-driven bean.
Les méthodes « callback » doivent être annotées avec les annotations de callback.

Voici un example :

@Stateful public class ShoppingCartBean implements ShoppingCart {
private float total;
private Vector productCodes;
public int someShoppingMethod(){...};
...
@PreDestroy endShoppingCart() {...};
}


Vous pouvez définir des méthodes callback directement dans la classe de votre bean. Dans ce cas, la
signature de votre méthode doit respecter : public void <METHOD> ()

Cependant vous pouvez également utiliser une classe « listener de callback » qui peut être utilisée à
la place des méthodes intégrées au bean. Cela dans le but de séparer le traitement du cycle de vie du
traitement logique lié au bean.
Dans ce cas, il vous faudra utiliser l’annotation CallbackListener dans la classe du bean avec laquelle
vous voulez associer votre classe listener.
Une classe listener callback doit définir obligatoirement un constructeur public ayant aucun argument.
Les annotations utilisées dans la classe listener sont les mêmes que celles utilisées dans la classe du
bean. Cependant la signature des méthodes de la classe listener est : public void METHOD (Object)
où Object est le type du bean actuel.
2.3.3.Session Bean

                              4. Stateless
                                    1. Bean

La classe du bean doit être annotée avec « @Stateless ». Cette classe n’a pas à implémenter
l’interface javax.ejb.SessionBean.

                                      2. Callbacks

Les session bean supportent les callback d’évènement suivant :

    •   PostConstruct
    •   PreDestroy


La callback PostConstruct intervient après toutes les dépendances d’injection effectuée par le
conteneur et avant le premier appel de la méthode métier.
La callback PreDestroy est appelée au moment ou l’instance de du bean est détruite.

                                      3. Dépendance d’injection

Si un session bean utilise le mecanisme de dépendance d’injection afin d’acquérir des références à
d’autres ressources ou d’autres objets de l’application, alors le conteneur injecte ces références avant
que toute méthode liée au cycle de vie de l’EJB ou méthode métier soit appelée.

                                      4. Interceptor

Il est possible de créer des méthodes d’interception d’appel des méthodes métiers d’un bean. Ces
méthodes peuvent être définies directement dans le bean ou dans une classe d’interceptor et
appliquées aux méthodes métiers du bean.

                                      5. Exemple

Voici un exemple très simple de SessionBean (type stateless). Il faut bien entendu définir la ou les
interface(s) que notre bean va implémenter. Ici nous définissons simplement l’interface Remote qui
contient une seule méthode : getHello(). De plus nous l’annotons avec l’annotation @Remote afin de
la déclarer comme interface distante. Cependant il est possible de ne pas l’annoter directement mais
de la déclarer dans la classe du Bean, cela permet une entière compatibilité avec des clients utilisant
la JVM 1.4.

Voici le code de l’interface distante (Remote) :

import javax.ejb.Remote;

@Remote
public interface RemoteHello {
public String getHello();
}

La classe du bean implémente l’interface précédente et est annotée par @Stateless afin de la
déclarée comme Session Bean Stateless.
Voici le code de cette classe :

import javax.ejb.Stateless;

import com.society.testejb3.interfaces.RemoteHello;

@Stateless
public class HelloBean implements RemoteHello {
public String getHello() {
return "Hello world";
}
}



                             5. Stateful

Statefull session bean a les mêmes caractéristiques que le Stateless. Seuls les points en plus sont
expliqués dans les sections ci-dessous.

                                     1. Bean

La classe du bean doit être annotée avec « @Statefull ». Cette classe n’a pas à implémenter
l’interface javax.ejb.SessionBean.

                                     2. Callbacks

Les session bean supportent les callback d’évènement suivant :

    •   PostConstruct
    •   PreDestroy
    •   PostActivate
    •   PrePassivate


Les callback PrePassivate et PostActivate représentent les anciennes méthodes : « ejbActivate » et
« ejbPassivate ».

                                     3. Removal method

L’annotation @Remove permet de définir une méthode indiquant la suppression du Statefull Bean
lorsque cette méthode aura fini son exécution (exécution normal ou anormale).

2.3.4.Entity Bean
Les entity beans subissent de grandes modifications avec la version EJB3. En effet, les spécifications
ce ceux-là sont séparées de celles des Session Bean et Message Driven. Nous allons donc voir
l’ensemble des besoins et spécifications que les entity bean doivent respecter.

                             6. Bean Class

La classe du bean doit tout d’abord être annotée avec @Entity. Le constructeur doit avoir aucun
argument (comme le constructeur par défaut) mais il se peut que la classe ait d’autres constructeurs
en plus de celui-là.
Ce constructeur doit être public ou protected.
Si l’entity a la possiblité d’être utilisé comme objet détaché (comme dans une relation client de type
Remote), la classe doit implémenter l’interface java.io.Serializable.
La classe de l’entité ne doit pas être finale et aucune de ses méthodes aussi.

Les entités supportent l’héritage, le polymorphisme. Une classe abstraite peut être une entité.
L’état d’une entité est représenté par des variables d’instances qui doivent correspondrent à des
propriétés JavaBean.

                                     1. Champ persistants & propriétés

L’état persistant d’une entité est accessible par l’implémentation du fournisseur de persistance soit
comme dans le cas des JavaBeans soit par les variables d’instances.

    •   Si l’entité est annotée avec access=FIELD, le fournisseur accède directement aux variables
        d’instances que ne sont pas Transient, afin de les rendre persistantes.
    •   Si l’entité est annotée avec access=PROPERTY, le fournisseur accède aux champs
        persistants par le biais des méthodes setter/getter. Celles-ci doivent être public ou protected.
Vous pouvez utiliser les collections dans vos JavaBeans, les types supportés sont :
java.util.Collection, java.util.Set, java.util.List, java.util.Map.
L’utilisation des génèriques est également possible, par exemple : Set<Order> est autorisé.

Vous pouvez utiliser l’ensemble des types suivants pour vos champs persistants : java.lang.String,
java.math.BigInteger, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time,
java.sql.Timestamp et les tous types sérializables.

                                      2. Création d’instance

La création d’instance d’une entité, se fait simplement par la simple instanciation de la classe par
l’opréateur new. Cependant la persistance de l’objet créé est réalisée avec l’EntityManager API. Nous
verrons cela plus en détail ci-dessous.

                                      3. Primary key & Identité

Une simple clé primaire (non composite) doit correspondre à un simple champ persistant ou une
propritété de la classe. Une clé composite doit également correspondre soit à un champ persistant soit
à un ensemble de champs ou propriétés. Une classe pour la clé primaire doit impérativement être
définie pour représenter une clé composite. Vous utiliserez une clé composite lorsque vous ferez un
mapping avec une base de données dans laquelle la clé est composée de plusieurs champs.

Le type de la clé primaire peut être : n’importe quel type primitif de java, n’importe quel wrapper
(Integer, Double …), java.lang.String, java.util.Date, java.sql.Date.
On n’utilise généralement pas des types float ou double en tant que clé primaire.

Voici les différentes règles à respecter pour une clé primaire composite :

    •   La classe de la clé primaire doit être public et doit avoir un construteur public sans argument
    •   Si l’accès est de type PROPERTY, la classe peut être soit public soit protected
    •   La classe doit être Serializable
    •   La classe doit définir les méthodes equals et hashCode
    •   Si la clé primaire est mappée à de multiples champs ou propriétés, le noms des champs de la
        clé primaire doivent être les mêmes que ceux utilisés dans le bean.

                                      4. Mapping par défaut

Lorsque vous allez créer votre bean, il vous faudra mapper l’ensemble de vos champs. Cependant il
est possible d’utiliser le mapping par défaut. Ce mapping sera utilisé lorsque vous n’aurez pas utilisé
d’annotation pour un champ (qu’il soit persistant ou relationnel).

Le mapping standard est utilisé lorsque c’est un champ persistant (non relationnel) : @Basic.

                                      5. Exemple simple

Voici un exemple de POJO (Plain Oriented Java Object) simple. Il permet de définir la structure de la
table « COUNTRY ». Il implémente Serializable afin de pouvoir être envoyé directement à un client
distant (et donc de passer à travers un réseau).
Les annotations obligatoires sont :

    •   @Entity qui déclare la classe comme étant une entité
    •   @Id qui déclare la clé primaire (generate permet de définir le type de génération à utiliser pour
        cette clé. GeneratorType.AUTO définit une clé auto incrémentée).

Les autres annotations ne sont pas obligatoires mais permettent de spécifier plus précisément
différents paramètres :

    •   @Table définit le nom de la table à utiliser
    •   @Basic définit le type d’un champ persistant (par défaut cet type est utilisé)


import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "COUNTRY")
public class Country implements Serializable {
private int id;
private String name;
public Country() { }

@Id(generate = GeneratorType.AUTO)
@Basic
public int getId() { return id; }
public void setId(int id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) { this.name = name; }
}




                                      6. Relations

Les relations entre entités peuvent être: one-to-one, one-to-many, many-to-one, many-to-one, many-
to-many.
Si vous souhaitez relier deux entités, vous pourrez utiliser les annotations : OneToOne, OneToMany,
ManyToOne, ManyToMany. Pour les associations qui ne spécifient pas le type de bean « target »
(utilisation de Collection sans les génériques), il est nécessaire de spécifier l’entité qui est la cible.

Les relations peuvent être unidirectionnelles ou bidirectionnelles.Un relation bidirectionnelle est
accessible des deux côtés (des deux beans) alors qu’une relation unidirectionnelle n’est valable que
depuis un bean.

Voici les règles à respecter pour les relations :

    •   L’inverse parti d’une relation bidirectionnelle doit être référencée par l’utilisation de l’attribut
        mappedBy des éléments : OneToOne, OneToMany, ManyToOne, ManyToMany. Cet attribut
        désigne la propriété ou le champ de l’entité qui est le propriétaire de la relation.
    •   Vous n’avez pas besoin de définir l’attribut mappedBy dans le cas des relations
        bidirectionnelles OneToMany et ManyToOne.
    •   Pour les relations OneToOne, la partie propriétaire correspond à la partie contenant la clé
        étrangère.
    •   Dans le cas de la relation ManyToMany, les deux côtés sont propriétaires !

                                      7. Exemple relationnel

Ce bean intègre une relation avec un autre bean de type : ManyToOne. Nous pouvons remarquer que
cette relation met en évidence un champ de type Country dans ce bean.
L’utilisation des annotations relationnelles décrit plus en détail la relation :

    •   @ManyToOne définit le type de la relation (ManyToOne)
    •   @JoinColumn définit la colonne de jointure pour cette relation


package com.society.stockmanager3.entities.beans;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "CITY")
public class City implements Serializable {
protected int id;

protected String name;

protected Country country;

public City() {
}

public City(String name) {
this.name = name;
}

@ManyToOne(optional = false)
@JoinColumn(name = "countryId")
public Country getCountry() {
return country;
}

public void setCountry(Country country) {
this.country = country;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Id(generate = GeneratorType.AUTO)
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}
}




                                     8. Héritage

Une entité peut hériter d’un autre. Les entités supportent l’héritage, l’association polymorphique, les
requêtes polymorphiques.
Les classes abstraites ou concrètes peuvent être des entités.
Des entités peuvent hériter de classes (non entité) et inversement.
Lorsqu’une entité hérite d’une autre entité, les clés primaires doivent être du même type.

                                              1. Abstract class

Une classe abstraite peut être une entité. La différence avec une classe concrète est qu’il n’est pas
possible d’instancier cette entité directement. Cependant vous pouvez utiliser la classe abstraite en
tant que target dans une requête (ce qui vous permettra par exemple de récupérer l’ensemble des
sous types de votre abstract classe).
Vous devez bien entendu définir votre classe abstraite comme une entité (via Entity).

                                              2. Héritage
Une entité peut avoir une classe parente (abstract ou concrète) qui ne soit pas une entité. La
superclasse sert seulement pour l’héritage d’un comportement. En effet, aucune des proprités de la
super classe (non entité) ne sera rendu persistante.
Vous ne pourrez pas passer en argument une classe « non entité » à l’EntityManager.

Il existe 3 stratégies de mapping :

    •   Une table pour une hiérarchie de classe
    •   Une table par classe concrète
    •   Stratégie consistant à séparer les champs spécifiques d’une classe fille dans une table
        séparée par rapport à la table contenant les données de la table parente. Une jonction est
        alors faite pour instantier la classe fille.

                                                        1. Une table pour une hiérarchie
                                                           de classe

Dans cette stratégie, toutes les classes de la hiérarchie sont mappées dans une même et unique
table. La table a une colonne qui définit le type de données (pour l’enregistrement).
Cette stratégie fournit de nombreux avantages concernant les relations polyphormiques entre entités
et pour les requêtes.

Cependant, les données utilisent plus de place car l’ensemble des colonnes n’est pas toujours utilisé
(suivant le type de données enregistrées).

                                                               1. Exemple

Voici un exemple d’héritage de type : ONLY_ONE_TABLE (cela est fait par l’utilisation de l’annotation
@Inheritance, c’est le type par défaut d’héritage). Il faut définit une colonne permettant de différencier
les différents types d’entité (cela est effectuée par @DiscrimanatorColumn). De plus il faut indiquer
la valeur de « descriminitation » pour chaque entité (via discriminatorValue, attribut de
@Inheritance).

import java.io.Serializable;

import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "record")
@Inheritance(discriminatorValue="B")
@DiscriminatorColumn(name="record_type")
public class Record implements Serializable {
protected int id;

protected double saving;

protected double result;

public Record() {
}

public Record(int id, double result, double saving) {
this.id = id;
this.result = result;
this.saving = saving;
}

@Id(generate = GeneratorType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}

public double getResult() {
return result;
}

public void setResult(double result) {
this.result = result;
}

public double getSaving() {
return saving;
}

public void setSaving(double saving) {
this.saving = saving;
}

public void setFund(Fund fund) {
this.fund = fund;
}

public void setInvestor(Investor investor) {
this.investor = investor;
}

}


Voici la classe fille TimeRecord :

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.Inheritance;

@Entity
@Inheritance(discriminatorValue = "T")
public class TimeRecord extends Record {

private Timestamp ts;

public TimeRecord() { }

public TimeRecord(double result, double saving, Timestamp ts) {
super(id, result, saving);
this.ts = ts;
}

public Timestamp getTs() {
return ts;
}

public void setTs(Timestamp ts) {
this.ts = ts;
}
}


Voici le résultat au niveau de la base de données (une seule table pour deux entités) :
                                                      2. Une table par classe concrète

Chaque concrète classe est liée à sa propre table. Cela signifie que toutes les propriétés de la classe
(incluant les propriétés héritées) sont mappées dans la table liée à la classe.

Voici les inconvénients de cette statégie :

    •   Mauvaise gestion des relations polymorphiques
    •   Le SGDB doit effectuer une requête de type UNION pour récupérer l’ensemble des données
        de l’ensemble de la hiérarchie des classes.

                                                              1. Exemple

Voici un exemple d’utilisation de l’héritage de type : TABLE_PER_CLASS. Une petite modification a
également été faite au niveau des accès aux données. L’accès se fait directement via les variables
d’instances (définit via access=AccessType.FIELD) ; ce type d’accès vous évite de créer des getters
et setters pour chacune de vos propriétés. Le type d’héritage est déclarée dans l’annotation
@Inheritance grâce à l’attribut (strategy=InheritanceType.TABLE_PER_CLASS).


import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;

@Entity(access=AccessType.FIELD)
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Animal {

@Id protected Integer id;
@Basic protected String name;
@Basic protected Integer age;

public Animal() {
}
}


Voici une classe fille : Bird de la classe Animal :

import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Entity;

@Entity(access=AccessType.FIELD)
public class Bird extends Animal {

@Basic protected Integer nbPlume;

}
Voici une classe fille : Dog de la classe Animal :

import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Entity;

@Entity(access=AccessType.FIELD)
public class Dog extends Animal {

@Basic protected String race;

public Dog() {
}

}


Voici le résultat en base de données (toutes les données sont intégrées dans chacune des tables) :




                                                      3. Une table pour une partie de
                                                         données spécifiques

Dans cette stratégie, la classe mère des entités est représentée par une simple table. Chaque sous
classe est représentée par une table séparée contenant l’ensemble des propriétés spécifiques à la
classe fille (et bien entendu la clé primaire). La clé primaire de la classe parente est utilisée comme
clé étrangère avec la clé primaire de la classe fille.

Cette stratégie fournit un bon support pour les relations polyphormiques entre entités.

L’inconvénient est qu’elle requière l’utilisation de plusieurs jointures entre les tables lors de la
récupération de données. Dans le cas de hiérarchie importante (grande profondeur de hiérarchie) cela
peut entrainer de mauvaise performance.

                                                               1. Exemple

Seul le type d’héritage change dans cet exemple. Il est déclaré à : « InheritanceType.JOINED ».

import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Version;

@Entity(access=AccessType.FIELD)
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Employee {

@Id protected Integer id;
@Version protected Integer version;
@Basic protected String name;

public Employee() {
}
}
Voici une classe fille PartTimeEmployee de Employee :

import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.PrimaryKeyJoinColumn;

@Entity(access=AccessType.FIELD)
@Inheritance(discriminatorValue="PT")
@PrimaryKeyJoinColumn(name="PT_EMPID")
public class PartTimeEmployee extends Employee {

@Basic protected Float hourlyWage;

public PartTimeEmployee() {
}
}


Voici une classe fille FullTimeEmployee de Employee :

import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.PrimaryKeyJoinColumn;


@Entity(access=AccessType.FIELD)
@Inheritance(discriminatorValue="FT")
@PrimaryKeyJoinColumn(name="FT_EMPID")
public class FullTimeEmployee extends Employee {

@Basic protected Integer salary;

public FullTimeEmployee() {
}
}


Voici le résultat au niveau de la base de données (optimisation de la place prise par les données) :




                              7. Opérations sur les entités

Nous allons décrire, dans cette partie, l’utilisation de l’EntityManager API afin de gérer le cycle de vie
d’une instance d’entité et l’utilisation de l’API Query qui permet d’exécuter des requêtes sur des objets
persistants.

                                      1. EntityManager

Une instance d’un EntityManager est associée avec un contexte de persistance. Ce contexte est un
ensemble d’instances d’entités pour lequel pour n’importe quelle entité, il existe une unique instance
de l’entité. Grâce à ce contexte, les instances d’entités et leur cycle de vie peuvent être gérés.
The EntityManager interface définit l’ensemble des méthodes que l’on peut utiliser pour intérargir avec
le contexte de persistance. Cette API est utilisée pour créer et supprimer des instances d’entités ou
encore pour en trouver via leur clé primaire ou par le biais de requêtes sur les objets eux-mêmes.

L’ensemble des entités qui peuvent être gérer par une instance d’EntityManager est définit par une
unité persistante. Cette unité définit l’ensemble des classes qui sont liées à l’application et qui peuvent
être liées au sein d’un même mapping dans une même base de données.

Voici les méthodes les plus utilisées concernant cette interface :

    •   persist : permet de persister une instance d’une entité
    •   merge : permet de sauvegarder les modifications faites concernant une entité dans le
        contexte
    •   remove : permet de supprimer une instance d’une entité
    •   find : permet de récupérer une entité en fonction de sa clé primaire
    •   refresh : permet de réinitialiser l’état d’un objet par rapport à la base de données (annule toute
        modification)
    •   createQuery : méthode retournant un objet Query permettant d’effectuer des requêtes plus
        spécialisées

                                      2. Gestion du cycle de vie d’une entité

Nous allons décrire, dans cette section, les opérations liées à la gestion du cycle de vie des entités.
Une instance peut avoir l’état de : nouveau, gérée, détachée ou supprimée.

    •   Une nouvelle instance n’a pas d’identité persistante, elle n’est pas encore associée au
        contexte de persistance.
    •   Une instance gérée est une instance avec une identité persistante, elle est associée avec un
        contexte persistant.
    •   Une instance détachée est une instance ayant une identité persistante mais qui n’est plus liée
        au contexte de persistance.
    •   Une instance supprimée est une instance ayant une identité persistante, associée avec le
        contexte de persistance mais qui est programmée pour être supprimée de la base de
        données.

                                              1. Persister une instance d’une entité

Une nouvelle instance d’entité devient persistante et gérée lorsque l’on invoke la méthode persist avec
l’instance en paramètre ou lorsque cette opération est effectuée en cascade.
Voici quelques détails concernant cette opération (E est une entité) :

    •   Si E est une nouvelle entité, elle passe à l’état gérée. L’entité E sera ajoutée en base de
        données avant que la transaction soit commitée ou lorsque que la méthode flush est appelée.
    •   Si E est une entité préexistante, elle est ignorée par l’opération de persistance. Cependant
        l’opération est de type « cascade » sur l’ensemble des entités référencées par E, si les
        relations de E vers d’autres entités sont marquées avec cascade=PERSIST ou cascade=ALL
        (nous verrons plus tard l’ensemble des différentes annotations)
    •   Si E est une entité supprimée, elle passe en état : « gérée »
    •   Si E est détachée, alors une exception de type IllegaleArgumentException est lancée




                                              2. Supprimer une instance

Vous pouvez supprimer une entité en appelant la méthode remove de l’EntityManager et en lui
passant l’entité à supprimer, ou alors l’entité peut être supprimée par « cascade ».
Voici quelques détails concernant cette opération (E est une entité) :

    •   Si E est une nouvelle entité, l’opération est ignorée. Cependant l’opération de suppression est
        de type « cascade » sur l’ensemble des entités référencées par E, si les relations sur ces
        autres entités sont marquées comme cascade=REMOVE ou cascade=ALL.
    •   Si E est détachée, alors une exception de type IllegaleArgumentException est lancée
    •   Si E est supprimée, elle est ignorée par cette opération
                                              3. Synchronisation avec la base de
                                                 données

Cette opération met à jour l’état d’un bean par rapport aux valeurs de la base de données. Cette
synchronisation écrit l’ensemble des modifications des entités persistantes et de leurs relations.
Le fournisseur de persistance est permit d’effectuer une synchronisation vers la base de données à
d’autres moment, comme par exemple avant l’exécution d’une requête. La méthode flush permet de
forcer une synchronisation.
Cette synchronisation est appliquée aux entités associées au contexte de persistence. Il existe
l’annotation FlushMode qui vous permet de spécifier de façon plus détaillée l’opération de
synchronisation.
Voici quelques détails concernant cette opération (E est une entité) :

    •    Si E est une instance ayant l’état gérée, elle est synchronisée avec la base de donnés
             o Pour toutes les entités Y référencées par E, si la relation entre ces deux entités est
                  annotée : cascade=PERSIST ou cascade=ALL, l’opération de persistance est
                  appliquée à Y.
             o Pour toutes les entités Y référencées par E, si la relation entre ces deux entités n’est
                  pas annotée : cascade=PERSIST ou cascade=ALL :
                       Si Y est nouveau, une exception IllegaleStateException est jetée et la
                          transaction est « rolled back ».
                       Si Y est détaché, cela dépend du type de relation qu’il y a entre Y et E. Si E
                          connaît la relation, aucune modification est effectuée par rapport à la
                          relation.Si Y connaît la relation, le comportement est indéfinit.
    •    Si E est supprimée, l’entité est supprimée de la base de données. Aucune opération de
         « cascade » n’est lancée.

                                              4. Entités détachées

Lorsque le contexte de persistance est stoppé, toutes les entités rattachées à ce contexte sont
détachées.
L’application doit alors accéder avec prudence aux propriétés de ces objets.
Voici les états sur lesquels il faut faire attention :

    •    Les champs persistants marqués par : fetch=LAZY

Les instances détachées continuent de vivre à l’extérieur du contexte de persistance et leur état n’est
plus guaranti d’être mis à jour avec la base de données.

Une entité détachée peut simplement être le résultat d’une sérialisation d’une entité ou lorsque vous le
passer à un autre tiers de votre application (une remote interface par exemple). Dans ce cas, les
mêmes règles s’appliquent.

                                              5. « Merge » opération

L’opération « merging » vous permet de propager l’état « détaché » d’une entité vers l’état persistant
grâce à l’EntityManager.
Voici quelques détails concernant cette opération (E est une entité) :

    •    Si E est une instance détachée, elle est copiée dans une instance « gérée » E’ préexistante
         de la même entité ou alors une nouvelle instance de E est créée.
    •    Si E est une nouvelle instance, une nouvelle instance « gérée » E’ est créée et l’état de E est
         copié dans E’.
    •    Si E est une instance supprimée, une IllegaleArgumentException est jetée.

                                              6. Exemple d’utilisation

//TODO

import java.util.Collection;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.society.stockmanager3.entities.beans.City;
import com.society.stockmanager3.entities.beans.Country;
import com.society.stockmanager3.exceptions.NotFindException;
import com.society.stockmanager3.sessions.interfaces.LocalStorage;
import com.society.stockmanager3.sessions.interfaces.RemoteStorage;

@Stateless
public class StorageBean implements RemoteStorage, LocalStorage {

// Injection
@PersistenceContext(unitName = "stockManager")
protected EntityManager em;

public StorageBean() {
}

public Collection<Country> getAllCountry() {
return em.createQuery("from Country c").getResultList();
}

public void addCountry(Integer countryId, String name) {
Country country = new Country();
country.setId(countryId);
country.setName(name);
em.persist(country);
}

public void removeCountry(Integer countryId) {
em.remove(em.find(Country.class, countryId));
}

public void addCity(Integer cityId, String name, Integer countryId) {
City city = new City();
city.setCountry(em.find(Country.class, countryId));
city.setId(cityId);
city.setName(name);
em.persist(city);
}

public void removeCity(Integer cityId) {
em.remove(em.find(City.class, cityId));
}
}



2.3.5.Message Driven Bean (MDB)

                             8. Business Interface

L’interface à utiliser, est bien entendu, l’interface définit par JMS (Java Message Service), c'est-à-dire
javax.jms.MessageListener.
Cette interface peut dépendre du type de messagerie sur laquelle vous voulez connecter votre bean
(c’est généralement JMS).

Il existe cependant un nouveau type de Message Driven Bean, ce sont les Message Driven Bean
POJOs. Ce type permet d’interconnecter un client avec un MDB et de permettre à ce client de faire de
l’appel de méthode à distance via JMS.
Ce type d’utilisation se rapproche des Session bean. Cependant la connexion se fait alors par
message JMS et non via RMI.
De plus les appels et la réception de résultat sont asynchrones.

                             9. Bean Class

Vous devez simplement annoter votre classe avec @MessageDriven. Votre classe doit également
implémenter la méthode onMessage(Message msg) dans le cas d’une utilisation directe avec JMS.
Il vous faut également définir les différents paramètres à utiliser pour la connexion à la destination …
Pour cela on utilisera les propriétés : destinationType et destination.
Voici un exemple de paramétrage d’un bean :

@MessageDriven(activateConfig =
{
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination",
propertyValue="queue/mdb")
})

Cette annotation est bien entendu à utiliser sur la classe d’implémentation du bean.

                             10.Exemple

//TODO

import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

@MessageDriven(activateConfig =
{
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination",
propertyValue="queue/simpleQueue")
})
public class SimpleMessageBean implements MessageListener {

@Resource MessageDrivenContext mdc;

public void onMessage(Message inMessage) {
TextMessage msg = null;

try {
if (inMessage instanceof TextMessage) {
msg = (TextMessage) inMessage;
System.out.println
("MESSAGE BEAN: Message received: "
+ msg.getText());
} else {
System.out.println
("Message of wrong type: "
+ inMessage.getClass().getName());
}
} catch (JMSException e) {
e.printStackTrace();
mdc.setRollbackOnly();
} catch (Throwable te) {
te.printStackTrace();
}
}
}

3.EJB-QL

La spécification 3 des EJB continue de définir un langage de requête (Query Langage). Celui-ci a
beaucoup plus de fonctionnalités que celui de la version 2.1.
Nous allons détailler l’ensemble de celles-ci.
3.1.Nouveautés

Dans cette version vous avez accès :

    •   Requêtes de type UPDATE et DELETE.
    •   Opérations de type JOIN
    •   GROUP BY
    •   HAVING
    •   Projection
    •   Sous requête

Mais l’une des grandes nouveautés est sans doute la possibilité de créer des requêtes à la volée
(totalement dynamique).

3.2.Type de requêtes

Avec l’EJB-QL vous pouvez faire des requêtes de type SELECT, UPDATE ou DELETE. De plus vous
pouvez construire chacune de ces requêtes de façon dynamique et vous pouvez utiliser des
paramètres pour chacun des types.

3.2.1.SELECT
Une requête SELECT est une chaîne de caractère ayant :

    •   Une clause SELECT qui détermine le type d’objet recherché ou les valeurs à charger
    •   Une clause FROM qui définit le domaine des entités à utiliser dans l’ensemble de la requête
    •   Une clause WHERE (optionnelle) qui permet de restreindre le nombre de résultat et donc de
        préciser la recherche
    •   Une clause GROUP BY (optionnelle) qui permet aux requêtes de faire de l’aggrégation via
        des groupes
    •   Une clause HAVING (optionnelle) qui permet de filtrer les résultats par rapport aux groupes
    •   Une clause ORDER BY (optionnelle) qui permet d’ordonner les résultats retourner par la
        requête

Voici comment est définie cette requête :
select_statement :: = select_clause from_clause [where_clause] [groupby_clause]
[having_clause] [orderby_clause]

3.2.2.UPDATE et DELETE
Les opérations d’update et de suppression peuvent être appliquées qu’à une seule class entité (avec
l’ensemble de ses sous classes). Seul un schéma abstrait peut être spécifié dans la clause FROM ou
la clause UPDATE.
Voici une description théorique des deux types de requêtes :
update_statement ::= update_clause [where_clause]
update_clause ::= UPDATE abstract_schema_name [[AS] identification_variable]
SET update_item {, update_item}*

update_item ::= [identification_variable.]state_field = new_value

new_value ::=
simple_arithmetic_expression |
string_primary |
datetime_primary |
boolean_primary

delete_statement ::= delete_clause [where_clause]
delete_clause ::= DELETE FROM abstract_schema_name [[AS] identification_variable]

Remarque : l’utilisation de ces requêtes n’est pas toujours très conseillée. Effectuez toujours
l’exécution de ces requêtes dans une transaction séparée.

3.3.Schéma abstrait & Domaine de requête

L’EJB-QL est un langage typé, beaucoup d’expression utilise donc un type.
Le schéma abstrait d’une entité est définit dans les annotations de la classe ou dans le descripteur de
déploiement.
Le domaine de requête est définit par l’ensemble des schémas abstraits qui sont dans la même unité
de persistance.

Vous pouvez donc désigner une entité directement via le nom de son schéma abstrait.

3.4.La clause FROM

La clause FROM permet de définir le domaine de la requête.
Voici la description de cette clause :
from_clause ::=
FROM identification_variable_declaration
{, {identification_variable_declaration | collection_member_declaration}}*

identification_variable_declaration ::= range_variable_declaration { join | fetch_join }*

range_variable_declaration ::= abstract_schema_name [AS] identification_variable

join ::= join_spec association_path_expression [AS] identification_variable

fetch_join ::= join_spec FETCH association_path_expression

association_path_expression ::=
collection_valued_path_expression | single_valued_association_path_expression

join_spec::= [ LEFT [OUTER] | INNER ] JOIN

collection_member_declaration ::=
IN (collection_valued_path_expression) [AS] identification_variable

3.4.1.Les identifiants
Un identifiant est une séquence de caractère (longueur infinie). Cependant vous devez faire attention
de ne pas utiliser un identifiant réservé.
Voici la liste de ceux-la :
SELECT, FROM, WHERE, UPDATE, DELETE,
JOIN, OUTER, INNER, LEFT, GROUP, BY, HAVING, FETCH, DISTINCT, OBJECT, NULL, TRUE,
FALSE, NOT, AND, OR, BETWEEN, LIKE, IN, AS, UNKNOWN[13], EMPTY, MEMBER, OF, IS, AVG,
MAX, MIN, SUM, COUNT, ORDER, BY, ASC, DESC, MOD, UPPER, LOWER, TRIM, POSITION,
CHARACTER_LENGTH, CHAR_LENGTH, BIT_LENGTH, CURRENT_TIME, CURRENT_DATE,
CURRENT_TIMESTAMP, NEW, EXISTS, ALL, ANY, SOME.

Nous retrouvons, parmi les identifiants, ceux utilises pour les variables. Ils doivent être déclarés dans
la clause FROM. Ces identifiants sont « case sensitive ».
Ces variables permettent de représenter une valeur d’un type d’une expression.
SELECT DISTINCT o
FROM Order o JOIN o.lineItems l JOIN l.product p
WHERE p.productType = ‘office_supplies’

Dans l’exemple ci-dessus :

    •   o.lineItems l indique que l est évalué comme étant n’importe quel LineItem directement lié à
        un objet Order.
    •   p représente un « Product »
    •   o représente un « Order »

3.4.2.Jointures (JOINS)
Vous pouvez spécifier explicitement une jointure dans votre clause FROM.
Il existe différentes jointures :

    •   Inner Join
    •   Outer Join
    •   Fetch Join

Bien que l’on puisse utiliser d’autres manières de relier deux entités (ou plus) via l’intruction « IN » par
exemple, le concept de jointure est toujours présent et nous allons détailler son fonctionnement.
                             11.Inner Join

La syntaxe d’une jointure interne est :
[ INNER ] JOIN association_path_expression [AS] identification_variable

Voici un exemple concret d’utilisation de ce type de jointure :
SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1
Cette requête joint les commandes (orders) et les clients (Customer).
Remarque : le mot clé INNER est optionnel. Il est principalement utilisé ici pour préciser le type de
jointure au développeur.
La requête précédente équivaut à :
SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1
L’utilisation du « IN » utilise donc une jointure interne implicite.

                             12.Outer Join

Ce type de jointure permet de récupérer un ensemble d’entité pour lesquels des valeurs validant la
condition de jointure peuvent être absent.

Voici la syntaxe d’une jointure externe :
LEFT [OUTER] JOIN association_path_expression [AS] identification_variable

Voici un exemple d’utilisation de ce type de jointure :
SELECT c FROM Customer c LEFT JOIN c.orders o WHERE c.status = 1
Remarque : le mot clé OUTER est optionnel.

                             13.Fetch Join

Ce type de jointure est spécial dans le sens où il s’applique aussi bien à une jointure interne qu’à une
jointure externe.
L’utilisation d’une FETCH JOIN permet d’établir le « fetching » d’une association en tant que requête
indépendante. Ce type de jointure est spécifié par rapport à une entité et ses entités relationnelles.

Voici la syntaxe de ce type de jointure :
fetch_join ::= [ LEFT [OUTER] | INNER ] JOIN FETCH association_path_expression

Voici un exemple d’utilisation de cette requête :
SELECT DISTINCT c
FROM Customer c LEFT JOIN FETCH c.orders
WHERE c.address.state = ’CA’

La requête précédente retourne un ensemble de client (Customer). De plus les commandes (orders)
associées aux clients sont également retrouvées même si elles ne sont pas explicitées définies dans
la clause FROM.
Les champs persistants des commandes sont donc également initialisés.

3.4.3.Collections
Un identificateur de variable déclaré comme un « membre de collection ». Cet identifiant est déclaré
en utilisant un spécifique opérateur : « IN ». Le paramètre à donner à cet opérateur est l’objet
collection à utiliser.
Voici la declaration théorique :
collection_member_declaration ::=
IN (collection_valued_path_expression) [AS] identification_variable

Voici un exemple de requite utilisant l’opérateur IN :
SELECT DISTINCT o
FROM Order o, IN(o.lineItems) l
WHERE l.product.productType = ‘office_supplies’

Cette requête correspond à :
SELECT DISTINCT o
FROM Order o JOIN o.lineItems l JOIN l.product p
WHERE p.productType = ‘office_supplies’

Dans cet exemple, lineItems est le nom du champ relationnel qui représente une collection d’instance
d’objet de type : LineItem.
3.5.La clause WHERE

La clause WHERE est tout à fait similaire à celle que l’on connaît en SQL. Elle est utilisée pour
préciser une recherche plus spécifique dans la requête. Elle est aussi bien utilisée dans les requêtes
SELECT que UPDATE ou encore DELETE.

Voici la déclaration théorique :
where_clause ::= WHERE conditional_expression

Vous pouvez utiliser n’importe quelle expression conditionnelle.
Par exemple :
WHERE p.productType = ‘office_supplies’

Permet de sélectionner l’ensemble des produits dont le type est égal à : « office_supplies ».


3.6.Expressions conditionnelles

Les expressions conditionnelles sont nombreuses. Elles sont utilisées dans la clause WHERE ou
HAVING.
Nous retrouvons l’ensemble des opérateurs SQL. Quelques petites différences existent :

    •   EMPTY permet de vérifier qu’une collection est vide
    •   MEMBER [OF] permet de savoir si une valeur (ou objet) appartient à une collection
    •   ALL permet de vérifier qu’une condition est correcte pour un ensemble d’objet

SELECT emp
FROM Employee emp
WHERE emp.salary > ALL (
SELECT m.salary
FROM Manager m
WHERE m.department = emp.department)

    •   ANY (ou SOME) permet de verifier qu’une condition est correcte pour une partie d’un
        ensemble d’objet (ou pour tout l’ensemble)

3.7.GROUP BY & HAVING

Les clauses GROUP BY et HAVING sont les mêmes qu’en SQL. Ils vous permettent de gérer
l’aggrégation des résultats suivant des groupes. Vous devez, bien entendu, respecter les mêmes
règles qu’en SQL.

Voici un exemple:
SELECT c.status, avg(c.filledOrderCount), count(c)
FROM Customer c
GROUP BY c.status
HAVING c.status IN (1, 2)




3.8.La clause SELECT

La clause SELECT permet de définir les différents résultats retournés.
Voici la description théorique de cette clause :
select_clause ::= SELECT [DISTINCT] select_expression {, select_expression}*

select_expression ::=
single_valued_path_expression |
aggregate_select_expression |
identification_variable |
OBJECT(identification_variable) |
constructor_expression

constructor_expression ::=
NEW constructor_name ( constructor_item {, constructor_item}* )

constructor_item ::= single_valued_path_expression | aggregate_select_expression

aggregate_select_expression ::=
{ AVG | MAX | MIN | SUM } ([DISTINCT] state_field_path_expression) |
COUNT ([DISTINCT] identification_variable | state_field_path_expression |
single_valued_association_path_expression)

Voici un exemple :
SELECT c.id, c.status
FROM Customer c JOIN c.orders o
WHERE o.count > 100


Remarque : une clause SELECT ne doit renvoyer que de simples valeurs. La requête ci-dessous
n’est pas correcte :
SELECT o.lineItems FROM Order AS o

3.8.1.Utilisation d’un constructeur
Un constructeur est utilisé pour retourner une collection d’instance Java. La classe spécifiée n’a pas
besoin d’être une entité ou d’être mappée à la base de données. Cependant vous devez spécifier le
nom complet du constructeur.

Voici un exemple :
SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count)
FROM Customer c JOIN c.orders o
WHERE o.count > 100


3.9.ORDER BY

La clause ORDER BY permet d’ordonner les résultats de la requête.

Voici la description théorique:

orderby_clause ::= ORDER BY orderby_item {, orderby_item}*
orderby_item ::= state_field_path_expression [ASC | DESC]

Vous pouvez donc l’utiliser comme vous avez l’habitude en SQL.


3.10.Exemples

Voici quelques examples de requêtes. N’hésitez pas à vous en servir lorsque vous avez à créer vos
propres requêtes.

3.10.1.Requêtes simples
Retourne l’ensemble des commandes :
SELECT o
FROM Order o

Retourne l’ensemble des commandes dont le destinataire habite dans l’état « CA ».
SELECT o
FROM Order o
WHERE o.shippingAddress.state = ‘CA’

Retourne l’ensemble des villes ayant des commandes :
Find all states for which there are orders:
SELECT DISTINCT o.shippingAddress.state
FROM Order o
3.10.2.Requêtes relationnelles
Retourne l’ensemble des commandes ayant des lignes :
SELECT DISTINCT o
FROM Order o, IN(o.lineItems) l

Vous pouvez également l’écrire comme suit :
SELECT o
FROM Order o
WHERE o.lineItems IS NOT EMPTY

Retourne l’ensemble des commandes n’étant pas totalement envoyées :
SELECT DISTINCT o
FROM Order o JOIN o.lineItems l
WHERE l.shipped = FALSE
Retourne l’ensemble des commandes dont l’adresse de destination n’est pas la même que celle de
facturation :
SELECT o
FROM Order o
WHERE
NOT (o.shippingAddress.state = o.billingAddress.state AND
o.shippingAddress.city = o.billingAddress.city AND
o.shippingAddress.street = o.billingAddress.street)

Vous pouvez effectuer la meme operation de façon plus simple :
SELECT o
FROM Order o
WHERE o.shippingAddress <> o.billingAddress



4.Packaging et déploiement

Nous avons vu, dans les précédentes sections, comment créer vos EJB (session, entity …).
Cependant avant de pouvoir les utiliser il faut les packager et les déployer.

4.1.Session & MessageDriven

Les session et les message driven beans peuvent être placé dans le même jar pour le déploiement.
L’ensemble des classes utilisées par ces ejbs doivent être placées dans un fichier jar ayant l’extension
« .ejb3 ».
Vous n’avez pas besoin de descripteur de déploiement ou autre fichier de paramétrage xml.

Le simple fait de déployer votre fichier contenant l’ensemble des classes de vos ejbs permettra de
déployer vos EJB.


4.2.Entity

Les entity bean sont sans doute les « plus durs » à packager. Mais cette opération a été simplifiée de
façon radicale depuis EJB3.

4.2.1.L’unité de persistance
L’unité de persistance contient :

    •   Un nom pour le manager d’entités avec son fournisseur et ses informations de configuration
    •   L’ensemble des classes utilisées par l’unité de persistance
    •   Le mapping des méta-données (via annotations ou fichiers xml), afin de faire la relation avec
        la base de données




                             14.L’archive de persistance

La norme J2EE spécifie que l’archive utilisera l’extension « .par » afin de regrouper l’ensemble des
fichiers liés aux entités.
Si vous regrouper l’ensemble de vos applications dans une archive d’entreprise « .ear » vous devez
spécifier votre archive de persistance dans votre fichier application.xml grâce aux lignes suivantes :
<application>
<module>
<persistence>orderEntities.par</persistence>
</module>
</application>

Vous pouvez déployer plusieurs archive de persistance dans une même application d’entreprise.

                              15.Le fichier persistence.xml

C’est le fichier qui permet de configurer l’unité persistante. Ce fichier doit être placé dans le dossier
META-INF de l’archive de persistance.

Voici un exemple de fichier :
<entity-manager>
<name>em1</name>
<provider>com.acme.persistence</provider>
<jta-data-source>jdbc/MyDB</jta-data-source>
<mapping-file>ormap.xml</mapping-file>
<jar-file>MyApp.jar</jar-file>
<class>com.widgets.Order</class>
<class>com.widgets.Customer</class>
<properties>
<property name="sql-logging" value="on"/>
</properties>
</entity-manager>

Voici une description de chacun des elements :

      •   name : nom de l’entity manager. Si vous ne spécifiez pas de nom, le nom du fichier sera
          utilisé comme nom de l’entity manager (exemple : orderEntities.par => orderEntities)
      •   provider : spécifie le nom du fournisseur de persistance. La classe indiquée doit héritée de la
          classe : « javax.persistence.spi.PersitenceProvider ».
      •   jta-data-source / non-jta-data-source : définit le nom JNDI global d’une source de données
          JTA ou non.
      •   mapping-file/jar-file/class : declare l’ensemble des classes gérées par l’unité de persistance.
          Vous pouvez utiliser un ou plusieurs des éléments suivants :
                o fichier de mapping objet/relationnel
                o fichier jar
                o une liste de classe (explicite)
                o les classes contenues dans l’archive persistante
      •   properties : liste de propriétés utilisées pour spécifier des informations de configuration
          spécifiques au fournisseur.

5.EJB 3 en pratique

Nous reprenons l’exemple établi dans l’essentiel EJB 2&2.1. Cela permet de comparer les deux
spécifications mais également les deux méthodes utilisées (xDoclet + ant dans l’ancien cas) ; ant
seulement dans le nouveau (pour le packaging).




5.1.1.Session Bean
Pour le session bean, nous avons créé l’interface distante et la classe du bean.
Voici le code de l’interface distante :
package com.society.stockmanager3.sessions.interfaces;

import javax.ejb.Remote;

import com.society.stockmanager3.entities.beans.Product;
import com.society.stockmanager3.entities.beans.ProductFamily;
import com.society.stockmanager3.exceptions.NotFindException;

/**
* Remote interface for StorageService.
*/
@Remote
public interface RemoteStorage
{
/**
* Business method
*/
public java.lang.String sayHello( );

/**
* Business method
* @throws NotFindException
*/
public java.lang.Integer[] getAllProductIds( ) throws NotFindException;


/**
* Business method
*/
public java.util.Collection<Product> getAllProduct( )
throws NotFindException;

/**
* Business method
*/
public java.util.Collection<ProductFamily> getAllProductFamily( );


/**
* Business method
*/
// public void addProduct( com.society.stockmanager.vo.ProductData datas,java.lang.String
codeFamily );


/**
* Business method
*/
public void removeProduct( java.lang.Integer productId );


/**
* Business method
*/
public void addProductFamily( java.lang.String code,java.lang.String designation );


public void addProduct(Product product);


/**
* Business method
*/
public void removeProductFamily( java.lang.String code );


/**
* Business method
*/
// public void addProvider( com.society.stockmanager.vo.ProviderData datas,java.lang.Integer
cityId,java.util.Collection productIds );


/**
* Business method
*/
public void removeProvider( java.lang.Integer providerId );
/**
* Business method
*/
public void addCountry( java.lang.Integer countryId,java.lang.String name );

/**
* Business method
*/
public void removeCountry( java.lang.Integer countryId );

/**
* Business method
*/
public void addCity( java.lang.Integer cityId,java.lang.String name,java.lang.Integer countryId );

/**
* Business method
*/
public void removeCity( java.lang.Integer cityId );

/**
* Business method
*/
// public com.society.stockmanager.vo.ProductFamilyData findProductFamily( java.lang.String code );

/**
* Business method
*/
public java.util.Collection<Product> findProductByCountryId( java.lang.Integer countryId );

}


Détails des annotations :

    •   Remote : indique que l’interface est de type Remote (le serveur d’application fera la liaison
        automatiquement avec les différents bean implémentant cette interface)


Voici le code de l’interface locale :
package com.society.stockmanager3.sessions.interfaces;

import javax.ejb.Local;

import com.society.stockmanager3.entities.beans.Product;
import com.society.stockmanager3.entities.beans.ProductFamily;
import com.society.stockmanager3.exceptions.NotFindException;

/**
* Remote interface for StorageService.
*/
@Local
public interface LocalStorage
{
/**
* Business method
*/
public void addProduct(Product product);

}

Détails des annotations :

    •   Local : indique que l’interface est de type Locale (le serveur d’application fera la liaison
        automatiquement avec les différents bean implémentant cette interface)
Voici le code de la classe d’implémentation :
package com.society.stockmanager3.sessions.beans;

import java.util.Collection;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.society.stockmanager3.entities.beans.City;
import com.society.stockmanager3.entities.beans.Country;
import com.society.stockmanager3.entities.beans.Product;
import com.society.stockmanager3.entities.beans.ProductFamily;
import com.society.stockmanager3.entities.beans.Provider;
import com.society.stockmanager3.exceptions.NotFindException;
import com.society.stockmanager3.sessions.interfaces.LocalStorage;
import com.society.stockmanager3.sessions.interfaces.RemoteStorage;

@Stateless
public class StorageBean implements RemoteStorage, LocalStorage {

@PersistenceContext(unitName = "stockManager")
protected EntityManager em;

public StorageBean() {
}

public Collection<Product> findProductByCountryId(Integer countryId) {
return em
.createQuery(
"select p from Provider provider, IN(provider.products) p WHERE provider.city.country.id = "
+ countryId.intValue()).getResultList();
}

public Collection<Product> getAllProduct() throws NotFindException {
return em.createQuery("from Product p").getResultList();
}

public Collection<ProductFamily> getAllProductFamily() {
return em.createQuery("from ProductFamily pf").getResultList();
}

public String sayHello() {
return "Hello";
}

public Integer[] getAllProductIds() throws NotFindException {
Collection<Product> allProducts = getAllProduct();
Integer[] result = new Integer[allProducts.size()];
int index = 0;
for (Product product : allProducts) {
result[index] = product.getId();
index++;
}
return result;
}

public void removeProduct(Integer productId) {
em.remove(em.find(Product.class, productId));
}

public void addProductFamily(String code, String designation) {
ProductFamily productFamily = new ProductFamily();
productFamily.setName(designation);
em.persist(productFamily);
}

public void removeProductFamily(String code) {
em.remove(em.find(ProductFamily.class, code));
}

public void removeProvider(Integer providerId) {
em.remove(em.find(Provider.class, providerId));
}

public void addCountry(Integer countryId, String name) {
Country country = new Country();
country.setId(countryId);
country.setName(name);
em.persist(country);
}

public void removeCountry(Integer countryId) {
em.remove(em.find(Country.class, countryId));
}

public void addCity(Integer cityId, String name, Integer countryId) {
City city = new City();
city.setCountry(em.find(Country.class, countryId));
city.setId(cityId);
city.setName(name);
em.persist(city);
}

public void removeCity(Integer cityId) {
em.remove(em.find(City.class, cityId));
}

public void addProduct(Product product) {
em.persist(product);
}
}

Détails des annotations :

    •   Stateless : spécifie que le type de l’EJB est Stateless (session bean sans état)
    •   PersistenceContext : permet d’indiquer une demande d’injection automatique d’un objet de
        type « EntityManager » permettant de gérer des entity bean, directement dans la classe du
        bean lors de sa création
            o unitName : définit le nom de l’unité persistante à utiliser




5.1.2.Entity Bean
Dans cette partie, nous allons décrire les différents bean entités (et les annotations) utilisés.

                               16.Country

Voici le code de l’entité Country :
package com.society.stockmanager3.entities.beans;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "COUNTRY")
public class Country implements Serializable {
private int id;

private String name;

public Country() {
}

public Country(String name) {
this.name = name;
}

@Id(generate = GeneratorType.AUTO)
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Voici le détail des annotations utilisées :

    •    Entity : déclare que notre classe est de type entity bean
    •    Table : définit le nom de la table à rattacher à notre entité
              o name : nom de la table à utiliser
    •    Id : définit le champ persistant servant de clé primaire
              o generate : attribut définissant le type de génération à utiliser (ici « auto incrémenté »)


Remarque : l’utilisation d’un id auto incrémenté obligé d’utiliser un type « nombre » (entier, double …)
. Cela est bien entendu lié à la base de données.

                               17.City

Voici le code de l’entité City :
package com.society.stockmanager3.entities.beans;

import java.io.Serializable;

import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "CITY")
public class City implements Serializable {
protected int id;
protected String name;
protected Country country;

public City() {
}

public City(String name) {
this.name = name;
}

@ManyToOne(optional = false)
@JoinColumn(name = "countryId")
public Country getCountry() {
return country;
}

public void setCountry(Country country) {
this.country = country;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Id(generate = GeneratorType.AUTO)
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}
}

Voici le détail des annotations utilisées :

    •    ManyToOne : déclare un champ relationnel de type « many to one » (N-1)
             o optional : attribut
    •    JoinColumn : définit le nom du champ qui sera utilisé comme champ de jointure
             o name : attribut définissant le nom du champ

                               18.ProductFamily

Voici le code de l’entité ProductFamily :
package com.society.stockmanager3.entities.beans;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "PRODUCTFAMILY")
public class ProductFamily implements Serializable {
protected int id;

protected String name;

public ProductFamily() {
}

public ProductFamily(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
@Id(generate = GeneratorType.AUTO)
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Override
public String toString() {
return name;
}
}

                              19.Product

Voici le code de l’entité Product :
package com.society.stockmanager3.entities.beans;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "PRODUCT")
public class Product implements Serializable {
protected int id;
protected String name;
protected Collection<Provider> providers;

public Product() {
}

public Product(String name) {
this.name = name;
}

@ManyToMany
public Collection<Provider> getProviders() {
return providers;
}

public void setProviders(Collection<Provider> providers) {
this.providers = providers;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Id(generate = GeneratorType.AUTO)
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}
}
Voici le détail des annotations utilisées :
    •   ManyToMany : défini une relation ManyToMany (n-m) pour le champ relationnel courant.

                               20.Provider

Voici le code l’entité Provider :
package com.society.stockmanager3.entities.beans;

import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "PROVIDER")
public class Provider implements Serializable {

protected int id;
protected String name;
protected String zipCode;
protected String society;
protected City city;
protected Collection<Product> products;

public Provider() {
}

public Provider(String name) {
this.name = name;
}

public String getSociety() {
return society;
}

public void setSociety(String society) {
this.society = society;
}

public String getZipCode() {
return zipCode;
}

public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}

@ManyToOne(optional = false)
@JoinColumn(name = "cityId")
public City getCity() {
return city;
}

public void setCity(City city) {
this.city = city;
}

@ManyToMany(mappedBy="providers")
public Collection<Product> getProducts() {
return products;
}

public void setProducts(Collection<Product> products) {
this.products = products;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Id(generate = GeneratorType.AUTO)
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}
}

Voici le détail des annotations :

    •    ManyToMany : déclaration d’un champ relationnel de type « Many to Many » (N-M)
            o mappedBy : définit le nom du champ persistant du propriétaire de la relation (ici
               Product)

5.1.3.Message Driven Bean
Voici l’exemple d’un Message Driven Bean permettant l’ajout d’un produit via un message JMS
(ObjectMessage) :

package com.society.stockmanager3.sessions.beans;

import javax.annotation.EJB;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;

import com.society.stockmanager3.entities.beans.Product;
import com.society.stockmanager3.sessions.interfaces.LocalStorage;

@MessageDriven(activateConfig =
{
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination",
propertyValue="queue/StockManager/ProductAddQueue")
})
public class ProductManageMessageBean
implements
MessageListener {

@EJB
protected LocalStorage storage;

public ProductManageMessageBean() {
}

public void onMessage(Message message) {
if(message instanceof ObjectMessage) {
ObjectMessage tmpMessage = (ObjectMessage)message;
try {
Product product = (Product) tmpMessage.getObject();
storage.addProduct(product);
System.out.println("New product added : " + product);
}
catch(Exception e) {
System.out.println("Can't add the product / cause : " + e);
}
}
else {
System.out.println("don't know the type of message");
}
}
}

Voici le détail des nouvelles annotations :

    •   MessageDriven : déclare un Message Driven Bean (à partir de sa classe)
    •   ActivationConfigProperty
            o propertyName : nom de la propriété
            o propertyValue : valeur de la propriété à affecter
    •   EJB : injection automatique lié à un EJB (instanciation automatique)

5.1.4.Clients

                               21.Client console

Voici l’exemple d’un client console :

package com.society.stockmanager3.clients;

import javax.naming.Context;
import javax.naming.InitialContext;

import com.society.stockmanager3.sessions.interfaces.RemoteStorage;

public class StorageClient {

public static void main(String[] args) {

try {
Context ctx = new InitialContext();
RemoteStorage storageService = (RemoteStorage) ctx
.lookup(RemoteStorage.class.getName());
System.out.println(storageService.sayHello());
storageService.addProductFamily("code1", "designation1");
storageService.addProductFamily("code2", "designation2");
System.out.println(storageService.getAllProductFamily());
System.out.println(storageService.getAllProduct());
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
}

                                Les Classes - Concepts & héritage



1.POO présentation

1.1.Notion d’objet
Les entités informatiques qui peuvent se définir en un ensemble d’entités sont appelées objet. Pour
expliquer la programmation objet on peut faire une analogie avec l’homme qui classe tout ce qui
l’entoure. La difficulté majeure consiste à modéliser les objets.

Les caractéristiques d’un objet :
Les méthodes : Actions que peut effectuer un objet.
Les attributs : Données stockant des informations sur un objet.
L’identité : Identifiant unique attribué a un objet lui permettant d’être distingué des autres objets.
1.2.Notion de classe
Les classes représentent la structure d’un objet (instance), qui peut être définie comme l’ensemble
des éléments qui composent cet objet. L’objet découle d’une classe. Les méthodes (opérations
appliquées aux objets) et les attributs (valeurs d’un objet) sont les deux types d’éléments qui
constituent les classes.

Prenons un cas concret :
La race « terrier » représente la classe principale.
Bull terrier et fox terrier sont des instanciations de la classe terrier.


1.3.Le Principe d’Encapsulation

Un objet regroupe ses attributs (variables) et ses méthodes (code) capables d’interagir dessus. De
plus, sa structure n’est pas visible de l’extérieur (Abstraction de données), ce qui permet de pouvoir
modifier la structure interne de la classe sans que cela ne change quoi que ce soit pour l’utilisateur.
L’interaction avec les objets se fait via les méthodes propres à ce dernier, ainsi on n’a aucune
information sur le déroulement interne, si le traitement demandé a fait appel a plusieurs méthodes ou
encore s’il a demandé la création d’objet, c’est ce qu’on appelle l’abstraction procédurale.

Cependant les langages objets n’obligent pas à respecter le principe d’encapsulation, c’est au
programmeur de faire attention.


2.Les Classes

2.1.Création d’une classe
Pour créer une application en Java, il faut créer une nouvelle classe. En créant une classe, on crée un
nouveau type de variable. En Java, il existe deux types de variables :
Les types primitifs : int, float, boolean, …
Les types de références (des classes) : Object, String (chaîne de caractères) , System, …
La JRE fournie de nombreuses classes, donc de nombreux types de références.
Le code définissant une classe est contenu dans un fichier texte ayant l’extension .java. ; après
compilation nous obtenons un fichier .class contenant le byte-code de cette classe.
Un fichier Java peut contenir plusieurs classes (déconseillé), cependant il ne peut y en avoir qu’une
seule classe déclarée publique. Les autres classes seront sans modificateur de visibilité (default).
La classe déclarée publique doit avoir exactement le même nom que le fichier.
On utilise le mot clé class pour déclarer la classe que l’on veut créer.
Exemple :
     //nom du fichier Exemple.java

       Exemple {
          // code de la classe
   }




2.2.Le constructeur

2.2.1.Définition
Le constructeur est une méthode d’une classe qui permet de créer un objet de cette classe. Le
constructeur définit les tâches à effectuer lors de la création du nouvel objet. Un constructeur possède
deux caractéristiques :
Il doit porter exactement le même nom que la classe à laquelle il appartient (faites attention à
respecter la casse, majuscule/minuscule)
Il ne peut retourner aucune valeur (y compris void)
Un constructeur dépourvu d’argument (ne prenant pas de paramètre) est défini par Java comme un
constructeur par défaut. Il n’est pas obligatoire de définir un constructeur de manière explicite dans sa
classe. En effet, il existe toujours un constructeur par défaut (sans argument) défini de manière
implicite. Vous n’êtes pas obligé de définir de constructeur, si vous estimez qu’aucune action
spécifique n’est à effectuer lors de la création de l’objet.
2.2.2.Surdéfinition du constructeur
De manière générale, la surdéfinition consiste à redéfinir une méthode de manière explicite (utilisé
pour l’héritage). Si vous déclarez un constructeur dans votre classe, vous remplacez alors celui par
défaut (implicite) par ce nouveau (explicite).

Exemple :
    // fichier Personne.java

     public     Personne {

          Personne() {          // constructeur par défaut de la classe personne
                                // déclaré de façon explicite
          }
     }


2.2.3.Surcharge du constructeur
Un constructeur (comme tout autre méthode) peut être surchargé, en fonction des paramètres passés,
l’interpréteur va appeler le constructeur qu’il faut.
Exemple :
      // fichier Personne.java

      Personne {

        Personne() {
                // constructeur de la classe personne (constructeur                              par
   défaut)
        }

          Personne(String nom) {
                  //surcharge
          }

          Personne(String nom, String adresse) {
                  //surcharge
          }
     }



2.3.Instancier une classe

2.3.1.Définition
On appelle instanciation le fait de créer un nouvel objet. L’instanciation consiste donc en une
allocation mémoire. On dit qu’un objet est une instance d’une classe. Généralement, on entend par
instanciation, la création de l’objet et aussi son association à un nom de variable (affectation de la
variable).

2.3.2.L’opérateur new
L’opérateur new spécifie que l’on veut créer un nouvel objet. Celui-ci est suivi de l’appel du
constructeur correspondant à la classe que l’on veut instancier.
Exemple :
      Exemple {

     }
      Exemple();

L’objet est bien créé cependant il représente peu d’intérêt puisque, ne possédant pas de nom. Il n’est
plus manipulable dès lors qu'il a été créé.

2.3.3.Affectation d’objets
On peut identifier les objets grâce une référence (ou identificateur) qu’on va lui donner au moment de
son instanciation. On va faire pointer la référence vers l’objet nouvellement créé en utilisant l’opérateur
égal "=". Cela s’appelle l’affectation.
Exemple :
      Exemple {

     }

     Exemple ex1 =        Exemple();

     //ou

     Exemple ex2;
     ex2 = Exemple();

Exemple ex1 = new Exemple();
Déclaration affectation Instanciation Initialisation

On peut également affecter un objet à un autre objet.

Exemple :
    Exemple ex1 =         Exemple() ;
    Exemple ex2 =         Exemple() ;
    ex1=ex2 ;

Dans la partie gauche de la ligne 1, on déclare une nouvelle variable ex1 de type Exemple (Exemple
ex1). ex1 peut donc référencer tout objet de type Exemple. De l’autre côté, on instancie un objet
Exemple qu’on appellera A. L’opérateur sert à spécifier que l’objet A nouvellement créé est référencé
par la variable ex1.
A la ligne 2, le principe reste le même : On crée un objet Exemple qu’on appellera B qui est référencé
par la variable ex2.
A la ligne 3, on décide que ex1 fait maintenant référence à l’objet B. ex1 et ex2 font référence au
même objet. L’objet A qui était référencé par ex1 n’est alors plus référencé.


2.4.La méthode main

2.4.1.Définition
La méthode main permet l’exécution du programme. C’est la première méthode qui est appelée lors
de l’exécution de la classe. On dit que c’est le point d’entrée du programme. Cette méthode est
similaire à celle du C/C++.

2.4.2.Contraintes
La méthode main doit être dans une classe publique.
Elle doit elle même être déclarée en publique (accessible par l’interpréteur java).
Cette méthode doit être statique (pas besoin de créer d’objet pour l’appeler).
Elle prend obligatoirement en paramètre un tableau de chaîne de caractères (String).
Enfin, cette méthode doit renvoyer un type de données void.

Exemple :
    //nom du fichier Exemple.java

      Exemple {
           main(String[] args) {
         }
     }


2.4.3.Paramètres
Le paramètre « String[] args » de la méthode main va permettre de récupérer les paramètres de la
ligne de commande en les stockant dans le tableau de chaîne de caractères (String) appelé args. Ce
tableau peut avoir le nom que l’on veut. Par convention, on l’appel args.

Exemple :
    //nom du fichier Exemple.java

      Exemple {
          main(String[] args) {
         System.out.println(args[0]);
         System.out.println(args[1]);
          }
     }

     //ligne de commande sous un prompt
     java Exemple argument1 argument2

     //le programme affiche
     argument1
     argument2



2.5.L’opérateur : "." et le pointeur "this"

2.5.1.L’opérateur "."
Il permet l’accès aux ressources et méthodes d’une instance, comme dans la majorité des langages
objets. Cependant l’accès est soumis à plusieurs règles (on ne peut pas accéder à n’importe quelles
ressources ou méthodes d’une instance à n’importe quel endroit du code, voir le chapitre sur la
« Visibilité »).

Principe d’utilisation :
nomInstance .nomVariale
nomInstance.nomMéthode()

Exemple :
    Exemple {

           a;

      String getText(){
                 "Hello World";
         }

      setText(String s){

          }
     }

     //création de l’instance
     Exemple ex1 = Exemple();

     //acces
     ex1.a; // accès à la variable int a
     ex1.getText(); // retourne un string
     ex1.setText("Hello"); // envoie un String par la méthode setText()


2.5.2.Le pointeur "this"
this est le seul pointeur en Java, il va permettre à une méthode d’accéder à la classe dans laquelle
elle est définie.
Par défaut, dans une méthode, Java fait référence à la variable de la classe. Cependant si une
variable dans cette même méthode porte le même nom, Java va faire référence à la variable de la
méthode, grâce à this on va pouvoir préciser la variable que l’on souhaite.

Exemple :
    ExempleThis {

        String s = "Variable de la classe";                              //variable de la
   classe

      main(String [] args) {
           ExempleThis().appel();
         }

      appel() {
                     System.out.println(s);                     //Affichage de la valeur de
   s
                     String s = "Variable de la méthode";
                     System.out.println(s);
                     System.out.println(.s);
            }
     }

L’affichage résultant donne :
      Variable de la classe
      Variable de la méthode
      Variable de la classe

On peut aussi utiliser la méthode this() pour appeler un constructeur d’une classe, cette méthode peut
être seulement appelé au début d’un constructeur. On l’utilise dans un constructeur pour appeler un
autre constructeur (surchargé).

Exemple :
    ExempleThis {

       a;

       ExempleThis() {
                  (217);
          }

             b) {
                     a = b;
            }
     }

Dans cet exemple this(217) va appeler le constructeur public ExempleThis(int b).


3.Utilisation des packages

3.1.Package

3.1.1.Définition
Les packages permettent de regrouper des classes ayant des caractéristiques communes. On peut
considérer un package comme une librairie ou une partie de librairie. Par exemple, les classes
permettant de travailler avec des flux d’entrée/sortie sont dans le package java.io. En réalité, un
package est un répertoire physique sur votre système. Un nom de package est généralement un nom
composé. Chaque mot formant le nom du package sont séparés par des points.
Prenons l’exemple de la classe "Tools" appartenant au package "com.sun.java". Au niveau du
système, on aura un répertoire com contenant un répertoire sun, contenant lui-même un répertoire
java. Le fichier Tools.class sera placé dans le répertoire java.

3.1.2.Utilisation
Au niveau du fichier, la déclaration du package se place en premier. Afin d’éviter que plusieurs
personnes utilisent le même nom de package, Sun recommande d’utiliser l’inverse de son nom de
domaine pour nommer ses packages (les packages venant de Sun s’appelleront com.sun.java).

Exemple :
    //fichier Tools.java
     com.sun.java;

     // La classe Tools va être situé dans le package com.sun.java
       Tools {
          //votre code
     }

On crée un répertoire "src" qui contiendra nos fichiers sources (.java). Dans notre exemple, il s’agirat
simplement du fichier Tools.java. On crée également un répertoire "bin" qui contiendra les bytes codes
(.class). Lors de la compilation, on spécifiera ces deux répertoires à javac.

     javac –d c:\java\bin –sourcepath c:\java\src com.sun.java.Tools.java

On peut remarquer que javac a crée le fichier c:\java\bin\com\sun\java\Tools.class ainsi que les
répertoires parents.
Pour l’exécution, on rajoute à la variable d’environnement CLASSPATH (chemin des classes) le
chemin du répertoire bin. Le classpath permet à l’interpréteur de savoir où sont situés physiquement
les fichiers contenant le byte code.

Exemple sous Unix :
set CLASSPATH=/home/myaccount/lib
     export CLASSPATH

Exemple sous Windows :
    set CLASSPATH=C:\Java\lib

On exécute en utilisant java.exe :
    java –classpath c:\java\lib com.sun.java.Tools



3.2.import
Le mot clé import permet d’importer (d’accéder et d’utiliser) des classes d’un package à partir d’une
classe d’un autre package. Il est possible d’importer toutes les classes d’un package grâce au
caractère étoile "*". L’importation se fait au début du code entre la déclaration du package et de la
classe.
     //fichier Ex.java
      com.sun.exemple;

      com.sun.java.Tools;
     //importe la classe Tools du package com.sun.java

      java.utils.Vector;
     //importe la classe Vector du package java.utils

      com.sun.*;
     //importe toutes les classes du package com.sun

      Ex {
         Tools tools = Tools();
         // ayant importé Tools, on peut l’utiliser
     }




4.Héritage

4.1.Définition
L’héritage est la capacité de construire une classe à partir d’une autre, cette dernière héritera des
variables et méthodes de la classe de base, et pourra y accéder suivant l’accessibilité définie.
Java se démarque du C++ ou d’autres langages objet puisque il ne propose pas d’héritage multiple.
Cependant il existe un moyen de contourner le problème grâce aux interfaces.
Prenons l’exemple d’une voiture « BX Leader » (ici BX Leader est un objet de la classe voiture), voici
ce que pourrait donner la hiérarchie des classes. Ceci n’est qu’un exemple, il n’a y a pas de solution
préétablie, il faut juste que l’ensemble soit cohérant et utile. Il n’y pas d’intérêt à faire une telle
hiérarchie si on utilise un seul type de véhicule (par exemple seulement les voitures).
Les classes parentes sont appelées super-classe, les classes dérivées sont appelées sous-classes ou
classes filles. Dans l’exemple précèdent, "Véhicule" est la super-classe, "Motorisé" la sous-classe,
etc.
En Java tout est objet. En effet, tout classe hérite de la classe Object. La classe Object est donc à la
racine de toute hiérarchie. On dit parfois que c’est la classe racine.

4.2.extends
L’héritage se fait via le mot clé extends, il se place après le nom de la classe, au moment de la
définition de cette dernière, et est suivit du nom de la classe à hériter.
Exemple :
class Personne { //super-classe

}
class Etudiant extends Personne { //sous-classe

     }



4.3.super
Le mot clé super est similaire à this. Contrairement au mot clé this qui permet de faire référence à la
classe dans laquelle il est appelé, le mot clé super fait référence à la super-classe de la classe dans
laquelle il est appelé.
Il permet d’invoquer le constructeur de la super-classe. super doit être déclaré au tout début d’un
constructeur d’une sous-classe. En effet, cela permet au constructeur d’effectuer au moins les mêmes
tâches que le constructeur de la classe mère.
Exemple :
      Personne {

          Personne(String nom) {
                  //constructeur
          }
     }

      Personne {

           note;

          Etudiant(String nom, note) {
                  (nom); // constructeur de la classe mère
                  .note = note;
          }
     }
Dans cet exemple super() fait appel au constructeur de la super-classe Personne.
5.Les interfaces (héritage multiple)

5.1.Définition
Une interface est très similaire d’une classe. Voici les différences par rapport à une classe :

    •    elle ne possède que des méthodes sans corps
    •    elle ne possède pas de constructeur
    •    elle n’est pas instanciable
    •    ses variables doivent être initialisées et sont implicitement publiques (public), statiques
         (static) et finales (final)

En fait, une interface est un peu le squelette de la classe, par conséquent toutes les méthodes
abstraites qu’elle possède devront être redéfinies dans la classe. Une interface peut hériter (utilisation
de extends) d’une ou plusieurs autres interfaces.

Exemple :
    Serializable {

            pi = 3.14;           // pi doit obligatoirement être initialisé

            );
   }



5.2.Implémentation
L’implémentation d’interface permet de palier à l’absence d’héritage multiple. Une classe peut
implémenter plusieurs interfaces. En implémentant une interface, une classe hérite de ses variables
ainsi que de ses méthodes qu’elle devra définir (sauf en cas d’utilisation du mot clé abstract).
Elle se fait grâce au mot clé implements.
Exemple :
       Math, Interface1 {

           rayon) {        // On définit le corps de la méthode
                   pi * rayon * 2; // pi est hérité de Math
           }
     }

        Interface1, Interface2, Interface3 {

     }


5.3.Comparaison avec les classes abstraites
Il est parfois difficile de faire la différence entre une classe abstraite et une interface. L’avantage d’une
classe abstraite est qu’elle peut contenir des méthodes classiques (non abstraites). Une interface ne
contient que des méthodes sans corps qui devront être défini par les classes qui l’implémente.
L’avantage des interfaces est qu’il est possible d’implémenter plusieurs interfaces, or on ne peut
hériter de plusieurs classes.




6.Modificateurs de visibilité

6.1.public
Accessible par toutes les classes de tous les packages. Ce modificateur peut s’appliquer aux classes,
méthodes et variables membres de classe. Certaines classes doivent être public pour permettre
l’exécution de l’application (celles contenant une méthode main()).

6.2.private
Ce modificateur peut s’appliquer aux méthodes et variables membres de classe ainsi qu’aux inner-
classes (classes contenues dans d’autres classes). Les classes de premier niveau (top-level/outer
classes, qui ne sont pas contenues dans d’autres classes) ne peuvent bénéficier de cet attribut. Ces
éléments, lorsqu’ils sont définis avec l’attribut private, ne sont pas accessibles à partir d’autres
classes ni par les sous-classes.

6.3.protected
Comme private, ce modificateur peut s’appliquer aux méthodes, variables membres et inner-classes.
Les éléments définis avec ce modificateur sont accessibles par toutes les classes dérivées et les
classes du même package.
L’attribut protected permet l’accès à la ressource par les classes dérivés (ces dernières peuvent être
situées dans un autre package) et toutes les classes du même package, comme le montre l’exemple
suivant :

     //Fichier (..)\exa\Mere.java
      exa;
     Mere {
          tdb;
   }
   //Fichier (..)\exb\Fille.java
     exb;

      exa;

      Mere {
     main(String[] args) {
                 tdb++; // accès autorisé

                     Fille objf ;
                     objf.tdb++;//accès autorisé

                     Mere objm;
                     objm.tdb++; //accès interdit, erreur de compilation
          }
     }


6.4.Aucun modificateur
Lorsque vous ne spécifiez aucun modificateur particulier, Java utilise le modificateur de visibilité par
défaut. Les variables, méthodes et classes déclarées ainsi sont accessibles par les classes qui font
partie du même package et inaccessible par les autres (équivalent du « friend » en C++).

7.Autres modificateurs

7.1. abstract

7.1.1.Définition
Une classe abstraite ne peut être instanciée. Les classes abstraites servent pour l’héritage comme
super-classe. Contrairement aux classes classiques, elles peuvent contenir des méthodes abstraites.
Les méthodes abstraites sont des squelettes de méthodes. Elles n’ont pas de corps (aucune ligne de
code). Une méthode abstraite étant vide, elle devra donc être surdéfinie (polymorphisme) par les
classes filles.

7.1.2.Utilisation
Les classes ou méthodes abstraites sont déclarées avec le mot clé abstract.
Exemple :
      Test {

            methodeAbstraite();

      methodeClassique() {
         }
     }
On ne peut pas avoir une méthode abstract dans une classe qui n’est pas elle-même déclarée en
abstract.
Exemple :
     IllegalClass { // abstract n'est pas spécifié

              illegalMethod(); // erreur de compilation
         }
Si vous essayez de compiler ce code vous aurez une erreur de type : “class IllegalClass must be
declared abstract”.

7.1.3.Intérêt
Une sous classe qui hérite d’une classe abstraite devra obligatoirement surdéfinir les méthodes
abstraites de sa classe mère (sauf si elle est elle-même déclarée abstraite). L’intérêt étant que chaque
sous classes devront définir ces méthodes.
Exemple :
     // Classe mère des animaux
      Animal {

         seDeplacer();
    // tous les animaux peuvent se déplacer mais pas forcément de la même
   manière

     }

     // Classe Tigre héritant d'Animal
      Animal {
         seDeplacer() {
                 courrir();
         }

             courrir() {
                     // code
             }

    }
   // Classe Aigle héritant d’Animal
      Animal {
         seDeplacer() {
                 voler();
         }

        voler() {
                // code
        }
   // Classe Application
     Application {

              main(String[] args) {
                     Animal animal1 = Tigre();
                     Animal animal2 = Aigle();
                     animal1.seDeplacer();
                     animal2.seDeplacer();
             }
     }

Dans cette exemple, la classe Animal déclare une méthode seDeplacer() qui devra être définie par
toutes les sous classes. Cette méthode est abstraite car tous les animaux se déplace mais pas de la
même manière. C’est donc aux sous classes de décrire la manière de se déplacer. Cette exemple,
montre l’utilisation du polymorphisme : on manipule animal1 et animal2 comme de simple animaux,
sans forcément connaître à quel type exact d’animaux ils appartiennent. On sait simplement que ce
sont des animaux donc ils possèdent une méthode seDeplacer().


7.2.final
Le mot clé final sert à spécifier ce qui ne peut être modifié. Ils peut être associé à une variable, une
méthode ou une classe.
7.2.1.Variable finale
Une variable finale ne peut être modifiée. On lui affecte une valeur lors de sa déclaration. Une fois que
l’objet est crée (appel du constructeur), on ne plus changer cette valeur. Une variable est en quelque
sorte une constance.

Exemple :
     Math {
          pi = 3.14;
   }


7.2.2.Méthode finale
Une méthode finale ne peut être surdéfinie dans une classe fille.

Exemple :
    Exemple {
          uneMethode(){
}
}

7.2.3.Classe finale
Une classe finale ne peut pas être dérivé et ne peut pas être abstraite. L’intérêt des classes finales
résident dans un soucis d’optimisation et de sécurité. Toutes classes en bout de hiérarchie devraient
être déclarées finales.

Exemple :
     Exemple {
   }



7.3.static
Le mot clé static (vu avec la méthode main) peut être associé à une variable ou une méthode. Il
permet d’accéder à cette ressource sans avoir à créer d’objet.

Exemple :
    Math {
          pi = 3.14;
            longueur) {
                   largeur+longueur)*2);
          }
    }

     Exemple {
          main(String[] args) {
                 p = Math.pi;
p = Math.perimetre(3, 4);
}
}

Dans cet exemple, on accède à la variable pi et à la méthode perimetre() sans avoir crée d’objet de
type Math.

Une autre particularité est qu’une variable statique aura la même valeur pour toutes les instances
(objets) de la classe. Il faut donc faire très attention lorsqu’on modifie la valeur d’une variable statique.
C’est pour cette raison que les constantes sont déclarés static et final.

Exemple :
    Exemple2 {
          main(String[] args) {
                 Math m1 = Math();
                  m1.pi = 4;
                 Math m2 = Math();
                  p = m2.pi; // p vaut 4
   }



8.Garbage collector

8.1.Rôle
Contrairement au C++, Java ne possède pas de destructeur, le garbage gollector (ou ramasse miette)
s’occupe de détruire les instances qui ne sont plus référencées. Le garbage collector surveille toutes
les instances, détecte celles qui sont inutiles et les retire de la mémoire. Cependant, juste avant de
détruire les instances il va appeler la méthode finalize(), si elle existe.


8.2.Méthode finalize()

La méthode finalize est donc appelée par le garbage collector juste avant la destruction d’une
instance, cela permet par exemple de libérer les ressources que l’instance utilise ou encore d’exécuter
certaines tâches à la destruction de l’instance. La méthode doit être écrite par le développeur, il faut
donc la définir dans la classe.

Exemple :
     Exemple {
          Exception {
                 // faire attention aux exceptions (voir
                 // cours sur les exceptions)
   }



8.2. Contrôle du Garbage collector
NA




                          Les web-services - Publication de services


1.Introduction

1.1.Introduction aux Web Services

De plus en plus, avec l’essor d’Internet, le développement tend vers les technologies du Web. Il est
difficile de faire la distinction entre les différents logiciels qui sont de plus en plus intégrés au Web.
Les Web Services entrent dans l’optique de différencier bien précisément les différentes couches
d’une application (présentation, métier et données).
Les Web Services sont des applications modulaires basées sur Internet qui exécutent des tâches
précises et qui respectent un format spécifique. Ils permettent à vos applications de faire appel à des
fonctionnalités (décrites par les web services) à distance (soit sur le même réseau, soit sur Internet)
en simplifiant ainsi l’échange de données.

Les Web Services permettent aux applications de dialoguer à travers le réseau, indépendamment de
leur plate-forme d’exécution et de leur langage d'implémentation. Ils s’inscrivent dans la continuité
d'initiatives telles que CORBA (Common Object Request Broker Architecture, de l'OMG) en apportant
toutefois une réponse plus simple, s’appuyant sur des technologies et standards reconnus et
maintenant acceptés de tous.
Les protocoles existants : DCOM, RMI, CORBA, DIIOP et RPC sont progressivement abandonnés au
profit des Web Services.

La définition des Web Services ne serait pas complète si l’on n'évoquait pas ses principaux
standards : SOAP, WSDL et UDDI, des protocoles mis en place par Microsoft, IBM, Sun, …
Les Web Services servent principalement pour le développement d’applications distribuées et sont
accessibles depuis n’importe quel type de clients (WAP, Web, Applicatif, ...).
1.2.Qu'est-ce qu'un Web Service ?

Un Web Service est un composant implémenté dans n'importe quel langage, déployé sur n'importe
quelle plate-forme et enveloppé dans une couche de standards dérivés du XML. Il doit pouvoir être
recherché et invoqué dynamiquement par d'autres services.

Le plus souvent les Web Services sont des fonctionnalités basiques du serveur d’entreprise mis à
disposition.

Cette technologie, initiée par IBM et Microsoft, puis en partie normalisée par le W3C, est maintenant
acceptée par l'ensemble des acteurs de l'industrie informatique sans exception. C'est surtout ce point
qui fait des Web Services une technologie révolutionnaire et qui les rends aussi populaire.

1.3.Le concept des Web Services

Les aspects purement technologiques n’ont eux rien de fondamentalement novateurs. Au contraire,
l’architecture des Web Services s’est imposée (tout comme le langage XML) grâce à sa simplicité, à
sa lisibilité et sa normalisation.

L’XML (eXtended Markup Language) est à la base de tous les protocoles décrits ci-dessous. Le fait
que les Web Services utilisent l’XML leurs procurent l'avantage d’être non propriétaire et ainsi
réellement multi-plateforme. Il est donc recommandé de posséder un minimum de bases (XML, DTD,
les schémas, XSL, ...) afin de pouvoir mettre en place des Web Services réellement optimisés.

Le concept des Web Services s’articule actuellement autour des trois acronymes suivants :

    •   SOAP (Simple Object Access Protocol) est un protocole d'échange inter-applications
        indépendant de toute plate-forme, basé sur le langage XML. Un appel de service SOAP est
        un flux ASCII encadré dans des balises XML et transporté dans le protocole HTTP.
    •   WSDL (Web Services Description Language) donne la description au format XML des Web
        Services en précisant les méthodes pouvant être invoquées, leurs signatures et le point
        d’accès (URL, port, etc..). C’est, en quelque sorte, l’équivalent du langage IDL pour la
        programmation distribuée CORBA.
    •   UDDI (Universal Description, Discovery and Integration) normalise une solution d’annuaire
        distribué de Web Services, permettant à la fois la publication et l'exploration (recherche) de
        Web Services. UDDI se comporte lui-même comme un Web service dont les méthodes sont
        appelées via le protocole SOAP.

Un avantage significatif des Web services, relativement aux autres solutions d’architecture distribuée,
est son support des pare-feux (firewalls) : l’utilisation du protocole HTTP sur le port 80, généralement
ouvert, leur permet de passer sans encombre les barrières de l'entreprise. Cette facilité engendre
d’autres soucis de sécurité, l’utilisation par défaut de ces caractéristiques est trop permissive et
nécessite une prise en compte de la sécurité au niveau des protocoles. Cette gestion est néanmoins
réalisable grâce à un ensemble de librairies en Java afin d’assurer une transmission des données
transactionnelles et sécurisé.

                                        1.4. Pour quoi faire ?

Les Web Services comportent de nombreux avantages : utilisables à distance via n'importe quel type
de plate-forme, ils peuvent servir au développement d’applications distribuées et sont accessibles
depuis n’importe quel type de clients. Les Web Services appartiennent à des applications capables de
collaborer entre elles de manière transparente pour l’utilisateur.

A quelles applications se destinent les technologies des Web Services ? Pourquoi les utiliser ?
Comment les mettre en place ?

Les technologies des Web Services peuvent être appliquées à toutes sortes d’applications auxquelles
elles offrent de considérables avantages en comparaison aux anciennes API propriétaires, aux
implémentations spécifiques à une plate-forme et à quelques autres restrictions classiques que l’on
peut rencontrer (multi-plateforme, multi-langage, disponible sur Internet avec une information
actualisée disponible en temps réel, ...).

L’application des Web Services est multiple, autant dans les domaines du B2C, B2B que pour des
domaines de gestion de stocks, etc...
B2C (Business to Consumer) : Qualifie une application, un site Internet destiné au grand public.
B2B (Business to Business) : Qualifie une application, un site Internet destiné au commerce de
professionnel à professionnel.
Les Web Services peuvent être utiles dans la plupart des scénarios applicatifs lorsque la
communication peut être établie sur un modèle bidirectionnel (requête/réponse). C’est néanmoins loin
d'être aussi limitatif, beaucoup d’autres modèles peuvent avoir recours aux Web Services, sans même
que vous vous en rendiez compte. Les entreprises qui mettent à disposition leurs Web Services
permettent aux développeurs intéressés par ses fonctionnalités de les réutiliser sans avoir à les
recoder.

Le principe des Web Services permet d’avoir un partage des fonctionnalités et facilite grandement le
développement.


2.Le protocole SOAP

SOAP (Simple Object Access Protocol) est un protocole à la fois simple et léger destiné à l'échange
d’informations. SOAP diffère de RMI, CORBA et COM car il concentre les informations et utilise le
principe d’auto description des données. SOAP fait partie de la couche de communication des Web
Services. La force de ce protocole réside dans son universalité et sa flexibilité. Il définit la structure
des messages XML utilisés par les applications pour dialoguer entre elles. SOAP fait figure de pièce
centrale parmi tous les protocoles évoqués. SOAP est très complexe si l’on voulait en détailler toutes
les spécificités. Nous allons donc seulement évoquer les plus utilisées.

2.1.Généralités

La communication avec les Web Services s’effectue via le protocole SOAP.
SOAP peut être utilisé :

    •   pour l’appel de méthodes (SOAP RPC)
    •   pour l’échange de messages (SOAP Messaging)

SOAP définit la structure principale du message, dite « enveloppe » qui contient deux parties :

    •   l’entête (Header) : facultatif
    •   le corps (Body) : obligatoire




SOAP définit aussi l’encodage pour les différents types de données qui est basé sur la technologie
schéma XML du W3C. Les données peuvent être de type simple (chaîne, entier, flottant, ...) ou de
type composé.

2.2.Les messages SOAP

SOAP régit le format des données lors de leur envoi/réception. C’est ainsi que l’on désigne le trafic en
plusieurs types différents :
    •   Les messages
    •   Les enveloppes

Tous les messages SOAP contenus dans un document XML sont appelés des enveloppes qui sont
des conteneurs d’un message SOAP. Une enveloppe est le premier des éléments dans un document
XML qui représente un message SOAP.
C’est la neutralité de ce principe qui permet la mobilité de la technologie des Web Services.
L’enveloppe étant formatée selon les spécificités de SOAP, l’expéditeur et le consommateur peuvent
totalement être écrit sur des plateformes différentes sans que cela pose problème.

2.3.Les requêtes HTTP

Les requêtes HTTP consistent à avoir une méthodologie de requête, une requête en URL qui spécifie
les entêtes et corps du flux envoyé. Les messages SOAP sont transmis via HTTP permettent une
complète interopérabilité entre les clients et les Web Services. Lors de l’envoi d’une requête HTTP, le
champ d’entête doit avoir la valeur « SOAPAction : » pour que le message soit assimilé à un message
SOAP.

    •   GET – retourne la ressource identifiée par la requête.
    •   HEAD – retourne l’entête identifiée par la requête.
    •   POST – envoie des valeurs sans limite de taille vers le serveur.
    •   PUT – stocke une ressource pour la requête.
    •   DELETE – supprime la ressource identifiée par la requête.
    •   OPTIONS – retourne les méthodes HTTP supportées par le serveur.
    •   TRACE – retourne le contenu des entêtes utilisées avec l’option TRACE.

HTTP 1.0 inclut seulement les méthodes GET, HEAD et POST. Bien que les serveurs J2EE
requièrent seulement la version HTTP 1.0, beaucoup de serveurs supportent la version HTTP 1.1.

POST /LocalWeather HTTP/1.0
Host: www.mindstrm.com
Content-Type: text/xml; charset="utf-8"
Content-Length: 328
SOAPAction: "WeatherStation"
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<m:GetCurrentTemperature xmlns:m="WeatherStation">
<m:scale>Celsius</m:scale>
</m:GetCurrentTemperature>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

2.4.Les réponses HTTP

Les réponses HTTP utilisent le même procédé.
Une réponse HTTP contient :

    •   le code du résultat
    •   l’en-tête
    •   le corps

Voici quelques erreurs identifiées par un numéro précis sur le protocole HTTP :

    •   404 – indique que la ressource demandée est indisponible.
    •   401 – indique que la requête requière une authentification HTTP.
    •   500 – indique une erreur interne au serveur HTTP.
    •   503 – indique que le serveur est surchargé.

HTTP/1.0 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: 359
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<m:GetCurrentTemperatureResponse xmlns:m="WeatherStation">
<m:temperature>26.6</m:temperature>
</m:GetCurrentTemperatureResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Pour la fiabilité des données transmises, IBM a mis en place un standard : HTTPR (HTTP Reliable,
HTTP Fiable). Ce standard doit permettre d’assurer l’envoi ou la réception fiable de messages SOAP.

2.5.Les namespaces

Il est important de comprendre les namespaces XML pour comprendre SOAP.
<m:GetCurrentTemperature xmlns:m="WeatherStation">
<m:scale>Celsius</m:scale>
</m:GetCurrentTemperature>

Analysons le flux XML ci-dessus, XML permet de créer des noms de variables de manière similaire à
un langage de programmation. Ici l’identifiant du namespace se nomme « m » et cette variable
contient « WeatherStation » comme namespace. Ce principe de variable est très utilisé dans les
fichiers de description de données. Nous en avons un exemple, « m:GetCurrentTemperature » permet
directement d’identifier le namespace grâce à l’identifiant « m ».
XML supporte cependant les namespaces par défaut, vous pouvez donc rencontrer des flux tels que
celui-ci.
<GetCurrentTemperature xmlns="WeatherStation">
<scale>Celsius</scale>
</GetCurrentTemperature>

3.WSDL : Données et sécurité

C’est un format de description des méthodes et des paramètres des composants invocables par le
biais des messages au format SOAP. WSDL (Web Service Description Language) est une grammaire
dérivée de l’XML. A l’arrivée des Web Services il y a quelques années, la normalisation n’était pas
encore apparue ce qui a eu pour effet de voir plusieurs implémentations propriétaires pas toujours
compatibles. Maintenant les principes de fonctionnement des Web Services sont beaucoup plus
normalisés ce qui les rend totalement inter opérable. L’échange de données s’appuie sur le protocole
SOAP pour l’échange des messages et sur le langage WSDL pour la définition du contrat de
l’interface.

Lorsque vous voulez avoir des informations sur un Web Service, pour pouvoir l’utiliser par exemple,
vous devez faire une référence au fichier WSDL.
Ex : http://www.xmethods.net/sd/2001/TemperatureService.wsdl

3.1.Les données accessibles

WSDL comprend la définition de plusieurs données :

    •   type : un conteneur de la définition du type de données utilisées.
    •   message : description du type de données transmises lors des messages
    •   operation : description d’une des actions supportées par le Web Service.
    •   portType : description de l’ensemble des actions supportées.
    •   binding : protocole et format de données spécifiques pour un type de port précis.
    •   port : un endpoint défini comme une combinaison entre une liaison et une adresse réseau.
    •   service : une collection qui relie les endpoints

WSDL est un système de communication « point à point ». Un consommateur du Web Service
interroge le serveur, sur lequel celui-ci est disponible, et celui-ci retourne la description du Web
Service demandé.

3.2.La sécurité

La gestion de la sécurité et des transactions est possible grâce aux deux principales normes : XACML
(eXtensible Access Control Markup Language) et SAML (Security Assertion Markup Language).
Il existe plusieurs points qui nécessitent une sécurisation :

    •   XML Encryption : mécanisme d’encryptage et de décryptage des documents XML.
    •   XML Signature : permet d’implémenter des signatures digitales dans des documents XML.

XACML est capable de faire communiquer les politiques de contrôle d’accès dans l’environnement
des Web Services.
SAML exprime des informations d’authentification à l’aide d’assertion sur des sujets (humains ou
virtuels) qui ont une identité pour le système de sécurité considéré.
En ce qui concerne l’aspect transactionnel, beaucoup d’implémentations sont actuellement testés.
C’est BTP (Business Transaction Protocol) qui semble être le plus utilisé.

4.L'annuaire UDDI

UDDI (Universal Description, Discovery and Intégration) est une spécification définissant la manière
de publier et de découvrir les Web Services sur un réseau. Ainsi, lorsque l’on désire mettre à
disposition un nouveau service, on crée un fichier appelé business Registry qui décrit le service en
utilisant un langage dérivé de l’XML selon les spécifications de UDDI.

4.1.Le principe d’annuaire

UDDI permet de classer et de rechercher des Web Services. Si l’accès à l’information est trop élevé,
le recours aux Web Services ne devient plus intéressant. C’est pourquoi le principe d’annuaire
universel a été mis en place.
Les annuaires UDDI ne répondent pas aux mêmes besoins que les annuaires de type LDAP
(Lightweight Directory Access Protocol) dont la vocation sont de référencer aussi bien des personnes
que des ressources matérielles ou logicielles et de gérer les droits d’accès à ces ressources.

Les annuaires UDDI sont des annuaires privés sur le Web dont l’usage est orienté dans le cadre
d’échanges B2B. On y trouve aussi bien des informations techniques (documents WSDL) que des
informations à caractère général sur une entreprise (page Web d’accueil ou de produits, par exemple).

L’API UDDI est divisée en une interface de programmation pour l’enregistrement de Web Services
dans un annuaire UDDI et une interface de programmation pour la recherche d’informations.
Cette API est composée de 2 grandes bibliothèques :

    •   API de requête
    •   API de publication

4.2.La recherche d’un Web Service

La recherche se fait grâce à un moteur de recherche intégré au site de l’opérateur UDDI choisi.
Ce moteur de recherche vous permettra d’affiner votre recherche selon plusieurs critères :

    •   Nom de l’entreprise
    •   La localisation de l’entreprise
    •   Identifiant de l’entreprise
    •   Le nom du Web Service
    •   …

L’API de requête regroupe les appels aux sites opérateurs qui n’ont pas besoin d’authentification
particulière.
Les annuaires UDDI ont pour but de localiser virtuellement des Web Services hébergés sur les
serveurs du monde entier. Lors de votre recherche vous pouvez ainsi vous renseigner sur tous les
services mis à disposition d’une entreprise, avoir des informations sur l’entreprise, …
Les opérateurs UDDI vous certifient la sécurité et l’intégrité des Web Services contenus dans leurs
annuaires.
Les appels aux sites des opérateurs donnent accès aux différents éléments de l’enregistrement d’un
Web Service dans un annuaire UDDI :

    •   find_binding : récupère la liaison du service considéré.
    •   find_business : récupère l’identité de l’entreprise productrice du Web Service.
    •   find_relatedbusiness : récupère la liste des entreprises étant reliées (filiale, département,
        partenaire, …) à l’entreprise productrice du Web Service.
    •   find_service : récupère la définition du service.
    •   find_tmodel : récupère le modèle de données associé.
    •   get_bindingDetail : récupère, par une liaison précédemment établie par find_binding les
        champs individuels.
    •   get_businessDetail, get_businessDetailExt : récupère une entité précédemment établie par
        find_business les attributs individuels.
    •   get_serviceDetail : récupère un service précédemment établi par find_service les attributs
        individuels du service (prototypes des méthodes).
    •   get_tmodelDetail : récupère un modèle établie par find_tmodel les champs individuels

4.3.La publication d’un Web Service

Comme dans WSDL, la liaison UDDI regroupe, pour un protocole de communication donné, les
données techniques nécessaires à l’exploitation du Web Service (adresse IP, noms de domaines, les
informations sur les modalités d’usage du service, …)
La publication par une entreprise d’un Web Service requière que celle-ci s’authentifie auprès du site
de l’opérateur UDDI. L’entreprise doit s’enregistrer chez l’opérateur si cela n’est pas déjà le cas.
Une fois le site de l’opérateur choisi, les modifications ultérieures ou la mise à jour de cet
enregistrement doivent être faites auprès du même opérateur.
Lors de l’enregistrement, vous pouvez enregistrer simultanément un ensemble d’entreprises affiliées,
des relations entres entreprises indépendantes décrivant des accords croisés.
L’API se décompose en 3 groupes :

    •   La manipulation (save et delete)
    •   L’authentification des commandes par jeton (get_authToken et discard_authToken)
    •   L’ajout de relations inter entreprises (joint_ventures)

4.4.L’avenir de l’UDDI

Au niveau des services, on pouvait penser que la proposition d'annuaire UDDI apporterait une solution
définitive. On constate qu'il n'en est rien, il ne convient pas à une problématique d'échanges entre
entreprises se connaissant. Il se voit maintenant concurrencer par WS-Inspection (proposé par IBM et
Microsoft, pourtant à l'origine de UDDI). Moins ambitieux puisque consistant en une simple exposition,
par agrégation, des services d'une entreprise, il est toutefois plus adapté à cette seconde
problématique.




5.L’architecture des Web Services

L’architecture des Web Services repose sur un mécanisme de transport d’une demande de service
entre un client et un serveur, tous deux connectés au réseau (réseau d’entreprise ou Internet).
La normalisation actuelle autour des Web Services est cependant un vaste chantier qui va bien au-
delà de la simple invocation d'une méthode d’un objet distant. Différents travaux ont ainsi démarrés
pour définir une véritable infrastructure distribuée, capable de satisfaire l’ensemble des besoins d’une
application distribuée, aussi bien en terme de normalisation des échanges qu’en terme de services.




On peut schématiser cette organisation des comités de normalisation selon le découpage suivant :
    •   Normalisation des services transverses sur trois axes horizontaux :
           o Couche de transport : Définition de la structure des messages utilisés par les
               applications pour se découvrir et dialoguer entre elles
           o Couche de sémantique : Normalisation des données participant aux échanges selon
               des critères métiers
           o Couche de gestion des processus : Standardisation de la gestion des processus
               métiers qui s'étendent sur plusieurs applications disponibles sur l'Internet
    •   Normalisation des services transverses sur trois axes verticaux :
           o Service d'annuaire : Standardisation des moyens d’accès à un service à partir d'une
               requête portant sur le contenu d'un service ou sur un fournisseur
           o Service de sécurité : Normalisation des moyens permettant de couvrir les
               problématiques d’authentification et de gestion des droits d'accès.
    •   Service de transaction :
           o Normalisation des moyens permettant de garantir l'intégrité des transactions longues
               impliquant plusieurs Web Services (orchestration de Web Services).

Tous les acteurs majeurs (J2EE ou .NET) proposent une solution orientée Web Services ainsi que
l’intégration des standards associés. Parallèlement, les premières utilisations de cette technologie
voient le jour, notamment dans le domaine des solutions de CRM (Sevina ou Onyx), de gestion des
ressources humaines (CCMX) ou de mutualisation de contenu (Systeme U, Digiplug).

5.1.Les étapes de création d’un Web Service

La mise en place d’un Web Service étant très importante dans la logique d’entreprise, il est nécessaire
d’identifier correctement les traitements à effectuer.

La phase d’analyse avant de développer le Web Service doit particulièrement être importante :

    •   Identifier les traitements
    •   Exposer les services
    •   Administrer
    •   Orchestrer




Les Web Services peuvent donc se positionner à plusieurs niveaux au sein de l’entreprise. Vous
pouvez donc tout à fait avoir des Web Services au niveau métier, technique ou au niveau des
applications d’entreprises. On parle ainsi de multiples canaux de diffusion des Web Services.

JAX-RPC (Java API for XML Remote Procedure Call) est utilisé dans le développement des Web
Services, cette API supporte les classes suivantes du SDK de J2SE comme type de données :
java.lang.Boolean
java.lang.Byte
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Short
java.lang.String

java.math.BigDecimal
java.math.BigInteger

java.net.URI

java.util.Calendar
java.util.Date

Les types primitifs qu’il est possible d’utiliser sont les suivants :

boolean
byte
double
float
int
long
short

5.2. L’intégration d’un Web Service

L’intégration des Services Web à la plate-forme J2EE s’effectue selon plusieurs procédés. Vous
pouvez utiliser des outils de génération et d’intégration, ceux-ci se baseront sur des API et librairies
que vous pourriez utiliser à la main (sans faire de génération).
Les éléments utiles dans la mise en place de Web Services sont les suivants :

    •   API Java utiles aux Services Web : JAXP, JAX-RPC, JAXM et JAXR
    •   Les librairies Apache : AXIS, XML-RPC et WSIF
    •   Création d'un service avec AXIS




    •   Outils d'orchestration : BPEL, BPWS


Ces notions ne sont pas obligatoirement toutes à maîtriser, ce cours n’a pour but que de vous
présenter les différents aspects des Web Services. Si vous voulez plus de renseignements, vous avez
des points de départs pour effectuer vos recherches.




5.3.La recherche d’un Web Service

Pour rechercher les Web Services déjà créés et mis à votre disposition, il vous suffit de rechercher
dans des annuaires UDDI (Universal Description, Discovery and Intégration).
L'UDDI définit différents annuaires qui permettent de présenter les services d’une entreprise à
d'autres. Une compagnie peut donc publier un de ces Web Services, qu’elle a développé, dans un
annuaire afin de les partager à une personne qui peut en avoir besoin. C’est l'adoption de cet
annuaire par les sociétés qui permettra d’accélérer les échanges commerciaux par exemple.
Grâce à ces annuaires vous pourrez être assuré que les Web Services publiés sont fonctionnels et
vous pourrez en trouver certains qui sont des versions d’évaluations, d’autres gratuits et enfin des
versions professionnelles payantes.
Vous pouvez ainsi, vous-même, rechercher des Web Services existant afin d’accélérer votre
développement. Il vous suffit d'utiliser un moteur de recherche via annuaire UDDI. Il existe de très
nombreux sites qui sont dédiés à cette fonctionnalité, par exemple :

    •   https://uddi.ibm.com/beta/find
    •   http://www.strikeiron.com
    •   http://uddi.microsoft.com/
    •   …

5.4.L’appel d’un Web Service

Lorsque vous aurez trouvé, grâce aux annuaires UDDI, le Web Service que vous souhaitez utiliser,
vous devrez récupérer l’adresse du WSDL associée.
Ex : http://www.xmethods.net/sd/2001/TemperatureService.wsdl

Le client qui appelle un Web Service peut être soit un navigateur Web, soit une application cliente
(Swing, SWT, …). L’appel peut être fait expressément par l’utilisateur ou peut être automatisé (un
Web Service qui en appel un autre, …). Les appels peuvent être complètement indépendants de
l’utilisateur (procédure de tâches automatisées, Threads). Le client peut être une plateforme différente
de la plateforme hébergeant le Web Service (Mainframe, PC Serveur, PC Desktop, PC portable, PDA,
Téléphone portable, etc…). De même, si le Web Service a été écrit en C#, le client peut très bien être
fait en Java, PHP, etc …

En fait, le client n’a même pas à savoir le langage dans lequel a été programmé le Web Service pour
pouvoir l’utiliser.

Sachant que la consommation de Web Services passe nécessairement par un certain nombre de
dialecte XML, une généralisation est faite et l’on parle d’« XML Web Services ». Il est donc impératif
de maîtriser la syntaxe XML afin de comprendre comment les Web Services communiquent entre eux
et comment les implémentations sont faites.

6.Utilisation d’AXIS

Apache Axis est une implémentation de SOAP ("Simple Object Access Protocol") qui fourni un certain
nombre d’outils pour créer des Web Services et les déployer.

Pour plus d’informations vous pourrez visiter le site suivant : http://ws.apache.org/axis/
Vous devrez notamment télécharger AXIS à l’URL précisée ci-dessus afin de l’installer dans votre
serveur Tomcat.

6.1.Installation

Dans notre cas nous utiliserons AXIS conjointement à Tomcat.

Pour ce faire il suffit de copier le contenu du repertoire webapps de l’archive d’AXIS, que vous venez
de télécharger, dans le répertoire webapps de votre Tomcat.

Vous devriez ainsi vous retrouver avec une arborescence de votre Tomcat semblable à celle illustrée
ci-contre :




Lancez votre Tomcat à l’aide de la commande startup.bat située dans le repertoire bin ou
directement via Eclipse si vous avez le plugin WTP (Web Tools Platform).

Rendez vous ensuite à l’URL suivante : http://localhost:8080/axis

Vous devrez obtenir la page suivante :
Vous pouvez cliquer sur « Validation » afin de vous assurer qu’Axis est bien installé :

6.2.Descriptif des fonctionnalités

AXIS permet d’offrir un certain nombre de fonctionnalités liées au Web Services. Vous pourrez
déployer et retirer des Web Services sur votre serveur Tomcat très facilement.

De même vous pourrez consulter le WSDL directement via votre navigateur Web. Aucun fichier ayant
l’extension « .wsdl » ne sera donc généré.

Vous pourrez effectuer des appels de méthodes, directement via votre navigateur Web, en spécifiant
simplement le nom du Web Service que vous souhaitez interroger ainsi que le nom de sa méthode
que vous voulez appeler.

Vous pourrez également créer des clients en mode console, Swing ou via des pages JSP très
simplement grâce aux classes spécifiques comprissent dans AXIS qui vous faciliteront le travail. Un
moniteur de trafic est même disponible afin de mesurer les appels de chacun des Web Services mise
à disposition sur votre serveur.

6.3.JWS (Java Web Service)

Java Web Services est un nouveau standard dans l’implémentation des Web Services.
WebLogic Workshop est à l’origine de ce standard. De plus ce nouveau standard est supporté par la
JCP (Java Community Process).

Le but principal de JWS est de proposer une implémentation facile à apprendre et facile à utiliser.
AXIS se base sur ce standard pour offrir ces fonctionnalités aidant le développeur dans sa mise en
place et son utilisation des Web Services.

6.3.1.Le déploiement en utilisant JWS
Pour déployer un Web Service en utilisant les fonctionnalités de JWS, il suffit de renommer le fichier
Java (« .java ») correspondant à la classe qui régit le comportement du Web Service en lui attribuant
l’extension « .jws ».

Par exemple le fichier « MyHelloWorldWS.java » doit être renommé en « MyHelloWorldWS.jws ».

Une fois votre fichier JWS créé, vous devez le copier dans le repertoire « webapps\axis » de votre
serveur Tomcat. Pour vérifier que votre Web Service est correctement déployé, il vous suffit d’y
accéder directement avec un navigateur à l’URL suivante :
http://localhost:8080/axis/MyHelloWorldWS.jws

Vous aurez alors accès à la page suivante :
Pour visualiser le WSDL correspondant au Web Service situé à l’URL décrite ci-dessus, vous pouvez
cliquer sur le lien ou alors ajouter « ?wsdl » à l’URL précédente.

6.3.2.Le retrait en utilisant JWS
Le retait d’un Web Service utilisant JWS est très simple, il suffit pour cela de supprimer le fichier ayant
l’extension « .jws ». Par exemple « MyHelloWorldWS.jws ».

Si vous tentez encore d’accéder à l’URL précédente, vous vous retrouverez avec la page suivante :




Vous pouvez cliquer sur « List » de la page d’accueil d’Axis afin de visualiser la liste des Web
Services déployés :




6.4. L’appel de Web Services

L’appel de Web Services peut être fait directement via un simple navigateur Internet, via une
application web ou une application console.

Dans le cas de l’utilisation d’un navigateur, il faut donner le nom de la méthode que vous souhaitez
invoquer au paramêtre « method ». Utilisez donc l’URL suivante pour appeler la méthode
« sayHello() » du Web Service précédent :
http://localhost:8080/axis/MyHelloWorldWS.jws method=sayHello
Vous aurez ainsi accès à la page suivante qui comprend la réponse HTTP correspondante au
message SOAP de retour :


                        6.5.Modification des variables d’environnement

 Lors des prochaines étapes, nous allons utiliser une variable d’environnement nommée axis_libs,
voici l’étape à suivre pour créer cette variable d’environnement système sous Windows. Vous pouvez
    bien entendu nommée cette variable comme vous le souhaitez, il vous suffira dans les étapes
           suivantes de remplacer le nom de la variable par le nouveau que vous aurez créé.




axis_libs correspond à une variable système comprenant le chemin de tous les JARs d’Axis.

Pour se faire créez cette variable d’environnement système avec la chaîne de caractères suivante :

%CATALINA_HOME%\webapps\axis\WEB-INF\lib\activation.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\axis.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\axis-ant.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\commons-discovery-0.2.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\commons-httpclient-3.0-rc2.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\commons-logging-1.0.4.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\jaxrpc.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\log4j-1.2.8.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\mailapi_1_3_1.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\saaj.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\servlet.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\wsdl4j-1.5.1.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\xercesImpl.jar;
%CATALINA_HOME%\webapps\axis\WEB-INF\lib\xmlParserAPIs.jar

Ces chemins utilisent la variable système CATALINA_HOME, cette variable doit contenir le chemin
vers votre serveur Tomcat. Voici un exemple du contenu que peut avoir cette variable :

d:\java\tomcat5.0.28

Pour les utilisateurs sous linux, utilisez la commande export avec la syntaxe suivante :

export axis_libs=${CATALINA_HOME}\webapps\axis\WEB-INF\lib\activation.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\axis-ant.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\axis.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\commons-discovery-0.2.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\commons-httpclient-3.0-rc2.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\commons-logging-1.0.4.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\jaxrpc.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\log4j-1.2.8.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\mailapi_1_3_1.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\saaj.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\servlet.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\wsdl4j-1.5.1.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\xercesImpl.jar:
${CATALINA_HOME}\webapps\axis\WEB-INF\lib\xmlParserAPIs.jar

Faite attention à ce qu’il n’y ai pas un seul espace dans la valeur de votre variable d’environnement


6.6.Les descripteurs de déploiement
Afin d’utiliser des fonctionnalités avancées en terme de déploiement, vous pouvez utiliser les
descripteurs de déploiement. Ce principe implémenté par Axis permet de définir les méthodes que
vous souhaitez publier dans votre Web Service, le portType, l’encodage, … Ces éléments ne sont pas
paramétrable lorsque vous déployez un Web Service en utilisant le principe de JWS évoqué
précédemment.

Vous allez devoir utiliser des fichiers WSDD (Web Service Deployment Descriptors). Créez deux
fichiers spécifiques à chacun de vos Web Services, un pour le déployer et l’autre pour le retirer.
Par convention, ces fichiers sont souvent appelés deploy.wsdd et undeploy.wsdd.

6.6.1.Déploiement
Le descripteur de déploiement est en réalité un fichier Xml étant utilisé pour déployer votre Web
Service. Le WSDD doit contenir les namespaces étant utilisés afin d’assurer que celui-ci soit bien
valide et correctement formé.

Ensuite vous devez définir la balise « service » en spécifiant en attribut :

    •   le nom Web Service afin que celui-ci soit identifié par votre serveur d’application
    •   le mode : RPC (Remote Procedure Call)

Vous devez au minium définir deux balises « parameter ».

    •   Une de ces balises doit définir le nom de la classe associé au Web Service que vous
        souhaitez déployer. En effet, la particularité du descripteur de déploiement est de pouvoir
        spécifier un nom de service différent du nom de la classe décrivant son comportement.
    •   La deuxième balise « parameter » permet de définir les méthodes accessibles à partir du
        Web Service.

Voici un exemple du fichier deploy.wsdd à créer pour le Web Service d’exemple (MyHelloWorldWS) :

<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="MyHelloWorldWebService" provider="java:RPC">
<parameter name="className" value="MyHelloWorldWS"/>
<parameter name="allowedMethods" value="*"/>
</service>
</deployment>

Une fois ce fichier créé, vous allez devoir l’utiliser afin de déployer votre Web Service. Pour cela vous
allez devoir utiliser un utilitaire fourni par Axis appelé AdminClient. Cet utilitaire sera également utilisé
lors de la procédure de retrait du Web Service.

La syntaxe à employer avec cet utilitaire est la suivante :

java –cp %axis_libs% org.apache.axis.client.AdminClient deploy.wsdd

axis_libs correspond à la variable d’environnement créé précédement qui contient le chemin vers
chacun des jars disponibles dans le répertoire lib d’Axis.

Après l’exécution de la ligne ci-dessus, vous obtiendrez la réponse suivante :




Vous pouvez à présent vous rendre sur la page d’accueil d’Axis. Une fois sur la page d’accueil d’Axis,
cliquez sur le lien « List » et vous aurez accès à la page suivante où vous verrez votre Web Service
correctement déployé :
Cette fonctionnalité de listing des Web Services déployés n’était pas accessible en utilisant le
déploiement des Web Services avec JWS.
Vous pouvez également consulter le WSDL correspondant au Web Service.

Vous pouvez vous rendre à l’URL suivante afin de verifier la présence du Web Service :
http://localhost:8080/axis/services/MyHelloWorldWebService


Vous obtiendrez alors la page suivante :




Si vous souhaitez appeler une des méthodes du Web Service, procédez comme dans le cas d’un Web
Service déployé avec le principe de JWS, rajoutez le paramêtre « method » que vous affectez avec le
nom de la méthode que vous souhaitez appeler.

Voici donc l’URL à saisir afin d’appeler la méthode sayHello() du Web Service étant nommé
MyHelloWorldWebService :
http://localhost:8080/axis/services/MyHelloWorldWebService method=sayHello

Vous aurez ainsi accès à la page suivante qui comprend la réponse HTTP correspondante au
message SOAP de retour :




6.6.2.Retrait
Le retrait de Web Service ayant été déployé avec un descripteur de déploiement est très simpliste.
Vous devez créer un fichier wsdd. Nous l’appelerons undeploy.wsdd. C’est un fichier XML qui
comporte une balise service avec une propriété « name » que vous devez renseigner grâce au nom
du Web Service que vous souhaitez retirer.

Voici un exemple du fichier undeploy.wsdd a utiliser pour le Web Service que l’on vient de déployer :

<undeployment
xmlns="http://xml.apache.org/axis/wsdd/">
<service name="MyHelloWorldWebService"/>
</undeployment>
Une fois ce fichier créé, vous allez devoir utiliser l’utilitaire AdminClient comme vous l’avez fait pour
le déploiement. La syntaxe à employer pour le retrait est exactement là même, vous n’avez que le
nom du fichier wsdd à changer :

java –cp %axis_libs% org.apache.axis.client.AdminClient undeploy.wsdd

Après l’exécution de la ligne ci-dessus, vous obtiendrez la réponse suivante :




Si vous revenez sur la page de listing des Web Services déployés, vous pourrez voir que le
MyHelloWorldWebService a correctement été retiré :




Si vous essayez d’accéder au Web Service directement via l’URL suivante vous obtiendrez également
une erreur :
http://localhost:8080/axis/services/MyHelloWorldWebService




Vous savez maintenant comment utiliser les fichiers wsdd afin de déployer mais également de retirer
un Web Service.

6.7.L’utilitaire WSDL2Java

6.7.1.Présentation de l’utilitaire
WSDL2Java est un autre utilitaire d’Axis, il permet de générer grâce à un fichier WSDL un
ensemble de classes et d’interfaces Java utilisable au niveau du client mais également au
niveau du serveur.
La différenciation des classes et interfaces générées se fait lors de l’appel de l’utilitaire.
Jusqu'à présent nous avons vu comment créer un Web Service, le déployer et le retirer du
serveur, mais dans la plupart dés cas vous allez réutiliser des Web Services déjà existants.
Une fois que vous avez défini le Web Service que vous souhaitez utiliser, sauvegarder le wsdl
lui correspondant dans un fichier local portant l’extension « .wsdl ».
Vous devez au préalable avoir effectué l’étape 6.5, afin d’avoir le paramétrage correct de vos
variables d’environnements systèmes afin de pouvoir utiliser correctement l’utilitaire
WSDL2Java.

6.7.2.Génération coté client
Pour utiliser un Web Service et l’appeler directement dans une classe Java, il faut utiliser des
classes et des interfaces Java. Ces classes et interfaces peuvent être créé « à la main » mais
c’est une opération fastidieuse et qui se résume aux mêmes automatismes à chaque fois que
vous souhaiter dialoguer avec un Web Service.
Coté client, il faut utiliser un stub, des Holders (lorsque les paramètres d’appel sont de type
out ou inout) ainsi que des interfaces représentant les objets à manipuler.

Voici un exemple de la syntaxe à employer pour utiliser l’utilitaire WSDL2Java :
java –cp %axis_libs% org.apache.axis.wsdl.WSDL2Java NomDuFichierWSDL.wsdl

L'utilitaire WSDL2Java génère un certain nombre de classes et interfaces Java:

    1. Une classe pour chaque type de données complexe éventuellement défini par le
       service (dans le fichier .wsdl).
    2. Une interface correspondant au service avec les différentes méthodes exposées par ce
       service. Au sens de la spécification WSDL, il s'agit d'un type de port (<<portType>>)
       qui implémente plusieurs <<opérations>> (les méthodes de notre service).
    3. Un proxy client (<<stub>>) pour chaque <<binding>> défini par le service. Un
       <<binding>> est l'implémentation d'un service selon un modèle d'échange donné
       (orienté RPC ou orienté message) utilisant un protocole particulier (SOAP ou autre) à
       travers un transport déterminé (HTTP par exemple).
    4. Une classe de localisation de service (<<service locator>>) comparable à une classe de
       fabrique, qui va permettre d'instancier la classe d'implémentation correspondant au
       <<binding>> désiré et de récupérer l'interface <<portType>> sur le <<stub>>
       correspondant.

6.7.3.Génération coté serveur
La génération coté serveur s’utilise simplement en utilisant la commande précédente en y
ajouter des options. Ce principe sera mise en œuvre uniquement si vous désirez déployer sur
votre serveur. L’avantage de l’utilisation de ce mode est la génération automatique du
descripteur de déploiement.

Voici la syntaxe à employer pour utiliser l’utilitaire WSDL2Java avec les options
appropriées :
java –cp %axis_libs% org.apache.axis.wsdl.WSDL2Java
–-server-side -–skeletonDeploy true NomDuFichierWSDL.wsdl

L'utilitaire WSDL2Java génèrera :

    •   deploy.wsdd
    •   undeploy.wsdd
    •   localhost\axis\<NomDuWS>_jws\<NomDuWS>.java
    •   localhost\axis\<NomDuWS>_jws\<NomDuWS>Service.java
    •   localhost\axis\<NomDuWS>_jws\<NomDuWS>ServiceLocator.java
    •   localhost\axis\<NomDuWS>_jws\<NomDuWS>SoapBindingImpl.java
    •   localhost\axis\<NomDuWS>_jws\<NomDuWS>SoapBindingSkeleton.java
    •   localhost\axis\<NomDuWS>_jws\<NomDuWS>SoapBindingStub.java

7.Cas pratique
7.1.Cas n°1 - Axis

Dans le cas suivant nous allons développer et utiliser un Web Service avec Axis.
Créez un nouveau projet Java dans Eclipse que vous nommerez WSExempleAxisCours1.
Créez ensuite une classe MyHelloWorldWS et écrivez le code suivant :
public class MyHelloWorldWS {
public String sayHello(){
return "Hello world !!!";
}
}
Déployé ce Web Service selon le principe évoqué au chapitre 6.3.1 - Le déploiement en
utilisant JWS.


7.2.Cas n°2 - Axis
Créez dans Eclipse un nouveau projet de type Java que vous nommerez WSExempleAxisCours2.
Nous allons à présent utiliser WSDL2Java sur un Web Service donné, voici le lien du WSDL à
récupérer.
Pour cela nous allons sauvegarder le contenu du wsdl défini à l’url suivante dans un fichier
« Version.wsdl » : http://localhost:8080/axis/services/Version wsdl
Sauvegardez ce fichier au sein de votre projet.

Exécutez ensuite la commande suivante :
java -cp %axis_libs% org.apache.axis.wsdl.WSDL2Java Version.wsdl


Créez un package client qui contiendra votre client qui appellera le Web Service en utilisant les
classes générées.

Vous obtiendrez alors l’arborescence suivante :




Créez une classe dans votre package client et utilisez le code suivant :

package client;

import java.rmi.RemoteException;

import javax.xml.rpc.ServiceException;

import localhost.axis.services.Version.Version;
import localhost.axis.services.Version.VersionService;
import localhost.axis.services.Version.VersionServiceLocator;

public class VersionClient {
public static void main(String[] args) {
VersionService service = new VersionServiceLocator();
String reponse=null;
try {
//Récupère le Web Service
Version port = service.getVersion();
//Appel la méthode "getVersion()" du Web Service
reponse = port.getVersion();
} catch (RemoteException e) {
e.printStackTrace();
} catch (ServiceException e1) {
e1.printStackTrace();
}
System.out.println();
System.out.println("Réponse du Web Service : ");
System.out.println(reponse);
}
}
7.3.Cas n°3 - WTP

Dans le cas suivant nous allons développer et utiliser un Web Service avec les plugins WTP (Web
Tools Platform) :
La phase complète impose 3 étapes différentes :

    •   Création de classes Java de Service Web
    •   Déploiement d'un Service Web
    •   Test d'un Service Web




7.3.1.Développement
Voici un exemple de l’arborescence à adopter dans le cas d’un projet Web Dynamique au sein
d’Eclipse.




7.3.2.Déploiement
Lorsque votre phase de développement est finie, vous devrez créer le Web Service sur votre serveur
et Eclipse vous permettra également de générer le client associé afin de pouvoir tester si votre Web
Service est en bon fonctionnement.

Sur votre classe « Calcul.java » vous devez simplement créer un Web Service via le menu :
Clic droit → Web Services → Create Web Service
Parmi les écrans de génération de déploiement vous aurez la possibilité de choisir les plateformes sur
lesquelles votre Web Service et votre client seront exécutés.




L’un des écrans vous permet d’obtenir un listing de l’ensemble des méthodes que fournit votre Web
Service.
7.3.3.Utilisation
Lorsque le déploiement est fini, vous avez dans un dossier « sample » avec des pages JSP qui ont
été générées. Exécuter la page « TestClient.jsp » sur votre serveur.




Voici la page client JSP appelant le Web Service venant d’être déployé :




Parmi les différentes pages JSP, celles-ci permettent différentes fonctionnalités :

    •   La frame « Methods » permet de lister les méthodes du Web Service appelé.
    •   La frame « Inputs » permet de saisir un paramètre à envoyer si nécessaire et permet
        d’appeler la méthode.
    •   La frame « Result » permet d’afficher le résultat ou, le cas échéant, les messages d’erreurs.


Vous pourriez ensuite créer vos propres JSP vous permettant d’appeler un Web Service selon le
principe ci-dessous :

Page de saisie du nombre :
Page d’appel du Web Service et d’affichage du résultat :




Ci-dessous vous trouverez le code à écrire afin de reproduire la deuxième des pages. La première
page étant un simple formulaire, elle ne présente pas un grand intérêt. La deuxième page en
revanche s’occupe de récupérer le paramètre saisi et ensuite d’utiliser le Bean créé par Eclipse lors
de la génération du Web Service. Vous pouvez ensuite, grâce à ce Bean (ligne 1), appeler la méthode
que vous souhaitez (calcul.carre() ligne 5).




8.La modélisation BPML

BPML (Business Process Modelling Language) permet de modéliser les processus métiers complets,
mettant éventuellement en œuvre plusieurs entreprises connectées et plusieurs Web Services
communiquant entre eux. BPM rentre notamment en œuvre lors de la phase d’analyse et de mise en
place d’un Web Service.
Des efforts de modélisation et de représentation en XML de l’enchaînement des opérations des
processus ont été faits, il en résulte 3 grands principes :

8.1.L’orchestration

La représentation de l’orchestration d’un ensemble d’activités doit en restituer à la fois des données
statiques (la définition des acteurs & le rôle des acteurs) et des données dynamiques (les états et
transitions & le modèle & l’instanciation & l’état d’instanciation).


8.2.La chorégraphie

C’est un principe mis en place lors de la phase d’analyse qui consiste à séparer la gestion des
données (internes & externes).
Le but de ce dialecte XML est donc en partie de chorégraphier les différents modèles possibles afin de
pouvoir définir des priorités dans les actions faites et d’orchestrer les différents flux dans l’application.


8.3.Modélisation des processus

La base de la modélisation des processus métiers est évidement la conception de processus mais
également leurs coordinations. La notion de processus métier et la segmentation en différentes
couches de votre application devient alors une nécessitée.
Lors de la modélisation, il faut identifier plusieurs points différents : les messages, les participants, les
activités, les règles métiers, …

Les messages échangés entre les participants doivent être correctement définis. L’ensemble de tous
les participants utilisateurs, qu’ils soient statiques ou dynamiques, doit être prévu. L’activité (encore
appelée tâche) correspond à des unités d’exécution. Les opérations élémentaires d’un processus
étant regroupées par domaine d’activité. Une règle métier est une contrainte, exprimée sur les flux
transmis, utilisée pour assurer l’intégrité des données. Une défaillance génère une exception.

Le BPML définit un système de gestion des exceptions. Une transaction permet de spécifier le
comportement du traitement à effectuer selon des attributs définis. L’abstraction de processus est la
description « non instanciée » des interactions entre un processus et ses participants. Une exécution
est une instanciation, correctement paramétrée pour permettre l’exécution du processus.


9.Conclusion

Les Web Services peuvent fonctionner de différentes manières (selon le choix du programmeur qui les
a développé). Ils peuvent traiter des opérations unidirectionnelles, des opérations requêtes/réponses,
des opérations sollicitations/réponses ou bien des opérations de notifications.




Les Web Services offrent de nombreuses fonctionnalités et une réelle souplesse d'utilisation. Ils vous
permettront d'accélérer votre développement.




9.1.Est-il urgent d'attendre

Les Web Services provoquent un intérêt évident auprès des architectes et des décideurs. La question
récurrente concerne leur degré de maturité.

Il est clair que sur une technologie aussi récente, le recul n’existe pas. Mais, même si l’édifice est
encore fragile, il repose sur des bases solides (SOAP et WSDL) qui ont prouvé leur efficacité et leur
maturité. D’ores et déjà, les Web Services ont quitté le champ des échanges interentreprises pour
s’accaparer celui du référencement et de la mise à disposition des ressources de l’entreprise,
empiétant en ce sens sur les technologies de type EAI. Cette utilisation à elle seule prouve la qualité
du modèle et sa pérennité, notamment au niveau des couches les plus basses.

Par contre, la normalisation complète d’une architecture distribuée fondée sur les Web Services n’est
pour le moment qu’un rêve annoncé chaque année, pour la fin de l'année suivante ! Par ailleurs, ce
modèle n’échappe pas à des problèmes de performance : les données sont transmises en ASCII dans
une encapsulation XML elle-même intégrée dans une enveloppe SOAP puis HTTP… Le problème du
choix de la bonne granularité du service, commun à toutes les architectures distribuées, se présente
dans le cas des Web Services de manière plus aiguë encore.

Même s’ils n'ont pas acquis la maturité nécessaire à leur industrialisation, les Web services
s’annoncent plus que jamais comme la réponse appropriée aux problématiques d’échange de
données et d’intégration d’applications.




                           JDBC - Acceder à une base de données
1.Introduction

1.1.Rappel BD
Une base de données relationnelle : ensemble de tables ou de relations.
SGBD : logiciel permettant de gérer des bases de données :

    •   Créer et modifier des tables,
    •   Interroger la base de données,
    •   Assurer la sécurité et l’intégrité des données,
    •   Gérer les transactions et les accès concurrents.

Transaction : unité logique de traitement qui, appliquée à un état cohérent de la base de données,
restitue un nouvel état cohérent mais modifié de la base.
SQL : langage de manipulation de données.
ODBC : Open DataBase Connectivity. Interface d’accès aux bases de données SQL conçu par
Microsoft.

1.2.JDBC = Java DataBase Connectivity
JDBC est une API d’accès aux systèmes de gestion de base de données relationnelles qui permet
d’exécuter des requêtes SQL au sein d’un programme Java et de récupérer les résultats ; ce qui
représente une alternative aux solutions propriétaires. C’est de plus une tentative de standardiser
l’accès aux bases de données car l’API est indépendante du SGBD choisi, pourvu que le pilote JDBC
existe pour ce SGBD, et qu’il implémente les classes et interfaces de l’API JDBC.

2.JDBC et les architectures clients-serveurs multi-tiers

2.1.Architecture client-serveur 2/tiers
Dans une architecture client-serveur 2/tiers, un programme client accède directement à une base de
données sur une machine distante (le serveur) pour échanger des informations, via des commandes
SQL JDBC automatiquement traduite dans le langage de requête propre au SGBD.
Le principal avantage de ce type d’architecture est qu’en cas de changement de SGBD, il n’y a qu’à
mettre à jour ou changer le driver JDBC du coté client. Cependant, pour une grande diffusion du
client, cette architecture devient problématique, car une telle modification nécessite la mise à jours de
chaque client.




2.2.Architecture 3/tiers
Dans une architecture 3/tiers, un programme client n’accède pas directement à la base de données,
mais à un serveur d’application qui fait lui-même les accès à la base de données.
Il y a plusieurs avantages à cette architecture. Tout d’abord, il est possible de gérer plus efficacement
les connexions au niveau du serveur d’application et d’optimiser les traitements. De plus,
contrairement à l’architecture 2/tiers, un changement de SGBD ne nécessite pas une mise à jour des
drivers sur tous les clients, mais seulement sur le serveur d’application.




3.API JDBC
3.1.Structure générale
Pour effectuer un traitement avec une base de données, il faut :

    1.   Charger un pilote en mémoire,
    2.   Etablir une connexion avec la base de données,
    3.   Récupérer les informations relatives à la connexion,
    4.   Exécuter des requêtes SQL et/ou des procédures stockées,
    5.   Récupérer les informations renvoyées par la base de données (si nécessaire),
    6.   Fermer la connexion.

3.2.Bibliothèques nécessaires
Pour instancier les Objets nécessaires au dialogue avec une base de données, il faut importer les
bibliothèques suivantes :

    •    java.sql.*;
    •    sun.jdbc.odbc.*; (pour inclure le pont JDBC-ODBC)

3.3.Charger un pilote en mémoire

3.3.1.Différents types de pilotes
Il existe quatre types de pilotes JDBC :

    1. Type 1 (JDBC-ODBC bridge) : le pont JDBC-ODBC qui s'utilise avec ODBC et un pilote
         ODBC spécifique pour la base à accéder. Cette solution fonctionne très bien sous Windows.
         C'est la solution idéale pour des développements avec exécution sous Windows d'une
         application locale. Cette solution « simple » pour le développement possède plusieurs
         inconvénients :
             o La multiplication du nombre de couches rend complexe l'architecture (bien que
                  transparentes pour le développeur) et détériore les performances,
             o Lors du déploiement, ODBC et son pilote doivent être installé sur tous les postes où
                  l'application va fonctionner,
             o La partie native (ODBC et son pilote) rend l'application moins portable et dépendant
                  d'une plateforme.




    2. Type 2 : un pilote écrit en java appelle l'API native de la base de données.

Ce type de pilote convertit les ordres JDBC pour appeler directement les APIs de la base de données.
Il est de ce fait nécessaire de fournir au client l’API native de la base de données. Elles sont
généralement en C ou en C++.




    3. Type 3 : un pilote écrit en Java utilise un protocole réseau spécifique pour dialoguer avec un
         serveur intermédiaire.
Ce type de pilote utilise un protocole réseau propriétaire spécifique à une base de données. Un
serveur dédié reçoit les messages par ce protocole et dialogue directement avec la base de données.
Ce type de driver peut être facilement utilisé par une applet, mais dans ce cas le serveur intermédiaire
doit obligatoirement être installé sur la machine contenant le serveur Web.




    4. Type 4 : un pilote Java natif.

Ce type de pilote, écrit en java, appelle directement le SGBD par le réseau. Ils sont fournis par
l'éditeur de la base de données. Ce type de driver est la solution idéale, tant au niveau de la simplicité
que des performances et du déploiement.




Liste de quelques pilotes :

    •       Pour une base Oracle : oracle.jdbc.driver.OracleDriver
    •       Pour une base Access : sun.jdbc.odbc.JdbcOdbcDriver
    •       Pour une base PostgreSQL : postgresql.Driver
    •       Pour une base MySQL : org.gjt.mm.mysql.Driver


Vous trouverez les différents drivers sur le lien suivant :
http://servlet.java.sun.com/products/jdbc/drivers


3.3.2.Principe
Le pilote JDBC connaît les méthodes pour se connecter à votre base de données, c’est pourquoi
celui-ci est essentiel.
Ce pilote est généralement disponible dans un package jar. Le chemin doit être ajouté à votre variable
d’environnement CLASSPATH pour permettre au programme de l’utiliser.
La première étape est de charger le pilote en utilisant la méthode Class.forName(String driver).
Cette classe permet ainsi au programme de rester totalement indépendant de la base de données
utilisée en conservant le nom du pilote dans un fichier de propriétés.
La méthode Class.forName(String driver) peut lever une exception de type
ClassNotFoundException si il y a une erreur lors du chargement du driver.
Voici un exemple avec le pilote de Sun utilisé pour se connecter à une base de données via ODBC
(sun.jdbc.odbc.JdbcOdbcDriver) :

        {
             Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
     }      (ClassNotFoundException e) {
             ...
     }

Pour se connecter à une base en utilisant un driver spécifique, la documentation du driver fourni le
nom de la classe à utiliser. Par exemple, si le nom de la classe est package.DriverXXX, le
chargement du driver se fera avec le code suivant :
    Class.forName("package.DriverXXX");
Exemple :
    {
         Class.forName("org.gjt.mm.mysql.Driver");
    }   (ClassNotFoundException e) {
         ...
    }

Cet exemple montre le chargement du pilote pour une base MySQL.


3.4.Etablir une connexion

3.4.1.Définir la base de données
En premier lieu nous devons définir la base de données.
Voici comment paramétrer une source de données ODBC :

   1.   Ouvrir le Panneau de configuration
   2.   Sélectionner l'Administrateur de source de données ODBC,
   3.   Cliquer sur l'onglet DSN Système,
   4.   Cliquer sur le bouton Ajouter,




   5. Sélectionner dans la liste le pilote Microsoft Access driver (*.mdb), puis cliquer sur le
        bouton Terminer,
    6. Indiquer un nom pour la source de données, une description
    7. Cliquer sur le bouton Sélectionner pour définir la localisation de la base, puis sur Ok, et
       fermer la fenêtre de l'administrateur de source ODBC.




Le nom de la base de données étant celle déclarée dans le panneau de configuration ODBC, c'est-à-
dire le nom du DSN. La syntaxe de l'URL peut varier légèrement selon le type de la base de données.
Il s'agit généralement d'une adresse de la forme:
      String url = "jdbc:sousprotocole:SourceDeDonnées";

Exemples :

    •    Grâce au pont JDBC-ODBC

    String url = "jdbc:odbc:DBemployes";


    •    Grâce à un driver spécifique MySQL

    String url = "jdbc:mysql://server/DBemployes";


    •    Grâce à des drivers spécifique Oracle

    String url = "jdbc:oracle:oci8:@DBemployes";
    //DBemployes étant le SERVICE_NAME
   String url = "jdbc:oracle:thin:@server:1521:DBemployes";



3.4.2.Utilisation de l’interface Connection
La connexion à une base de données se fait par le biais de l’instanciation d’un objet de l’interface
Connection. Elle représente une session de travail avec une base de données.
L’interface Connection utilise les méthodes getConnection(…) de la classe DriverManager pour
établir la connexion avec la base de données. Pour cela on passe l’url de la base de données en
paramètre à la méthode.
Les méthodes getConnection(…) peuvent lever une exception de la classe java.sql.SQLException.

La création d'une connexion simple se fait grâce à la méthode suivante :

        SQLException;
   {
        Connection connect = DriverManager.getConnection(url);
    } (SQLException e){
        ...
     }


La création d'une connexion avec un nom d’utilisateur et un mot de passe se fait grâce à la fonction :
       SQLException;
   {
          Connection connect = DriverManager.getConnection(url, "login",
   "password");
     } (SQLException e) {
          ...
     }

A la place de "login" ; il faut mettre le nom d’utilisateur qui se connecte à la base et mettre son mot de
passe à la place de "password".

Il est aussi possible de créer une connexion avec un nom d’utilisateur et un mot de passe grâce à la
fonction :

      SQLException;
   Properties infos = Properties();
    infos.put("userid", "admin");
    infos.put("password", "adminpass");
    {
         Connection connect = DriverManager.getConnection(url, infos);
    } (SQLException e) {
         ...
    }

L’interface Connection dispose de plus de méthodes permettant de fermer la connexion ainsi que de
tester son état :

        SQLException;
        SQLException;


3.5.Traitement des requêtes SQL
Pour traiter une requête SQL, on dispose de plusieurs objets capables d’envoyer celle-ci à la base de
données :

    •    Statement : objet utilisé pour l’exécution d’une requête SQL statique retournant les résultats
         qu’elle produit.
    •    PreparedStatement : utilisé lorsqu’il est nécessaire d’exécuter une requête plusieurs fois,
         avec des paramètres différents.
    •    CallableStatement : objet utilisé pour appeler une procédure stockée.


Des instances de ces objets sont disponibles grâce à l’instance de Connection.

3.5.1.L’interface Statement : les requêtes simples
L’objet Statement représente une instruction de traitement SQL. Il est créé par l’intermédiaire d’une
instance de Connection par la méthode :
       SQLException;

Exemple :
    {
         Statement state = connect.createStatement();
     } (SQLException e) {
         ...
     }

Cet objet est pourvu de méthodes permettant de faire exécuter :
    •    Une requête SQL de consultation (SELECT), retournant les résultats de celle-ci :public
         ResultSet executeQuery(String sql) throws SQLException;

    •    Une requête de modification (UPDATE, DELETE, INSERT, CREATE, DROP), ne renvoyant
         que le nombre d’occurrences affectées par celle-ci :

public int executeUpdate(String sql) throws SQLException;

    •    Un lot de requêtes, renvoyant un tableau d’entier correspondant au résultat de la requête :
         public int[] executeBatch() throws SQLException; utilisé conjointement avec les
         méthodes : public void addBatch(String sql) throws SQLException;public void
         clearBatch() throws SQLException;


Exemples :
    {
        ResultSet resultat = state.executeQuery(
                               "SELECT DISTINCT nom FROM eleves ORDER BY
   nom;");
    } (SQLException e) {
        ...
    }
Ceci permet d'obtenir les noms des élèves de la table eleves, classés par ordre alphabétique.

     {
         state.executeUpdate("CREATE TABLE vendeur" +
                 " (NumVendeur integer" +
                 ", Nom char(15)" +
                 ", Prenom char(10)" +
                 ", DateEmbauche date" +
                 ", NumChef integer" +
                 ", Salaire numeric(6, 0)" +
                 ", Commission numeric(4, 1)" +
                 ", ContratID logical" +
                 ");");
     } (SQLException e) {
         ...
     }
Ceci permet de créer une table vendeur dans la base de données courante.

     {
         state.addBatch("DELETE FROM vendeur WHERE numvendeur = '06897'");
         state.addBatch("UPDATE vendeur SET sal = 1215 WHERE sal < 1215");
         [] results = state.executeBatch();
     } (SQLException e) {
         ...
     }
Ceci permet de supprimer l’occurrence du vendeur ayant le numéro 06897 et de fixer tous les salaires
à 1215 minimum. Le tableau d’entier results contiendra par exemple [1,15], ce qui signifie que la
première requête ("DELETE…") aura affectée une seule ligne, tandis que la seconde ("UPDATE…")
en aura affectée 15.

3.5.2.L’interface PreparedStatement : les requêtes précompilées
L’objet PreparedStatement représente une instruction de traitement SQL précompilée. C’est une
sous-interface de Statement.
Cet objet est généralement utilisé lorsqu’il est nécessaire de réutiliser plusieurs fois la même requête
avec des paramètres différents, identifiés par un « ? » dans la requête. Il est créé par l’intermédiaire
d’une instance de Connection par la méthode :
       SQLException;

Exemple :
    {
            PreparedStatement prepState = connect.prepareStatement(sql);
    } (SQLException e) {
        ...
    }

Cet objet est pourvu de méthodes permettant de :

    •    Exécuter la requête SQL, retournant les résultats de celle-ci : public ResultSet
         executeQuery() throws SQLException;public int executeUpdate() throws
         SQLException;

    •    Spécifier le type et la valeur de tel ou tel argument : public void setXxxx(int
         indiceParameter, Xxxx value) throws SQLException; où Xxxx est le type de la valeur
         setString(int indiceParameter, String value); setInt(int indiceParameter, int value) ; …

    •    Vider la liste des paramètres : public void clearParameters() throws SQLException;

Exemple :

    String sql = "SELECT nom FROM vendeur WHERE sexe = ? and niveau > ?";
    {
        PreparedStatement prepState = connect.prepareStatement(sql);
        prepState.setString(1, "F");
        prepState.setInt(2, 5);
        ResultSet rs = prepState.executeQuery();
        ...
        prepState.clearParameters();
        prepState.setString(1, "M");
        prepState.setInt(2, 3);
        rs = prepState.executeQuery();
        ...
    } (SQLException e) {
        ...
    }

Ceci permet d'obtenir les noms des vendeuses de niveau supérieur à 5, puis des vendeurs de niveau
supérieur à 3.

3.5.3.L’interface CallableStatement : les procédures stockées
L’objet CallableStatement représente une instruction de traitement SQL stockée sur la base de
données. C’est une sous-interface de PreparedStatement.
Tout comme l’interface PreparedStatement, il est possible de mettre des « ? » pour réutiliser la
requête avec des paramètre différents. Il est créé par l’intermédiaire d’une instance de Connection
par la méthode :

        SQLException;


Exemple :

    {
        CallableStatement prepState = connect.prepareCall(sql);
    } (SQLException e) {
        ...
    }

Les syntaxes disponibles pour l’appel de procédures stockées sont les suivantes :
{call procedure_name[( ?, ?, …)]}
{? = call procedure_name[( ?, ?, …)]}
La seconde permettant d’avoir un paramètre de retour.

Cet objet est pourvu de méthodes permettant de :
    •   Exécuter la requête SQL, retournant les résultats de celle-ci : public ResultSet
        executeQuery() throws SQLException;public int executeUpdate() throws
        SQLException;

    •   Spécifier le type et la valeur de tel ou tel argument : public void setXxxx(int
        indiceParameter, Xxxx value) throws SQLException; où indiceParameter est le rang du
        paramètreoù Xxxx est le type de la valeur setString(int indiceParameter, String value);
        setInt(int indiceParameter, int value); …

    •   Vider la liste des paramètres : public void clearParameters() throws SQLException;

    •   Spécifier le type du paramètre renvoyé par la procédure stockée : public void
        registerOutParameter(int numParam, int typeSQL) throws SQLException; où numParam
        est le rang du paramètreoù typeSQL est le type SQL du paramètre (constantes disponibles
        dans java.sql.Types)

Exemple :

     String sql = "{call liste_vendeur_par_ville(?)}";
     {
         CallableStatement callState = connect.prepareCall(sql);
         callState.setString(1, "Paris");
         ResultSet rs = callState.executeQuery();
         ...
     } (SQLException e) {
         ...
     }


3.5.4.Paramétrage du type d’accès
Il est aussi possible de paramètre le type d’accès que l’on aura au niveau des résultats obtenus grâce
aux méthodes de l’interface Connection :

      SQLException;
     SQLException;
     SQLException;

Les paramètres resultSetType et resultSetConcurrency étant des constantes de l’interface ResultSet
utilisées pour la récupération des données renvoyées par la base de données.
Le paramètre resultSetType correspondant au type de parcours du curseur sur les résultats :

    •   TYPE_FOWARD_ONLY : parcours des résultats en avant seulement.
    •   TYPE_SCROLL_INSENSITIVE : parcours des résultats en avant ou en arrière. Insensible aux
        changements effectués sur la base par d’autres utilisateurs.
    •   TYPE_SCROLL_SENSITIVE : parcours des résultats en avant ou en arrière. Sensible aux
        changements effectués sur la base par d’autres utilisateurs.

Le paramètre resultSetConcurrency correspondant à la possibilité de mise à jour des données
renvoyées par la base de données :

    •   CONCUR_READ_ONLY : données en lecture seule.
    •   CONCUR_UPDATABLE : données modifiables ; un verrou en écriture est posé sur celles-ci
        dans la base de données.

3.5.5.Informations de la structure de la base de données
Il est aussi possible d’accéder aux informations de la structure de la base de données grâce à la
méthode de l’interface Connection :
        SQLException;

Exemple :
    {
            DatabaseMetaData dbMetaData = connect.getMetaData();
        System.out.println("Base : " +
   dbMetaData.getDataBaseProductName());
        ...
    } (SQLException e) {
        ...
    }


3.6.Récupération des résultats
Lors d’une requête SQL de consultation (SELECT), un objet ResultSet est retourné. Celui-ci
représente la liste des données retournées par la requête.

3.6.1.Consultation de la structure des données
Lors de la récupération des résultats, ceux-ci sont formatés d’une certaine manière. Il est alors
nécessaire d’accéder à la structure des enregistrements. Pour cela, ResultSet fourni une méthode
permettant de récupérer une instance de ResultSetMetaData, qui comprend toutes les propriétés de
la structure (nom de colonne, type, accès…) :
       ResultSetMetaData getMetaData();

ATTENTION : l’indice de la première colonne est 1, il n’y a pas de colonne 0.


Exemple :

     ResultSet rs = state.executeQuery("SELECT * FROM vendeur");
     ResultSetMetaData rsmd = rs.getMetaData();
       colomnCount = rsmd.getColumnCount();
       i = 1; i <= colomnCount; i++) {
          System.out.print(rsmd.getColumnName(i) + "\t");
     }
     (rs.next) {
          System.out.print("-");
       i = 1; i <= colomnCount; i++) {
                  System.out.print(rs.getObject(i) + "\t");
          }
          System.out.println("");
     }

Ceci permet d’afficher les noms des colonnes, ainsi que la liste des valeurs quelles contiennent.

3.6.2.Lecture des données
Pour pouvoir récupérer les données contenues dans l’instance de ResultSet, celui-ci met à
disposition des méthodes permettant de :

    •   Positionner le curseur sur l’enregistrement suivant :public boolean next();Renvoi un booléen
        indiquant la présence d’un élément suivant.
    •   Accéder à la valeur du type donné et à la colonne donnée (par indice ou par nom) de
        l’enregistrement actuellement pointé par le curseur : public Xxxx getXxxx(int indiceCol);
        public Xxxx getXxxx(String nomCol);où Xxxx est le type de la valeur getString(int
        indiceCol); getInt(int indiceCol); …


Exemple :
    ResultSet rs = state.executeQuery("SELECT * FROM vendeur");
    (rs.next) {
          System.out.println("[" +
                  rs.getString("nomvendeur") +
                  ", " +
                  rs.getInt("agevendeur") +
                  ", " +
                  rs.getDate("dateembvendeur") +
                  "]");
    }
Cet exemple permet d’afficher la liste des vendeurs, suivi de leur age et de leur date d’embauche
respectifs.

3.6.3.Modification des données
Pour pouvoir modifier les données contenues dans l’instance de ResultSet, celui-ci met à disposition
des méthodes permettant de :

    •   Modifier la valeur du type donné et à la colonne donnée (par indice ou par nom) de
        l’enregistrement actuellement pointé par le curseur : public void updateXxxx(int indiceCol,
        Xxxx value) ;public void updateXxxx(String nomCol, Xxxx value) ;où Xxxx est le type de
        la valeur updateString(int indiceCol, String value) ; updateInt(int indiceCol, int value) ; …
    •   Appliquer dans la base de données les changements effectués sur l’enregistrement
        actuellement pointé par le curseur :public void updateRow();
    •   Insérer dans la base de données l’enregistrement actuellement pointé par le curseur :public
        void insertRow();
    •   Aller sur un emplacement vide permettant d’insérer un nouvel enregistrement :public void
        moveToInsertRow();


ATTENTION : Certain pilotes ne gèrent pas ou gère mal ces méthodes (ex : pour Access). Il est
nécessaire de vérifier que ceux-ci soient compatibles avec ces méthodes (ex : pour mySQL, pilote
compatible à partir de la version 3.0.7)

Exemple :
    ResultSet rs = state.executeQuery("SELECT * FROM vendeur");
    rs.next();
    ...
    rs.updateString("nomvendeur", "Dupont");
    rs.updateInt("agevendeur", 28);
    rs.updateDouble("salvendeur", 1300);
    updateRow();

     rs.moveToInsertRow();
     rs.updateString("nomvendeur", "Robert");
     rs.updateInt("agevendeur", 32);
     rs.updateDouble("salvendeur", 1215);
     rs.insertRow();

Cet exemple permet modifier un enregistrement, puis d’en insérer un nouveau et d’affecter les
changements dans la base pour chacun d’eux.


3.7.Gestion des transactions
Il existe deux façons de gérer les transactions dans une application Java :

    •   grâce à l’API JDBC, en local pour chaque client, avec les méthodes appropriées,
    •   grâce à l’API JTA (Java Transaction API), partagée entre plusieurs clients.




3.7.1.Gestion des transactions en local : JDBC
L’instance de l’objet Connection fourni les méthodes pour :

    •   Activer/désactiver la validation automatique de chaque requête passée (activé par défaut) :
        public void setAutoCommit(boolean autoCommit) throws SQLException;Valider
        manuellement la/les requête(s) passée(s) : public void commit() throws
        SQLException;Annuler les dernières modifications faites par la/les requête(s) passée(s) :
        public void roolback() throws SQLException;


Exemple :
    String sql1 = "UPDATE vendeur SET sal = 1215 WHERE sal < 1215";
    String sql2 = "DELETE FROM vendeur WHERE sal < 1215";
     {
         connect.setAutoCommit();
         Statement state = connect.createStatement();
         state.executeUpdate(sql1);
         Statement state2 = connect.createStatement();
         state2.executeUpdate(sql2);
         connect.commit();
         ...
     } (Exception e) {
         {
                 connect.rollback();
         } (SQLExection e) {
                 ...
         }
         ...
     }


3.7.2.Gestion des transactions partagées : JTA
JTA est une API fournie dans la version J2EE de Java. Elle permet une gestion à distance des
transactions.
Le principe est similaire à celui de JDBC du coté client : on appelle les fonctions commit() et
rollback() d’une instance de l’objet UserTransaction (javax.transaction.UserTransaction) comme
si on appelait les méthodes de Connection. Cependant, la transaction n’est pas gérée du coté client,
mais l’appel de ces méthodes invoque les méthodes correspondantes sur un serveur implémentant
JTS (Java Transaction Service) ; c’est ce serveur qui agira directement sur la base de données.

L’objet UserTransaction fournit les méthodes permettant de :

    •    Avertir le serveur du début d’une nouvelle transaction : public void begin() throws
         NotSupportedException;
    •    Valider la transaction : public void commit() throws RollbackException, […];
    •    Annuler les dernières modifications faites par la/les requête(s) passée(s) : public void
         rollback() throws IllegalStateException, […];
    •    Spécifier la durée de vie de la transaction : public void setTransactionTimeout(int
         secondes) throws SystemException;
    •    Connaître l’état de la transaction : public int getStatuts() throws SystemException; La
         valeur retournée correspond à une constante de la classe javax.transaction.Status


Exemple :
    String sql = "DELETE FROM vendeur WHERE sal < 1215";
    {
          //récupération d’une connexion à la base par JDBC
          ...
          UserTransaction ut = getSessionContext().getUserTransaction();

         ut.begin();
         connect.executeUpdate(sql);
         ut.commit();
     } (Exception e) {
         {
                 ut.rollback();
         } (IllegalStateException e) {
                 ...
         }
         ...
     }



3.8.Fermeture de connexion
Après toutes les actions effectuées sur la base de données, il faut fermer les instances qui permettent
la connexion à celle-ci.
Cette action est effectuée par l’appel de la fonction close() des objets Statement,
PreparedStatement, CallableStatement, Connection, ResultSet.
Exemple :
    {
        rs.close();
        state.close();
        connect.close();
    } (Exception e) {
        ...
    }

REMARQUE : l’instance d’un Statement doit être fermée avant de relancer une autre requête SQL.


4.Correspondance des types de données SQL/Java

Pour récupérer les valeurs des champs d'une base de données, il faut connaître son type SQL. Selon
son type SQL, la récupération se fait dans un type ou un autre. Voici la correspondance entre les
méthodes de lecture et les types de données SQL. Les « x » représentent les types compatibles, et
les « X » les correspondances les mieux adaptées.

                        TINYINT SMALLINT INTEGER BIGINT REAL FLOAT DOUBLE

   getByte              X          x            x          x         x      x        x

   getShort             x          X            x          x         x      x        x

   getInt               x          x            X          x         x      x        x

   getLong              x          x            x          X         x      x        x

   getFloat             x          x            x          x         X      x        x

   getDouble            x          x            x          x         x      X        X

   getBigDecimal        x          x            x          x         x      x        x

   getBoolean           x          x            x          x         x      x        x

   getString            x          x            x          x         x      x        x

   getBytes

   getDate

   getTime

   getTimeStamps

   getAsciiStream

   getUnicodeStream

   getBinaryStream

   getClob

   getBlob

   getArray

   getRef

   getCharacterStream

   getObject            x          x            x          x         x      x        x



                            DECIMAL NUMERIC BIT CHAR VARCHAR LONGVARCHAR
     getByte              x        x         x   x      x        x

     getShort             x        x         x   x      x        x

     getInt               x        x         x   x      x        x

     getLong              x        x         x   x      x        x

     getFloat             x        x         x   x      x        x

     getDouble            x        x         x   x      x        x

     getBigDecimal        X        X         x   x      x        x

     getBoolean           x        x         X   x      x        x

     getString            x        x         x   X      X        x

     getBytes

     getDate                                     x      x        x

     getTime                                     x      x        x

     getTimeStamps                               x      x        x

     getAsciiStream                              x      x        X

     getUnicodeStream                            x      x        X

     getBinaryStream

     getClob

     getBlob

     getArray

     getRef

     getCharacterStream                          x      x        X

     getObject            x        x         x   x      x        x




                      BINAR   VARBINAR   LONGVARBINAR                TIEMSTAM
                                                        DATE TIME               CLOB
                      Y       Y          Y                           P

getByte

getShort

getInt

getLong

getFloat

getDouble

getBigDecimal

getBoolean

getString             x       x          x              x    x       x

getBytes              X       X          x

getDate                                                 X            x
getTime                                                         X   x

getTimeStamps                                           x       x   X

getAsciiStream        x        x            x

getUnicodeStream      x        x            x

getBinaryStream       x        x            X

getClob                                                                 X

getBlob

getArray

getRef

getCharacterStrea
                      x        x            x
m

getObject             x        x            x           x       x   x   x




                                   BLOB ARRAY REF STRUCT JAVA_OBJECT

             getByte

             getShort

             getInt

             getLong

             getFloat

             getDouble

             getBigDecimal

             getBoolean

             getString

             getBytes

             getDate

             getTime

             getTimeStamps

             getAsciiStream

             getUnicodeStream

             getBinaryStream

             getClob

             getBlob               X

             getArray                   X

             getRef                             X

             getCharacterStream

             getObject             x    x       x   X       X
Le cas des valeurs nulles

Il se peut qu'une valeur dans le ResultSet soit NULL au sens SQL, mais Java ne connaît pas cette
valeur pour les types primitifs. Aussi, si il y a un NULL pour un cham


5.Exemple General :

NA




                               Le XML en Java - SAX & DOM
1.Introduction

1.1.Rappel XML

1.1.1.A quoi sert XML
La norme XML (eXtensible Markup Language) permet de stocker des données de façon structurée, il
s’agit d’un document XML.
Un document XML contient des balises qui encapsulent d’autres balises ou du texte, comme on peut
le voir dans un document HTML par exemple. Les balises peuvent contenir des attributs, que nous
détaillerons un peu plus loin.

XML possède de nombreuses applications, dont :

    •   séparation des données et de l’affichage
    •   échange d’informations standardisées entre applications (WebService)
    •   stockage d’informations organisés, lisible par un humain ou un ordinateur, standardisé.

1.1.2.Structure d’un document XML
Un document XML est organisé en plusieurs parties :
Le prologue qui est en fait la premiere ligne d’un document XML indique la version de la norme XML
utilisée pour créer le document et le jeu de caractères utilisé dans le document.
Voici le prologue pour la table de caractères qui reconnaît les caractères accentués utilisés en
France :

<?xml version="1.0" encoding="ISO-8859-15"?>

On peut ensuite trouver une référence vers une déclaration de type de document (à l'aide d'un fichier
appelé DTD - Document Type Definition) qui contient les règles permettant de savoir si un document
XML bien formé est valide (un document XML est considéré comme bien formé si il respecte le
standard XML).
La suite du fichier XML contient « l’arbre XML » qui contient les données organisées.

La syntaxe des éléments en XML

L'arbre des éléments qui compose un document XML, est constitué d'une hiérarchie de balises
comportant éventuellement des attributs.
Un attribut est une paire (clé, valeur) écrit sous la forme cle="valeur", ainsi une balise affectée d'un
attribut aura la syntaxe suivante:

<balise cle="valeur">

Les données sont encapsulées entre une balise ouvrante <balise> et une balise fermante </balise>.
Ces données sont composées de texte et/ou d’autres balises.
Un élément vide ne contient qu'une balise dont la syntaxe est la suivante : <balise_vide/>.

Voici un exemple de fichier XML que nous utiliserons pour la suite des exercices :
<?xml version="1.0" encoding="ISO-8859-15"?>

<personne>
<nom>Dupond</nom>
<prenom>Martin</prenom>
<adresse>
<numero>3</numero>
<rue>rue de la paix</rue>
<ville>Paris</ville>
<codePostal>75001</codePostal>
</adresse>
</personne>


1.2.Les API Java pour XML
Une API est une interface de programmation applicative (application programming interface). Il s’agit
d’une interface normalisée permettant à un logiciel de faire appel aux fonctions d'un autre programme
déjà disponible sur une machine. Concrètement, les API que nous allons voir par la suite vont
permettre au programmeur Java de gérer facilement et efficacement l’accès aux fichiers XML en
faisant appel à des méthodes prédéfinies.
Java possède, depuis le JDK1 1.4, deux API permettant la gestion de fichiers XML regroupées dans le
package JAXP (Java API for XML Parsing). On peut trouver cette API séparément sur le site de Sun
Microsystem – http://java.sun.com .

Nous allons voir comment utiliser les API SAX et DOM dans ce cours.

2.L’API SAX

2.1. Présentation
SAX (Simple API for XML) est basé sur un modèle événementiel, ce qui signifie que l'analyseur
appelle automatiquement une méthode lorsqu'un événement est détecté dans le fichier XML.
Evénement signifie, par exemple, détection d'une balise ouvrante, de la fin du document etc…
Voici les 5 évènements détectés par SAX ainsi que les méthodes qui sont appelées :

    •   →Détection d'une balise de début startElement()
    •   →Détection d'une balise de fin endElement()
    •   →Détection de données entre deux balises characters()
    •   →Début du traitement du document XML startDocument()
    •   →Fin du traitement du document XML endDocument()

2.2. Quand utiliser SAX

    •   Document XML volumineux
    •   Lecture rapide et efficace
    •   Economie de mémoire car SAX ne construit pas une représentation en mémoire de la
        structure en arborescence des fichiers XML lus
    •   SAX gère les fichiers XML comme un flux de données, on accède aux données quand elles
        arrivent, mais on ne peut pas revenir en arrière ou sauter à une position dans le fichier. En
        général, SAX est efficace uniquement pour lire des données pour que l’application travaille
        avec.

2.3. Comment utiliser SAX

2.3.1.Créer le squelette de l’application
Commencez par créer le fichier TestXML.java et écrire le squelette de l’application :
     TestXML {
          main(String argv[]) {
        }
   }


2.3.2.Importer les classes
Il faut importer les classes suivantes afin d’utiliser l’API SAX pour l’accès au fichier XML :
        java.io.*;
        org.xml.sax.*;
        org.xml.sax.helpers.DefaultHandler;
        javax.xml.parsers.SAXParserFactory;
        javax.xml.parsers.ParserConfigurationException;
        javax.xml.parsers.SAXParser;

      TestXML {
     ...

Les classes java.io servent aux entrées/sorties dans le fichier XML. Le package org.xml.sax définit
les interfaces que nous allons utiliser pour l’analyseur SAX. La classe SAXParserFactory crée
l’instance que nous allons utiliser. Il peut lever l’exception ParserConfigurationException s’il ne peut
pas produire un analyseur qui correspond à l’option de configuration.

2.3.3.Implémenter l’interface ContentHandler
Il est nécessaire d’implementer l’interface ContentHandler pour lire un fichier XML en utilisant la
méthode SAX. Cette interface necessite certaines méthodes que l’analyseur SAX invoque lorsque
certains événements sont détectés pendant l’analyse. Les méthodes les plus utilisées pour réagir aux
événements sont startDocument(), endDocument(), startElement(), endElement(), et characters().
La façon la plus facile pour implémenter cette interface est d’hériter de la classe DefaultHandler
définie dans le package org.xml.sax.helpers. Cette classe fournit des méthodes vides pour tous les
événements du ContentHandler. Si l’on souhaite implémenter ContentHandler, il faut redéfinir les
méthodes suivantes :

    •    public void setDocumentLocator(Locator value)
    •    public void startDocument() throws SAXException
    •    public void endDocument() throws SAXException
    •    public void startPrefixMapping(String prefix, String URI) throws SAXException
    •    public void endPrefixMapping(String prefix) throws SAXException
    •    public void startElement(String nameSpaceURI, String localName, String rawName,
         Attributes attributs) throws SAXException
    •    public void endElement(String nameSpaceURI, String localName, String rawName)
         throws SAXException
    •    public void characters(char[] ch, int start, int end) throws SAXException
    •    public void ignorableWhitespace(char[] ch, int start, int end) throws SAXException
    •    public void processingInstruction(String target, String data) throws SAXException
    •    public void skippedEntity(String arg0) throws SAXException



        DefaultHandler {
           ...
     }

Toutes les méthodes à implémenter doivent lever une SAXException pour être conforme à l’interface
ContentHandler. Lorsqu’une exception est levée par l’interface.

Quand un tag de début ou un tag de fin est rencontré, le nom du tag est passé comme chaîne de
caractères aux méthodes startElement() ou endElement(). Quand un tag de début est rencontré, les
attributs qu’il possède sont aussi passés dans une liste d’attribut (Collection). Les caractères trouvés
dans l’élément sont passés en tant que tableau de caractères avec le nombre de caractères et le
décalage (offset) dans le tableau qui pointe sur le premier caractère.

2.3.4.Paramétrer l’analyseur
Maintenant vous pouvez paramétrer l’analyseur grâce au code ci-dessous :
      IOException {
           (args.length != 1) {
                   System.err.println("Erreur, il manque un argument");
                   System.exit(1);
         }

           // Use an instance of ourselves as the SAX event handler
           DefaultHandler handler = AnalyseSAX();

           // Use the default (non-validating) parser
           SAXParserFactory factory = SAXParserFactory.newInstance();
             {
                   // Parse the input
                   SAXParser saxParser = factory.newSAXParser();
                   saxParser.parse( File(argv[0]), handler);
           } (Throwable t) {
                   t.printStackTrace();
           }
           System.exit(0);
     }

Ces lignes permettent de créer une instance de SAXParserFactory, conforme aux paramètres du
système xml.parsers.SAXParserFactory.
Vous obtenez ensuite un analyseur à partir de la fabrique (factory) qui permet d’avoir l’analyseur
(parser) afin de récupérer les événements d’analyse lorsqu’on lui passe le nom du fichier XML.

2.3.5.Manipuler les événements de contenu
Pour finir, écrivons le code qui gère les événements du ContentHandler.
Les événements de document
Le code ci-dessous permet d’effectuer un traitement lors du début et de la fin d’un document :
       SAXException {
          System.out.println("Debut du document");
     }

        SAXException {
           System.out.println("Fin du document");
    }



Les événements des éléments
Le code ci-dessous permet d’effectuer un traitement lorsqu’un élément tel qu’une balise ouvrante ou
fermante est détecté.
      startElement(String uri,
          String localName, // simple name
          String qName, // qualified name
          Attributes attrs) SAXException {

           String eName = localName; // element name

            ("".equals(eName)) {
                   eName = qName; // not namespaceAware
        }
        System.out.println("Balise ouvrante: " + eName);
        ) {
                //listage des attributs
                  (attrs.getLength() != 0)
                         System.out.println("Parametres pour " + eName +
   ":");
                  i = 0; i < attrs.getLength(); i++) {
                         String aName = attrs.getLocalName(i);
                         // récupération du nom de l'attribut
                           ("".equals(aName)) {
                                  aName = attrs.getQName(i);
                         }
                         System.out.println(aName + "=" + attrs.getValue(i)
   + "");
                }
        }
    }

        endElement(
           String uri,
           String localName,
           String qName)
             SAXException {
           String nomElement = localName;
             (nomElement.equals("")) {
                    nomElement = qName;
           }
           System.out.println("endElement : " + nomElement);
    }

On remarque que lors de la détection d’une balise ouvrante il est possible de détecter les paramètres
inclus dans la balise.

Voici la signification des arguments concernant les méthodes que nous venons de voir :

    •    uri est la chaîne de caractères contenant l'URI complète de l'espace de nommage du tag ou
         une chaîne vide si le tag n'est pas compris dans un espace de nommage,
    •    localName est le nom du tag sans le préfixe s'il y en avait un,
    •    qName est le nom du tag version XML 1.0 c'est à dire $prefix:$localname,
    •    attributs est la liste des attributs du tag que l'on étudiera un peu plus loin.
Comme vous avez pu le voir dans l’exemple, les methodes suivantes permettre de manipuler les
attributs en utilisant le parameter de type Attributes:

    •   int getLength() : Retourne le nombre d’attributs dans la liste.
    •   String getLocalName(int index) : Récupère le nom d’un attribut à partir de l’index
    •   String getValue(int index) : Récupère la valeur de l’attribut à partir de l’index.

Les événements de caractère
Pour finir la manipulation des événements de contenu, vous avez besoin de pouvoir gérer les
caractères que l’analyseur récupère entre chaque balise.
Les lignes ci-dessous permettent de récupérer un buffer avec les caractères lus, et de les afficher à
l’écran.
        SAXException {
            String chaine = String(ch, start, end);
            chaine = chaine.trim();
              (!chaine.equals("")) {
                      System.out.println("donnees : " + chaine );
            }
      }

Signification des arguments :

    •   ch – le tableau de caractères
    •   start – L’indice de départ dans le tableau de caractères
    •   length – Le nombre de caractere à récupérer du tableau.

L’application permettant de lire un fichier XML en utilisant la méthode SAX est terminée.

2.4.Exemple général
Pour illustrer l'utilisation de SAX, voici un exemple concret de code source qui analyse un fichier :
    // Parse un document XML en JAVA avec l'Api SAX
    // on importe les API necessaires
    // pour l'analyse du XML
       org.xml.sax.*;
       org.xml.sax.helpers.DefaultHandler;
       javax.xml.parsers.SAXParserFactory;
       javax.xml.parsers.ParserConfigurationException;
       javax.xml.parsers.SAXParser;
    // pour l'accès aux fichiers
       java.io.*;

     DefaultHandler {
        // Méthode principale
         IOException {
                // Si l'utilisateur a oublié de passer
                // le nom du fichier XML en paramètre => erreur
                  (args.length != 1) {
                         System.err.println("Erreur, il manque un
   argument");
                         System.exit(1);
                }
                // on lance l'analyseur avec le fichier XML en parametre
                DefaultHandler handler = AnalyseSAX();
                SAXParserFactory factory;
                factory = SAXParserFactory.newInstance();
                  {
                         SAXParser saxParser = factory.newSAXParser();
                         saxParser.parse( File(args[0]), handler);
                } (Throwable t) {
                         // Si on a une erreur pendant l'analyse
                         t.printStackTrace();
                         System.exit(1);
                }
                System.exit(0);
        } // fin du main
          // Les méthodes qui suivent sont appelées
          // automatiquement par l'analyseur
          // lorsqu'un événement est détecté
          // dans le fichier XML.
            SAXException {
                   System.out.println("debut du document");
          }

           SAXException {
                  System.out.println("fin du document");
          }

           startElement(String namespaceURI, String sName, // simple name
          String qName, // qualified name
          Attributes attrs) SAXException {
                  String eName = sName; // element name
                    ("".equals(eName)) {
                           eName = qName; // not namespaceAware
                  }
                  System.out.println("balise ouvrante: " + eName);
                  ) {

                       // Listage des attributs
                         (attrs.getLength() != 0) {
                                System.out.println("listage des parametres
   pour la balise " + eName + ":");
                       }
                         i = 0; i < attrs.getLength(); i++) {
                                String aName = attrs.getLocalName(i);
                                // Récupération du nom de l'attribut
                                  ("".equals(aName)) {
                                         aName = attrs.getQName(i);
                                }
                                System.out.println(" " + aName + "=\"" +
   attrs.getValue(i) + "\"");
                       }
                }
        }

           endElement(
                  String namespaceURI,
                  String simpleName,
                  String qualifiedName)
                   SAXException {

                    String nomElement = simpleName;
                      (nomElement.equals("")) {
                             nomElement = qualifiedName;
                    }
                    System.out.println("endElement : " + nomElement);
          }

           SAXException {
                  String chaine = String(ch, start, end);
                  chaine = chaine.trim();
                    (!chaine.equals("")) {
                           System.out.println("donnees : " + chaine);
                  }
        }
    } // Fin de la classe




2.5.Résultat
Lorsqu'on exécute le programme ci-dessus sur le fichier XML présenté à la partie 2, on obtient le
résultat suivant :
     C:\> java AnalyseSAX tst.xml

     debut du document
     balise ouvrante: personne
     balise ouvrante: nom
     donnees : Dupond
     endElement : nom
     balise ouvrante: adresse
     listage des parametres pour la balise adresse:
       type="france"
     balise ouvrante: numero
     donnees : 3
     endElement : numero
     balise ouvrante: rue
     donnees : rue de la paix
     endElement : rue
     balise ouvrante: ville
     donnees : Paris
     endElement : ville
     balise ouvrante: codePostal
     donnees : 75001
     endElement : codePostal
     endElement : adresse
     endElement : personne
     fin du document

L'analyseur XML a détecté les balises ouvrantes et les balises fermante, puis a lancé les méthodes
correspondantes qui nous permettent d'effectuer le traitement désiré.

3.DOM

3.1.Présentation
La programmation en utilisant DOM (Document Object Model) est totalement différente de la méthode
SAX.
En effet, le document n’est plus lu de façon linéaire mais il est parcouru plusieurs fois par l’analyseur
et stocké entièrement en mémoire sous forme d’un arbre que l’on appelle arbre DOM.
Contrairement à SAX, cette API présente l’intérêt de pouvoir tenir compte de la hiérarchie des
éléments, et d’ajouter, supprimer ou modifier des éléments de cet arbre.

Ainsi le fichier XML suivant peut etre transformé en arbre :

<personne>
<nom>Dupond</nom>
<prenom>Martin</prenom>
<adresse>
<numero>3</numero>
<rue>rue de la paix</rue>
<ville>Paris</ville>
<codePostal>75001</codePostal>
</adresse>
</personne>
Ce qui donne :
3.3.Comment utiliser DOM

3.3.1.Création d’un arbre DOM
Il existe deux façons pour créer un arbre DOM. Soit on créé un arbre vide qui ne contient que la
racine, puis on ajoute des branches et des sous branches pour obtenir un arbre complet, soit on
génère un arbre en parsant un flux XML à partir d’un fichier par exemple.
Pour ces deux alternatives, il faut tout d’abord initialiser une instance de DocumentBuilderFactory.
Puis à partir de cette instance on créé un DocumentBuilder qui va permettre de créer un Document
qui sera un arbre vide ou un arbre généré à partir d’un flux comme les montrent les deux exemple ci-
dessous.

    1. A partir d’un fichier arbre vide

      java.io.*;
      org.w3c.dom.*;
      javax.xml.parsers.*;

      Dom1 {

          main(String args[]) {
                   {
                         DocumentBuilderFactory factory =
   DocumentBuilderFactory.newInstance();
                         DocumentBuilder builder =
   factory.newDocumentBuilder();
                         Document document = builder.newDocument();
                 } (Exception e) {
                         e.printStackTrace();
                 }
        }
    }


    2. A partir d’un fichier XML

      java.io.*;
      javax.xml.parsers.*;
      org.w3c.dom.*;

      Dom1 {

         main(String args[]) {
                  (args.length == 0) {
                         System.err.println("Usage : java Dom1
   nomFichier.xml");
                         System.exit(-1);
                }
                  {
                         // Chargement du document
                         DocumentBuilderFactory factory =
   DocumentBuilderFactory.newInstance();
                         DocumentBuilder builder =
   factory.newDocumentBuilder();
                         FileInputStream fis = File(args[0]));
                         Document doc = builder.parse(fis);
                } // fin try
                         (Exception e) {
                         e.printStackTrace();
                }// fin catch
        } // fin main
    } // fin class

Ces exemples sont difficilement exploitables étant donné qu’ils créent un arbre DOM en mémoire,
mais aucun traitement n’est effectué dessus et nous n’exportons pas l’arbre généré.
Nous allons voir dans les chapitres qui suivent comment exporter cet arbre à l’écran ou dans un
fichier, mais aussi comment modifier et lire les informations d’un arbre DOM.
3.3.2.Exportation d’un arbre DOM dans un flux
Maintenant que nous savons créer un arbre DOM en mémoire, il peut êre intéressant de pouvoir
l’exporter dans un flux afin de l’afficher à l’écran ou de le sauvegarder dans un fichier.
Les APIs Java de base ne permettent pas d’exporter un arbre DOM dans un flux. Nous sommes donc
obligés de télécharger une API et de mettre à jour la variable CLASSPATH pour réaliser cette
opération.

    1. Instalation de JAXP

Il faut tout d’abord télécharger JAXP sur le site officiel de Sun : http://java.sun.com/xml/jaxp/
Nous allons ensuite mettre à jour la variable d’environnement CLASSPATH en y ajoutant
l’emplacement des librairies JAXP :
Windows :
      set CLASSPATH=%CLASSPATH%;X:\chemin\vers\JAXP\lib\endorsed\sax.jar;X:
     \chemin\vers\JAXP\lib\endorsed\
     xalan.jar;X:\chemin\vers\JAXP\lib\endorsed\
      xercesImpl.jar;X:\chemin\vers\JAXP\lib\endorsed\ jaxp-api.jar
Linux :
     export CLASSPATH=$CLASSPATH:/chemin/vers/JAXP/lib/endorsed/dom.jar:/c
   hemin/vers/JAXP/lib/endorsed/xalan.jar:/chemin/vers/JAXP/lib/endorsed/
   xercesImpl.jar:/chemin/vers/JAXP/lib/endorsed/jaxp-api.jar


    2. Ajout dans le code source

Ajouter un import :
      org.apache.xalan.serialize.*;

Sérialiser l’instance de Document à l’aide des lignes suivantes :
     SerializerToXML ser = SerializerToXML ();// Instanciation du
   serializer
     ser.setOutputStream (System.out);
     ser.serialize(dom);



3.3.3.Parcours d’un arbre DOM
Pour accéder aux éléments dans un arbre, il existe plusieurs méthodes de la classe Node qui
permettent de parcourir l’arbre.

    •   Node getFirstChild()
    •   Node getLastChild()
    •   Node getNextSibling()
    •   Node getPreviousSibling()
    •   Node getParentNode()
    •   NodeList getChildNodes()
    •   boolean hasChildren()
       java.io.*;
       javax.xml.parsers.*;
       org.w3c.dom.*;

       Dom1 {

         main(String args[]) {
                  (args.length == 0) {
                         System.err.println("Usage : java Dom1
   nomFichier.xml");
                         System.exit(-1);
                }
                  {
                         // Chargement du document
                         DocumentBuilderFactory factory =
   DocumentBuilderFactory.newInstance();
                         DocumentBuilder builder =
   factory.newDocumentBuilder();
                         FileInputStream fis = File(args[0]));
                         Document doc = builder.parse(fis);

                              // Modification du document
                              Node personne = doc.getChildNodes().item(0);
                              System.out.println(personne.getNodeName());

                              Node nom = personne.getChildNodes().item(1);
                              System.out.println(nom.getNodeName());

                              Text txt = (Text) nom.getChildNodes().item(0);

                            System.out.println(txt.getData());
                    } (Exception e) {
                            e.printStackTrace();
                    }
          }
    }



3.3.4.Modification d’un arbre DOM
Les Nodes fils dans un arbre DOM peuvent être manipulés :
Les méthodes suivantes permettent d’ajouter, éditer, supprimer, déplacer et copier un nœud.

   •    Node removeChild(Node old) throws DOMException
   •    Node insertBefore(Node new, Node ref) throws DOMException
   •    Node appendChild(Node new) throws DOMException
   •    Node replaceChild(Node new, Node old) throws DOMException
   •    Node cloneNode(boolean deep)
      java.io.*;
      org.w3c.dom.*;
      org.apache.xalan.serialize.*;
      javax.xml.parsers.*;

      Dom1 {

         main(String args[]) {
                 {
                       DocumentBuilderFactory factory =
   DocumentBuilderFactory.newInstance();
                       DocumentBuilder builder =
   factory.newDocumentBuilder();
                       Document document = builder.newDocument();

                       // Génération de l'arbre de noeuds
                       Element root    =
   document.createElement("PERSONNE");
                       Element first =
   document.createElement("FIRSTNAME");
                       Element second =
   document.createElement("LASTNAME");
                       Text firstTxt =
   document.createTextNode("Dominique");
                       Text seconTxt = document.createTextNode("Liard");

                             document.appendChild(root);
                             root.appendChild(first);
                             root.appendChild(second);

                             first.appendChild(firstTxt);
                             second.appendChild(seconTxt);

                             root.setAttribute("Machin", "truc");

                             // Instanciation du serializer
                             SerializerToXML ser = SerializerToXML();
                             ser.setOutputStream (System.out);
                             ser.serialize(document);

                   } (Exception e) {
                           e.printStackTrace();
                   }
          }
    }

Nous pouvons aussi modifier la valeur d’un champ grâce à la méthode setData de la classe Text
comme illustré ci-dessous :

    ...
                   Text txt = (Text) nom.getChildNodes().item(0);

    // Instanciation du serializer pour écrire le xml dans un flux
                SerializerToXML ser = SerializerToXML ();
                ser.setOutputStream (System.out);

                   System.out.println("Avant modification:");
                   ser.serialize(dom);

                   txt.setData("Dubois");

                   System.out.println("Apres modification:");
                   ser.serialize(dom);
    ...
3.4.Exemple général
L’exemple qui suit permet de parcourir récursivement l’arbre dom et d’afficher à l’écran le contenu
XML :

     // Import des classes Java

       java.io.*;
       javax.xml.parsers.*;
       org.w3c.dom.*;
       org.apache.xalan.serialize.*;

     Dom1 {
         main(String args []) {
                 {
                       // Instanciation du parseur
                       File fdom = File (args[0]);
                       DocumentBuilderFactory factory =
   DocumentBuilderFactory.newInstance();
                       DocumentBuilder builder =
   factory.newDocumentBuilder();
                       // Analyse du document
                       Document dom = builder.parse(fdom);
                       Node elementRacine = dom.getDocumentElement();

                               // On initialise le parcours avec le numéro 1
                               parcours(elementRacine);

                       // Sérialisation du résultat
                       SerializerToXML ser = SerializerToXML ();     //
   Instanciation du serializer
                       ser.setOutputStream (System.out);
                       //ser.serialize(dom);
                } (Exception e) {
                       System.out.println("Erreur ! " + e.getMessage());
                }
        }

           parcours(Node noeud) {
                  String str = String();

                      (noeud.getNodeType() == Node.TEXT_NODE) {
                             System.out.println(noeud.getNodeValue());
                    }

                    // Parcours récursif si le noeud a des fils
                     (noeud.hasChildNodes()) {
                            // Récupère la liste des fils du
                            // noeud courant (liste de type NodeList)
                            NodeList fils = noeud.getChildNodes();

                               // Parcours de la liste et appel récursif
                                 I = 0; i < fils.getLength(); i++) {
                                        parcours (fils.item(i));
                               }
                    }
          }
   }



4. Conclusion
NA
                                  RMI - Application distribuée


1.Introduction

Remote Method Invocation (RMI) permet la création et l’utilisation d’applications Java distribuées de
manière aisée.

1.1.Applications distribuées
Une application distribuée est une application dont les classes sont réparties sur plusieurs machines
différentes. Dans de telles applications, on peut invoquer des méthodes à distance. Il est alors
possible d'utiliser les méthodes d'un objet qui n'est pas situé sur la machine locale.
Déjà dans le langage C, il était possible de faire de l'invocation à distance en utilisant RPC (Remote
Procedure Calls). RPC étant orienté "structure de données", il ne suit pas le modèle "orienté objet".
RMI (Remote Method Invocation) permet l'utilisation d'objets sur des JVM distantes. Il va plus loin que
RPC puisqu'il permet non seulement l'envoie des données d'un objet, mais aussi de ses méthodes.
Cela se fait en partie grâce à la sérialisation des objets (cf. cours Input/Output).Il est également
possible de charger le byte-code des classes dynamiquement.
Il existe une technologie, non lié à Java, appelée CORBA (Common Object Request Broker
Architecture) qui est un standard d'objet réparti. CORBA a été conçut par l’OMG (Object Management
Group), un consortium regroupant 700 entreprises dont Sun. Le grand intérêt de CORBA est qu'il
fonctionne sous plusieurs langages; mais il est plus lourd à mettre en place.
RMI est un système d’objets distribués plus simple que CORBA. Nous ne traiterons pas ce dernier
dans ce cours. Sachez tout de même qu'il existe l'IIOP (Internet InterObject Protocol) qui permet
l’interopérabilité entre RMI et CORBA.



1.2.RMI
Le but de RMI est de créer un modèle objet distribué Java qui s'intègre naturellement au langage Java
et au modèle objet local. Ce système étend la sécurité et la robustesse de Java au monde applicatif
distribué. RMI permet aux développeurs de créer des applications distribuées de manières simples
puisque la syntaxe et la sémantique reste la même que pour les applications habituelles.
RMI est apparu dès la version 1.1 du JDK et a été étoffé pour la version 2.
Avec RMI, les méthodes de certains objets (appelés objets distants) peuvent être invoquées depuis
des JVM différentes (espaces d’adressages distincts) de celles où se trouvent ces objets, y compris
sur des machines différentes via le réseau. En effet, RMI assure la communication entre le serveur et
le client via TCP/IP et ce de manière transparente pour le développeur.
Il utilise des mécanismes et des protocoles définis et standardisés tels que les sockets et RMP
(Remote Method Protocol). Le développeur n'a donc pas à se soucier des problèmes de niveaux
inférieurs spécifiques aux technologies réseaux.
L'architecture RMI définit la manière dont se comportent les objets, comment et quand des exceptions
peuvent se produire, comment gérer la mémoire et comment les méthodes appelées passent et
reçoivent les paramètres.
RMI préserve la sécurité inhérente à l’environnement Java notamment grâce à la classe
RMISecurityManager et au DGC (Distibuted Gabage Collector).

2.Architecture RMI

2.1.Interfaces
L’interface est le cœur de RMI. L’architecture RMI est basé sur un principe important : la définition du
comportement et l'exécution de ce comportement sont des concepts séparés.
RMI permet au code qui définit le comportement et au code qui implémente ce comportement de
rester séparé et de s’exécuter sur des JVM différentes. La définition d’un service distant est codée en
utilisant une interface Java. L’implémentation de ce service distant est codée dans une classe.
Par conséquent, la compréhension de RMI réside dans le fait que les interfaces définissent le
comportement et les classes définissent l'implémentation.
RMI supporte deux types de classe qui implémente la même interface. La première est
l'implémentation du comportement et s'exécute du côté serveur. La deuxième agit comme un proxy
pour le service distant et s'exécute sur le client.
Un programme client crée des appels de méthodes sur le proxy, RMI envoie la requête à la JVM
distante et la transfère à l'implémentation. Toutes les valeurs de retours fournies par l'implémentation
sont retournées au proxy puis au programme client.
2.2.Les différentes couches de RMI
RMI est essentiellement construit sur une abstraction en trois couches.

2.2.1.Stubs et Skeletons
La première couche est celle des Stubs et Skeletons. Cette couche intercepte les appels de méthodes
lancés par le client à l'interface de référence et redirige ces appels à un service RMI distant. Cette
structure évite au programmeur de devoir communiquer explicitement avec l’objet distant.

2.2.2.Remote Reference Layer
La couche suivante est la couche de référence distante. Cette couche comprend comment interpréter
et gérer les références faites du client vers les objets du service distant. Elle permet l’obtention d’une
référence d’objet distant à partir de la référence locale (le stub).
Dans le JDK 1.1, cette couche connecte les clients aux objets du service distant qui sont exécutés et
exportés sur un serveur. Cette connexion est unicast (point à point).
Dans le JDK 2, cette couche a été améliorée pour soutenir l'activation des objets du service distant en
sommeil via la classe RemoteObjectActivation. Elle assure une référence persistante vers des
objets distants (reconnexion éventuelle).
Ce service est assuré par le lancement du programme rmiregistery.

2.2.3.Couche Transport
La couche transport est basée sur les connexions TCP/IP entre les machines. Elle fournie la
connectivité de base entre les 2 JVM.
De plus, cette couche fournie des stratégies pour passer les firewalls. Elle suit les connexions en
cours. Elle construit une table des objets distants disponibles. Elle écoute et répond aux invocations.
Cette couche utilise les classes Socket et SocketServer. Elle utilise aussi un protocole propriétaire
R.M.P. (Remote Method Protocol).

En utilisant une architecture en couche, chaque couche peut être augmentée ou remplacée sans
affecter le reste du système. Voici un schéma montrant le transfert d’informations entre chaque
couche :




2.3.Utilisation de RMI
Nous allons voir les principales étapes nécessaires à la mise en place d’un service de type RMI.

2.3.1.L’interface
La première étape consiste à créer une interface distante qui décrit les méthodes que le client pourra
invoquer à distance.
Pour que ses méthodes soient accessibles par le client, cette interface doit hériter de l'interface
Remote. Toutes les méthodes utilisables à distance doivent pouvoir lever les exceptions de type
RemoteException qui sont spécifiques à l'appel distant.
Cette interface devra être placée sur les deux machines (serveur et client).
2.3.2.L’implémentation
Il faut maintenant implémenter cette interface distante dans une classe. Par convention, le nom de
cette classe aura pour suffixe Impl.
Notre classe doit hériter de la classe java.rmi.server.RemoteObject ou de l’une de ses sous-classes.
La plus facile d’utilisation étant java.rmi.server.UncicastRemoteObject.
C’est dans cette classe que nous allons définir le corps des méthodes distantes que pourront utiliser
nos clients. Evidement, il est possible d’ajouter d’autres méthodes mais les clients ne pourront y
accéder et donc ne pourront les utiliser.

2.3.3.Stubs et Skeletons
Lorsque notre client fera appel à une méthode distante, cet appel sera transféré au stub. Le stub est
donc un relais du côté client. Il devra donc être placé sur la machine cliente.
C’est le représentant local de l’objet distant. Il « marshalise » (emballe) les arguments de la méthode
distante et les envoie dans un flux de données. D’autre part, il « démarshalise » (déballe) la valeur ou
l’objet de retour de la méthode distante. Il communique avec l’objet distant par l’intermédiaire du
skeleton.
Le skeleton est lui aussi un relais mais du côté serveur. Il devra être placé sur la machine servant de
serveur. Il « démarshalise » les paramètres de la méthode distante, les transmet à l’objet local et
« marshalise » les valeurs de retours à renvoyer au client.
Les stubs et les skeletons sont donc des intermédiaires entre le client et le serveur qui gèrent le
transfert distant des données.
On utilise le compilateur rmic pour la génération des stubs et des skeletons. C’est un utilitaire fournie
avec le JDK
Depuis la version 2 de Java, le skeleton n’existe plus. Seul le stub est nécessaire du côté client mais
aussi du côté serveur.




Invocation d’une méthode distante




Renvoie de la valeur de retour

2.3.4.Le registre RMI
Les clients trouvent les services distants en utilisant un service d'annuaire activé sur un hôte connu
avec un numéro de port connu. RMI peut utiliser plusieurs services d'annuaire, y compris Java
Naming and Directory Interface (JNDI).
Il inclus lui-même un service simple appelé Registry (rmiregistry).
Le registre est exécuté sur chaque machine qui héberge des objets distants (les serveurs) et accepte
les requêtes pour ces services, par défaut sur le port 1099.
Un serveur crée un service distant en créant d'abord un objet local qui implémente ce service.
Ensuite, il exporte cet objet vers RMI. Quand l'objet est exporté, RMI crée un service d'écoute qui
attend qu'un client se connecte et envoie des requêtes au service. Après l'exportation, le serveur
enregistre l'objet dans le registre de RMI sous un nom public qui devient accessible de l’extérieur.
Le client peut alors consulter le registre distant pour obtenir des références à des objets distants.

2.3.5.Le serveur
Notre serveur doit enregistrer auprès du registre RMI l’objet local dont les méthodes seront
disponibles à distance. Cela se fait grâce à l’utilisation de la méthode statique bind() de la classe
Naming. Cette méthode permet d’associer (enregistrer) l’objet local avec un synonyme dans le
registre. L’objet devient alors disponible par le client.
     ObjetDistantImpl od = ObjetDistantImpl();
     Naming.bind("serveur", od);


2.3.6.Le client
Le client peut obtenir une référence à un objet distant par l’utilisation de la méthode statique lookup()
de la classe Naming. Il peut ensuite invoquer des méthodes distantes sur cet objet. La méthode
lookup() sert au client pour interroger un registre et récupérer un objet distant. Elle prend comme
paramètre une URL qui spécifie le nom d'hôte du serveur et le nom du service désiré. Elle retourne
une référence à l’objet distant. La valeur retournée est du type Remote. Il est donc nécessaire de
caster cet objet en l’interface distante implémenter par l’objet distant. En effet, le client travail avec
l’interface distante et non avec l’objet distant lui-même.
      ObjetDistant od = (ObjetDistant)
     Naming.lookup("//172.16.X.X/serveur");


2.3.7.Lancement
Il est maintenant possible d’exécuter l’application. Cela va nécessiter l’utilisation de trois consoles.
La première sera utiliser pour activer le registre. Pour cela, vous devez exécuter l’utilitaire rmiregistry.
Dans une deuxième console, exécuter le serveur. Celui-ci va charger l’implémentation en mémoire,
enregistrer cette référence dans le registre et attendre une connexion cliente.
Vous pouvez enfin exécuter le client dans une troisième console.

Même si vous exécutez le client et le serveur sur la même machine, RMI utilisera la pile réseau et
TCP/IP pour communiquer entre les JVM.

2.3.8.Répartitions des classes
Dans un cas concret d’application client/serveur la répartition doit se faire de la manière suivante :

    •   Le client doit avoir accès aux interfaces distantes et aux Stubs de leurs implémentations.
    •   Le serveur doit avoir accès aux interfaces distantes, aux implémentations et aux Stubs.




2.3.9.Schéma récapitulatif

    1. On exécute rmiregistry
    2. On exécute le Serveur
    3. On crée l’objet distant
    4. On l’enregistre dans l’annuaire avec la méthode Naming.bind()
    5. On exécute le Client
    6. On recherche un objet distant avec la méthode Naming.lookup()
    7. L’annuaire renvoie une référence de l’objet distant
    8. On invoque une méthode distante
    9. On reçoit une valeur de retour
3.Exemple : Serveur de date

Nous allons voir ici un exemple concret en suivant les étapes décrites dans le chapitre précédant. Le
but de cette application est d’utiliser l’appel de méthodes distantes avec un simple serveur de date.
Celui-ci permet aux clients de déterminer la date et l’heure du serveur.

3.1.L’interface DateServer
La seule méthode distante nécessaire à notre application est une méthode qui renvoie un objet de
type java.util.Date. On construit donc notre interface de la manière suivante :
       java.rmi.Remote;
       java.rmi.RemoteException;
       java.util.Date;
     Remote {
          Date getDate() RemoteException;
}

Les points importants à retenir sont que notre interface étend de l’interface Remote. Cela permet de
conférer un comportement distant aux classes qui l’implémenteront. De plus, notre méthode getDate()
peut lever des exceptions de type RemoteException. Si nous avions déclaré d’autres méthodes, elles
auraient aussi dû inclure cette clause pour être accessibles par le client. La méthode getDate()
renvoie un objet de type java.util.Date qui implémente l’interface java.io.Serializable. Tous les objets
de retours d’une méthode distante doivent implémenter cette interface pour transiter sur le réseau.
En effet, une méthode distante peut renvoyer trois types de valeur :

    •   Un type primitif (boolean, int, float, …)
    •   Un objet implémentant l’interface Serializable
    •   Un objet distant

3.2.L’implémentation DateServerImpl
La classe DateServerImpl implémente notre interface DateServer que nous avons déclarée
précédemment. Elle étend la classe UnicastRemoteObject qui est une sous-classe de la classe
RemoteObject et qui permet de faire la liaison avec le système RMI. Il faut fournir un constructeur en
déclarant qu’il peut lever des RemoteException.

      java.rmi.RemoteException;
      java.rmi.server.UnicastRemoteObject;
      java.util.Date;
     DateServer {
          RemoteException{}
            RemoteException {
                    Date();
}
}

C’est dans cette classe que nous définissons la méthode getDate() qui ne fait que retourner un objet
de type java.util.Date. Cela est possible car l’objet Date est sérialisable.



3.3.MyServer
La classe Server va créer un objet de type DateServerImpl. Ensuite cet objet sera enregistrer auprès
du Registre sous le nom MyServer, via la méthode rebind() de la classe Naming.
      java.rmi.Naming;
      java.rmi.RemoteException;
     MyServer {
           main(String[] args) {
                      {
                              DateServerImpl ds = DateServerImpl();
                              Naming.rebind("rmi://localhost/MyServer", ds);
                     } (RemoteException re) {
                              re.printStackTrace();
                     } (Exception e) {
                              e.printStackTrace();
}
}
}
Il est aussi possible d’utiliser la méthode bind(), mais rebind() est plus efficace car elle peut
réenregistrer l’objet auprès de l’annuaire si le nom existe déjà.


3.4.MyClient
Le client va afficher sur la console la date que lui a renvoyé le serveur. Pour cela on crée un objet de
type DateServer (notre interface) en appelant l’objet du registre MyServer. Puis on appelle la
méthode distante getDate().
       java.rmi.Naming;
       java.rmi.RemoteException;
     MyClient {
             RemoteException {
                        MyClient(args[0]);
           }
             MyClient(String host) {
                        {
                       DateServer server = (DateServer) Naming.lookup("rmi://" +
   host + "/MyServer");

                 System.out.println("Date du serveur : " +
    server.getDate());
                 }
                   (Exception e) {
                          e.printStackTrace();
                 }
         }
    }



3.5.Compilation
Pour utiliser javac et rmic, vérifier que le chemin des exécutables se trouve bien dans la variable
d’environnement PATH.
Pour cela, faites un clic-droit sur Poste de travail puis Propriétés. Allez sur l’onglet Avancé puis
cliquez sur Variables d’environnement… Editer la variable Path et ajouter C:\…\j2sdk1.4.x\bin si
besoin.
3.5.1.javac
set CLASSPATH=C:\dossier_contenant_vos_classes
     // où cd dossier_contenant_vos_classes
javac DateServer.java
javac DateServerImpl.java
     javac MyServer.java
javac MyClient.java

3.5.2.rmic
set CLASSPATH=C:\dossier_contenant_vos_classes
     // où cd dossier_contenant_vos_classes
rmic DateServerImpl
Vous pouvez remarquer qu’il ne faut pas mettre l’extension .class
De plus, vous devez spécifier le nom complet des classes (avec les noms de packages).
Deux nouvelles classes viennent d’être créées : DateServerImpl_Stub et DateServerImpl_Skel. Ce
sont les stubs et skeletons de notre implémentation.

3.6.Lancement
set CLASSPATH= //pour annuler la valeur du CLASSPATH
start rmiregistry
      start java MyServer
      java MyClient localhost

Il est nécessaire d’annuler la valeur du CLASSPATH lors de chargement dynamique. Ce point sera
abordé dans un prochain chapitre.

3.7.Répartitions des classes
Nous allons maintenant voir comment répartir les classes entre le serveur et le client.
Sur le poste client disposez les classes :

    •   MyClient.class
    •   DateServer.class
    •   DateServerImpl_Stub.class

Sur le poste serveur :

    •   MyServer.class
    •   DateServer.class
    •   DateServerImpl.class
    •   DateServerImpl_Stub.class

4.Le package java.rmi

Nous allons maintenant voir plus en détail les différents outils fournis par le package java.rmi.

4.1.Interface Remote

L’interface Remote sert à identifier les interfaces dont les méthodes pourront être invoquées depuis
une JVM distante. Chaque objet distant doit implémenter cette interface directement ou indirectement.
Seules les méthodes spécifiées dans une interface distante sont disponibles à distances.
Les classes d’implémentation peuvent implémenter plusieurs interfaces distantes et peuvent hériter
d’autres classes d’implémentation distantes. RMI fournit quelques classes dont les objets distants
peuvent hériter et qui facilitent la création d’objets distants (ex : la classe UnicastRemoteObject).
4.2.Classe UnicastRemoteObject
La classe UnicastRemoteObject définit un objet non répliqué dont les références sont valides
seulement si le processus serveur est actif. Elle fournit un support pour les références à des objets
actifs utilisant des flux TCP/IP.
Les objets qui exigent un comportement distant doivent étendre de RemoteObject, typiquement via
UnicastRemoteObject. Dans le cas contraire, ils doivent alors assumer eux-mêmes la responsabilité
des méthodes hashCode(), equals() et toString(). Il est donc plus simple d’utiliser la classe
UnicastRemoteObject, car vous n’avez pas à définir vous même ces méthodes.
Le constructeur par défaut utilise le port TCP 1099. Il est toutefois possible d’utiliser un autre
constructeur qui prend en paramètre un int correspondant au numéro de port à utiliser pour le
transfert des paquets TCP.

4.3.Classe Naming
La classe Naming fournie des méthodes pour enregistrer et obtenir des références à des objets
distants dans un registre distant (comme rmiregistry). Chaque méthode de la classe Naming prend
comme argument une String de type URL comprenant les paramètres suivants :

    •    hôte correspond au nom (ou adresse IP) de la machine où est situé le registre
    •    port est le numéro de port sur lequel le registre écoute
    •    nom correspond au nom sous lequel l’objet distant est inscrit dans le registre

Si hôte est omis, il s’agira de la machine locale (localhost). Il en va de même pour le port, s’il est omis,
ce sera le port 1099 qui sera utilisé par défaut.
Il existe plusieurs façons d’écrire l’URL :

    •    rmi://host :port/nom
    •    //host :port/nom
    •    //host/nom (forme la plus courante)
    •    //host (spécifique à la méthode list())
    •    nom (dans ce cas on sait que le registre est exécuté en local sur le port 1099)


Nous allons voir plus en détail les méthodes fournies par la classe Naming. Toutes ces méthodes
sont statiques. Elles peuvent lever des exceptions de type RemoteException s’il y a un problème au
niveau de la communication distante. De plus, elles peuvent aussi lever des exceptions de type
MalformedURLException dans le cas où l’URL n’aurait pas la bonne syntaxe.

4.3.1.bind()
        AlreadyBoundException, MalformedURLException, RemoteException
Cette méthode permet d’enregistrer un objet dans le registre en lui associant un nom. Elle prend en
argument une String désignant l’URL de l’objet et un objet de type Remote. S’il existe déjà un lien
pour cette URL dans le registre, une exception de type AlreadyBoundException sera levée.

4.3.2.rebind()
        RemoteException, MalformedURLException
Son utilisation est identique à la méthode précédente mais elle permet d’associer un nouvel objet
distant au nom spécifié en paramètre. Si ce nom était déjà associé à un objet, il sera remplacé. On
préférera utiliser cette méthode plutôt que la méthode bind() pour éviter les exceptions de type
AlreadyBoundException.

4.3.3.unbind()
        RemoteException, NotBoundException, MalformedURLException
Détruit le lien entre le nom spécifié en paramètre et l’objet distant dans le registre RMI. Si le lien est
inexistant, une exception de type NotBoundException sera levée.

4.3.4.list()
        RemoteException, MalformedURLException
Cette méthode retourne un tableau de String correspondant à la liste des noms de tous les liens vers
des objets distants dans le registre dont l’URL est spécifiée en paramètre. L’URL spécifié est de la
forme //hôte:port.

4.3.5.lookup()
        NotBoundException, MalformedURLException, RemoteException
La méthode lookup() est utilisée par les clients pour obtenir une référence à un objet distant. L’objet
retourné est de type Remote. Cette méthode prend comme paramètre une String correspondant à
l’URL de l’objet distant. Si le lien dans le registre n’existe pas, une exception de type
NotBoundException sera levée.

4.4.Exceptions RemoteException
RemoteException est la super-classe commune à de nombreuses exceptions, liées à la
communication, qui peuvent survenir durant l’appel d’une méthode distante. Chaque méthode d’une
interface distante doit pouvoir lever des exceptions de type RemoteException. Il faut donc la déclaré
dans la clause throws de la déclaration de votre méthode.

4.5.Utilisation de rmic
rmic est le compilateur RMI, fournie en standard avec le JDK. Il s’utilise de manière similaire à javac
à la différence près qu’il ne faut pas spécifier l’extension des fichiers. C’est lui qui permet de générer
les stubs et les skeletons des classes d’implémentations de service distant. Pour l’utiliser vous devez
vous positionner dans le dossier contenant vos fichiers. Son utilisation est la suivante :
      rmic <option> <class names>
où class names correspond aux noms complets des classes (sans l’extension). Si vos classes sont
déclarées dans un package, il faut le spécifier :
rmic <option> nom_package.nom_classe

Voici les options disponibles :

    •    -keep Ne supprime pas les fichiers sources intermédiaires générés
    •    -keepgenerated (équivalent de "-keep")
    •    -g Génère des informations de debugage
    •    -depend Recompile récursivement les fichiers obsolètes
    •    -nowarn Ne génère pas d’avertissements
    •    -verbose Affiche les messages sur ce que fait le compilateur
    •    -classpath <path> Spécifie où trouver les fichiers classes et source d’entrée
    •    -d <directory> Spécifie où placer les fichiers classes générés
    •    -J<runtime flag> Passe des arguments à l’interpréteur java

5.Chargement dynamique de classe

Le framework RMI inclus aussi la distribution automatique du byte-code des stubs. Dans ce cas,
l'application (au niveau client ou au niveau serveur) ne nécessite que ses propres classes et chaque
interfaces distantes qu'il utilise. Les autres classes nécessaires faisant partie d'un appel à une
méthode distante seront automatiquement téléchargées à partir d'un serveur web (ftp ou http). Cette
opération s'effectue à travers de la classe RMIClassLoader. Si un client ou un serveur exécute un
système RMI et remarque qu'il doit charger une classe située à distance, il fera automatiquement
appel à RMIClassLoader pour effectuer ce travail. Le chargement des classes par RMI est contrôlé
par le paramètre java.rmi.server.codebase de l’interpréteur Java.
Voici la syntaxe pour définir les paramètres à l’exécution de l’interpréteur :
      java [ -D<PropertyName>=<PropertyValue> ] <ClassFile>

La propriété java.rmi.server.codebase sert à indiquer l'URL où l'on peut trouver les fichiers à
télécharger. Si un programme s'exécutant sur une JVM envoie une référence d’objet à une autre JVM
(comme valeur de retour d'une méthode), cette autre JVM aura besoin de charger le fichier classe de
cet objet. Lorsque RMI envoie l'objet par sérialisation, il ajoute l'URL à côté de l'objet. RMI n'envoie
donc pas les fichiers classes avec les objets sérialisés.
Si la JVM distante a besoin de charger un fichier .class pour un objet, il cherche l'URL et contacte le
serveur qu'elle désigne pour le fichier. Quand la propriété java.rmi.server.useCodebaseOnly est
active, la JVM charge les classes, soit à un endroit spécifié dans le CLASSPATH, soit à l'URL spécifié
par cette propriété.
En utilisant plusieurs combinaisons de propriétés système disponibles, vous pouvez créer différentes
configurations système RMI.
Dans l’exemple du serveur de date, on peut imaginer que vous ayez placé le fichier
DateServerImpl_Stub.class correspondant au stub sur un serveur web. Si vos programmes (client
ou serveur) ne disposent pas de ces classes dans leur CLASSPATH, ils iront les chercher à l’URL
que vous leur spécifier. Pour cela, il faut taper une des commandes suivantes :

    java –Djava.rmi.server.codebase=http://172.16.X.X/path_des_classes/
   MyServer
Si les stubs sont sur un serveur http.
      java –Djava.rmi.server.codebase=ftp://172.16.X.X/path/ MyServer
Si les stubs sont sur un serveur ftp
      java –Djava.rmi.server.codebase=http://172.16.X.X:2001/ MyServer

Si le serveur Web écoute sur le port TCP 2001
Dans ces exemples, on exécute le serveur mais la syntaxe est identique pour exécuter le client.
Le dernier slash (/) de l’Url est obligatoire.
L’intérêt d’utiliser un serveur web est de pouvoir modifier vos objets distants sans changer quoique ce
soit sur les clients. Généralement le service web est lancé sur la même machine que le serveur RMI.

6.Stratégie de sécurité

En Java, il est possible de mettre en place une sécurité système propre à vos applications. Nous ne
traiterons pas de sujet en détail car il sera traité dans un autre cours. Nous n’étudierons donc ici que
la sécurité à mettre en place pour le fonctionnement de RMI.
Sur certains systèmes le chargement dynamique de classe peut être refuser pour des raisons de
sécurité. Dans ce cas, une exception du type AccessControlException sera levée lors de l’utilisation
des méthodes de la classe Naming.
Pour pallier à ce problème, il est possible de mettre en place une stratégie de sécurité autorisant ce
phénomène. Cela est possible grâce à la mise en place du gestionnaire de sécurité spécifique à RMI
via l’objet RMISecurityManager. Il vérifie la définition des classes et autorise seulement le passage
des arguments et des valeurs de retour des méthodes distantes.
Voici comment utiliser ce gestionnaire de sécurité :
      )
      System.setSecurityManager(new RMISecurityManager()) ;
On vérifie que le système n’a pas de gestionnaire de sécurité (pour ne pas écraser celui existant), puis
on lui dit d’utiliser un objet de RMISecurityManager.
Ces lignes de code sont à utiliser avant de faire appel au registre par l’utilisation des méthodes de la
classe Naming.
Vous pouvez utiliser ce gestionnaire de sécurité aussi bien du côté client que du côté serveur. Tout
dépend de la nécessité de charger des classes dynamiquement.
Il vous reste maintenant à définir les différentes permissions. La solution consiste à placer vos
différentes permissions dans un fichier texte spécifique. Ce fichier sera lut lors de la création du
RMISecurityManager. Par convention, il porte l’extension .policy.
Voici un exemple de contenu pour ce fichier :
grant {
permission java.security.Allpermission
};
Dans cet exemple, tout est permis !
C’est la permission la plus simple à configurer mais aussi la plus dangereuse.
grant {
permission java.net.SocketPermission «172.16.23.32:1099», «connect»;
}
On accepte les connexions sur le port 1099 de la machine dont l’adresse IP est 172.16.23.32
Pour lire ce fichier, vous devez dire à l’interpréteur où il peut le trouver. Cela se fait en utilisant le
paramètre java.security.policy. Ce paramètre sera combiner avec java.rmi.server.codebase.
      C:\...\> java –Djava.security.policy=path\fichier.policy
      –Djava.rmi.server.codebase=http://serveur:port/path/ ClassAExecuter

Votre programme sait maintenant où trouver le byte-code des stubs et s’il a le droit de le récupérer.

6.Distributed Garbage Collector

NA
                         JNDI - Java Naming and Directory Interface
1.Nommage et Annuaire

1.1.Concept de nommage
Un service fondamental dans n’importe quel système informatique est le service de nommage : le
moyen par lequel des noms sont associés aux objets et les objets sont trouvés grâce à leurs noms. En
utilisant presque n’importe quel programme ou système, vous attribuez toujours un nom à un objet.
Par exemple, quand vous utilisez un système de messagerie électronique, vous devez fournir le nom
du destinataire à qui vous voulez envoyez l’email. Pour accéder à un dossier, vous devez fournir son
nom. Un service de nommage vous permet de rechercher un objet en fournissant son nom.
La fonction primaire d’un service de nommage est de faire la jonction entre des noms significatifs et
des objets, tels que des adresses, des identifiants ou des objets utilisés par des programmes
informatiques. Par exemple, le système de nom de domaine d’Internet (DNS) fait la jonction entre les
noms de machine et leur adresse IP. Un système de fichier fait le lien entre un nom de fichier et le
fichier lui-même.

1.1.1.Noms
Pour rechercher un objet dans un système de nommage, vous lui fournissez le nom de cet objet. Le
système de nommage détermine la syntaxe que le nom doit suivre. Cette syntaxe s’appelle parfois la
convention de nommage du système de nommage.
Par exemple, la convention de nommage du système de fichier UNIX™ est qu’un fichier est appelé
depuis son chemin relatif à la racine du système de fichier, avec chaque élément du chemin séparé de
gauche à droite par le caractère slash (« / ».) Le chemin UNIX /usr/hello, par exemple, fait référence à
un fichier hello dans le répertoire usr, lui-même situé à la racine du système de fichier.
La convention de nommage DNS requiert que les éléments du nom DNS soient ordonnés de droite à
gauche et séparés par des points (« . »). Ainsi le nom DNS finance.societe.com appelle une entrée
DNS avec le nom finance relativement à l’entrée societe.com. L’entrée DNS societe.com, à son tour,
appelle une entrée avec le nom societe dans l’entrée com.
La convention de nommage Lightweight Directory Access Protocol (LDAP) ordonne les composants
de droite à gauche, séparé par des virgules (« , »). Ainsi le nom LDAP cn=Renaud Roustang, o=labo-
sun, c=fr appelle une entrée LDAP cn= Renaud Roustang relatif à l’entrée o=labo-sun, qui à son tour
est relative à c=fr. LDAP spécifie la règle supplémentaire suivante : chaque composant du nom doit
être une paire clé/valeur où la clé et la valeur sont séparés par le caractère égal (« = »).

1.1.2.Liaisons (bindings)
L’association d’un nom avec objet est appelée liaison (binding). Par exemple, un nom de fichier est lié
(bind) à un fichier.
DNS contient les liaisons qui font la jonction entre les noms de machine avec les adresses IP. Un nom
LDAP est lié à une entrée LDAP.

1.1.3.Références et adresses
Selon le service de nommage, des objets ne peuvent être stockés directement ; c'est-à-dire qu’une
copie de l’objet ne peut être placé à l’intérieur du service de nommage. Au lieu de cela, ils peuvent
être stocké par référence ; cela signifie qu’un pointeur ou une référence à l’objet est placée à l’intérieur
du service. Une référence est l’information précisant comment accéder à un objet. Typiquement c’est
une représentation beaucoup plus compacte pouvant être utilisée pour communiquer avec l’objet,
l’objet lui-même pouvant contenir plus d’information sur son état. En utilisant des références, vous
pouvez contacter l’objet et obtenir plus d’information sur l’objet lui-même.
Par exemple, un objet avion pourrait contenir une liste des passagers, l'équipage de l'avion, son plan
de vol, le niveau de carburant, son numéro de vol et horaire de départ. En revanche, une référence
d'objet avion pourrait contenir seulement son numéro de vol et horaire de départ. La référence est une
représentation beaucoup plus compacte d'informations sur l'objet avion et peut être employée pour
obtenir des informations additionnelles. Un objet fichier, par exemple, est consulté en utilisant une
référence de fichier, également appelée pointeur sur fichier. Un objet imprimante, par exemple,
pourrait contenir l'état de l'imprimante, tel que sa file d'attente courante et la quantité de papier dans le
bac. Une référence d'objet imprimante, d'autre part, pourrait contenir seulement l'information sur la
façon dont atteindre l'imprimante, tel que son nom de serveur d'impression et protocole d’impression.
Bien qu'en général une référence puisse contenir n'importe quelle information de manière arbitraire, il
est utile de se rapporter à son contenu en tant qu’adresse (ou point final de communication):
informations spécifiques sur la façon d’accéder à l'objet.
Pour simplifier, nous utiliserons le terme « objet » pour nous parler d’objets et de références d'objet
quand une distinction entre les deux n'est pas exigée.
1.1.4.Contexte
Un contexte est un ensemble de liens nom – objet. Chaque contexte a une convention de nommage
associée. Un contexte fournit un moyen de consultation (résolution) qui renvoie l'objet et peut fournir
des opérations telles que la liaison de noms (binding), la suppression de cette liaison (unbinding), et
énumérer les noms liés. Un nom dans un objet de contexte peut être lié à un autre objet de contexte
(appelé un sous-contexte) qui a la même convention de nommage.
Par exemple, un répertoire, comme /usr, dans le système de fichiers Unix est un contexte. Un nom de
répertoire relatif à ce répertoire est appelé un sous-contexte (on dit plus couramment un sous-
répertoire). Le répertoire /usr/bin, le répertoire bin est un sous-contexte d'usr. Dans un autre exemple,
un domaine DNS, tel que com, est un contexte. Un domaine DNS appelé relativement à un autre
domaine DNS est un sous-contexte. Par exemple, dans le domaine DNS labo-sun.com, le domaine
DNS labo-sun est un sous-contexte de com.
Enfin, une entrée LDAP, telle que c=us, est un contexte. Une entrée de LDAP relative à une autre
entrée LDAP est un sous-contexte.
Par exemple, dans l’entrée « o=labo-sun, c=fr », l'entrée o=labo-sun est un sous-contexte de c=fr.

1.1.5.Système de nommage et espace de nom
Un système de nommage est un ensemble relié de contextes du même type (ils ont la même
convention de nommage) et fournit un ensemble commun d'opérations.
Par exemple, un système qui implémente DNS est un système de nommage. Un système qui
communique en utilisant LDAP est un système de nommage.
Un système de nommage fournit un service de nommage à ses clients pour effectuer des opérations
liées aux noms. Un service de nommage est consulté par sa propre interface. Par exemple, DNS offre
un service de nommage qui relie des noms de machine aux adresses IP. LDAP offre un service de
nommage relie des noms LDAP aux entrées LDAP. Un système de fichiers offre un service de
nommage qui relie des noms de fichier aux fichiers et aux dossiers.
Un espace de nom (« namespace ») est l'ensemble des noms dans un système de nommage. Par
exemple, le système de fichiers d'Unix a un espace de nom composer de tous les noms des dossiers
et des fichiers dans ce système de fichiers. L’espace de nom DNS contient les noms des domaines et
entrées DNS. L’espace de nom LDAP contient les noms des entrées LDAP.


1.2.Concept d’annuaire
Beaucoup de services de nommage sont étendus avec un service d'annuaire. Un service d'annuaire
associe des noms aux objets et permet également à de tels objets d'avoir des attributs. Ainsi, vous
pouvez non seulement rechercher un objet par son nom mais également obtenir les attributs de l'objet
ou rechercher l'objet via ses attributs.
Un objet d'annuaire représente un objet dans un environnement informatique. Un objet d'annuaire
peut être employé, par exemple, pour représenter une imprimante, une personne, un ordinateur, ou un
réseau. Un objet d'annuaire contient les attributs qui décrivent l'objet qu'il représente.

1.2.1.Attributs
Un objet d'annuaire peut avoir des attributs. Par exemple, une imprimante peut être représenté par un
objet d'annuaire qui a comme attributs sa vitesse, sa résolution. Un utilisateur pourrait être représenté
par un objet d'annuaire qui a comme attributs l'adresse email de l'utilisateur, ses divers numéros de
téléphone, son adresse et des informations sur son compte.
Un attribut a un identifiant et un ensemble de valeur. Un identifiant d'attribut est une marque qui
identifie un attribut indépendamment de ses valeurs. Par exemple, deux comptes différents pourraient
avoir un attribut "email"; "email" est l’identifiant de l'attribut. La valeur d'un attribut est le contenu de cet
attribut. L’adresse email, par exemple, pourrait avoir comme identifiant "email" et comme valeur
"renaud.roustang@supinfo.com".

1.2.2.Annuaires et Services d’annuaires
Un annuaire est un ensemble connecté d'objets d'annuaire. Un service d'annuaire est un service qui
fournit des opérations pour créer, ajouter, enlever, et modifier les attributs liés aux objets dans un
annuaire. Le service est consulté par sa propre interface.
Beaucoup d'exemples de services d'annuaire sont possibles. Le service d'annuaire de Novell (NDS)
est un service d'annuaire qui fournit des informations de nombreux services de gestion de réseau, tels
que les services de dossier et d'impression. Le service d'information de réseau (NIS) est un service
d'annuaire disponible sur Solaris pour stocker des informations sur les relations entre les systèmes
connectés, tels que des machines, des réseaux, des imprimantes, et des utilisateurs. Le serveur
d'annuaire SunONE Directory Server est un service d'annuaire basé sur le standard LDAP.
1.2.3.Recherches et Filtres de recherche
Vous pouvez rechercher un objet d'un annuaire en fournissant son nom au service d'annuaire.
Alternativement, beaucoup d'annuaires, comme ceux basés sur LDAP, supporte la notion des
recherches. Quand vous effectuez une recherche, vous pouvez fournir une requête se composant
d'une expression logique dans laquelle vous indiquez les attributs que le ou les objets doivent avoir.
La requête s'appelle un filtre de recherche. Ce modèle de recherche s'appelle parfois une recherche
inversée (reverse lookup) ou recherche basée sur le contenu (content-based searching). Le service
d'annuaire recherche et renvoie les objets qui satisfont le filtre de recherche.
Par exemple, vous pouvez interroger le service d'annuaire pour trouver tous les utilisateurs qui ont
comme attribut « âge » supérieur 40. De même, vous pouvez l’interroger pour trouver toutes les
machines dont l’adresse IP commence par 192.113.50.

1.2.4.Combiné service de nommage et d’annuaire
Les annuaires arrangent souvent leurs objets de manière hiérarchique. Par exemple, LDAP arrange
tous les objets d'annuaire dans un arbre, appelé Directory Information Tree (DIT). Dans le DIT, un
objet organisation, par exemple, pourrait contenir des objets groupe qui pourraient à leur tour contenir
des objets personne. Quand des objets d'annuaire sont arrangés de cette façon, ils jouent le rôle de
contextes de nommage en plus de celui de conteneurs d'attributs.


1.3.Application Java utilisant des annuaires
Le service d'annuaire est un composant essentiel du réseau informatique. En employant un service
d'annuaire, vous pouvez simplifier les applications et leur administration en centralisant le stockage
d'information partagée. À mesure que l'utilisation du langage Java pour développer des applications
dans un environnement réseau augmente, la faculté d'accéder à des services d'annuaire devient
essentielle.

1.3.1.Usage traditionnel des annuaires
Une application directory-enabled est une application qui utilise un service de nommage ou
d'annuaire. Les applications Java directory-enabled et les applets, comme n'importe quel autre
programme fonctionnant en réseau, peuvent employer l'annuaire de manière traditionnelle, c.-à-d.,
stocker et rechercher des attributs des objets d'annuaire. Un programme Java de type client mail, par
exemple, peut employer un annuaire comme carnet d'adresses pour rechercher les adresses mails
des destinataires. Un programme Java de transferts de mail peut l'employer pour rechercher des
informations sur le routage du courrier. Un programme en Java peut également l'employer pour
rechercher les préférences d'un utilisateur.
Les applications peuvent partager l'infrastructure commune fournie par l'annuaire. Ce partage rend les
applications qui sont déployées à travers le système, et même le réseau, plus cohérente et plus
gérable. Par exemple, la configuration d’une imprimante et l'information de routage du courrier
peuvent être stockées dans l'annuaire de sorte qu'il puisse être répliqué et distribué à toutes les
applications et services utilisant les imprimantes et les mails.

1.3.2.L’annuaire pour stocker des objets
En plus d'employer l'annuaire de manière traditionnelle, les applications Java peuvent également
l'utiliser comme lieu de stockage d’objets Java, pour stocker et rechercher des objets Java. Par
exemple, un programme d'impression Java devrait pouvoir rechercher une imprimante via l'annuaire
et envoyer un flux de données à l'imprimante pour l'impression.


                                          2.Aperçu de JNDI

   Java Naming and Directory Interface (JNDI) est une API (Application Programming Interface) qui
fournie les fonctionnalités de nommage et d'annuaire aux applications écrites en Java. Elle est définie
 pour être indépendante du service d'annuaire. Ainsi une variété d'annuaires -- nouveaux, émergeant,
                    et déjà déployé -- peuvent être consulté de manière commune.

                                          Architecture
     L'architecture de JNDI se compose d’une API et d’un Service Provider Interface (SPI). Les
  applications de Java emploient l’API JNDI pour accéder à une variété de services de nommage et
    d'annuaire. Le SPI permet de brancher, de manière transparente, une variété de services de
  nommage et d'annuaire ; permettant ainsi à l'application Java d’accéder à ces services en utilisant
                                             l’API JNDI.
                                             figure 1.2.1

Packages
JNDI est inclus dans le JDK Standard (Java 2 SDK) depuis la version 1.3. Pour utiliser JNDI, vous
devez possédez les classes JNDI et au moins un SPI (JNDI Provider). La version 1.3 du JDK inclus 3
SPI pour les services de nommage/annuaire suivant :
Lightweight Directory Access Protocol (LDAP)
le service de nommage de CORBA (Common Object Request Broker Architecture) Common Object
Services (COS)
Le registre de RMI (Remote Method Invocation) rmiregistry
D’autres fournisseurs de services peuvent également être télécharger sur le site Web JNDI.
JNDI est divisé en 5 packages :
javax.naming
javax.naming.directory
javax.naming.event
javax.naming.ldap
javax.naming.spi

2.2.Le package Naming
Le package javax.naming contient des classes et interfaces pour accéder aux services de nommage.

2.2.1.Context
Le package javax.naming définit une interface Context qui est l’interface pour rechercher (lookup),
lier (bind), délier (unbind), renommer des objets, créer et supprimer des sous contextes.
L'opération généralement la plus utilisée est lookup(). Vous passez à la méthode lookup() le nom de
l'objet que vous voulez rechercher, et elle retourne l'objet lié à ce nom. L’exemple de code recherche
une imprimante et envoie un document à imprimer.
      Printer printer = (Printer) ctx.lookup("treekiller");
      printer.print(report);


2.2.2.Name
Chaque méthode de l'interface Context possèdent deux signatures : une qui prend en argument un
objet Name et une qui prend en argument un String correspondant au nom. Name est une interface
qui représente un nom générique – une séquence ordonnée de zéro composant ou plus. Pour les
méthodes de l'interface Context, un argument Name qui est une instance de CompositeName
représente un nom composite, ainsi vous pouvez nommer un objet en utilisant un nom qui englobe
plusieurs namespaces. Un argument Name de tout autre type représente un nom composé (un seul
namespace). Les méthodes qui prennent en argument un objet Name sont utiles pour les applications
qui doivent manipuler des noms, c'est-à-dire., les composer, comparer leur composants, et ainsi de
suite.
Un argument de type java.lang.String représente un nom composite. Les méthodes qui acceptent
des noms de type java.lang.String sont susceptibles d'être plus utiles pour des applications simples,
qui se contentent de lire dans un nom et de rechercher l'objet correspondant.
2.2.3.Liaisons
La méthode listBindings() renvoie une énumération des liaisons nom-à-objet. Chaque liaison est
représentée par une instance de la classe Binding. Une attache est un tuple contenant le nom de
l'objet, le nom de la classe de l'objet, et l'objet lui-même.

La méthode list() est semblable à listBindings(), sauf qu'elle renvoie une énumération de
NameClassPair. NameClassPair contient le nom d'un objet et le nom de la classe de l’objet. list() est
utile pour des applications telles que les browsers qui veulent obtenir des informations sur les objets
liés dans un contexte mais qui n'ont pas besoin de l’objet réel. Bien que listBindings() fournisse
toutes les informations, c'est potentiellement une opération beaucoup plus coûteuse.

2.2.4.Références
Les objets sont stockés dans les services de nommage et d'annuaire de plusieurs manières. Un
service qui supporte le stockage des objets Java peut les stocker sous sa forme sérialisée.
Cependant, certain service de nommage et d'annuaire ne peuvent stocker d'objets Java. Une
référence doit être une représentation très compacte de l’objet, alors que sa forme sérialisée peut
contenir beaucoup plus d’information.

JNDI définit la classe Reference pour représenter une référence. Une référence contient les
informations sur la manière de construire une copie de l'objet. JNDI essayera de transformer les
références, recherchées dans l'annuaire, en l’objet Java qu'elles représentent ; de sorte que les clients
JNDI aient l'illusion que ce qui est stocké dans l'annuaire sont les objets Java eux-mêmes.

2.2.5.Le contexte initial
Dans JNDI, toutes les opérations de nommage et d'annuaire sont effectuées relativement à un
contexte. Il n'y a pas de racines absolues. Par conséquent, JNDI définit un contexte initial,
InitialContext, qui fournit un point de départ pour les opérations de nommage et d'annuaire. Une fois
que vous avez un contexte initial, vous pouvez l'utiliser pour rechercher d'autres contextes et objets.

2.2.6.Exceptions
JNDI définit une hiérarchie de classe pour les exceptions qui peuvent être levées au cours des
opérations de nommage et d'annuaire. La racine de cette hiérarchie de classe est NamingException.
Les programmes faisant face à une exception particulière peuvent attraper la sous-classe
correspondant à exception. Sinon, ils peuvent attraper les NamingException.


2.3.Le package directory
Le package javax.naming.directory étend le package javax.naming pour fournir les fonctionnalités
pour accéder à des services d'annuaire en plus des services de nommage. Ce package permet à des
applications de rechercher des attributs liés aux objets stockés dans l'annuaire et de rechercher des
objets en utilisant les attributs indiqués.

                             1. Le contexte d’annuaire
L'interface DirContext représente un contexte d'annuaire. Elle définit des méthodes pour examiner et
mettre à jour les attributs liés à un objet d’un annuaire.
Vous utilisez la méthode getAttributes() pour rechercher les attributs liés à un objet d'un annuaire (à
qui vous fournissez le nom). Les attributs sont modifiés en utilisant modifyAttributes(). Vous pouvez
ajouter, remplacer, ou enlever des attributs et/ou attribuer des valeurs via cette méthode.
DirContext se comporte également comme un contexte de nommage en héritant de l'interface
Context. Ceci signifie que n'importe quel objet d'un annuaire peut également fournir un contexte de
nommage. Par exemple, un objet d'annuaire pour une personne pourrait contenir des attributs au sujet
de cette personne et fournir aussi un contexte pour nommer des objets comme les imprimantes de la
personne ou le système de fichiers relatifs à cet objet d'annuaire personne.

                             2. Recherches
DirContext contient des méthodes pour effectuer des recherches basées sur contenu de l'annuaire.
Dans le cas d’utilisation la plus simple et la plus commune, l'application indique un ensemble
d'attributs (probablement avec des valeurs spécifiques) qui correspondent et soumettre cet attribut à
la méthode search(). D'autres formes de la méthode search() supportent des filtres plus sophistiqués
de recherche.
2.4.Le package event
Le package javax.naming.event contient des classes et interfaces qui supportent la notification
d’évènement dans les services de nommage et d’annuaire.

2.4.1.Les évènements (events)
Un NamingEvent représente un évènement qui est généré par un service de nommage/annuaire.
L’évènement contient un type qui identifie le type d’évènement. Par exemple, les types d’évènements
sont classés dans les catégories qui affectent le namespace, tel que « object added » et celles qui ne
l’affectent pas, comme « object changed ». Un NamingEvent contient également des informations sur
le changement, tel que des informations sur l'objet avant et après le changement.

2.4.2.Les écouteurs (listeners)
Un NamingListener est un objet qui est à l’écoute des NamingEvents. Chaque type d'événement a
un NamingListener correspondant. Par exemple, un NamespaceChangeListener représente un
listener à l’écoute des changements d’un namespace et un ObjectChangeListener représente un
listener à l’écoute des changements d'un objet.

Pour recevoir des notifications d'événement, un listener doit être inscrit à un EventContext ou à un
EventDirContext. Une fois enregistré, le listener recevra des notifications d'événement quand les
changements correspondants se produisent dans le service de nommage/d’annuaire.

2.5.Le package LDAP
Le package javax.naming.ldap contient des classes et des interfaces pour l'usage des
caractéristiques spécifiques à LDAP v3 qui ne sont pas encore couverts par le package plus
générique javax.naming.directory. En fait, la plupart des applications JNDI qui utilisent LDAP
trouveront le package javax.naming.directory suffisant et n'auront pas du tout besoin d'utiliser le
package javax.naming.ldap. Ce package est principalement fait pour les applications qui doivent
employer des opérations "extended", des contrôles, ou des notifications non sollicités.

2.5.1.Opérations "extended"
En plus de spécifier des opérations bien définies telles que la recherche et la modification, la version 3
de LDAP (RFC 2251) indique une manière de transmettre des opérations XXXX définies entre le client
et le serveur LDAP. Ces opérations s'appellent les opérations "extended". Une opération "extended"
peut être défini par un organisme de normalisation tel que le Internet Engineering Task Force (IETF)
ou par un fournisseur. Ce package définit des classes pour l’extension Start TLS.

2.5.2.Contrôles
Les contrôles vous permettent, dans une recherche, de spécifier des options à appliquer aux résultats.
Par exemple vous pouvez choisir de restreindre votre recherche au nœud courant ou aux sous
racines. Vous pourrez également indiquer que vous voulez au maximum 20 résultats… Nous
expliquerons en détail comment les utiliser par la suite.

2.5.3.Notifications non sollicitées
En plus de l'interaction normal requête/réponse entre le client et le serveur, LDAP v3 spécifie
également des notifications non sollicitées -- les messages qui sont envoyés du serveur au client de
manière asynchrone et non en réponse à une requête du client.

2.5.4.Le contexte LDAP
L'interface LdapContext représente un contexte pour exécuter des opérations "extended", envoyer
des requêtes de contrôles, et recevoir des réponses de contrôle.


2.6.Le package SPI
Le package javax.naming.spi fournit les moyens aux développeurs de différents fournisseurs de
service de nommage/annuaire de développer et connecter leurs réalisations de sorte que les services
correspondants soient accessibles à des applications qui emploient le JNDI.
(http://java.sun.com/products/jndi/serviceproviders.html)
3.Exemples

3.1.Exemple de nommage
Nous allons écrire un premier exemple de base pour l’utilisation de JNDI. Celui-ci va chercher l’objet
dont le nom a été donné en paramètre de la ligne de commande. Nous allons utiliser un SPI pour le
système de fichier. De ce fait, le nom que l’on passera en paramètre devra être un nom de
fichier/dossier.

3.1.1.Importer les classes JNDI
Afin d’utiliser JNDI il vous faut importer les classes dépendantes :
       javax.naming.Context;
       javax.naming.InitialContext;
       javax.naming.NamingException;
Vous pouvez tout à fait importer l’ensemble du package javax.naming;

3.1.2.Créer un contexte initial
Lorsque vous utilisez JNDI, il vous faut initialiser un contexte pour votre connexion vers votre
annuaire. Ce contexte contient l’ensemble des informations de connexion tel que le « provider »
(obligatoire), le login (pour certaine connexion), mot de passe…
Toutes ces informations sont regroupées dans une HashTable (cf. cours : Collections). Elles seront
alors passées au constructeur de la classe InitialContext.
Voici donc la partie de code à utiliser dans notre exemple :
      Hashtable env = Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY,
           "com.sun.jndi.fscontext.RefFSContextFactory");

     Context ctx =        InitialContext(env);
Remarque : l’utilisation de « com.sun.jndi.fscontext.RefFSContextFactory » permet de se
connecter au système de fichier (c’est le provider).

3.1.3.Chercher un objet
Une fois les initialisations faites vous allez pouvoir rechercher un objet. Pour cela, vous utilisez la
méthode : lookup de votre context initial. Cette méthode prend en paramètre soit une String soit un
objet Name. Nous utiliserons directement une String pour notre exemple.
Voici le code à utiliser :
     Object obj = ctx.lookup(name);
Remarque : la méthode lookup() retourne un objet. Cet objet vous permet, dans certain cas, de lier
l’objet entre votre application et le serveur (nous verrons cela dans les parties suivantes).

3.1.4.Attraper les exceptions : NamingException
La méthode lookup() peut lever une exception de type NamingException. Il vous faut donc englober
votre appel à cette méthode par un bloc try / catch.
Voici le code utilisé :
       {
           // Création du context
           Context ctx = InitialContext(env);

          // Recherche l’objet par son nom
          Object obj = ctx.lookup(name);

          // Affiche l’objet
          System.out.println(name + " est lié à : " + obj);

     }   (NamingException e) {
          System.err.println("Problème : " + name + ": " + e);
     }


3.1.5.Compilation et exécution
La compilation se passe comme pour n’importe quel projet. Cependant il faut que les classes de base
de JNDI soient dans votre variable d’environnement CLASSPATH (fichier jndi.jar :
http://java.sun.com/products/jndi/downloads/).
Si vous utilisez Eclipse vous pouvez directement ajouter le fichier jar dans les librairies via :
Project → Properties → Java Build Path → Librairies → Add external JARS.

3.1.6.Code complet
      jndi.cours;

      java.util.Map;
      javax.naming.Context;
      javax.naming.InitialContext;
      javax.naming.NamingException;

      NommageExemple {
           main(String[] args) {
                    (args.length > 0) {
                           String name = args[0];
                           Map env = System.getProperties();
                           env.put(Context.INITIAL_CONTEXT_FACTORY,
         "com.sun.jndi.fscontext.RefFSContextFactory");
                             {
                                   Context ctx = new InitialContext();
                                   Object obj = ctx.lookup(name);
                                   System.out.println(name + " est lié à : "
   + obj);
                           } (NamingException e) {
                                   System.err.println("Erreur : problème : "
   + e);
                           }
                  } {
                           System.out.println("Problème : veuillez fournir un
   chemin "
                                           + "vers un fichier ou dossier");
                  }
         }
    }



3.2.Exemple d’annuaire
Nous allons maintenant nous intéresser à la connexion vers un service d’annuaire. Ce programme va
vous montrer comment retrouver les attributs d’un objet sur un serveur d’annuaire LDAP.

3.2.1.Importer les classes JNDI
Afin d’utiliser JNDI il vous faut importer les classes dépendantes :
       javax.naming.Context;
       javax.naming.directory.InitialDirContext;
       javax.naming.directory.DirContext;
       javax.naming.directory.Attributes;
       javax.naming.NamingException;
Vous pouvez tout à fait importer l’ensemble du package javax.naming; et javax.naming.directory;

3.2.2.Créer un context d’annuaire initial
Ceci se fait de la même manière que dans l’exemple précédent. Il vous faut juste changer le type de
context et les informations concernant le provider. En effet, nous utilisons l’objet : DirContext.
Voici donc la partie de code à utiliser dans notre exemple :
     Hashtable env = Hashtable();
     env.put(Context.INITIAL_CONTEXT_FACTORY,
           "com.sun.jndi.fscontext.RefFSContextFactory");

     Context ctx =        InitialContext(env);
Remarque : l’utilisation de « com.sun.jndi.fscontext.RefFSContextFactory » permet de se
connecter au système de fichier (c’est le provider).
3.2.3.Obtenir les attributs d’un objet de l’annuaire
Maintenant que le context est opérationnel nous pouvons récupérer les objets et leurs attributs. Pour
cela nous allons utiliser la méthode getAttributes(String name) qui retourne un objet : Attributes.
Cette méthode prend en paramètre soit une String (contenant le nom de l’objet auquel on veut
accéder) soit un objet Name (comme la méthode lookup).

     Attributes attrs = ctx.getAttributes("cn = Personne, ou=Groupe");

Lorsque vous avez récupéré votre objet Attributes (qui en fait regroupe l’ensemble des valeurs de
l’objet) vous pouvez alors en extraire les informations (un attribut par attribut). Pour cela vous avez
juste à faire cela :

     attrs.get("attribut1").get();

Ce code récupère la valeur de l’attribut « attribut1 » de l’objet précédemment récupérer.
Remarque : si vous essayez d’afficher directement un objet Attributes (via un
System.out.println("") par exemple) celui-ci vous affichera l’ensemble des attributs de l’objet.

3.2.4.Code complet se connectant à un serveur LDAP publique
      jndi.cours;

      java.util.Map;

      javax.naming.Context;
      javax.naming.NamingException;
      javax.naming.directory.Attributes;
      javax.naming.directory.DirContext;
      javax.naming.directory.InitialDirContext;

      AnnuaireExemple {
          main(String[] args) {
                        Map env = System.getProperties();
                        // Paramètres
                        env.put(Context.INITIAL_CONTEXT_FACTORY,
                                       "com.sun.jndi.ldap.LdapCtxFactory"
   );
                       env.put(Context.PROVIDER_URL,
   "ldap://donald.duc.auburn.edu:389/");
                         {
                               DirContext ctx = InitialDirContext();
                               Attributes attrs =
   ctx.getAttributes("o=Auburn University,c=US");
                               System.out.println("Modifié par: " +
   attrs.get("lastmodifiedby").get());
                       } (NamingException e) {
                               System.err.println("Erreur : problème : "
   + e);
                       }
        }
    }


4.Opérations de nommage

Nous allons étudier en détail les différentes opérations que l’on peut effectuer en ce qui concerne la
nomination des objets avec JNDI. Pour faciliter les opérations nous utiliserons le provider de type FS
(File System). Cela nous évitera d’avoir à installer un serveur d’annuaire …
Voici comment nous initialiserons notre contexte dans chacun de nos exemples :
      // Set up environment for creating the initial context
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY,
           "com.sun.jndi.fscontext.RefFSContextFactory");
      env.put(Context.PROVIDER_URL, "file:/c:/tmp/cours/jndi");
      Context ctx = InitialContext(env);
4.1.Rechercher un objet
Nous avons déjà vu ce cas de figure dans les exemples précédents. Nous allons ici l’appliquer à un
« file system ». Lorsque vous utilisez la méthode lookup() celle-ci vous retourne un objet de type File
(bien que le type retourné par la méthode soit de type : Object). Il vous suffit donc de « caster » cet
objet en File.

      jndi.cours.operations;

      java.io.File;
      java.util.Hashtable;

      javax.naming.Context;
      javax.naming.InitialContext;
      javax.naming.NamingException;
     NommageOperations {
          main(String[] args) {
                 // Set up environment for creating the initial context
                 Hashtable env = Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,
                                    "com.sun.jndi.fscontext.RefFSContextFactor
   y");
                    env.put(Context.PROVIDER_URL, "file:/c:/tmp/cours/jndi");
                      {
                            Context ctx = InitialContext(env);
                            File tmpFile    = (File) ctx.lookup("doc1.txt");
                            System.out.println("doc1.txt est un fichier ? => "
   + tmpFile.isFile());
                    } (NamingException e) {
                            e.printStackTrace();
                    }
         }
     }



4.2.Lister une arborescence
Nous venons de voir qu’il était possible de récupérer un objet unique simplement. Nous allons
maintenant nous attarder sur le fait de lister un ensemble d’objet. En effet, la méthode list() de l’objet
Context vous permet de récupérer une énumération de type NameClassPair. Chaque objet de ce
type contient le nom et le nom de l’objet et le nom de son type (sa classe).
Voici le code qui permet de lister le répertoire du context initial :

      jndi.cours.operations;

      java.io.File;
      java.util.Hashtable;

      javax.naming.Context;
      javax.naming.InitialContext;
      javax.naming.NameClassPair;
      javax.naming.NamingEnumeration;
      javax.naming.NamingException;

     NommageOperations {
         main(String[] args) {
                // Set up environment for creating the initial context
                Hashtable env = Hashtable();
                env.put(Context.INITIAL_CONTEXT_FACTORY,
                               "com.sun.jndi.fscontext.RefFSContextFactor
   y");
                env.put(Context.PROVIDER_URL, "file:/c:/tmp/cours/jndi");

                     // Exemple List arbo
                      {
                             Context ctx = InitialContext(env);
                             NamingEnumeration list = ctx.list("./");
                                 (list.hasMore()) {
                                    NameClassPair nc = (NameClassPair)
   list.next();
                                  System.out.println(nc);
                              }
                     }   (NamingException e) {
                              e.printStackTrace();
                     }
           }
    }

Vous pouvez également utiliser la méthode : listBindings(). Celle-ci retourne une énumération de
« Binding » qui est une sous classe de NameClassPair. Un objet Binding contient non seulement le
nom de l’objet et le nom de la classe mais également l’objet lui-même.
Il suffit de remplacer le bloc while par celui-ci :

    NamingEnumeration bindings = ctx.listBindings("./");

        (bindings.hasMore()) {
           Binding bd = (Binding) bindings.next();
           System.out.println(bd.getName() + ": " + bd.getObject());
    }

Quelle est la différence entre ces deux méthodes ?

    •    list() est la méthode à utiliser lorsque l’on veut afficher le nom des objets d’un context (dans
         un navigateur par exemple).
    •    listBinding() est à utiliser lorsque vous voulez appliquer des opérations à un ensemble
         d’objets d’un context. Par exemple, une application qui effectuerait une sauvegarde de fichiers
         ou une application qui redémarrerait un ensemble d’imprimante dans un bâtiment. Pour
         effectuer ses opérations vous êtes obligé d’accéder aux différents objets c’est donc pour cela
         que vous utiliserez listBindings().

4.3.Renommer un objet
Vous pouvez renommer un objet via la méthode : rename() d’un objet Context.
Voici le code qui renomme un fichier rename1.txt en renamme2.txt
     // Renommer
       {
           Context ctx = InitialContext(env);
           ctx.rename("rename1.txt", "rename2.txt");
     } (NamingException e) {
           e.printStackTrace();
     }

Si vous tentez de renommer un objet qui n’existe pas vous aurez une levée d’exception du type :
NameNotFoundException.


4.4.Créer et détruire un sous-contexte
Dans notre cas, les contextes et les sous-contextes correspondent aux dossiers et fichiers.
Pour créer un sous contexte il faut utiliser la méthode createSubContext. :

    // Créer un context
      {
        Context ctx = InitialContext(env);
        // Ajoute le sous context new1
        Context newContext = ctx.createSubcontext("new1");
    } (NamingException e) {
        e.printStackTrace();
    }

Si vous tentez de créer un contexte qui existe déjà vous lèverez une exception de type :
NameAlreadyBoundException.
De même vous pouvez supprimer un sous contexte via la méthode : destroySubcontext().

     // Supprimer un context
       {
         Context ctx = InitialContext(env);
         ctx.destroySubcontext("new1");
     } (NamingException e) {
         e.printStackTrace();
     }
Remarque : vous ne pouvez pas supprimer un contexte si celui-ci n’est pas vide. Si vous tentez d’en
supprimer un non vide vous lèverez une exception de type : ContextNotEmptyException.

5.Opérations d’annuaire

Maintenant que vous connaissez les opérations de base sur les noms nous allons étudier les
opérations qui sont possibles d’effectuer sur des annuaires. Pour pouvoir effectuer les tests de cette
partie, il vous faudra avoir à dispositions un annuaire LDAP. Toutes les opérations de lectures
pourront être faites via un serveur LDAP publique tel que : donald.duc.auburn.edu.
Voici le code de base qui nous permettra de nous connecter au service d’annuaire publique :
     // Paramétrage
     Map env = System.getProperties();
     env.put(Context.INITIAL_CONTEXT_FACTORY,
                       "com.sun.jndi.ldap.LdapCtxFactory");
     env.put(Context.PROVIDER_URL, "ldap://donald.duc.auburn.edu:389/");
       {
             DirContext ctx = InitialDirContext();
             Attributes attrs = ctx.getAttributes("o=Auburn University,c=US");
             System.out.println("Modifié par: " +
    attrs.get("lastmodifiedby").get());
     } (NamingException e) {
             System.err.println("Erreur : problème : " + e);
     }
Nous verrons par la suite comment paramétrer un login / mot de passe.

5.1.Lire les attributs d’un objet
Vous pouvez récupérer l’ensemble des attributs d’un objet à partir de son nom via la méthode
getAttributes d’un objet DirContext. Cette méthode vous retourne un objet Attributes contenant un
ensemble d’objet Attribute.

Voici un exemple de code qui affiche tous les attributs et les valeurs d’un objet.

      jndi.cours.operations;

      java.util.Map;
      javax.naming.Context;
      javax.naming.NamingEnumeration;
      javax.naming.NamingException;
      javax.naming.directory.Attribute;
      javax.naming.directory.Attributes;
      javax.naming.directory.DirContext;
      javax.naming.directory.InitialDirContext;

      AnnuaireOperations {

         main(String[] args) {
                // Paramétrage
                Map env = System.getProperties();
                env.put(Context.INITIAL_CONTEXT_FACTORY,
                               "com.sun.jndi.ldap.LdapCtxFactory");
                env.put(Context.PROVIDER_URL,
   "ldap://donald.duc.auburn.edu:389/");

                     // Récupére les attributs
                      {
                             DirContext ctx = InitialDirContext();
                                Attributes answer = ctx.getAttributes("o=Auburn
   University,c=US");

                                  (NamingEnumeration ae = answer.getAll();
   ae.hasMore();) {
                                           Attribute attr = (Attribute) ae.next();
                                           System.out.print ("attribute: " +
   attr.getID() + " => ");
                                           // Affiche chaque attribut
                                            (NamingEnumeration e = attr.getAll();
   e.hasMore();) {
                                                      System.out.print("Valeur: " +
   e.next() + " | ");
                                           }
                                           // Retour à la ligne
                                           System.out.println("");
                               }
                     }    (NamingException e) {
                               System.err.println("Erreur : problème : " + e);
                     }
          }
     }


Vous pouvez spécifier un tableau de String qui correpond à l’ensemble des attributs que vous voulez
récupérer à la méthode getAttributes. Dans notre exemple vous pouvez utiliser :

     // Spécifier les identifiants que vous voulez charger
     String[] attrIDs = {"o", "telephonenumber", "lastmodifiedby"};

     // Récupérer les attributs


Attributes answer = ctx.getAttributes("o=Auburn University,c=US", attrIDs);


5.2.Modifier / Ajouter / Supprimer une valeur d’un attribut d’un objet
Si vous avez les droits d’écriture sur un ensemble de votre serveur d’annuaire vous allez pouvoir
modifier / supprimer / ajouter des valeurs dans vos espaces.
Lorsque vous souhaitez opérer sur une valeur d’un attribut d’un objet vous devez utiliser la classe :
ModificationItem. Cette classe vous permet de définir les opérations à effectuer à l’attribut que vous
voulez modifier.

Le constructeur prend en paramètre l’opération à effectuer et un objet Attribute. Cet objet vous
permet d’indiquer sur quel attribut votre modification sera appliquée et la nouvelle valeur à lui affecter.
Exemple de modification d’un attribut :

    ModificationItem mods[] = ModificationItem [1];
    ModificationItem mods[0] =
   ModificationItem(DirContext.REPLACE_ATTRIBUTE,
                        BasicAttribute("mail", "cyril@societe.com"));

      {
         ctx.modifyAttributes(name, mods);
     } (AttributeInUseException e){
         System.err.println(e);
     } (NamingException e) {
         e.printStackTrace();
     }

On remarque que l’on doit appliquer les opérations que l’on cherche à faire via la méthode
modifyAttributes qui prend en paramètre le pointeur vers l’objet que vous voulez modifier (soit une
String soit un objet Name) et un tableau d’objet ModificationItem qui définit les opérations à
effectuer.

Nous avons utilisé la constante DirContext.REPLACE_ATTRIBUTE afin de modifier la valeur de
l’attribut mail. Il existe également :
DirContext.ADD_ATTRIBUTE qui permet d’ajouter une valeur à un attribut
DirContext.REMOVE_ATTRIBUTE qui permet de supprimer une valeur d’un attribut

Voici un exemple complet :

      jndi.cours.operations;

      java.util.Properties;
      javax.naming.Context;
      javax.naming.NamingEnumeration;
      javax.naming.NamingException;
      javax.naming.directory.Attribute;
      javax.naming.directory.AttributeInUseException;
      javax.naming.directory.Attributes;
      javax.naming.directory.BasicAttribute;
      javax.naming.directory.DirContext;
      javax.naming.directory.InitialDirContext;
      javax.naming.directory.ModificationItem;

      AnnuaireOperationsSpe {

         /**
          * Main effectue des opérations
          * @param args
          */
          main(String[] args) {

                   // LDAP
                   String provider = "ldap://diorf.no-ip.org:389/";

                Properties envLdap = System.getProperties();
                envLdap.put("java.naming.factory.initial",
                               "com.sun.jndi.ldap.LdapCtxFactory");
                envLdap.put(Context.SECURITY_AUTHENTICATION, "simple");
                envLdap.put(Context.SECURITY_PRINCIPAL,
   "uid=popom,dc=diorf,dc=org");
                envLdap.put(Context.SECURITY_CREDENTIALS, "java");
                envLdap.put(Context.PROVIDER_URL, provider);

                   String name = "cn=Cyril Joui,ou=java,dc=diorf,dc=org";
                   DirContext ctx;

                   // Initialise le contexte
                     {
                           ctx = new InitialDirContext(envLdap);
                   } (NamingException e) {
                           System.err.println(e);
                           ;
                   }

                   // Affiche les attributs avant modification
                   afficheElement(ctx, name);

                   // Modification de champs
                   ModificationItem[] mods = new ModificationItem[1];
                   // Remplace la valeur du mail
                   mods[0] = ModificationItem(DirContext.REPLACE_ATTRIBUTE,
                            BasicAttribute("mail", "labo-sun@supinfo.com"));

                // Ajoute une valeur dans l'attribut mail
    //          mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
    //                  new BasicAttribute("mail", "labo-
   sun@supinfo.com"));

                // Efface la valeur du mail
    //          mods[0] = new
   ModificationItem(DirContext.REMOVE_ATTRIBUTE,
     //                 new BasicAttribute("mail", "labo-
    sun@supinfo.com"));

                               // Applique les modifications
                      {
                               ctx.modifyAttributes(name, mods);
                     }    (AttributeInUseException e) {
                               System.err.println(e);
                     }    (NamingException e) {
                               e.printStackTrace();
                     }

                     // Affiche l'élément après modifications
                     afficheElement(ctx, name);
          }

         /**
          * Affiche le détail d'un élémnent à partir d'un context et de
    son nom
          * @param ctx
          * @param name
          */
          afficheElement(DirContext ctx, String name){
                 // Récupére les attributs
                  {
                         // Tous les attributs
                         Attributes answer = ctx.getAttributes(name);

                               System.out.println("Attributs de : " + name +
    "\n");

                                 (NamingEnumeration ae = answer.getAll();
    ae.hasMore();) {
                                          Attribute attr = (Attribute) ae.next();
                                          System.out.print ("attribute: " +
    attr.getID() + " => ");
                                          // Affiche chaque attribut
                                           (NamingEnumeration e = attr.getAll();
    e.hasMore();) {
                                                    System.out.print("Valeur: " +
    e.next() + " | ");
                                          }
                                          // Retour à la ligne
                                          System.out.println("");
                               }
                     }    (NamingException e) {
                               System.err.println("Erreur : problème : " + e);
                     }
          }
}

5.3.Chercher dans l’annuaire
Pour le moment vous avez récupéré les attributs d’un objet grâce à son nom. Cependant il n’est pas
toujours aisé de connaître toujours le nom de cet objet.
Les annuaires mettent à dispositions la possibilité d’effectuer des recherches. Il existe 3 types de
recherche qui sont :

    •   Recherche basique
    •   Recherche avec filtres
    •   Recherche avec contrôles

5.3.1.La recherche basique
Avec ce type de recherche vous allez pouvoir vérifier s’il existe des objets correspondant exactement
à certaines conditions.

Pour effectuer votre recherche il vous faut définir un ensemble Attributs à vérifier. Vous allez, par
exemple, demander au service de rechercher tous les objets d’un contexte qui contiennent l’attribut
« nom » qui est égal à « Dupond » mais également de rajouter une contrainte sur le prénom
« Durand ». Vous pouvez autant de conditions que vous voulez.
Cet ensemble d’attribut devra être passé à la méthode search d’un objet contexte qui se chargera du
« matching ».

Voici un exemple d’utilisation de cette méthode :

      jndi.cours.operations;

      java.util.Properties;
      javax.naming.Context;
      javax.naming.NamingEnumeration;
      javax.naming.NamingException;
      javax.naming.directory.Attribute;
      javax.naming.directory.Attributes;
      javax.naming.directory.BasicAttribute;
      javax.naming.directory.BasicAttributes;
      javax.naming.directory.DirContext;
      javax.naming.directory.InitialDirContext;
      javax.naming.directory.SearchResult;

      AnnuaireOperations {

         main(String[] args) {
                // Paramétrage
                Properties env = System.getProperties();
                env.put(Context.INITIAL_CONTEXT_FACTORY,
                               "com.sun.jndi.ldap.LdapCtxFactory");
                env.put(Context.PROVIDER_URL,
   "ldap://donald.duc.auburn.edu:389/");

                    DirContext ctx;

                    // Récupére les attributs
                      {
                            ctx = new InitialDirContext(env);
                    } (NamingException e) {
                            System.err.println("Erreur : problème : " + e);
                            ;
                    }

                    // Opération de recherche
                    Attributes matchAttrs = );
                    // Attribut à rechercher
                    matchAttrs.put( BasicAttribute("ou", "College of
   Business"));
                rechercheLdap(ctx, matchAttrs, "o=Auburn
   University,c=US");
        }

          /**
          *        Méthode de recherche qui affiche les résultats
          */
            rechercheLdap(DirContext initCtx,
                           Attributes matchAtts, String baseName) {

                NamingEnumeration answer;
                  {
                        answer = initCtx.search(baseName, matchAtts);
                          (answer.hasMore()) {
                                 SearchResult sr = (SearchResult)
   answer.next();
                                 System.out.println(">>>" + sr.getName());
                        }
                } (NamingException e) {
                        System.err.println(e);
                }
        }
    }
5.3.2.La recherche avec filtres
Les annuaires sont souvent compares avec les « pages jaunes ». Il donc souvent intéressant de
pouvoir par exemple recherche toutes les personnes dont le nom commencent par tel ou telle syllabe
…
Les filtres sont là pour ça. Un filtre est en fait une expression logique qui définit ce que vous cherchez.
La syntaxe de ces filtres est très précise et vous pouvez retrouver toute une documentation à
l’adresse suivante : http://www.ietf.org/rfc/rfc2254.txt.
Par exemple, le filtre suivant : (&(sn=Geisel)(mail=*)) spécifie que le nom doit avoir un attribut « sn »
qui possède la valeur « Geisel » et qu’il doit posséder un mail. Dans la syntaxe de ces filtres,
l’operateur logique apparaît avant les arguments.
En ce qui concerne la recherche, il vous suffit de passer une String qui contient l’ensemble de vos
conditions en paramètre à la méthode search.

Voici un exemple qui retourne l’ensemble des objets dont l’attribut ou commence par « College ».

      jndi.cours.operations;

      java.util.Properties;
      javax.naming.Context;
      javax.naming.NamingEnumeration;
      javax.naming.NamingException;
      javax.naming.directory.Attribute;
      javax.naming.directory.Attributes;
      javax.naming.directory.BasicAttribute;
      javax.naming.directory.BasicAttributes;
      javax.naming.directory.DirContext;
      javax.naming.directory.InitialDirContext;
      javax.naming.directory.SearchResult;

      AnnuaireOperations {

         main(String[] args) {
                // Paramétrage
                Properties env = System.getProperties();
                env.put(Context.INITIAL_CONTEXT_FACTORY,
                               "com.sun.jndi.ldap.LdapCtxFactory");
                env.put(Context.PROVIDER_URL,
   "ldap://donald.duc.auburn.edu:389/");

                     DirContext ctx;

                     // Récupére les attributs
                       {
                             ctx = InitialDirContext(env);
                     } (NamingException e) {
                             System.err.println("Erreur : problème : " + e);
                             ;
                     }

                // Opération de recherche avec Filtre
                rechercheLdap(ctx, "(&(ou=College*)(object>, "o=Auburn
   University,c=US");
        }

          /**
           * Méthode qui recherche avec un filtre des objets
           * @param initCtx
           * @param filter
           * @param baseName
           */
           rechercheLdap(DirContext initCtx,
                         String filter, String baseName) {

                NamingEnumeration answer;
                 {
                        answer = initCtx.search(baseName, filter,
   SearchControls());
                         (answer.hasMore()) {
                                               SearchResult sr = (SearchResult)
   answer.next();
                                         System.out.println(">>>" + sr.getName());
                                 }
                      }     (NamingException e) {
                                 System.err.println(e);
                      }
            }
    }

Voici un ensemble de base des opérateurs que vous pouvez utiliser pour vos recherches dans les
annuaires :

        Symbole


        Description
        &                 conjunction (i.e., and – tout ce qui est dans la liste doit être vrai)
        |                 disjonction (i.e., or – une ou plusieurs expressions doivent être vraies)
        !                 négation (i.e., not – l’élément doit être faux)
        =                 égalité (les éléments doivent être égaux)
        ~=                égalité approximative
        >=                plus grand que
        <=                moins grand que
        =*                présence (l’attribut doit avoir une valeur)
        *                 joker : plusieurs caractères peuvent remplacer ce caractère
        \                 caractère d’échappement



5.3.3.La recherche avec contrôles
Ce type de recherche est un peu différent des autres. En effet, c’est plus un ensemble d’options que
vous ajoutez à votre recherche. Vous allez par exemple pouvoir contrôler les attributs à retourner, le
niveau de votre recherche (dans l’arbre), le nombre maximal d’entrées à retourner, le temps maximum
d’attente…

Pour cela vous allez utiliser la classe SearchControls et les méthodes :

setTimeLimit qui permet de définir le temps d’attente maximum.
setCountLimit qui permet de définir le nombre d’enregistrement maximum à retourner
setSearchScope qui définit si le moteur de recherche parcourt un seul niveau
(SearchControls.ONELEVEL_SCOPE) ou également les sous-niveaux
(SearchControls.SUBTREE_SCOPE).

Voici un exemple qui utilise ces méthodes (nous ne reprenons pas le code qui est au dessus) :
     SearchControls sControl = SearchControls();
     sControl.setSearchScope(SearchControls.SUBTREE_SCOPE);
     sControl.setTimeLimit(20000);
     sControl.setCountLimit(10);
     rechercheLdap(ctx, "(&(ou=College*))", "o=Auburn University,c=US",
   sControl);

Il vous faudra également modifier la méthode rechercheLdap :

            /**
             * Méthode qui recherche avec un filtre des objets et contrôles
             * @param initCtx
             * @param filter
             * @param baseName
             * @param con
         */
         rechercheLdap(DirContext initCtx,
                       String filter, String baseName, SearchControls
   con) {

                NamingEnumeration answer;
                  {
                        answer = initCtx.search(baseName, filter, con);
                          (answer.hasMore()) {
                                 SearchResult sr = (SearchResult)
   answer.next();
                                 System.out.println(">>>" + sr.getName());
                        }
                } (NamingException e) {
                        System.err.println(e);
                }
        }


5.3.4.Combiner les opérations de nommage et d’annuaire
Nous avons vu précédemment les rudiments pour le nommage et la gestion des attributs. La classe
DirContext surcharge les différentes méthodes : bind(), rebind() et createSubContext() de Context
afin qu’elles puissent prendre en paramètres un objet Attributes.
De ce fait, il est beaucoup plus simple d’ajouter les attributs à l’objet au moment où vous l’ajoutez à un
contexte …

Pour créer un contexte qui possède des attributs vous utiliserez createSubContext(). Celle-ci attend
une String pour le nom du contexte et un objet Attributes pour la liste des attributs du contexte.
Voici un exemple d’utilisation :

      jndi.cours.operations;

      java.util.Properties;
      javax.naming.Context;
      javax.naming.NamingException;
      javax.naming.directory.Attribute;
      javax.naming.directory.Attributes;
      javax.naming.directory.BasicAttribute;
      javax.naming.directory.BasicAttributes;
      javax.naming.directory.DirContext;
      javax.naming.directory.InitialDirContext;

      Combinaisons {

          /**
           * Main effectue des opérations
           * @param args
           */
           main(String[] args) {

                     // LDAP
                     String provider = "ldap://serveur.ldap.lan:389/";

                Properties envLdap = System.getProperties();
                envLdap.put("java.naming.factory.initial",
                               "com.sun.jndi.ldap.LdapCtxFactory");
                envLdap.put(Context.SECURITY_AUTHENTICATION, "simple");
                envLdap.put(Context.SECURITY_PRINCIPAL,
   "uid=popom,dc=diorf,dc=org");
                envLdap.put(Context.SECURITY_CREDENTIALS, "java");
                envLdap.put(Context.PROVIDER_URL, provider);

                     String name = "ou=java,dc=diorf,dc=org";
                     DirContext ctx;

                     // Initialise le contexte
                      {
                                ctx = InitialDirContext(envLdap);
                      }    (NamingException e) {
                                System.err.println(e);
                                ;
                      }

                      // Ajouter un context avec des attributs
                      // Créer les attributs associés au contexte
                      Attributes attrs = );
                      Attribute objclass = BasicAttribute("objectclass");
                      objclass.add("top");
                      objclass.add("organizationalUnit");
                      attrs.put(objclass);

                       {
                                 // Créer le context
                                 Context result = ctx.createSubcontext(
                                                "ou=juteux,ou=Fruits," + name,
   attrs);
                      }    (NamingException e1) {
                                e1.printStackTrace();
                      }
           }
     }



6.Opérations sur les schémas

6.1.1.Introduction
Dans cette partie nous allons voir les schémas LDAP et décrire comment ils sont mappés dans JNDI.
La version 3 de LDAP définit un schéma basé sur le standard X.500 pour les objets communs trouvés
dans un réseau tels que les pays, les villes, organisations, personnes, groupes et périphériques.
De plus dans cette version, le schéma est disponible directement dans l’annuaire. Les informations
sont représentées par des entrées avec leurs attributs.
LDAP v3 spécifie que chaque entrée de l’annuaire devrait contenir un attribut d’opération qui identifie
son « schéma de sous entrée ». Ceci contient le schéma de définition pour les définitions des classes
d’objet et types d’attribut utilisés par les entrées dans une partie particulière de l’arbre de l’annuaire. Si
une entrée particulière n’a pas de sous schéma alors le sous schéma du root DSE (qui est nommé par
la base DN) est utilisé.

Cependant sur la version 2 de LDAP, les schémas ne sont pas explicitement définis. Le serveur peut
tout à fait publier ses schémas afin qu’ils soient accessible par le client. Cependant la plupart des
serveurs n’autorisent pas l’accès à ces informations et/ou leur modification.

6.1.2.Les schémas dans JNDI
Représentation de la structure des schémas :
JNDI contient des méthodes qui permettent d’accéder aux informations des schémas par rapport aux
classes d’objet, types d’attribut et les syntaxes d’attribut.
Voici l’ensemble des méthodes à utiliser :

   Méthode


   Description
   DirContext.getSchema()                       Retourne le schéma pour l’objet.
   DirContext.getSchemaClassDefinition() Retourne la définition de la classe pour l’objet.
   Attribute.getAttributeDefinition()           Retourne la définition de l’attribut.
   Attribute.getAttributeSyntaxDefinition() Retourne la définition de la syntaxe pour l’attribut.

L’arbre schéma

La méthode getSchema() de l’objet DirContext retourne la racine de l’arbre schéma. Cet arbre
contient les liaisons listées ci-dessous :

        Nom


        Liaison
        AttributeDefinition Racine du type d’attribut dans l’espace de nom.
        ClassDefinition     Racine de la définition de la classe objet dans l’espace de nom.
        SyntaxDefinition    Racine de la définition de l’attribut syntaxe dans l’espace de nom.

Il se peut que certains ou tous ces attributs soit absent(s). Cela dépend de la configuration du serveur
auquel vous vous connecter qui peut ne pas publier les schémas.
Voici un exemple qui récupère la racine schéma de l’arbre pour l’entrée LDAP « ou=Personne » et
liste son contenu :

     // Obtient le schéma
     DirContext schema = ctx.getSchema("ou=People");

     // Liste le contenu
     NamingEnumeration bds = schema.list("");
       (bds.hasMore()) {
          System.out.println(((NameClassPair) (bds.next())).getName());
     }


6.1.3.Définition des classes d’objets
Tous les types d’entrées dans LDAP sont typés. La classe d’objet spécifie les attributs obligatoires et
ceux optionnels. Les classes d’objet dans l’annuaire forment une hiérarchie de classe. Les classes
« top » et « alias » représentent les classes racines de la hiérarchie. Par exemple, la classe :
« organizationalPerson » est une sous classe de « Person » qui est également une sous classe de
« top ». Quand vous créez une nouvelle entrée LDAP, vous devez toujours spécifier toutes les classes
d’objet que la nouvelle entrée contient.
Du fait que beaucoup d’annuaire ne supportent pas « le sous classement » vous devrez toujours
inclure toutes les supers classes d’un entrée. Par exemple, pour un objet « organizationalPerson »,
vous devrez lister les classes : « organizationalPerson », « person » et « top ».
Trois types de classe d’objet existent :

    •   Structural : indique les attributs qu’une entrée doit posséder et où chaque entrée devrait être
        dans le DIT (Directory Information Tree).
    •   Auxiliary : indique les attributs qu’une entrée doit posséder.
    •   Abstract : indique une spécification « partielle » d’un objet, ces objets n’apparaissent pas
        comme entrées dans l’annuaire.

Dans le schéma de l’arbre, le nom « ClassDefinition » est lié à un contexte qui contient des objets de
type DirContext qui représente des classes d’objets de définitions du schéma. Par exemple, si un
annuaire supporte un objet de type « person », alors le contexte « ClassDefinition » aura une liaison
avec le nom « person » qui sera lui-même lié à un objet DirContext.
Chaque objet dans le contexte « ClassDefinition » a des attributs obligatoires et optionnels décrit ci-
dessous : (seulement NUMERICOID est obligatoire)

   Identifiant Attribute


   Description de la valeur de l’attribut
   NUMERICOID (obligatoire)                  Unique objet identifier (OID)
   NAME                                      Nom de la classe de l’objet
   DESC                                      Description de la classe d’objet
   OBSOLETE                                  "true" si obsolète; "false" ou absent sinon
   SUP                                       Noms des classes supérieurs dont cette classe dérivent.
   ABSTRACT                                  "true" si la classe est abstraite; "false" ou absent sinon
   STRUCTURAL                                "true" si la classe est structural; "false" ou absent sinon
   AUXILIARY                                 "true" si la classe est auxiliary; "false" ou absent sinon
   MUST                                      Liste des noms d’attributs obligatoires.
   MAY                                       Liste des noms d’attributs non obligatoires.

Ces attributs correspondent à la définition « ObjectClassDescription » décrite dans RFC 2252. Tous les
attributs sont mappés à java.lang.String.

6.1.4.Récupérer une entrée (d’une classe d’objet)
Lorsque vous avez un objet DirContext représentant une entrée LDAP, vous pouvez récupérer
l’entrée de la classe d’objet en invoquant la méthode : DirContext.getSchemaClassDefinition().
Voici un exemple de code qui permet d’énumérer la classe de définition d’objet pour l’entrée
« cn=Cyril Joui, ou=Personne ». La méthode getSchemaClassDefinition() retourne le contexte qui
contient les informations concernant la classe d’objet. En utilisant ce contexte, vous pouvez
rechercher l’ensemble des définitions, chercher une définition précise, énumérer tous les définitions
ou effectuer d’autres opérations de l’objet DirContext.

     // Crée le contexte initiale
     DirContext ctx = InitialDirContext(env);

    // Obtient le contexte de l’entrée cn=Cyril Joui, ou=Personne
    DirContext tedClasses = ctx.getSchemaClassDefinition("cn= Cyril Joui,
   ou=Personne");

     // Enumère les définitions
     NamingEnumeration list = tedClasses.search("", );
       (list.hasMore()) {
          System.out.println(list.next());
     }

De même vous pouvez ajouter, modifier, supprimer des attributs comme une entrée normale.

7.Ressources

7.1.Les annuaires LDAP
Voici un récapitulatif des plus grands serveurs LDAP du marché :

    •    Sun Microsystems : Sun Directory Server
         (http://wwws.sun.com/software/download/products/3ee79e69.html)
    •    OpenLDAP : (http://www.openldap.org/)
    •    Netscape : Netscape Directory Server (http://www.netscape.com)
    •    Microsoft : Microsoft Active Directory
         (http://www.microsoft.com/france/technet/produits/win2000s/info/info.asp?mar=/france/technet/produits/wi
         n2000s/info/adcomp.html)
7.2.Les normes et concepts LDAP
http://abcdrfc.free.fr/rfc-vf/rfc2255.html
http://www.int-evry.fr/mci/user/procacci/ldap/Ldap_int.html




                     SWT - Créer des interfaces graphiques performantes



1.Introduction
SWT (Standard Widget Toolkit) est une librairie graphique qui vous permet de réaliser des
applications graphiques Java beaucoup plus avancées et surtout plus rapide à l’exécution. En effet
contrairement aux anciennes API graphiques (AWT et Swing), SWT se propose de réutiliser les
fonctions et objets systèmes de la plateforme sur laquel l’application s’exécute.
Cependant tous les composants possibles d’une application graphique ne sont pas toujours
disponibles sur toutes les plateformes. C’est pour cela que la librairie SWT vous propose, selon la
plateforme où vous souhaitez exécuter votre application, d’émuler les composants qui ne seraient pas
présent.
Si vous vous rendez à l’URL suivante afin d’effectuer un téléchargement, vous vérez que vous aurrez
le choix de télécharger plusieurs librairie SWT différentes selon la plateforme :
www.eclipse.org/downloads/
Voici la liste actuelle des plateformes supportées :

    •   Windows
    •   Windows CE (ARM PocketPC)
    •   Windows CE (ARM PocketPC, J2ME profile)
    •   Linux (x86/GTK 2)
    •   Linux (x86_64/GTK 2)
    •   Linux (PPC/GTK 2)
    •   Linux (x86/Motif)
    •   Solaris 8 (SPARC/GTK 2)
    •   Solaris 8 (SPARC/Motif)
    •   QNX (x86/Photon)
    •   AIX (PPC/Motif)
    •   HP-UX (HP9000/Motif)
    •   Mac OSX (Mac/Carbon)

Votre code restera inchangé, c’est simplement lors du déploiement de votre application qu’il faudra
inclure la librairie spécifique à la plateforme accueillant l’application. C’est une procédure quelque peu
fastidieuse qui n’était pas à réaliser avec Swing mais le gain en termes de performance est nettement
significatif, spécialement dans le cas d’application graphique complexe.
Avec Eclipse 3.1, vous aurrez directement la librairie d’installé et vous pourrez commencer à
construire vos applications graphiques en SWT.
Les nouvelles applications graphiques Java se réalisent de plus en plus en SWT tel que Eclipse 3,
Azureus, …
SWT n’est cépendant pas compatible avec Swing ou AWT. Avant d’apprendre à réaliser des
applications en SWT il est important de maitriser les notions de Swing. Reportez vous au cours de
Swing si vous avez des lacunes à ce sujet.
Si vous ne maitrissez pas ces concepts veuillez vous reporter au cours de Swing avant de poursuivre.

1.1.Pré-requis

Les pré-requis pour le bon déroulement de ce cours et d’avoir les notions de conceptions d’interfaces
graphique avec Swing. Les exemples seront réalisés sous Eclipse 3.1 et Visual Editor 1.1. Veuillez
donc procéder à l’installation de ces outils.

1.2.Prise en main de Visual Editor

Si vous avez réalisé les exercices présents dans l’essentiel de Swing, vous devriez avoir déjà ce
plugin installé. Visual Editor vous permet en effet de réaliser vos applications graphiques de manière
visuelle et donc de réduire considérablement votre temps de développement.

Visual Editor s’exécute dans la perspective Java, avec deux vues supplémentaires qui lui sont
associées : la vue « Java Beans » qui réprésente la hiérarchie de confinement de l’interface graphique
et la vue « properties » qui permet d’éditer les propriétés d’un composant.
Par défaut, la fenêtre du haut est un éditeur WYSIWYG (What You See Is What You Get) qui permet
de construre l’application par de simples glisser-déposer ; c’est en réalité la prévisualisation de la vue
Java beans. La deuxième fenêtre est la vue du code source de l’application.

Toutes ces vues sont synchronisées illustrant le concept MVC, un seul modèle et plusieurs vues
différentes.

Le dernier composant essentiel est la palette des composants qui va permettre de sélectionner les
composants à ajouter. Elle est constituée de plusieurs items sélectionnables.

Lorsque vous utilisez Visual Editor pour construire une application en SWT, vous pouvez également
voir les composants, les conteneurs et les menus Swing ainsi que les controles AWT. Vous ne
pourrez cependant pas déplacer ces composants et conteneurs dans votre application SWT, et
inversement. En effet une application SWT ne peut pas contenir d’éléments Swing ou AWT et des
composants et contenurs SWT ne peuvent pas être utilisés dans une application Swing ou AWT.

2.Utilisation de Visual Editor

Comme énoncé dans le cours de Swing, il existe un plugin qui peut s’intégrer à Eclipse afin de vous
aider dans la réalisation de vos interfaces graphiques en Java. Il existe également d’autre IDE tel que
JBuilder qui permet également de faciliter le développement d’interfaces graphiques par le biais
d’outils de drag & drop.

2.1.Installation du plugin et pré-requis

L’attrait de Visual Editor est d’être, tout comme Eclipse, totalement gratuit. Si vous souhaitez obtenir
plus de détails sur ce plugin vous pourrez visiter le site suivante : http://www.eclipse.org/vep/
VEP signifie Visual Editor Project, c’est en réalité un plugin qui utilise deux autres plugins que vous
devrez au préalable installer :

    •   EMF : (Eclipse Modeling Framework)

Ce plugin permet de lier des graphiques au code afin de faire de permettre la génération de code.
Vous pouvez avoir plus de détails et télécharger ce plugin à l’URL suivante : http://www.eclipse.org/emf/

    •   GEF : (Graphical Editing Framework)

Ce plugin permet de créer des graphiques, c’est sur ce plugin que va s’appuyer Visual Editor afin
d’effectuer le rendu de votre application dans une vue (une fenêtre) spécifique. Vous pourrez avoir
plus de détails et télécharger ce plugin à l’URL suivante : http://www.eclipse.org/gef/
Pour chaque installation de plugin, vous devez simplement télécharger les fichiers appropriés, les
décompresser puis copier les répertoires « plugins » et « features » dans le repertoire d’installation de
votre Eclipse. Veuillez installer ces 3 plugins une fois que votre Eclipse est fermé, relancez le, et vos
nouvaux plugins seront pris en compte.
Visual Editor vous permet d’accéler considérablement votre temps de développement dans vos
interfaces graphiques. Il est cependant essentiels que vous maitrissiez complétement la conception
d’application Swing sans l’utilisation de Visual Editor avant de l’essayer.

2.2.Explication de l’environnement de modélisation

Pour obtenir les outils dans Eclipse proposés par Visual Editor, il vous faut construire un projet de type
« Java Project ». L’utilisation de Visual Editor n’est pas obligatoire pour faire des applications
graphiques en Java, vous pouvez tout coder à la main sans utiliser cet assistant. Dans tout les cas
vous devrez importez les librairies SWT, pour cela allez dans le menu « Librairies » : Clique droit sur
le projet/Build Path/Add Librairies et vous devrez sélectionner la librairie SWT comme illustré ci-
contre :
Construisez si vous le souhaitez des packages puis créez ensuite une nouvelle classe de type
« Visual Class » comme illustrée ci-dessous :




Dans l’écran suivant vous pouvez créer votre classe comme si vous le faisiez de manière classique.
La particularité de cet écran par rapport à l’assistant classique de création de classe est la liste qui
apparaît sur la partie gauche nommée « Style ». Dans cette liste vous pouvez choisir le type
d’application que vous allez créer :

    •   Swing (Applications standard)
    •   AWT (Très peu utilisée)
    •   SWT (C’est cette librairie là que nous avons détaillée)
    •   RCP (Utilisé pour la création de plugin intégré à Eclipse ou conforme à cette nouvelle norme :
        Rich Client Platform)

Si vous choisissez une application de type SWT, vous aurrez accès à deux choix. Nous allons créer ici
une application de type « Shell ». Le type « Composite » vous permet d’avoir un conteneur vous
pouvez réutiliser dans vos applications SWT. C'est-à-dire un composant personnalisé (user control)
avec des composants spécifiques et un comportement défini qui pourra être réutilisé dans plusieurs
classes de votre application.
Voici l’environnement que vous obtiendrez. Vous remarquerez que plusieurs « vues » se sont
chargées :

    •   Palette
    •   Java Beans
    •   Properties
La fenêtre centrale, encore appelé « visual editor », permet d’avoir à la fois le rendue de l’application
mais également la correspondance avec le code généré instantanément dans la partie inférieure de la
fenêtre.




Le code généré reprend des normes auxquels vous ne serez peut-être pas habitué. Le principe est
créer une méthode non pas par composant graphique comme cela est fait dans le cas d’application
Swing mais une méthode par conteneur.
Cela permet, lorsque l’on y est habitué, d’avoir plus de clartée en terme de hierarchie dans votre code.
Vous avez toujours la visibilité sur le code généré, si le code ne vous conviens pas dans certains cas,
il vous apparatient de le modifier comme vous le souhaitez.

2.2.1.La palette graphique
Cette partie vous permet d’avoir accès à divers objets utilisables dans la librairie.
Cette palette se décompose en 3 parties à utiliser pour les applications SWT :


    •   SWT Controls
    •   SWT Containers
    •   SWT Menus
Dans le cas où vous souhaitez créer une application Swing où AWT, vous pourrez utiliserez les autres
sections de la palette.
Le fait d’avoir créer une Visual Class de type Shell permettera, lors chargement de l’environnement de
Visual Editor, d’avoir une fenêtre qui s’affiche.
Vous pouvez donc commencer à constituer votre application en faisant glisser les composants SWT
que vous souhaitez. Vous pouvez également ajouter des conteneurs SWT à votre application afin
d’implémenter plusieurs type de gestionnaire de positionnement différents par exemple.
Enfin la partie des menus vous permettra d’ajouter une barre de menu ainsi que ses éléments de
manières très simplifiée. Cette partie s’utilise différement des précédantes. En effet il ne faudra pas
déplacer ces objets directement dans la fenêtre de visualisation de l’application mais il vaudra utiliser
la vue « Java Bean » qui va être détaillé dans la partie suivante.
2.2.2.La vue « Java Bean »
Cette vue permet de voir l’architecture de votre application Swing. Cette vue sera très utilisée
notament dans le cas où vous créez une application avec une barre de menu.
Pour symboliser la hierarchie, les composants sont affichés ici dans un JTree.
Voici une illustration ci-contre d’un aspect que peut avoir votre vue « Java Beans » :




3.Hierarchie des composants

L’interface graphique est en réalité un regroupement de composants organisés selon une hiérarchie
précise. On retrouve la même notions qui à été présenté dans le cours de Swing. En SWT, on
différencie deux types d’objets. Il y a les composants dit « conteneur » et d’autres appelés simplement
« composant ». La fonction des composants cités est de contenir d’autres composants, un ou
plusieurs selon les spécificités du conteneur.
Il existe égalements les contrôles basiques qui vont permettre à l’utilsateur d’agir sur le systeme en
entrant des informations sous différentes formes possible, les composants informatifs non-éditables
qui fournissent des indications en lecture seule à l’utilisateur et les visualiseurs de données formatées
qui permettent de gérer de maniére simple des ressources importantes.
Voici un exemple d’architecture que vous pouvez rencontrer en SWT. Vous remarquerez que les nom
des composants ont quelques peu changés par rapport à Swing. Nous détaillerons les principaux
éléments nécessaires lors des prochains chapitres.
4.Les conteneurs SWT

Les conteneurs SWT sont assez peu nombreux. Vous avez le Shell, le Composite et le Group.
Ensuite vous avec des conteneurs qui permettent d’obtenir des espaces supplémentaires dans votre
interface graphique comme le TabFolder et le CTabFolder, la barre d’outils (CoolBar), le
ShashFrom, le Canvas et le ScrolledComposite.

4.1.Display

La classe Display permet de faire la connection entre l’application Java et le systême d’exploitation, il
permet de représenter l’écran et contient une liste de Shell.

4.2.Shell

Shell est similaire à une JFrame en Swing, en effet ce conteneur permet d’afficher une fenêtre et
d’inclure des éléments à l’intérieur de cette dernière. Le JRootPane n’a pas d’équivalence en SWT, il
faut utiliser GEF si cela est nécessaire. Il est nécessaire de créer une instance des classes Shell et
Display afin d’accueillir les autres conteneurs et composants qui constituront votre application.
Shell est l’élément racine d’une fenêtre contenant des conteneurs, des menus et des composants.
Voici un exemple code à mettre au sein de votre méthode main :




4.3.Composite

La classe Composite est similaire à la classe JPanel en Swing, en effet ce conteneur permet de
contenir à la fois des composants mais égalements d’autres conteneurs (Composite, …). L’avantage
de ce type de conteneur est donc la possibilité d’appliquer différents gestionnaires de positionnement
afin de remplir les impératifs que vous pouvez avoir dans votre application. Si vous avez besoin
d’utiliser à la fois un layout null et un layout de type GridLayout, vous pouvez utiliser un Composite
comme illustré dans l’application suivante :




Votre Composite doit obligatoirement être rattaché à un Shell, soit directement, soit indirectement via
l’intermédiaire d’autres conteneurs.

4.4.Group

La classe Group est très similaire à la classe Composite, elle impose simplement d’avoir une
délimitation autour du conteneur afin de mieu le visualiser et permet d’attacher un titre comme illustré
ci-dessous :
4.5.Les onglets

4.5.1.TabFolder

Le TabFolder est un composant permettant d’intégrer le principe des onglets dans vos applications.
Vous devrez donc intégrer des conteneurs dans chacun des onglets du TabFolder. Voici un exemple
d’utilisation :




4.5.2.CTabFolder
Ce composant est très semblable au TabFolder, le C signifiant Custom, ce composant permet de
personnalisé les onglets afin d’obtenir les mêmes onglets que vous pouvez rencontrer dans Eclipse
3.1 (coloration de l’onglet sélectionné avec un cadre, onglet arrondi, liste de choix automatiquement
créé lorsqu’il y a trop d’onglet et pas suffisament de place pour voir le titre de chacun des onglets :
très utile dans le cas d’édition de fichier, …).

Voici un aperçu de ce conteneur qui se comporte comme le TabFolder classique :
4.6.Les autres conteneurs

Il existe beaucoup d’autres conteneurs que vous pourrez utiliser, vous pourrez utiliser, en voici une
liste pour lesquels Visual Editor vous propose un assistant :




5.Les composants SWT

Les composants SWT sont assez semblables aux composants Swing, il ne sera donc pas très
compliqué pour un développeur d’applications Swing de passer au SWT. Un temps d’adaptation sera
bien évidement nécessaire ne serait ce que pour le nom des composants. Vous retrouverez les
mêmes types de composants que dans des applications Swing.
Voici un extrait des classes que vous pourriez être amené à utiliser :
5.1.Les composants paramétrables

La quasitotalité des classes de composants sont situées dans le package org.eclipse.swt.widgets.
Le rendu de ces composants se basera sur votre systême d’exploitation à la place d’éléments
graphiques émulés comme le faisait les applications utilisant Swing.
Tout les composants ayant des noms de classes commençant par C (Custom), comme CLabel et
CCombo symbolise les éléments graphique plus évolué permettant soit des fonctionnalités
suppémentaire au niveau fonctionnel ou alors des fontions de rendu graphique plus poussé. D’autres
composants comme Browser permettent d’intégrer directement les fonctions d’un navigateur Web à
votre application :
5.2.Exemple de creation d’un bouton

Une seule classe est utilise pour definir le comportement de elements graphiques différents. En effet,
plutôt qu’utiliser une classe par elements graphique. C’est le cas pour les boutons, une seule classe
est à utiliser pour plusieurs aspects différents. Vous devrez donc utiliser la classe Button dans le cas
où vous souhaitiez représenter l’un de différents boutons montrés dans l’illustration :




En terme de code cela se represente simplement par des arguments différents passes au
constructeur de la classe qui se chargera d’appeler le rendu graphique desiré sur votre systême
d’exploitation :

PUSH :




CHECK :




RADIO :




ARROW :




TOGGLE :




Vous pouvez utiliser la vue “Properties” afin de sélectionner le type de bouton :




5.3.Les composants liées

Certains composants sont liés entre eux, comme les menus ou les barre d’outils. Dans le cas d’une
barre d’outils, vous pourrez uniquement utilisez les composants spéciaux étant prévus pour être
ajoutés au sein d’une barre d’outils.
6.La gestion du positionnement

6.1.Null

Par défaut, les applications SWT n’utilise pas de layout manager, contrairement aux applications
Swing qui utilisent le BorderLayout. C’est donc à vous de gérer la position et les dimensions de vos
composants. C’est également à vous de gérer le redimensionnement qui n’est pas actif lorsqu’il n’y
pas de layout manager. Voici un exemple de rendu que votre application pourrait avoir. En utilisant
Visual Editor, une grille s’affiche permettant d’identifier la non présence de layout.




6.2.FillLayout

Ce gestionnaire de positionnement permet d’afficher tout les composants qui lui sont rattaché les uns
à la suite des autres, soit :

6.2.1.Affichage vertical
Voici un exemple de rendu en utilisant l’affichage en mode vertical :




Tous les objets sont redimensionnés de manière équitable. Dès que l’application est redimensionnée,
les composants se redistribut l’espace selon le même procédé.

6.2.2.Affichage horizontal
Voici un exemple de la même application utilsant cette fois ci l’affichage en mode horizontal :




Tous les objets sont redimensionnés de manière équitable. Dès que l’application est redimensionnée,
les composants se redistribut l’espace selon le même procédé.
6.3.GridLayout

Ce layout regroupe à la fois le GridLayout et le GridBagLayout de Swing. En effet ce gestionnaire
de positionnement permet d’afficher vos composants selon une grille.
En supplément du simple GridLayout de Swing, ici vous pourrez spécifier qu’un composant soit
positionné sur deux colonnes ou deux lignes, vous pourrez également spécifier que chaque colonne
fasse une taille différente.
Ces ainsi que vous pourez spéficier avec le même layout manager un affichage très varié illustré dans
les exemples suivants :




6.4.RowLayout

Le RowLayout s’utilise de manière similaire au FillLayout à la différence prés que ce dernier ne
permet pas l’affichage des composants sur une autre ligne/colonne. En effet le RowLayout permet de
définir le sens de votre affichage : VERTICAL ou HORIZONTAL. Vous pouvez bien évidement
spécifier :

    •   le nombre de composants maximum que vous souhaitez afficher par colonne ou ligne
    •   les différentes marges (gauche, droit, hauteur, largeur, du bord,…)
    •   les options de gestion (pack, wrap, …)

Voici un exemple très sommaire d’utilisation du RowLayout :
6.5.FormLayout

Le FormLayout est le layout manager le plus complexe dans une application SWT, sont maniment
peut demander un certain temps d’adaptation.
Ce gestionnaire de positionnement permet de spécifier que le composant/conteneur n’est
redimensionnable que sur une partie et qu’il est de taille fixe certaines autres.
Voici un exemple montrant comment, un seul des composants est rendu redimensionnable (le Text de
type MULTI : multiligne). En effet vous remarquerez que le composant Text simple lui ne change
jamais de taille. La zone de texte multilignes se redimensionne mais selon des contraintes bien
précise le bord supérieur et le bord gauche restent fixe et c’est simplement la partie droite et le bord
du bas qui permettent l’étirement du composant.




La taille entre chacun des bords est fixés afin que lorsque l’application est redimensionnée, les tailles
de marge spécifiée soit conservé et non pas mise à jour de manière proportionnel comme le
permettent beaucoup d’autres layout manager.
Voici un exemple du code permettant de reproduire l’application précédante :




Vous remarquerez la présence de plusieurs classes :

    •   FormLayout : spécification du layout manager
    •   FormData : regroupement de plusieurs FormAttachement
    •   FormAttachment : spécification d’une contrainte

Vous pourrez grâce à ces classes fixer la propriété (top par exemple) d’un composant avec la
propriété d’un autre. Ainsi si le composant, sur lequel un autre composant se rattache, vient à être
déplacé, alors le composant rattaché sera automatiquement mise à jour.
Ce gestionnaire de positionnement reprend donc vraiment les concepts de hierarchies de composants
car vous pouvez émettre vos contraints entre un composant et son conteneur parent (Composite,
Group, …).
Les développeurs d’applications Swing seront plus facilement attirés par les layouts de type
FillLayout, GridLayout ou RowLayout alors que c’est pourtant bien le FormLayout qui offre le plus
de possibilité possible. Dans les applications graphiques complexe, c’est généralement un
FormLayout qui est conjointement utilisé avec des GridLayout.
Vous pouvez bien-évidement, tout comme en Swing, créer vos propre layout dans le cas où ceux
proposé en standard par SWT ne vous suffirez pas. Pour ce faire il vous faudra créer une classe qui
hérite de la classe Layout.
Avant de commencer à appliquer un layout sur votre application ou de vous lancer dans le
développement de votre layout personnalisé il vous faut bien analyser vos besoins afin de déterminer
si la combinaison de plusieurs layout ne permettrait pas de remplir vos attentes. De plus il est
généralement conseillé d’utiliser différents layouts dans plusieurs conteneur pour simplifier le
maintient de vos applications.

7.Les évènements

Les évènements, bien qu’ils utilisent des classes totalement différentes, sont assez semblables à ceux
que vous pouviez rencontrer dans une application Swing.
Certaines différences sont néanmoins présente, notament dés la méthode main, pour le lancement de
votre application vous devrez effectuer une boucle afin de tester si l’application se ferme ou pas.
Cette boucle doit être explicitement codé par le programmeur afin de lire et de vérifier si l’application
n’est pas utilisée par une autre application et/ou processus. Si ce cas de « non utilisation » vient à se
produire, le CPU sera alors libéré de la mémoire afin d’aléger sa charge jusqu'à ce que l’application
soit à nouveau utilisé. Ce principe sera particulièrement utilisé lorsque l’application est réduite et
qu’elle ne fait aucune opération ou alors lorsque l’application est fermée.
Voici l’exemple de code qui correspond à cette boucle à coder dans la méthode main, Visual Editor
vous génèrera un code semblable par défaut :




En utilisant le mode visuel de Visual Editor, vous pourrez attacher directement des évènements à vos
composants graphiques via le menu de la même manière que vous l’aurriez faire pour une application
Swing. Le nom des évènements a cependant changé mais leurs utilisations reste similaires.




En utilisant le menu précédant, Visual Editor vous génrera le bloc de code suivant :




Vous n’avez donc qu’a remplacer l’affichage qui est généré par défaut par le comportement que vous
souhaiter donner à votre composant.

Vous pouvez également choisir de ne pas créer un listener par composant, pour cela il vous faudra
avoir recours au système d’héritage/implémentation. Voici la liste des évènements standards qui vous
seront proposés, pour chacun d’eux vous aurez le choix d’hériter d’une classe d’Adapter du listener
ou alors d’implémenter de l’interface :
8.Conclusion

JFace travaille en collaboration avec SWT pour les rendus graphiques des applications. Cela montre
donc bien la performance et la tendance à utiliser SWT.
SWT se basant directement sur les fonctions natives de vos systèmes d’exploitation, cela rend
l’application beaucoup plus rapide et performante.
Le seul inconvénient sera, par rapport à une application Swing, que vous devrez compiler votre
application selon le type de la plateforme sur lequel elle se destine. En effet avec une application
Swing vous utilisiez le même fichier Jar pour exécuter votre application sur plusieurs plateformes.
Avec un application SWT vous devrez avoir autant de fichier Jar que de plateforme pour laquel vous
destinez votre application, le code source lui restera le même quelque soit la plateforme et vous
aurrez un gain de performance à lors de l’exécution. Les API SWT sont portable, leur implémentation
que vous en ferez dans vos applications ne le sera pas. C’est pour cela qu’il vous faudra une
implémentation par plateforme de destination (JARs et d’éventuelles DLLs). Le code, utilisant les
fonctions et composants natifs de votre systême d’exploitation, il sera parfois nécessaire de modifier le
code source car les classes que vous utiliserez, selon la plateforme, ne s’emploierons pas
nécessairement de la même manière.
A vous de tester et de choisir donc entre les possibilités d’émulations que propose Swing ou
l’utilisation des composants et fonctions natives que propose SWT.
Ce cours a simplement eu pour vocation de vous présenter le principe des applications SWT et
comment aider vos développements graphiques à l’aide de Visual Editor, n’ésitez donc pas à tester
SWT pour vos futurs développements.
Il existe d’autres plugins de modeleur graphique comme SWT Designer ou d’autres intégrés
directement à d’autres IDE qu’Eclispe (JBuilder, Sun Java Studio Creator, Net Beans, ...). L’avantage
de Visual Editor est :

    •   Le plugin ainsi que sont intégration dans Eclipse sont gratuit, certains autres plugins offrent
        des fonctionnalités réduite dans leur version gratuite
    •   l’évolution du plugin assez rapide (mise à jour, correction, …)
                                      Applet - Applications Web

1.INTRODUCTION

Ce cours a pour but de vous présenter les applets en java. A la fin de ce petit tutorial vous serez donc
capable de créer une applet et l'insérer dans votre page web.
Le langage Java permet de concevoir deux types de programmes : les applications autonomes et les
applets.
Une application autonome est une application classique, qui s'exécute sous le contrôle direct du
système d'exploitation.
Une applet est une application qui est chargée par un navigateur (ou un appletviewer) et qui s'exécute
sous le contrôle de celui-ci.
Une applet est un programme Java s'exécutant sur une page WEB. Ce sont en fait des bytecodes que
vous téléchargez, puis s'exécute sur votre propre machine. Pour exécuter une applet, vous devrez
disposer d'un navigateur WEB (par exemple Internet Explorer ou Firefox).

2.Le cycle de vie d'une Applet

2.1. Présentation
Pour créer une applet, il faut créer une sous classe de la classe java.applet.Applet ou
javax.swing.JApplet et redéfinir ses méthodes. Les applets n'ont pas de fonction main(), elles
utilisent des méthodes qui sont appelées par le navigateur lorsque des événements spécifiques
surviennent durant leur fonctionnement.

Cette classe sera la classe de l'applet, c'est elle dont le nom doit figurer dans le champ code du tag
<APPLET> de la page HTML. Les méthodes de la classe de l'applet permettent de :

    •   Récupérer et gérer les paramètres
    •   Avoir des images
    •   Avoir et jouer des séquences sonores
    •   Interagir avec le navigateur
    •   Gérer proprement les événements du cycle de vie de l'applet

2.2. Ce que fait le navigateur
Un navigateur gère les éléments qu'il a chargés à l'aide d'une pile. Au sommet de celle-ci se trouve le
document en cours d'interprétation par le navigateur, c'est-à-dire la page visualisée par l'utilisateur.
Une page HTML aura donc deux états :

    •   Active : elle est au sommet de la pile
    •   Inactive : elle n'est pas au sommet de la pile

Lorsque dans une page active le navigateur rencontre la marque <APPLET
CODE="Nom_de_classe"...>, il charge le pseudo code de la classe spécifiée puis il essaie
d'exécuter l'applet.

2.2.1.Chargement du pseudo code :
Les navigateurs reconnaissant Java peuvent ajouter des définitions de classes à leur code lors de leur
exécution. C'est ce qui se passe lorsqu'une classe d'applet est chargée, il faut noter que c'est
l'environnement d'exécution Java du navigateur qui exécute cela, la classe chargée devenant partie
intégrante du système d'exécution.
Le navigateur crée une instance de la classe de l'applet en utilisant le constructeur par défaut de cette
classe, ce constructeur par défaut peut être surchargé. C'est cette instance qui est référencée par le
mot this dans le texte de la classe de l'applet.

2.2.2.Cycle de vie d'une applet :
Une fois construit, l'objet Applet peut avoir trois états :

    •   Initialisé: Dès que l'objet est construit, la machine virtuelle Java lui applique la méthode init()
        qui le fait passer dans l'état initialisé.
    •   Actif : si la page Web qui référence l'objet est active.
    •   Inactif : si la page Web qui référence l'objet se trouve inactive.
Le programmeur a la possibilité de surcharger les méthodes init(), start(), stop(), et destroy() qui sont
appelées à chaque changement d'état comme l'indique la figure qui suit. Ces méthodes sont héritées
par toute classe fille de la classe Applet, elles sont vides lorsqu'on en hérite.




Les méthodes start() et stop() peuvent être appelées plusieurs fois pendant la durée de vie d'une
applet (lorsque la navigateur quitte le contexte graphique de l'applet et y revient par exemple).
La méthode paint() prend en charge toutes les modifications du contexte graphique lié à l'applet.

2.3. Description des différentes méthodes
void init()
Méthode appelée par le navigateur lorsque l'applet est chargée. Cette méthode rassemble toutes les
initialisations nécessaires à l'applet. Elle est comparable à un constructeur. En principe, cette méthode
doit traiter les valeurs PARAM passée dans le code HTML et ajouter les composants de l'interface
utilisateur.

void start()
Méthode appelée par le navigateur pour demander l'activation de l'applet après l'appel de la méthode
init(). Elle est appelée chaque fois que l'utilisateur replace la page contenant l’applet au premier plan.

void paint()
Méthode appelée par le navigateur pour demander l’exécution du processus d’affichage des
composant de l’applet. Elle est appelée à chaque fois que l'applet devient active, ou lors d’une
modification du contexte graphique lié à l’applet (redimensionnement de la fenêtre, par exemple)

void stop()
Méthode appelée par le navigateur pour demander la désactivation de l'applet. Elle est appelée
chaque fois que l'utilisateur replace la page contenant l’applet à un second plan.

void destroy()
Méthode appelée par le navigateur lorsque l'applet ne sert plus, pour libérer les ressources (la
fermeture de la page, par exemple).

void setSize(int width, int height)
Change les dimensions de l'applet en fixant une nouvelle longueur et largeur. Cette méthode ne
fonctionne pas toujours correctement avec tous les navigateurs.

3.Structure et Eléments d'une Applet

3.1. Extension de classe
La classe Applet est la dernière classe d'une suite de dérivations, par suite lorsque le programmeur
dérive cette classe pour créer ses propres applets il peut utiliser un grand nombre de services.
La plupart des classes de quelque complexité étendent d'autres classes.

Etendre une autre classe consiste à écrire une nouvelle classe qui peut utiliser les champs et les
méthodes définies dans la classe étendue. La classe étendue est la classe parente, et la classe
faisant l'extension est la classe fille.

Une autre façon d'exprimer cela est de dire que la classe fille hérite des champs et des méthodes de
son parent ou de sa chaîne de parents. Les classes filles soit invoquent, soit recouvrent les méthodes
héritées. C'est ce qu'on appelle l'héritage simple.

Le schéma ci-dessous présente les classes dont dérive la classe Applet :
3.2. Description des classes
Object : C'est la classe mère de toutes les classes java. En java toutes les classes sont descendantes
de la classe Object et peuvent donc utiliser les méthodes dont elles héritent.

Component : C'est la classe de base des objets graphiques qui peuvent être affichés à l'écran et qui
peuvent interagir avec l'utilisateur.

Container : permet de définir des objets composants conteneurs d'autres composants.
Il possède une liste des objets contenus et un gestionnaire de mise en forme pour ces objets (layout
manager) déterminant leurs places dans le conteneur et leurs dimensions.

Panel : permet de définir des objets composants conteneurs d'autres composants. La différence avec
Container et Component est que ces deux dernières sont des classes abstraites et ne peuvent pas
être instanciées alors que Panel peut l'être, offrant ainsi une version minimale de conteneur, le
gestionnaire de mise en forme par défaut étant de type FlowLayout.

Applet : Cette classe permet de définir des objets panels particuliers embarqués dans un navigateur
pouvant les afficher.
L'applet implémente un certain nombre de méthodes qui peuvent être appelées par le navigateur.

JApplet : Cette classe permet, tout comme Applet, de définir des objets panels particuliers
embarqués dans un navigateur pouvant les afficher. Elle permet cependant d’utiliser, ici, des
JComponent, et de programmer alors l’applet grâce à SWING.

4.Ecriture simple d'une Applet

4.1. Exemple
Voici un exemple d'une applet très simple

   import java.applet.*;

   import java.awt.*;


   public class HelloWorld extends Applet {

          Label helloLabel;


          //Méthode appelée par le navigateur lorsque
          //l'applet est chargée

          public void init() {

                     helloLabel = new Label("Bonjour tout le monde");
                     setBackGround(Color.yellow);
                     add(helloLabel);
          }
   }

Pour qu'elle puisse être compilée, un fichier applet doit être suffixé par .java.
L'applet HelloWorld sera enregistrée sous le nom de HelloWorld.java
4.2. Quelques explications sur le code source

   import java.applet.*;
   import java.awt.*;


Ces deux lignes signifient que l'on va utiliser les librairies standard java.applet et java.awt qui
contiennent respectivement les classes pour construire des applets et des interfaces
graphiques.

   public class HelloWorld extends Applet {


La classe HelloWorld hérite des attributs et des méthodes de la classe Applet.

          Label helloLabel;


Une étiquette (label) est référencée en attribut de la classe, celle-ci contiendra le texte inséré
entre les guillemets.

          public void init() {

                    helloLabel = new Label("Bonjour tout le monde");
                    setBackGround(Color.yellow);
                    add(helloLabel);
          }


La méthode init() est celle qui est appelée à la création de l'applet. Ici, elle va donner une
couleur de fond jaune à l'applet et insérer l'étiquette helloLabel.

4.3. Compilation d'une Applet
La compilation est lancée par la commande javac suivie du nom du fichier source.
javac HelloWorld.java

Cette instruction provoque la compilation du fichier, mais elle ne crée pas de fichier
exécutable, mais des fichiers semi-compilés. Elle en crée autant qu'il y a de classes définies
dans le fichier source.
Une fois compilé : HelloWorld.class est destiné à être incorporé dans un document HTML.

5.Intégration d'une Applet dans une page HTML

5.1. Exemple
Les applets sont appelées à partir d'une source HTML lu à distance par votre navigateur.

   <HTML>
   <HEAD>
   <TITLE> Test de
   l'applet HelloWorld</TITLE>

   </HEAD>
   <BODY>
   Voici une applet :

   <APPLET CODE =
   "HelloWorld.class" WIDTH=150 HEIGHT=100 ALIGN=left>
   </APPLET>
   <SMALL>- Applet HelloWorld -</SMALL>
   </BODY>
   </HTML>


Le marqueur HTML <APPLET ...></APPLET> donne le nom de l'applet à exécuter.
 5.2. Attributs pour le marqueur Applet

 Voici la liste des attributs que l'on peut utiliser pour le marqueur APPLET :

Attribut

Utilité
CODEBASE =
                  Chemin indiquant où trouver les fichier .class concernant l’applet
"?"
                  Le texte entre guillemets est ce qui apparaît si le navigateur visualisant la
ALT = " ? "
                  page n'est pas capable de lancer des applets java
ALIGN = ?         Contrôle comment l'applet doit s'aligner avec le texte
HEIGHT = ?        Contrôle la dimension verticale de l'espace écran réservé à l'applet
WIDTH = ?         Contrôle la dimension horizontale de l'espace écran réservé à l'applet
                  Ajoute de l'espace supplémentaire à la droite ou à la gauche de l'applet,
HSPACE = ?
                  entre l'espace de l'applet et le texte adjoint.
                  Ajoute de l'espace supplémentaire au-dessous de l'applet, entre l'espace de
VSPACE = ?
                  l'applet et le texte adjoint.

 Vous pouvez essayer de remplacer dans votre feuille HTML la valeur left de l'attribut ALIGN
 successivement par les valeurs right, top et bottom en testant son effet sur votre feuille
 HTML à partir de votre navigateur après chaque modification.


 6.1. Exemple

 Il est possible de passer des paramètres à l’applet, ceux-ci provenant de la page HTML.

    <HTML>

    <HEAD>

    <TITLE> Test de l'applet HelloWorld</TITLE>

    </HEAD>

    <BODY>

    Voici une applet :

    <APPLET CODE =
    "HelloWorld.class" WIDTH=150 HEIGHT=100 ALIGN=left>

    <PARAM NAME = "hellotext" VALUE = "Hello everybody" />

    </APPLET>

    <SMALL>- AppletHelloWorld -</SMALL>

    </BODY>

    </HTML>



 Le marqueur HTML <PARAM NAME = "nom_variable" VALUE = "valeur" /> donne le nom
 (NAME) et la valeur (VALUE) du paramètre à passer à l'applet.

 On récupérera alors ce(s) paramètre(s) de la manière suivante :
import java.applet.*;

import java.awt.*;


public class HelloWorld extends Applet {

    Label helloLabel;

    //Méthode appelée par le navigateur lorsque
    //l'applet est chargée

    public void init() {

            helloLabel = new Label(getParameter("hellotext"));

            setBackGround(Color.yellow);

            add(helloLabel);

    }
}
                 Introduction au langage Java - Présentation &
                                   historique

1.Introduction

1.1.Historique
Le langage Java (café en argo anglais) a été créé dans les années 90, par la firme Sun
Microsystems, qui souhaitait créer un environnement indépendant du hardware et pouvant
permettre de programmer des appareils variés comme des téléviseurs, des magnétoscopes,
des téléphones ou des systèmes embarqués. Ce langage devait aussi permettre de les
contrôler et de les rendre interactifs.
Le langage de programmation Java est un langage orienté objet. Sa syntaxe est assez
similaire a celle du C / C++, mais en rejette quelques caractéristiques techniques de
programmation complexes et non sécurisées. Ainsi, en ayant retiré les difficultés liées au
C++ (pointeur, héritage multiple…), la plate-forme Java initialement développée, a permis de
palier à un manque dans la création de logiciels pour les réseaux.
Ce langage a été créé pour être fonctionnel au sein d’une architecture réseau de machines
et de systèmes d’exploitations hétérogènes, car son implémentation se base sur le
déploiement d’une machine virtuelle qui reste indépendante du support physique.

       Un peu d’histoire…
Dates importantes :

   •   1993 : projet Oak (langage pour l'électronique grand public)
   •   1995 : Java / HotJava1 à WWW3
   •   Mai 95 : Netscape achète la première licence de java
   •   Sept. 95 : JDK 1.0 b1 (Java Developpement Kit)
   •   Déc. 95 : Microsoft se dit intéressé
   •   Janv. 96 : JDK 1.0.1
   •   Eté 96 : Java Study Group ISO/IEC JTC 1/SC22
   •   Fin 96 : RMI, JDBC, JavaBeans, ...
   •   Fév. 97 : JDK 1.1

Le langage Java a débuté dans les années 90 avec James Gosling qui souhaitait développer
un langage de programmation indépendant de la plate-forme hardware. Oak (Traduction :
Chêne) fut tout de même un échec.
Par la suite Bill Joy (co-fondateur de la firme Sun Microsystems) proposa une nouvelle
version d’Oak appelée Java (en rapport avec l’île de Java d’où les programmeurs puisaient
le café nécessaire à leur création). Son but était de palier à une déficience, dans le domaine
des langages de programmation conçus pour des machines et des logiciels hétérogènes,
qu’englobe Internet.
On peut trouver, gratuitement, sur le marché une machine virtuelle (Java Virtual Machine),
un compilateur ainsi que de nombreux outils pour permettre la conquête du Web qui devra
être celle de Java.
Après de très nombreuses modifications visant à améliorer le système, Java est devenu plus
qu’une simple solution Internet, c’est dorénavant un langage utilisé pour toute sorte de
développement.

Né à la même époque que le Web, le langage Java répond à toutes les attentes de la
programmation Web :

   •   un langage permettant un fonctionnement sur des machines structurées
       différemment.
   •   une nécessitée de bande passante la plus minime possible, pour palier a l’étroitesse
       de celle offerte par le Web.

HotJava, navigateur Java né en 1994, était déjà capable de faire fonctionner les applets
(applications Java exécutées par le navigateur). Mais Netscape fut l’un des éléments
essentiels à la création et l’implantation de Java dans le parc informatique, en intégrant Java
dans son logiciel.
1.2.Avantages
Le langage Java est un langage capable de s’exécuter sur n’importe quelle plate-forme. Il est
semi-compilé, semi-interprété.
Le code source Java est transformé en de simples instructions binaires.
(Byte Code= Instructions générées par le compilateur qu’un ordinateur abstrait peut
exécuter).

   •   Robuste et sûr :

   •   Pas de pointeurs.
   •   Compilateur très strict car toutes les valeurs doivent être initialisées.
   •   Traitement des exceptions obligatoire.
   •   Les erreurs à l’exécution sont vérifiées tout comme les limites des tableaux.
           o Sécurisé :

Désallocation de la mémoire dynamique grâce au Garbage Collector (Ramasse Miettes) qui
permet un accroissement de la sécurité en résolvant les problèmes liés aux allocations et à
la libération explicite de mémoire.

   •   Simple :

Le code source en Java est simple. Il n'y a plus de préprocesseur, de « #define »,
« typedef », ou de recours aux fichiers headers, car Java permet la définition d'autres
classes et de leurs méthodes. De plus, les éléments tels que les pointeurs, l’héritage multiple
ou la surcharge d'opérateurs sont autant de points complexes du langage C++ qui sont soit
inexistants, soit traités implicitement en Java. Par ailleurs, la libération de mémoire est
transparente pour l'utilisateur en Java et une meilleure gestion des erreurs a été
implémentée.

   •   Portable :

Le compilateur Java génère du byte-code. Celui-ci constitue les instructions pour la machine
virtuelle JVM).
La Java Virtual Machine (JVM) est disponible gratuitement sur le site de Sun, et ceci, pour la
plupart des plateformes (Linux, Windows, Mac, Solaris…).
La taille des types primitifs est indépendante de la plateforme. Java supporte un code source
écrit en Unicode. (Code Universel). Java est accompagné d'une librairie standard, l’API Java,
téléchargeable sur le site de Sun.
2.Présentation

2.1.JRE
Le JRE (Java Runtime Environement) est le conteneur de l’environnement d’exécution du
Java qui doit être installé sur toutes les machines susceptibles de lancer des applications de
type Java.


2.2.JDK
Le JDK (Java Development Kit) contient le JRE. C’est l’environnement indispensable à la
création de programmes Java. Il inclut un compilateur, un interpréteur, ainsi qu’un
environnement de développement comprenant des outils et des utilitaires favorisant la
programmation.

Le JDK contient un grand nombre d'outils tels que :
javac : Java Compiler, le compilateur Java ; il compile un fichier source .java en un fichier
exécutable .class contenant du bytecode.
java : Exécute le ou les fichiers compilés par javac. C’est l’interpréteur Java, c'est-à-dire une
implémentation de la machine virtuelle Java (JVM).
javadoc : C'est un utilitaire très puissant qui permet de construire, à partir des commentaires
insérés dans des sources Java, des fichiers HTML à propos des classes, méthodes,
données membres...définies dans ces sources.
apt : Annotation processing tool, nouveauté de Java 2 Standard Edition 5.0 (1.5 « Tiger »),
permet l’exploitation des annotations (à ne pas confondre avec les commentaires).
appletviewer : Ce programme permet d'exécuter une Applet Java sans nécessiter d'utiliser
un navigateur web. Il dispose d'une interface graphique.
jdb : Débogueur Java qui permet de détecter les erreurs de programmation.
jar : Permet la création et la gestion des fichiers JAR (Java Archive).
Il existe plusieurs versions de Java, chacune d’entre elles y apportant des améliorations
visant à rendre Java toujours plus performant:
Depuis la version 1.2, le JDK se nomme plus simplement Java 2.
La version 1.3 de Java est désignée sous le nom Java 2 version 1.3.
La version 1.5 de Java est désignée par le numéro de version 5.0.
                           Les Exceptions - Gestion d'erreurs

1.Présentation

A l’origine, Java a été conçu dans l’optique de se définir comme un système embarqué. Ces
systèmes embarqués ou programmes, sont intégrés dans des périphériques spécialisés
comme les PDA1, les téléphones portables, et autres appareils électroménagers divers. Pour
ce type d’utilisation, il parait indispensable de prendre en compte une gestion efficace des
erreurs du logiciel.
En effet, il n’est pas concevable pour beaucoup d’utilisateurs d’avoir un appareil de type
téléphone, micro-onde ou encore grille pain qui devienne hors service suite à un
disfonctionnement du logiciel intégré dans l’appareil.
Il n’est pas possible d’éliminer toute possibilité d’erreur dans nos programmes, mais nous
pouvons tout de même tenter de résoudre les problèmes en ciblant et en analysant les
éventuelles erreurs de l’application qui pourront être prévues de manière rigoureuse et
systématique.

2.Traitement des erreurs

A la suite d’une erreur à l’exécution d’un programme deux manières de gérer cette erreur
sont possibles :

2.1.Gestion d’erreur

2.1.1.Erreur prévue
Si l’erreur a été prévue par le programmeur, ce qui peut être un cas exceptionnel suite à une
erreur engendrée par un utilisateur qui entrerait une valeur trop grande, le programme doit
prévoir des lignes de code spécialement prévues pour traiter ces erreurs particulières.

2.1.2.Erreur imprévue
Selon que l’erreur était prévisible ou non, deux cas de figure possible :

Erreur qui était prévisible
Si l’erreur était prévisible ce cas de figure ne peut avoir lieu en Java car le compilateur doit
dé