Chapitres traités Architecture multi-tiers



Documents pareils
Java pour le Web. Cours Java - F. Michel

1. Installation d'un serveur d'application JBoss:

24/11/2011. Cours EJB/J2EE Copyright Michel Buffa. Plan du cours. EJB : les fondamentaux. Enterprise Java Bean. Enterprise Java Bean.

Compte Rendu d intégration d application

RMI le langage Java XII-1 JMF

Java 7 Les fondamentaux du langage Java

Environnements de Développement

Refonte front-office / back-office - Architecture & Conception -

Institut Supérieur de Gestion. Cours pour 3 ème LFIG. Java Enterprise Edition Introduction Bayoudhi Chaouki

Chapitre 1 : Introduction aux bases de données

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère

Mise en œuvre des serveurs d application

Création d une application JEE

Architecture N-Tier. Ces données peuvent être saisies interactivement via l interface ou lues depuis un disque. Application

Remote Method Invocation (RMI)

Avant-propos 1. Avant-propos Organisation du guide À qui s'adresse ce guide?...4

Projet de Veille Technologique

Types d applications pour la persistance. Outils de développement. Base de données préexistante? 3 modèles. Variantes avec passerelles

RMI. Remote Method Invocation: permet d'invoquer des méthodes d'objets distants.

Généralités sur le Langage Java et éléments syntaxiques.

2 Chapitre 1 Introduction

Introduction à la plateforme J2EE

Institut Supérieure Aux Etudes Technologiques De Nabeul. Département Informatique

Sage CRM. 7.2 Guide de Portail Client

Architecture JEE. Objectifs attendus. Serveurs d applications JEE. Architectures JEE Normes JEE. Systèmes distribués

JAVA 8. JAVA 8 - Les fondamentaux du langage. Les fondamentaux du langage Java. Avec exercices pratiques et corrigés JAVA 8 29,90.

Etude de cas : PGE JEE V2

Préparer la synchronisation d'annuaires

Formation en Logiciels Libres. Fiche d inscription

Architecture d'entreprise : Guide Pratique de l'architecture Logique

La réplication sous SQL Server 2005

J2EE - Introduction. Développement web - Java. Plan du chapitre

Didacticiel de mise à jour Web

Livre Blanc WebSphere Transcoding Publisher

Qu'est-ce que le BPM?

Business & High Technology

MOTEUR DE WORKFLOW Mise en oeuvre d'openwfe Version septembre 2006

RENDRE VOS APPLICATIONS JAVA PLUS EFFICACES Ce qu'il faut savoir

Programme «Analyste Programmeur» Diplôme d état : «Développeur Informatique» Homologué au niveau III (Bac+2) (JO N 176 du 1 août 2003) (34 semaines)

RAPPORT DE CONCEPTION UML :

et Groupe Eyrolles, 2006, ISBN :

10. Base de données et Web. OlivierCuré

Encapsulation. L'encapsulation consiste à rendre les membres d'un objet plus ou moins visibles pour les autres objets.

Application web de gestion de comptes en banques

Compte-rendu de projet de Système de gestion de base de données

basée sur le cours de Bertrand Legal, maître de conférences à l ENSEIRB Olivier Augereau Formation UML

TP3. Mail. Attention aux fausses manoeuvres lors de ce TP vous pouvez endommager votre mail sur ouindose.

Acronis Backup & Recovery 10 Advanced Server Virtual Edition. Guide de démarrage rapide

TP Composants Java ME - Java EE. Le serveur GereCompteBancaireServlet

Module 0 : Présentation de Windows 2000

Manuel utilisateur. des. listes de diffusion. Sympa. l'université Lille 3

EXA1415 : Annotations

WEBSERVICES. Michael Fortier. Master Informatique 2ème année. A308, Université de Paris 13

SIO Page 1 de 5. Applications Web dynamiques. Prof. : Dzenan Ridjanovic Assistant : Vincent Dussault

contact@nqicorp.com - Web :

TAGREROUT Seyf Allah TMRIM

Les Architectures Orientées Services (SOA)

Architecture Orientée Service, JSON et API REST

Le stockage local de données en HTML5

Messagerie asynchrone et Services Web

Serveur FTP. 20 décembre. Windows Server 2008R2

La Solution Crypto et les accès distants

Programmation par composants (1/3) Programmation par composants (2/3)

Interfaces graphiques avec l API Swing

JOnAS Day 5.1. Outils de développements

Description de la formation

Conception d'un système d'information WEB avec UML Par Ass SERGE KIKOBYA

Microsoft Application Center Test

1 JBoss Entreprise Middleware

Formation Webase 5. Formation Webase 5. Ses secrets, de l architecture MVC à l application Web. Adrien Grand <jpountz@via.ecp.fr> Centrale Réseaux

Réseau : Interconnexion de réseaux, routage et application de règles de filtrage.

Module BD et sites WEB

Quelques patterns pour la persistance des objets avec DAO DAO. Principe de base. Utilité des DTOs. Le modèle de conception DTO (Data Transfer Object)

DirXML License Auditing Tool version Guide de l'utilisateur

L'évolution de VISUAL MESSAGE CENTER Architecture et intégration

CONNECTEUR PRESTASHOP VTIGER CRM

Fiche de l'awt Intégration des applications

1. Introduction Création d'une macro autonome Exécuter la macro pas à pas Modifier une macro... 5

Service de certificat

2 Grad Info Soir Langage C++ Juin Projet BANQUE

Architectures en couches pour applications web Rappel : Architecture en couches

Communiqué de Lancement

Intergiciel - concepts de base

Java Naming and Directory Interface

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

Logiciel de gestion de caisse et d ardoises

Séquence de découverte de SparkAngels Logiciel d entraide numérique

JOnAS Day 5.1. Clustering

Assistance à distance sous Windows

Extension SSO Java. Cette note technique décrit la configuration et la mise en œuvre du filtre de custom SSO Java.

Cours Bases de données

Chapitre 10. Architectures des systèmes de gestion de bases de données

DÉVELOPPEMENT INFONUAGIQUE - meilleures pratiques

Nouvelles Plateformes Technologiques

TP JEE Développement Web en Java. Dans ce TP nous commencerons la programmation JEE par le premier niveau d une application JEE : l application web.

TP1 : Initiation à Java et Eclipse

Projet : PcAnywhere et Le contrôle à distance.

Le rôle Serveur NPS et Protection d accès réseau

Transcription:

EJB3 - Bean session Chapitres traités Architecture multi-tiers Après avoir longuement travailler sur les applications Web, nous allons passer, à partir de maintenant, sur l'autre grande ossature concernant Java EE, c'est-à-dire les EJB. Je rappelle que EJB veut dire Entreprise Java Bean. C'est ce type de composants qui s'intéressent plus particulièrement à la logique métier au travers d'objets distants. Ces EJB servent d'intermédiaire entre les applications de type fenêtrées, ou application Web, et la base de données. Après avoir vu les différents concepts généraux sur les systèmes client-serveur et les architectures multi-tiers, nous passerons ensuite sur l'installation et l'utilisation de serveur d'applications qui intègrent ces EJB. Nous montrerons l'utilisation de ces EJB au travers d'applications classiques, en mode console et en mode graphique, mais aussi au travers des applications Web. Par contre, nous nous limiterons dans cette étude qu'à une partie des EJB, je veux dire les Beans de type session. Architecture multi-tiers Avant de rentrer dans le vif du sujet concernant les Beans de type session, nous allons revoir les principes fondamentaux constituant les applications distribuées. Toute l'application est sur une même machine - fonctionnement en standalone Une application monolitique est un programme constitué d'un seul bloc et s'exécute sur une seule machine. Ces applications sont généralement utilisées dans le domaine du temps réel ou bien au sein d'applications demandant de grandes performances. Ces applications sont utilisées en standalone (de manière autonome) sur des machines personnelles. L'avantage de cette structure c'est que l'application possède un grand niveau de performance en terme de temps de réponse. Le problème, c'est de pouvoir déployer cette application sur l'ensemble du parc machines de l'entreprise, avec également le souci de la gestion des versions. Comme nous l'avons découvert dans une étude antérieure, pour gérer le déploiement, il est possible de passer par Java Web Start au travers d'un serveur Web. Grâce à cette technique, la gestion des versions est totalement assurée.

Apllication client-serveur Dès l'apparition des réseaux, ces applications ont cherché à évoluer et ont abouti à des architectures dites client-serveur, permettant de séparer la partie cliente qui s'intéresse plus particulièrement à l'ihm, et de regrouper la partie applicative sur un serveur. Cependant, le développement de ce genre d'application nécessite la création d'un protocole de communication entre le client et le serveur. Ce protocole étant souvent propriétaire, l'évolution de ces applications doivent se faire par les mêmes développeurs. Par ailleurs, le serveur doit gérer la connexion de plusieurs clients en même temps. Pour les systèmes d'information d'entreprise, ces solutions restent trop limitées. En effet, leur problème majeur est leur manque de séparation entre les différents éléments qui les constituent. C'est également le manque de standard qui a poussé la communauté au concept de séparation par tiers afin d'optimiser leurs développements. Application multi-tiers Dans le milieu professionnel, les applications doivent être plus robustes et travaillent généralement sur des gros volumes de données. Elles doivent, de plus, connecter différents départements au sein même d'une entreprise. La maintenance et la stabilité de ces applications sont donc des priorités pour les architectes et les développeurs. Différents modèles existent. Le plus connu est sans doute le modèle trois-tiers, largement utilisé par les grandes entreprises ayant besoin de systèmes complexes basés sur la même organisation des informations : la logique métier. Ce modèle permet donc d'avoir plusieurs applications différentes avec une même logique métier, elles peuvent alors mettre en place facilement des applications distribuées dans un environnement hétérogène. De manière théorique, une application distribuée est une application découpée en plusieurs unités. Chaque unité peut être placée sur une machine différente, s'exécuter sur un système différent et être écrite dans un langage différent. Le modèle trois-tiers : Ce modèle est une évolution du modèle d'application client-serveur. L'architecture trois-tiers est donc divisées en trois niveaux : > Tiers client qui correspond à la machine sur laquelle l'application cliente est exécutée. > Tiers métier qui correspond à la machine sur laquelle l'application centrale est excutée. > Tiers accès aux données qui correspond à la machine gérant le stockage des données.

Ce système utilise un navigateur pour représenter l'application sur la machine cliente, un serveur Web pour la gestion de la logique de l'application et un serveur de bases de données pour le stockage des données. La communication entre le serveur Web peut s'effectuer via le protocole HTTP, la communication avec la base de données via l'api JDBC. La séparation entre le client, l'application et le stockage, est le principal atout de ce modèle. Toutefois, dans des architectures qui demandent de nombreuses ressources, il sera assez limité. En effet, aucune séparation n'est faite au sein même de l'application, qui gère aussi bien la logique métier que la logique fonctionnelle ainsi que l'accès aux données. Le modèle multi-tiers : Dans le cadre d'applications beaucoup plus importantes, l'architecture trois-tiers montre ses limites. L'architecture multi-tiers est simplement une généralisation du modèle précédent qui prend en compte l'évolutivité du système et évite les inconvénients de l'architecture trois-tiers vus précédemment. Dans la pratique, on travaille généralement avec un tiers permettant de regrouper la logique métier de l'entreprise. L'avantage de ce système, c'est que ce tiers peut être appelé par différentes applications clientes, et même par des applications classiques, de type fenêtrées, qui ne passent donc pas par le serveur Web. Entre parenthèses, dans ce dernier cas de figure, nous nous retrouvons de nouveau avec une architecture trois-tiers. Si l'architecture est bien étudiée dès le début et s'exécute sur une plate-forme stable et évolutive, le développeur n'aura alors plus qu'à connecter les différents systèmes entre eux. De même, les types de clients peuvent être plus variés et évoluer sans avoir d'impact sur le coeur du système. La logique métier est la principale de toute l'application. Elle doit s'occuper aussi bien de l'accès aux différentes données qu'à leurs traitements, suivant les processus définis par l'entreprise. On parle généralement de traitement métier qui regroupe : - 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. Il est cependant plus propre de séparer toute la partie accès aux données de la partie traitement de la logique métier. Cela offre plusieurs avantages. Tout d'abord, les développeurs ne se perdent pas entre le code métier (représenté par les EJBs de type session), qui peut parfois être complexe, et le code d'accès aux données (représenté par les EJBs de type entité), plutôt élémentaire mais conséquent. Cela permet aussi d'ajouter un niveau d'abstraction sur l'accès aux données et donc d'être plus modulable en cas de changement de type de stockage. Il est alors plus facile de se répartir les différentes parties au sein d'une équipe de développement. Les Entreprises JavaBeans

Le modèle d'architecture distribuée que nous venons de découvrir impose l'idée qu'une application est découpée en plusieurs unités. Des standards ont vu le jour. Le plus général est sans doute CORBA qui correspond au modèle idéal des applications distribuées. Cependant la lourdeur et la complexité de mise en oeuvre de ce genre d'application sont les inconvénients majeurs de cette technologie. C'est pourquoi, un modèle plus restrictif mais plus performant a vu le jour : le modèle EJB. Objets distribués La communication entre les applications, comme nous l'avons vu dans nos différentes études antérieures, a été introduite par la programmation client-serveur et le principe des sockets. Ce modèle de bas niveau oblige les concepteurs et développeurs à inventer des protocoles pour faire communiquer leurs applications. Avec l'arrivée de le programmation orientée objet, la communauté a souhaité développer des standards et surtout faciliter la communication inter-applications via des modèles de plus niveaux par l'intermédiaire de la technique d'objets distants. Ainsi, des objets existent sur différentes machines et communiquent entre eux, c'est ce que nous définissons par objets distribués. Les objets distribués sont une solution à ce problème d'efficacité. Nous pouvons les considérer simplement comme des objets pouvant communiquer entre eux par le réseau de façon autonome. Il est souhaitable, alors, d'avoir un mécanisme permettant, au développeur d'application cliente, d'effectuer un appel de méthode sur l'objet de façon ordinaire, sans se préoccuper du format de la requête. De la même façon, le développeur de l'application serveur pourra répondre aux applications clientes, sans avoir à s'inquiéter du protocole à mettre en place. Au travers de ce mécanisme, nous utilisons ainsi un objet à distance. Nous avons déjà abordé cette approche au travers notamment de RMI. Les EJB, toutefois, représentent à un niveau beaucoup plus sophistiqué, les objets distants que nous avons déjà mis en oeuvre lors de l'études des RMI. Faisons quand même un petit rappel sur cette technologie RMI. RMI RMI (Remote Method Invocation) correspond au modèle d'invocation à distance mis en oeuvre par Java. Grâce à RMI, Java permet l'accès via un réseau aux objets se trouvant sur un ordinateur distant. Pour créer un objet avec RMI : 1. Il faut d'abord concevoir une interface étendant l'interface java.rmi.remote. Cette interface définit les opérations que doit exposer l'objet accessible à distance. 2. L'étape suivante consiste à concevoir l'objet comme une classe implémentant l'interface préalablement définie. Cette classe doit étendre la classe java.rmi.unicastremoteobject qui fournit les moyens nécessaires à la communication entre l'objet et ses clients. 3. Enfin, il reste à définir l'application qui créera une instance de cet objet et l'enregistrera dans le registre RMI. Ce registre est un simple service de localisation permettant aux ordinateurs distants de trouver l'objet à l'aide du nom qui lui est attribué. 4. Ce service est mis à contribution par l'application cliente, qui demande au registre l'objet nommé et effectue un casting vers l'interface créée lors de la première étape. Ce que fournit les bases de l'implémentation d'une architecture client - serveur : 1. Un registre pour la localisation des composants, 2. Les moyens de communication nécessaires pour l'invocation des opérations et le passage des paramètres et des valeurs de retour, 3. Ainsi qu'un mécanisme simple pour la gestion des accès aux ressources systèmes. Toutefois, RMI est une technologie légère, insuffisante pour satisfaire les besoins des applications d'entreprises distribuées. Il lui manque les éléments essentiels que sont une gestion avancée de la sécurité, le contrôle des transactions ou la faculté de répondre efficacement à une montée en charge. Bien qu'elle fournisse les classes fondamentales, elles ne constitue pas une infracstructure pour un serveur d'applications devant recevoir les composants métier et s'adapter à l'évolution du système et de sa charge. Les Entreprises JavaBeans C'est là qu'intervient les Entreprise JavaBeans. Les EJB sont des composants Java qui implémentent la logique métier de l'application, ce qui permet à cette logique d'être décomposée en éléments indépendants de la partie de l'application qui les utilise. L'architecture Java EE comporte un serveur qui sert de conteneur pour les EJB. Ce conteneur charge tous les composants à la demande et invoque les opérations qu'ils exposent, en appliquant les règles de sécurité et en contrôlant les transactions. Cette architecture est très complexe mais heureusement totalement transparente au développeur. Le conteneur d'ejb fournit automatiquement toute la plomberie et le câblage nécessaire pour la réalisation d'applications d'entreprise.

La création des EJB ressemble beaucoup à celle des objets RMI. Cependant, le conteneur fournissant des fonctionnalités supplémentaires, vous pouvez passer plus de temps à créer l'application au lieu d'avoir à gérer des problèmes d'intendance tels que la sécurité ou les transactions. Il existe plusieurs types d'ejb : 1. Les beans sessions : Les beans sessions sont des composants conçus pour implémenter la logique de l'application. Une application comporte généralement plusieurs beans sessions qui sont individuellement chargés d'une partie du traitement. Les beans sessions sont alors des briques de l'ossature globale. En général, un bean session est responsable d'un groupe de fonctionnalités dans un domaine particulier. Par exemple, une application pour une école peut posséder un bean session contenant la logique nécessaire pour gérer les étudiants. Un autre sera responsable de la liste des cours et des programmes. Les beans sessions, comme leur nom l'indique, ont une durée de vie correspondant à celle de la "conversation" ou "session" entre l'application cliente et le composant. Selon le choix de celui-ci, un bean session peut maintenir un état (stateful) pendant toute la durée de la session (c'est-à-dire conserver l'état des attributs internes aux objets de façon à maintenir la conversation avec le client) ou au contraire sans état (stateless), ce qui signifie qu'il fournit un accès à des méthodes implémentant la logique applicative (comme RMI), mais ne conserve aucun résultat auquel le client pourrait faire référence ultérieurement. 2. Les beans entités : Avant le développement de la programmation objet, les programmes étaient généralement développés à l'aide de langages procéduraux et stockaient les données dans des bases de données relationnelles. Les bases de données relationnelles ont ainsi acquis une maturité telle qu'il est souvent avantageux de les utiliser également dans des applications orientés objets. Le problème de cette approche est qu'il existe une différence fondamentale entre ces deux technologies et que leur cohabitation au sein d'une même application est un peu contre nature. L'utilisation des beans entités permet de bénéficier du meilleur des deux mondes. Ce sont des objets qui utilisent le mécanisme de persistance. Rappelons que la persistance correspond à l'utilisation d'une base de données qui stocke la valeur des attributs de chacun de ces beans entités. Avec les beans entités, il n'est pas du tout nécessaire de maîtriser le langage SQL ainsi que la connectivité JDBC. De fait, la base de données de type relationnelle devient une base de données de type objet. Ce type de bean est vraiment intéressant puisque sans cette technologie, le développeur passe généralement beaucoup de temps à la gestion de la base de données. Avec un bean entité, le développeur ne voit pas du tout la base de données et donc ne s'en occupe pas ; il peut alors passer tout son temps sur l'application elle-même. Dans un scénario EJB type, lorsqu'un bean session doit accéder à des données, il appelle les méthodes d'un bean entité. Par exemple, une application pour la gestion d'une école peut posséder un bean entité nommé Etudiant qui possèdera une instance pour chaque étudiant inscrit. Les beans entités lisent et écrivent les données dans des tables fournissant ainsi une abstraction orienté objet d'une base de données relationnelle. 3. Les beans contrôlés par messages : Au sein d'une application d'entreprise de grande ampleur, il peut être intéressant de faire communiquer entre elles, les différentes sous-applications clientes et serveur. Par communication, il faut comprendre un envoi de données directement interprétables et utilisables par les autres applications. Ce sont les beans contrôlés par messages qui permettent de traiter les messages venant d'autres applications. Ces messages sont de type asynchrone, ce qui sous-entends que le serveur d'application met en oeuvre un service de messagerie. Ainsi, l'application qui doit recevoir le message ne doit pas nécessairement être active au moment de l'envoi du message. De toute façon, elle le recevra. Les messages asynchrones échangés par les systèmes sont analogues aux événement déclenchés par les composants d'une interface utilisateur et envoyés au gestionnaire d'événements de la JVM. Par exemple, dans une application de commerce, un module de vente en gros pourrait utiliser un tel composant pour recevoir les commandes envoyées sous forme électronique par les détaillants. Architecture du serveur d'applications, relation avec l'extérieur Un serveur d'application met en oeuvre toute les spécifications prévues par Java EE. Nous avons déjà pris connaissance que Java EE permet de fabriquer des applications Web à l'aide de servlet et de pages JSP, le tout orchestré par la technologie JSF. Par ailleurs, nous découvrons maintenant que Java EE intègre les EJB. En réalité, un serveur d'application possède deux conteneurs, un pour la partie Web et un autre pour les objets distribués. C'est comme si nous avions deux services en un seul. L'avantage ici, c'est que ces deux services font parties de la même machine virtuelle Java. Du coup, il est possible d'utiliser les EJB comme si c'étaient des objets normaux et non comme des objets distants. Dans ce cas là, il faut passer par l'intermédiaire des composants issues de l'application Web. Si vous désirez atteindre les EJB sans passer par l'application Web, c'est que vous utilisez une autre machine virtuelle qui est d'ailleurs issue d'un autre poste sur le réseau local. Dans ce dernier cas, vous faites un accès distant par RMI qui est l'ossature interne des EJB. Le conteneur d'ejb les EJB sont des applications exécutées côté serveur mais également englobées dans un conteneur. Chaque partie a ses propres obligations et règles. Le rôle principal du conteneur EJB est de gérer le cycle de vie des applications EJB qui lui sont associées. C'est lui qui les déploie, les stoppe et les redéploie, tout en gérant leur conformité avec les spécifications du serveur. Même si le conteneur a le rôle le plus important, c'est le serveur qui aiguille l'ensemble des requêtes et gère l'ensemble des conteneurs et services. Le serveur se doit de gérer, de plus, un ensemble de services d'infrastructure communs à l'ensemble des conteneurs ou des services. Ainsi, la spécification Java EE oblige le

serveur d'application à offrir un service d'annuaire JNDI, dont le rôle et de permettre de retrouver facilement les EJB présents dans le conteneur, également un service de messagerie inter-applications au travers de JMS, et etc. Un conteneur est un intermédiaire entre un composant et les fonctionnalités de bas niveau fournies par la plate-forme. Les services offerts par les conteneurs de la plate-forme Java EE regroupent : 1. La sécurité : le modèle de sécurité permet de configurer les ressources accessibles pour les utilisateurs autorisés. 2. La gestion des transactions : le modèle transactionnel offre la possibilité de définir les relations entre les méthodes. 3. Les recherches JNDI : fournissent une interface pour se connecter aux services de noms ou d'annuaires (LDAP, par exemple). 4. Les connexions distantes : le conteneur gère l'ensemble des connexions distantes entre le client et les objets dont il a la responsabilité. Il gère également la distribution de ces objets si nécessaire. 5. La montée en charge : le conteneur est responsable de la bonne utilisation et du recyclage des ressources (connexion SGBD, mémoire,...). Le client Le tiers client est représenté par des applications se connectant aux EJB. Ces applications sont généralement écrites en Java, toutefois, il est également possible de se connecter à un EJB avec un client écrit dans un autre langage via un accès par le service web. Nous pouvons également passer par une application Web qui joue le rôle d'intermédiaire et qui utilise en interne les compétences des EJB. Dans ce cas là, un simple navigateur suffit. Ainsi, la façon d'accéder aux EJB dépend du type de client : 1. Si vous désirez plutôt mettre en oeuvre des applications fenêtrées, le client est alors appelé client riche, et vous devez alors utiliser JNDI et RMI pour se connecter et pour appeler les méthodes des EJB. 2. Si, par contre, vous préférez travailler avec un simple navigateur, le client est alors un client léger, et vous utiliser tout simplement le protocole standard HTTP. Ici, l'accès aux EJB se fait indirectement. 3. Il existe toutefois la possibilité d'utiliser le support HTTP pour travailler de nouveau avec une application fenêtrée et se retrouver ainsi comme un client lourd, pour cela il faut mettre en oeuvre un service Web au moyen de SOAP. 4. Pour terminer, nous pouvons faire communiquer les systèmes extérieurs avec une messagerie inter-applications, comme JMS. Visibilité des EJB Au-delà de la simple délimitation des différentes couches applicatives, les EJB définissent la manière dont interagissent les différents intervenants d'une architecture Java EE. Ils définissent de plus, des possibilités offertes aux divers clients (application Java, applet, navigateur Web, application s'exécutant sur le même serveur d'applications) et les modalités de communication. Ainsi, il sera possible de définir des EJB suivant deux perspectives pour le client : une vue locale (local) et une vue distante (remote) : 1. Visibilité locale : En adoptant une vue locale (local) pour un EJB, tout client exécuté dans la même machine virtuelle (autre EJB, servlet,...) est en mesure d'appeler les méthodes de cet EJB. Dans cette vue, les appels de méthode de l'ejb par le client sont effectués comme dans toute application Java classique. Les arguments sont passés par référence et il est possible, pour le client de modifier directement les objets récupérés. Il en résulte une démarcation plus faible de l'ejb vis-à-vis de ses clients. Il faut alors envisager que différents clients manient le même objet au même moment et donc anticiper les effets indésirables que cela peut induire. En revanche, l'utilisation d'une vue locale permet d'optimiser les performances du serveur d'applications et de minimiser les ressources. Celui-ci n'a alors pas à s'occuper des spécificités liés au transport via le réseau (pas de sérialisation des objets, aucun communication réseau,...). 2. Visibilité distante : En adoptant une vue distante (remote), un EJB met à disposition ses méthodes à des clients s'exécutant sur des machines virtuelles différentes, et donc sur des machines physiques différentes (applets, applications Java, etc.). Dans le cadre d'une vue distante, les démarcations sont plus fortes entre un EJB et sont client. les appels de méthodes se font via la technologie RMI, les arguments et valeurs de retour doivent être sérialisés et ne se transmettrent plus par référence. Il n'est alors plus possible qu'un client modifie le même objet d'un autre client, et il est donc plus aisé de délimiter les différents domaines de sécurité. Par contre, l'utilisation d'une vus distante a aussi ses inconvénients. Les objets devant être transportables à distance, le conteneur doit sérialiser/désérialiser ces objets pour les transmettre via le réseau. il en résulte des temps de traitements plus élevés par rapport aux appels locaux. 3. Visibilté service Web : Les services Web se répandent de plus en plus sur Internet, parce qu'ils permettent d'utiliser n'importe quel service à partir de n'importe quel langage. Il est possible de spécifier la visibilté de votre EJB avec le type webservice pour qu'il puisse être utilisé à la manière d'un service Web. Toutefois, ce choix se restreint aux beans sessions de type stateless. Cette technologie manque cependant de maturité. Comment choisir le type d'accès? L'adoption d'une vue locale ou distante se décide lors de la création des EJB. Il est tout à fait possible d'envisager l'utilisation de ces deux types de vue simultanément, mais il faudra prendre en compte le fait qu'elles ne sont pas totalement équivalentes (notamment au niveau de la sécurité avec les passages par référence/valeur). Si l'application cliente s'exécute au sein de la même machine virtuelle alors la vue locale sera certainement la plus appropriée et inversement pour la vue à distance. Le choix ne doit pas être générique, mais doit être fait par rapport aux besoin de l'application. Il est cependant possible de favoriser les accès locaux en plaçant un intermédiaire (le conteneur web) entre les EJB et le client distant. La vue webservice, quant à elle, est à utiliser lorsque le client n'est pas écrit en Java ou lorsque vous souhaitez rendre votre service le plus ouvert possible. Typiquement, si vous souhaitez donner la possibilité à vos acheteurs de consulter votre catalogue de produits de n'importe qu'elle manière, il peut être judicieux de leur donner l'accès à un tel service web. Les beans sessions Les principes fondamentaux de l'architecture métier définissent la création de services en tant qu'intermédiaires entre les applications clientes et l'accès aux données. Au sein d'une architecture Java EE, ce sont des EJB qui remplieront cette fonction : les beans sessions. Plus que de simple classes composés de propriétés et de méthodes, ces beans sessions sont de véritables passerelles de services au sein même de l'application, permettant à tout type de client de les interroger. Qu'est-ce qu'un bean session? Un bean session est une application côté serveur permettant de fournir un ou plusieurs services à différentes applications clientes. Un service sert, par exemple, à récupérer la liste des produits d'une boutique, à enregistrer une réservation ou encore à vérifier la validité d'un stock. Un bean session définit les étapes successives afin d'aboutir à un objectif final. Les beans sessions constituent donc les briques de la logique métier d'une application. Ils traitent les données et effectuent les opérations liées à la logique de l'entreprise. Les beans sessions font office de pont entre les clients et les données. Alors que les beans entités servent à accéder aux données (ajout, modification, suppression,...), les beans sessions offrent généralement un accès en lecture seule sur celles-ci. Ils sont divisés en deux types : Stateless et Stateful. Le choix du type Stateless ou Stateful s'appuie sur l'interaction désirée entre le client et le bean session. Les beans sessions Stateless Un bean session Stateless est une collection de services dont chacun est représenté par une méthode. Stateless signifie que le service est autonome dans son exécution et qu'il ne dépend donc pas d'un contexte particulier ou d'un autre service. Le point important réside dans le fait qu'aucun état n'est conservé entre deux invocations de méthodes.

Lorsqu'une application cliente appelle une méthode d'un bean session, celui-ci exécute la méthode et retourne le résultat. L'exécution ne se préoccupe pas de ce qui a pu être fait avant ou ce qui pourra être fait après. Ce type d'exécution est typiquement le même que celui du protocole HTTP (mode déconnecté). Il est souvent conseillé de proposer des beans sessions Stateless généraux afin de pouvoir être réutilisés dans d'autres contextes. L'avantage du type Stateless est sa performance. En effet, plusieurs clients utilisent la même instance. Les beans sessions Stateful Un bean session Stateful est une extension de l'application cliente. Il introduit le concept de session entre le client et le serveur. On parle précisément d'état conversationnel pour qualifier ce type de communication. De ce fait, une méthode appelée sur l'ejb peut lire ou modifier les informations sur l'état conversationnel. Cet EJB est partagé par toutes les méthodes pour un unique client. Contrairement au type Stateless, les bean sessions Stateful tendent à être spécifique à l'application. Le caddie virtuel est l'exemple le plus commun pour illustrer l'utilisation d'un bean session Stateful. Dans le cas d'un Stateful, chaque client est lié à une instance de l'ejb. Ce type de bean session consomme donc d'avantage de mémoire que le type Stateless. De plus, le travail et le maintien d'association constitue une tâche supplémentaire importante pour le conteneur. Il en résulte une moins bonne montée en charge et parfois une dégradation des performances lorsqu'une application utilise le type Stateful abusivement et sans raison. Quand utiliser les beans sessions? Voici quelques considérations concernant l'utilisation des beans sessions dans un système d'entreprise : 1. A n'importe quel instant. Seulement un client a accès à l'instance du bean session. 2. L'état du bean n'est pas persistant, il n'existe que pour une courte durée (environ quelques heures). 3. Le service peut être accessible également via un service web. Les beans sessions Stateful sont appropriés si l'une des conditions suivantes (non exhaustive) est vrai : 1. L'état du bean représente l'interaction entre le bean et un client particulier. 2. Le bean doit conserver les informations concernant le client durant l'exécution des méthodes. 3. Le bean fait la liaison entre le client et d'autres composants de l'application, présentant une vue simplifiée au client. 4. En coulisse, le bean contrôle plusieurs autres beans. Pour améliorer les performances, vous pouvez choisir d'utiliser un bean session Stateless s'il a une de ces caractéristiques : 1. L'état du bean n'a pas de donnée spécifique à un client. 2. Dans un seul appel de méthodes, le bean accomplit une tâche générique pour tous les clients. Par exemple, envoyer un email qui confirme un ordre en ligne. 3. Le bean récupère d'une base de données un ensemble de données en lecture seule qui sont souvent utilisées par les clients. Un tel bean, par exemple, pourrait récupérer les lignes d'une table qui représente les produits qui sont en vente ce mois. Premier bean session Stateless avec un accès distant Nous avons passé beaucoup de temps à comprendre l'ossature de la plate-forme Java EE et à définir ainsi le rôle des EJB. Après toute cette théorie, nous allons maintenant entrer dans le vif du sujet et mettre en pratique nos nouvelles connaissances. Vous vous en doutez, la connaissance complète des EJB va prendre plusieurs études. Je rappelle toutefois que lors de cette étude, nous nous consacrons uniquement sur les EJB de type session.

Dans ce chapitre, nous allons mettre en oeuvre notre premier EJB de type session Stateless. Par ailleurs, cet EJB sera, pour l'instant, accessible uniquement à distance. Comme nous sommes en phase d'apprentissage, je vous propose de faire un EJB modeste, afin de bien maîtriser les nouveaux concepts et, derrière, les nouvelles écritures associées. Vue d'ensemble du projet Côté serveur d'application, le service proposé par l'ejb est de réaliser la conversion entre des uros et des Francs (formatée ou pas). Côté client, une fenêtre sera ouverte afin de permettre la saisie des valeurs et le choix du type de conversion à réaliser. Le traitement proprement dit sera fait par le service proposé par l'ejb de conversion. Avec cette approche, l'ejb est considérée par l'application cliente comme un objet distant. Pour atteindre l'objet distant, et pour que ce dernier puisse rendre le service désiré par le client, nous devons systématiquement passé par une interface qui représente cet objet. Nous nous retrouvons ici exactement suivant le même principe que nous avons évoqué lors de l'étude de RMI. En effet, un objet distant doit systématiquement implémenter une interface qui va spécifier les méthodes qui sont accessibles depuis un poste client. Ce sont d'ailleurs les seules méthodes autorisées. Cet objet distant doit alors respecter le contrat prévu par l'interface et définir le comportement qui va correspondre au traitement nécessaire pour chacune des méthodes prévues. Ainsi, l'interface sera présente à la fois sur le serveur d'application et aussi sur chacun des postes clients. Pour en savoir plus sur RMI. De plus, pour que le client puisse atteindre le serveur d'application, il est nécessaire de rajouter un fichier jndi.properties qui spécifie le type de serveur utilisé (dans cet exemple Glassfish) et surtout sa localisation sur le réseau local. Par ailleurs, vous devez déployer quelques librairies qui sont nécessaires au bon déroulement du processus. Ici aussi, les librairies utilisées dépendent du serveur d'applications utilisé. Il est à noter que le fichier de propriétés ainsi que les librairies à déployer seront toujours les mêmes, quelque soit le nombre d'applications clientes que vous devez placer sur un poste. Il suffit donc de les déployer une fois pour toute. Vous pensez peut-être que nous avons beaucoup de chose à prendre en compte. Oui c'est vrai, mais vous allez aussi découvrir que la programmation devient extrêmement simple et surtout intuitive. En effet, lorsque vous demandez un service particulier, vous faites appel à une simple méthode d'un objet, et vous avez alors l'impression que cet objet est sur le poste client, alors qu'en réalité il est à distance sur le serveur d'applications. Vous n'avez plus besoin, dans la programmation, de stipuler tout ce qui concerne la problématique du réseau (les sockets, les flux, les threads, le protocole d'échange, etc.). Il est possible d'avoir une autre approche et de proposer un conteneur spécifique côté client qui s'affranchit de tout ces fichiers annexes. Ce conteneur s'appelle Application Client Container. Plus loin, tout un chapitre est consacré à ce sujet particulier. Côté serveur d'application Côté serveur, il suffit donc d'implémenter deux éléments. D'une part l'interface qui va stipuler toutes les méthodes que les clients seront autorisés à utiliser et d'autre part la classe qui va implémenter cette interface et qui va donc redéfinir toutes les méthodes spécifiées pour réaliser les traitements nécessaires. Toutefois, rien n'empêche à la classe, suivant le besoin, de définir d'autres méthodes pour atteindre le résultat requis. Dans ce cas là, ces nouvelles méthodes sont généralement privées. Puisque nous devons mettre en oeuvre un service de conversions entre les uros et les Francs, le nom des composants utilisés commencerons systématiquement par Conversion. Par convention, pour reconnaître l'objet distant, nous plaçons le suffixe Bean au nom du service choisi. Ainsi la classe sera nommé ConversionBean. Pour le nom de l'interface, nous rajoutons le suffixe qui correspond à la méthode d'accès. Ainsi, puisque nous faisons un accès à distance, l'interface sera donc nommé ConversionRemote. Toutefois, ces noms sont totalement libres, vous pouvez choisir le nom qui vous plait. ejb3.conversionremote.java package ejb3; import javax.ejb.remote; @Remote public interface ConversionRemote { final double TAUX = 6.55957; double eurofranc(double euro); double franceuro(double franc); String eurofranc(string euro);

String franceuro(string franc); Nous connaissons déjà les interfaces. Vous avez juste à déclarer toutes les méthodes publiques qui doivent être accessibles par le client. Vous n'avez pas besoin de mettre systématiquement le qualificateur public puisque toutes les méthodes qui sont dans l'interface sont nécessairement publiques. Je prévois deux méthodes pour chacune des conversions prévues. Une qui réalise le traitement direct et une qui formate en plus le résultat. Dans une interface, vous pouvez placer des attributs, mais ils doivent impérativement être constants. J'en profite pour placer ainsi le taux de conversion. Lorsque vous devez mettre en oeuvre des interfaces pour être en relation avec des EJB, vous devez spécifier le type d'accès. Ici, nous désirons que cet EJB soit accessible à distance, vous devez donc rajouter l'annotation @Remote juste avant la déclaration de l'interface. Je rappelle que les annotations sont préfixées par le symbole @. Pour que cette annotation soit prise en compte, vous devez importer cette annotation depuis le paquetage javax.ejb. Pour cela, vous devez intégrer les bibliothèques nécessaires à votre projet. Dans le cas de Glassfish, il faut prendre l'archive javaee.jar. Dans le cas de JBoss, c'est plutôt jboss-ejb3x.jar. ejb3.conversionbean.java package ejb3; import java.text.*; import javax.ejb.stateless; @Stateless public class ConversionBean implements ConversionRemote { public double eurofranc(double euro) { return euro*taux; public double franceuro(double franc) { return franc/taux; public String eurofranc(string euro) { NumberFormat nombre = NumberFormat.getCurrencyInstance(); DecimalFormat franc = new DecimalFormat("#,##0.00 F"); try { double valeur = nombre.parse(euro).doublevalue(); return franc.format(eurofranc(valeur)); catch (ParseException ex) { return franc.format(0); public String franceuro(string franc) { NumberFormat nombre = NumberFormat.getNumberInstance(); NumberFormat euro = NumberFormat.getCurrencyInstance(); try { double valeur = nombre.parse(franc).doublevalue(); return euro.format(franceuro(valeur)); catch (ParseException ex) { return euro.format(0); Une fois que l'interface est construite, vous pouvez maintenant vous occuper de la classe qui va implémenter cette interface et qui va donc redéfinir, au moins, toutes les méthodes désignées et ainsi réaliser tout le traitement de la logique métier. Dans notre cas, nous définissons juste les méthodes prévues par l'interface. Il n'existe pas de méthodes supplémentaires. Encore une fois, nous avons besoin d'utiliser une annotation qui va spécifier quel est le bean de type session à construire. Je rappelle qu'il existe deux types de bean session, soit Stateless, soit Stateful. Juste avant la déclaration de la classe, vous spécifiez l'annotation correspondante au type de bean session, ici donc @ Stateless. Encore une fois, il est nécessaire de faire l'importation correspondante. Au niveau codage, tout est fini. Remarquez bien l'extrême simplicité d'écriture. C'est notamment beaucoup plus simple que RMI puisque vous n'avez pas besoin de vous occuper de créer l'objet distant. C'est le serveur d'application qui gère tout cela. Dans notre cas, puisque nous spécifions une classe de type Stateless, un seul objet sera créé qui répondra aux attentes de plusieurs clients. Vous devez ensuite déployer votre EJB sur le serveur d'application. Il faut alors faire une archive (extension.jar) qui comporte ces deux éléments : l'interface métier et la classe du bean. L'idéal est de disposer d'un outil de développement qui permet de réaliser tout cela automatiquement. Pour ma part, j'utilise Netbeans avec comme serveur d'application, soit Glassfish, soit JBoss. Avec un tel outil, il suffit de cliquer sur le bouton Run pour que tout soit : compilé, archivé et déployé. Bien évidemment, il faut que votre serveur d'application soit pris en compte par votre outil de développement. Côté application cliente Passons maintenant à la programmation de l'application cliente. Deux aspect sont ici à prendre en compte. Nous devons d'abord réaliser l'ihm qui va permettre la saisie des valeurs à soumettre et l'affichage du résultat. Nous devons ensuite communiquer avec l'objet distant afin que ce dernier fasse tous les traitements souhaités suivant les requêtes soumises par l'opérateur, soit une conversion en Francs, soit une conversion en uros. Pour que la communication puisse se faire avec l'objet distant, vous devez placer dans votre projet l'interface qui représente l'ejb correspondant. Le client doit localiser l'ejb qu'il souhaite récupérer via le service JNDI. Effectivement, les composants déployés sur le serveur d'applications sont enregistrés dans l'annuaire du serveur avec donc un nom JNDI associé à l'ejb.

Attention, le pilote JNDI, appelé service provider change d'un serveur à l'autre. Il faudra donc prévoir un fichier de propriétés associé au type de serveur utilisé. Je rappelle que les appels de méthodes distantes se font par RMI alors que les appels de méthodes locales se font directement dans la JVM du serveur.. Au moyen de l'interface, le client récupère une référence de l'ejb qu'il souhaite utiliser. Celui-ci peut alors appeler les méthodes de l'objet récupéré sans se soucier des contraintes de communication. En effet, l'appel d'une méthode est automatiquement transmis à l'instance de l'ejb dans le conteneur (généralement par un système de proxy). Cette instance traite la méthode et retourne le résultat au client. La création du proxy est à la charge du conteneur et reste totalement transparente pour le client. clientconversion.client.java 1 package clientconversion; 2 3 import ejb3.conversionremote; 4 import java.awt.event.*; 5 import javax.naming.*; 6 import javax.swing.*; 7 import java.awt.*; 8 9 public class Client extends JFrame implements ActionListener { 10 private JTextField uro = new JTextField("0 ", 11); 11 private JTextField franc = new JTextField("0 F", 11); 12 private JButton bouton uro = new JButton("Francs"); 13 private JButton boutonfranc = new JButton(" uros "); 14 private JPanel haut = new JPanel(); 15 private JPanel bas = new JPanel(); 16 private static ConversionRemote convert; 17 18 public Client() { 19 settitle("conversion uro/franc"); 20 setdefaultcloseoperation(exit_on_close); 21 setsize(280, 105); 22 getcontentpane().setbackground(color.red); 23 haut.setopaque(false); 24 haut.add( uro); 25 bouton uro.addactionlistener(this); 26 haut.add(bouton uro); 27 bas.setopaque(false); 28 bas.add(franc); 29 boutonfranc.addactionlistener(this); 30 bas.add(boutonfranc); 31 add(haut, BorderLayout.NORTH); 32 add(bas, BorderLayout.SOUTH); 33 setvisible(true); 34 35 36 public static void main(string[] args) throws NamingException { 37 Context ctx = new InitialContext(); 38 convert = (ConversionRemote) ctx.lookup(conversionremote.class.getname()); 39 new Client(); 40 41 42 public void actionperformed(actionevent e) { 43 if (e.getsource()==bouton uro) franc.settext(convert.eurofranc( uro.gettext())); 44 if (e.getsource()==boutonfranc) uro.settext(convert.franceuro(franc.gettext())); 45 46 Regardons un peu le code de notre application cliente. La majeure partie est du code classique qui correspond à la mise en oeuvre d'une interface graphique. Seules les lignes 37 et 38 suivies des lignes 43 et 44 sont concernées pour la communication avec l'objet distant. La première chose à faire, nous l'avons souligné en préambule, est de récupérer une instance de l'ejb au moyen de JNDI. Pour cela vous devez mettre en oeuvre un contexte pour l'application cliente qui va permettre de faire la recherche du nom JNDI stocké dans l'annuaire. Il faut d'abord initialiser ce contexte avec toutes les bons paramètres requis. Le plus facile, à mon avis (puisque portable), est de placer ces différents paramètres, dans un fichier de configuration dont le nom est bien précis (jndi.properties) et qui doit être placé dans le répertoire racine du projet. Voici celui qui est prévu pour accéder à un EJB stocké sur le serveur d'application Glassfish : jndi.properties (Glassfish) # Accès au serveur d'application Glassfish java.naming.factory.initial=com.sun.enterprise.naming.serialinitcontextfactory java.naming.factory.url.pkgs=com.sun.enterprise.naming java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.jndistatefactoryimpl org.omg.corba.orbinitialhost=portable org.omg.corba.orbinitialport=3700 L'avant dernière ligne stipule la localisation du serveur sur le réseau local. La dernière ligne est optionnelle si vous conservez le numéro de service du serveur d'application. Dans le cas de Glassfish, le numéro de service installé par défaut est 3700. Dans le cas où vous devez utiliser le serveur d'application JBoss, voici le fichier de configuration que vous devez mettre en place : jndi.properties (JBoss) # Accès au serveur d'application JBoss java.naming.factory.initial=org.jnp.interfaces.namingcontextfactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=portable:1099

Cette fois-ci, c'est la dernière ligne qui stipule à la fois la localisation du serveur et de son numéro de service. Dans le cas de JBoss, le numéro de service par défaut est 1099. L'initialisation du contexte avec la prise en compte des bons paramètres se fait à la ligne 37 : Context ctx = new InitialContext(); A l'aide de cette simple ligne, l'application est maintenant au courant de la localisation du serveur d'applications ainsi que son bon numéro de service pour l'atteindre correctement. Il faut maintenant localiser l'ejb qui va réaliser le traitement de la logique métier au sein du serveur d'applications. Cela se fait par l'intermédiaire de la méthode lookup() du contexte créé précédemment. Vous spécifiez alors en argument le nom JNDI de l'objet distant. Si tout se passe bien, la référence de l'objet est alors récupérée par la méthode. Il faut, par contre transtyper cette référence pour qu'elle corresponde à l'interface représentant l'objet distant. C'est tout ce que réalise la ligne 38 : convert = (ConversionRemote) ctx.lookup(conversionremote.class.getname()); Le nom JNDI dans le cas de Glassfish est, en réalité, le nom de l'interface métier. Dans le cas de JBoss, le nom JNDI est plutôt le nom du bean suivi de la méthode d'accès. Voici ce qu'il faudrait alors écrire : convert = (ConversionRemote) ctx.lookup("conversionbean/remote"); Une fois que la référence de l'objet est obtenu, tout devient très simple. Effectivement, il suffit de faire appel aux bonnes méthodes de l'objet qui réalisent le traitement désiré. C'est la démarche que nous appliquons sur les lignes 43 et 44 : if (e.getsource()==bouton uro) franc.settext(convert.eurofranc( uro.gettext())); if (e.getsource()==boutonfranc) uro.settext(convert.franceuro(franc.gettext())); Conclusion sur la mise en oeuvre d'une connexion à distance Remarquez bien l'extrême simplicité d'écriture pour accéder à des traitements définis sur un serveur d'application. Il existe toutefois deux contraintes : 1. Vous devez fabriquer un fichier de propriétés jndi.properties associé au serveur d'application utilisé. 2. Vous devez également déployer, en même temps que votre application cliente, des bibliothèques (archives) qui vont permettre de faire fonctionner convenablement tout le système. Voici d'ailleurs les archives à déployer pour le cas où nous utilisons Glassfish : Dans le cas où vous utilisez plutôt JBoss, voici les archives à prendre en compte : jboss-aop-jdk50-client.jar jboss-aspect-jdk50-client.jar jbossall-client.jar jboss-ejb3.jar jboss-ejb3x.jar Il est à noter que le fichier de propriétés ainsi que les librairies à déployer seront toujours les mêmes, quelque soit le nombre d'applications clientes que vous devez placer sur un poste. Il suffit donc de les déployer une fois pour toute. Attention, lorsque vous proposer une connexion distante, toutes les informations qui transitent sur le réseau doivent impérativement être sérialisables.. Bean session Stateless en accès local Dans ce chapitre, nous allons reprendre l'étude précédente, en proposant cette fois-ci un accès local à l'ejb de type session Stateless. Nous concervons effectivement l'ejb que nous avons construit dans le chapitre précédente, en proposant malgré tout quelques petites retouches. L'élément qui va utiliser le service proposé par l'ejb n'est plus du tout le même, puisque maintenant c'est une application Web qui s'en occupe. Je rappelle que cette application Web se trouve également sur le serveur d'applications. L'avantage ici, c'est que la connexion entre l'application Web et l'ejb se fait en mode local. Effectivement, puisque nous sommes sur la même machine virtuelle, nous n'avons plus besoin d'échange sur le réseau avec toute la problématique que nous avons découvert lors du chapitre précédent. Finalement, le client sera cette fois-ci un simple navigateur et l'échange entre le poste client et le serveur d'applications se fera par l'intermédiaire du protocole HTTP, ce qui permet du coup d'envisager de diffuser l'information sur Internet.

Côté conteneur d'ejb Côté conteneur d'ejb, il faut encore une fois implémenter deux éléments. D'une part l'interface qui va stipuler toutes les méthodes que le conteneur Web sera autorisé à utiliser et d'autre part la classe qui va implémenter cette interface et qui va donc redéfinir toutes les méthodes spécifiées pour réaliser les traitements nécessaires. ejb3.conversionlocal.java package ejb3; import javax.ejb.local; @Local public interface ConversionLocal { final double TAUX = 6.55957; double eurofranc(double euro); double franceuro(double franc); String formatfranc(double valeur); String formateuro(double valeur); double valeurfranc(string franc); double valeureuro(string euro); Nous retrouvons la même ossature d'une interface qui propose une relation avec un EJB. Effectivement, nous devons utiliser une annotation qui spécifie le type d'accès. Ici, toutefois, puisque nous prévoyons un accès local, vous devez placer l'annotation @Local avant la déclaration de l'interface en lieu et place de l'annotation @Remote. ejb3.conversionbean.java package ejb3; import java.text.*; import javax.ejb.stateless; @Stateless public class ConversionBean implements ConversionLocal { public double eurofranc(double euro) { return euro*taux; public double franceuro(double franc) { return franc/taux; public String formatfranc(double valeur) { String motif = MessageFormat.format("#,##0.00 Franc{0, choice, 0# 1#s", valeur);

DecimalFormat franc = new DecimalFormat(motif); return franc.format(valeur); public String formateuro(double valeur) { String motif = MessageFormat.format("#,##0.00 Euro{0, choice, 0# 1#s", valeur); DecimalFormat euro = new DecimalFormat(motif); return euro.format(valeur); public double valeurfranc(string franc) { try { NumberFormat nombre = NumberFormat.getNumberInstance(); return nombre.parse(franc.trim()).doublevalue(); catch (ParseException ex) { return 0.0; public double valeureuro(string euro) { try { NumberFormat nombre = NumberFormat.getNumberInstance(); return nombre.parse(euro.trim()).doublevalue(); catch (ParseException ex) { return 0.0; Mis à part les modifications de code prévues par les nouvelles méthodes déclarées dans l'interface locale, la façon d'écrire votre EJB est totalement identique puisque notre bean est toujours un bean session de type Stateless. Nous avons modifier et déclarer de nouvelles méthodes pour permettre une meilleure adéquation avec la présentation de la partie Web.. Côté conteneur Web Le client de l'ejb est l'application Web. Pour la mise en oeuvre de cette application Web, j'utilise la technologie JSF qui propose une façon de procéder vraiment intéressante, puisqu'elle respecte l'architecture MVC (Modèle-Vue-Contrôleur). Mon application Web comporte donc trois éléments : 1. La partie contrôleur : dont l'action est assuré par la servlet FacesServlet. 2. La partie vue : dont l'affichage (la mise en page) est assurée par la page JSP Conversion.jsp. 3. La partie modèle : dont la gestion est assurée par le JavaBean Conversion qui est en relation directe avec la page Web précédente, qui mémorise donc toute les interventions de l'opérateur et qui finalement donne la réponse souhaitée en faisant appel au service proposé par l'ejb de conversions monétaires. La relation avec l'ejb se fait par le biais de l'interface locale ejb3.conversionlocal qui devra donc être présente dans le conteneur Web. Voici donc le descripteur de déploiement <web.xwl> de l'application Web : web.xml <?xml version="1.0" encoding="utf-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.facesservlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>faces/conversion.jsp</welcome-file> </welcome-file-list> </web-app> Suivi du fichier de configuration <faces-config.xml> qui permet de créer le bean conversion dans la session qui est en relation avec la page Web : faces-config.xml <?xml version='1.0' encoding='utf-8'?> <faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"> <managed-bean> <managed-bean-name>conversion</managed-bean-name> <managed-bean-class>bean.conversion</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config> Lorsque nous utilisons le navigateur pour utiliser notre application Web, voici ce qui doit apparaître :

Voilà donc en conséquence la page Web correspondante. Remarquez au passage la simplicité d'écriture et surtout la légéreté du code lorsque nous utilisons cette technologie JSF : Conversion.jsp <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <f:view> <html> <body bgcolor="green" text="yellow"> <h:form> <h:inputtext value="#{conversion.euro" /> <h:commandbutton action="#{conversion.changefranc" value="franc" /> <br /> <h:inputtext value="#{conversion.franc" /> <h:commandbutton action="#{conversion.changeeuro" value="euro " /> <h4><h:outputtext value="taux de conversion : #{conversion.taux" /></h4> </h:form> </body> </html> </f:view> Et pour finir, voici le JavaBean qui effectue la relation avec la page Web d'une part, et propose la connexion avec le bean session d'autre part : bean.conversion.java 1 package bean; 2 3 import javax.ejb.ejb; 4 import ejb3.conversionlocal; 5 6 public class Conversion { 7 private double franc; 8 private double euro; 9 @EJB 10 private ConversionLocal conversion; 11 12 public String getfranc() { 13 return conversion.formatfranc(franc); 14 15 16 public void setfranc(string franc) { 17 this.franc = conversion.valeurfranc(franc); 18 19 20 public String geteuro() { 21 return conversion.formateuro(euro); 22 23 24 public void seteuro(string euro) { 25 this.euro = conversion.valeureuro(euro); 26 27 28 public void changefranc() { 29 franc = conversion.eurofranc(euro); 30 31 32 public void changeeuro() { 33 euro = conversion.franceuro(franc); 34 35 36 public double gettaux() { 37 return conversion.taux; 38 39 Cette fois-ci, la connexion avec l'ejb relatif à la classe ConversionBean se trouve respectivement sur les lignes 9 et 10. L'écriture de la connexion est extrêmement simplifiée par rapport au mode distant. Ici, vous devez juste spécifier l'annotation @EJB devant l'interface locale que vous utilisez pour atteindre le bean session. Grâce à cette annotation et au moyen de l'interface locale, le JavaBean, et de ce fait, l'application Web, récupère la référence de l'ejb souhaité. Au moyen de @EJB, l'injection de la référence se fait automatiquement. Plus besoin de mettre en place un contexte, et de préciser ainsi sa localisation au moyen d'un fichier de propriétés jndi.properties et plus besoin non plus de fichier d'archives à déployer puisque tout se trouve sur place. Effectivement, le conteneur Web et le conteneur d'ejb se trouvent sur le même serveur d'applications, et par là, sur la même machine virtuelle, donc pas besoin de recherche particulière. Je rappelle que les appels de méthodes distantes se font par RMI alors que les appels de méthodes locales se font directement dans la JVM du serveur. Le gros avantage d'une connexion locale, c'est qu'il n'y a plus besoin de mécanisme de communication en réseau. C'est comme si l'ejb était un objet normal faisant partie de la même application. Cela devient un JavaBean quelconque, avec toutefois une autorisation d'accès contrôlé puisque vous devez tout de même passer par

l'interface. Le temps de réponse est largement plus rapide qu'avec un accès distant. Toutefois, il faut modérer ce dernier point, puisque le véritable client reste un poste que se trouve à distance et qui utilise un navigateur avec donc, de nouveau, une communication en réseau. Certainement le plus gros avantage d'un accès en mode local, c'est que vous n'avez pas besoin de sérialiser vos valeurs qui transitent entre l'ejb et l'application Web. Par ailleurs, le fait d'avoir une application Web, vous n'avez plus besoin de vous soucier de déployer votre application puisque le client est un simple navigateur sans compétence particulière même sur Java. En effet, votre application Web propose des pages Web dynamiques (donc en finalité des pages HTML standard). Déploiement dans une application d'entreprise Lorsque vous désirez mettre en relation les deux conteneurs et donc faire en sorte que la communication entre ces deux éléments se fasse correctement, vous devez packager votre projet global dans une même entité qui se nomme alors : application d'entreprise. Si vous ne faites pas cela, ce n'est pas considéré comme le mode local. En réalité, l'application d'entreprise est une archive qui porte l'extension <*.ear> (Archive d'entreprise) et qui comporte d'autres archives, comme l'archive de l'application Web <*.war> (archive Web), et l'archive concernant l'ensemble des EJB <*.jar>. Voici d'ailleurs un exemple d'application d'entreprise correspondant au projet que nous venons de mettre en oeuvre : Accès à la foisà distanceet en local sur un bean session Stateless Nous connaissons maintenant les différentes accès à un bean session de type Stateless ; à distance sur le réseau local avec un accès de type remote, et en local, en conjonction avec une application Web sur le serveur d'application. Dans ce dernier cas de figure, nous utilisons l'ejb depuis Internet (éventuellement) par le protocole HTTP, en passant donc par l'intermédiaire de l'application Web. L'idéal, c'est de proposer les deux types de service en même temps. C'est bien entendu possible et généralement souhaitable. La seule petite contrainte, c'est de proposer les deux types d'interface et ensuite que L'EJB les implémente simultanément.

Nous pourrions nous interroger sur l'intérêt de posséder deux interfaces différentes. Il faut se souvenir que lors d'un accès à distance, les données qui transitent sur le réseau doivent impérativement être sérialisables alors que dans le mode local cette contrainte n'est pas du tout à prendre en compte puisque les objets qui sont en relation sont, en réalité, sur la même machine virtuelle. Il s'agit alors d'un accès direct à la donnée souhaitée avec un simple appel de méthode. Je pense que du coup, il est préférable de définir des méthodes qui soient spécifiques au type d'accès et, part là, avoir des interfaces adaptées à la situation. Par ailleurs, les utilisations de l'ejb peuvent être totalement différentes suivant que nous l'utilisons à partir de l'application Web ou à partir d'un autre poste sur le réseau local. Codage des interfaces et du bean session Stateless Nous visualisons uniquement les interfaces et le bean session correspondant. Effectivement, vous n'avez aucun changement notable à faire, à la fois sur l'application fenêtrée et sur l'application Web. ejb3.conversionlocal package ejb3; import javax.ejb.local; @Local public interface ConversionLocal { double eurofranc(double euro); double franceuro(double franc); String formatfranc(double valeur); String formateuro(double valeur); double valeurfranc(string franc); double valeureuro(string euro); double gettaux(); ejb3.conversionremote package ejb3; import javax.ejb.remote; @Remote public interface ConversionRemote { double eurofranc(double euro); double franceuro(double franc); String eurofranc(string euro); String franceuro(string franc);

Nous retrouvons finalement les mêmes déclarations d'interface que lors des chapitres précédents. Nous avons juste éliminé la déclaration de la constante TAUX qui sera reportée dans le bean session Stateless. Remarquez au passage des déclarations de méthode qui sont communes aux deux interfaces. Cela ne pose aucun problème, puisque nous attendons un comportement identique. ejb3.conversionbean package ejb3; import javax.ejb.stateless; import java.text.*; @Stateless public class ConversionBean implements ConversionRemote, ConversionLocal { private final double TAUX = 6.55957; public double eurofranc(double euro) { return euro*taux; public double franceuro(double franc) { return franc/taux; public String formatfranc(double valeur) { String motif = MessageFormat.format("#,##0.00 Franc{0, choice, 0# 1#s", valeur); DecimalFormat franc = new DecimalFormat(motif); return franc.format(valeur); public String formateuro(double valeur) { String motif = MessageFormat.format("#,##0.00 Euro{0, choice, 0# 1#s", valeur); DecimalFormat euro = new DecimalFormat(motif); return euro.format(valeur); public double valeurfranc(string franc) { try { NumberFormat nombre = NumberFormat.getNumberInstance(); return nombre.parse(franc.trim()).doublevalue(); catch (ParseException ex) { return 0.0; public double valeureuro(string euro) { try { NumberFormat nombre = NumberFormat.getNumberInstance(); return nombre.parse(euro.trim()).doublevalue(); catch (ParseException ex) { return 0.0; public double gettaux() { return TAUX; public String eurofranc(string euro) { NumberFormat nombre = NumberFormat.getCurrencyInstance(); DecimalFormat franc = new DecimalFormat("#,##0.00 F"); try { double valeur = nombre.parse(euro.trim()).doublevalue(); return franc.format(eurofranc(valeur)); catch (ParseException ex) { return franc.format(0); public String franceuro(string franc) { NumberFormat nombre = NumberFormat.getNumberInstance(); NumberFormat euro = NumberFormat.getCurrencyInstance(); try { double valeur = nombre.parse(franc.trim()).doublevalue(); return euro.format(franceuro(valeur)); catch (ParseException ex) { return euro.format(0); Notre EJB est finalement un peu plus conséquent que lors des deux chapitres précédents. C'est normal puisque ce bean session capitalise (factorise) le comportement commun aux deux interfaces. Remarquez que nous proposons, bien sûr, une seule définition de méthode par rapport à celles qui sont déclarées sur les deux interfaces. C'est normal puisque le comportement attendu demeure identique. Application Client Container En reprenant l'étude précédente, il est possible de proposer une petite modification et faire en sorte que l'application cliente fasse partie intégrante de l'application d'entreprise. Du coup, dans l'application d'entreprise, nous trouvons trois modules :

1. Le module EJB : archive qui intègre et gère l'objet distribué. Cette archive est pris en charge par le conteneur EJB. 2. Le module Web : archive qui intègre et gère l'application Web. Cette archive est pris en charge par le conteneur Web. 3. L'application cliente : archive qui intègre et gère l'application fenêtrée qui exploite l'objet distant et qui pourra être déployé sur le poste désiré sur le réseau local. Cette archive est pris en charge par le conteneur d'application cliente (Application Client Container). Dans un des chapitres précédents, nous avons mis en oeuvre une application cliente distante (dit standalone) qui se trouvait totalement détachée de l'application d'entreprise. Il fallait alors mettre en place toute une infrastructure complexe, avec les fichiers jndi.properties ainsi que les archives propres au serveur d'application, pour que la communication puisse s'établir correctement. Il est possible de revoir notre copie afin que cette application cliente fasse partie intégrante de notre application d'entreprise. Pour cela, nous devons utiliser un conteneur supplémentaire proposé par le serveur d'application qui se nomme Application Client Container. Dans cette nouvelle façon de voir, il ne sera plus nécessaire de prévoir ces fichiers annexes pour l'application cliente. De plus la programmation s'en trouvera largement simplifiée par l'utilisation de l'injection automatique par le seul biais de l'annotation @EJB comme nous l'avons déjà employé en mode local. Application Client Container Il existe effectivement un conteneur client spécifique qui offre beaucoup d'avantage pour la mise en oeuvre de ces clients distants. C'est le conteneur d'application cliente (ACC, Application Client Container). Le conteneur d'application cliente inclut un ensemble de classes Java, de librairies, et d'autres fichiers requis. Cet ensemble est donc généralement fourni avec le serveur d'applications et ses dépendances sont distribuées automatiquement avec le client Java qui s'exécute dans sa propre machine virtuelle sur le poste distant. Le conteneur, déployé sur le poste client avec l'ensemble des librairies nécessaires, gère l'exécution du programme client et offre l'accès à de nombreux services Java EE, qui sont eux disponibles sur le serveur d'applications, via le protocole RMI-IIOP. Si nous le comparons avec les autres conteneurs (EJB, WEB), il est alors qualifié de conteneur léger.

Contrairement aux clients dit standalone, un client container-managed peut utiliser l'injection, grâce à l'annotation @EJB, pour récupérer des références vers les EJB dont il dépend. Cela évite l'écriture de la localisation JNDI. Les références vers les EJB sont automatiquement détectées et gérées par le conteneur client. Avec cette approche, l'application cliente ne fait plus référence à un serveur d'applications en particulier. Du coup, l'écriture devient standard et s'applique à tous les serveurs, ce qui offre une meilleure portabilité à vos applications. Le conteneur d'application cliente existe sur le serveur d'application, mais cette architecture est déployée également sur les poste clients qui le désirent. Cela permet de s'affranchir des archives nécessaires au déploiement puisqu'elles sont déjà présentes dans l'acc et nous n'avons également plus besoin de mettre en oeuvre un contexte puisque l'acc possède tous les renseignements nécessaires. Source de l'application cliente Voici donc le source de l'application cliente qui sera compilé et mis en place dans le conteneur d'application cliente. conversion.client.java 1 package conversion; 2 3 import ejb3.conversionremote; 4 import java.awt.event.*; 5 import javax.naming.*; 6 import javax.swing.*; 7 import java.awt.*; 8 import javax.ejb.ejb; 9 10 public class Client extends JFrame implements ActionListener { 11 private JTextField uro = new JTextField("0 ", 11); 12 private JTextField franc = new JTextField("0 F", 11); 13 private JButton bouton uro = new JButton("Francs"); 14 private JButton boutonfranc = new JButton(" uros "); 15 private JPanel haut = new JPanel(); 16 private JPanel bas = new JPanel(); 17 @EJB 18 private static ConversionRemote convert; 19 20 public Client() { 21 settitle("conversion uro/franc"); 22 setdefaultcloseoperation(exit_on_close); 23 setsize(280, 105); 24 getcontentpane().setbackground(color.red); 25 haut.setopaque(false); 26 haut.add( uro); 27 bouton uro.addactionlistener(this); 28 haut.add(bouton uro); 29 bas.setopaque(false); 30 bas.add(franc); 31 boutonfranc.addactionlistener(this); 32 bas.add(boutonfranc); 33 add(haut, BorderLayout.NORTH);

34 add(bas, BorderLayout.SOUTH); 35 setvisible(true); 36 37 38 public static void main(string[] args) throws NamingException { 39 // Context ctx = new InitialContext(); 40 // convert = (ConversionRemote) ctx.lookup(conversionremote.class.getname()); 41 new Client(); 42 43 44 public void actionperformed(actionevent e) { 45 if (e.getsource()==bouton uro) franc.settext(convert.eurofranc( uro.gettext())); 46 if (e.getsource()==boutonfranc) uro.settext(convert.franceuro(franc.gettext())); 47 48 La grande nouveauté, par rapport à une application standalone, c'est que nous mettons en commentaires les lignes de code 39 et 40. Elles ne sont plus nécessaires. Plus besoin de mettre en place, à la fois le contexte de l'application et faire la recherche par le service JNDI. Tout se fait automatiquement, à la condition, bien entendu, de le spécifier au moyen de l'annotation @EJB en ligne 17. Attention, l'annotation @EJB ne peut être utilisée qu'à l'intérieur même de la classe de démarrage et doit être positionnée avec le qualificateur static. Cela est du au fait que le conteneur exécute la méthode main() qui est elle-même statique. L'application cliente s'exécute dans un conteneur. De ce fait, c'est ce dernier qui démarre l'application et non la machine virtuelle directement (comme pour les applications Java standalone). Le temps de démarrage est du coup sensiblement plus long. Déploiement de l'application cliente avec son ACC Le déploiement de l'application cliente avec son conteneur spécifique est cette fois-ci un peu particulier. Cela dépend, bien entendu du serveur d'application. Nous allons voir ici le serveur Glassfish, qui est pour l'instant le seul à proposer ce genre de service. Si vous vous connecter au serveur à l'aide du navigateur et en mode administrateur, vous devez faire en sorte de visualiser l'application d'entreprise tel que cela vous est montré ci-dessous : Nous voyons alors apparaître les trois archives constituant notre application d'entreprise. il suffit alors de cliquer dans la rubrique Launch pour faire apparaître la page de téléchargement de l'application cliente : Si vous cliquez de nouveau sur la rubrique Launch, Java Web Start est alors opérationnel et se charge du déploiement automatique de l'application cliente pourvue de son conteneur ACC. Remarquez, au passage, qu'il est possible de prévoir ce téléchargement sans passer par le mode administrateur.

Dans l'absolue, vous n'êtes pas obligé de placer votre application cliente dans une application d'entreprise même si cela me paraît préférable. Effectivement, si vous désirez utiliser l'acc, vous pouvez placer votre application cliente directement dans le module Application Client Modules indépendamment du module Entreprise Applications. A ce sujet, vous pouvez placer également directement vos applications Web et vos EJB dans leur module respectif. Bean session Stateful en accès distant et en accès local Après avoir longuement travaillé sur le bean session de type Stateless, venons en maintenant à l'étude du type Stateful. Je rappelle qu'il introduit le concept de session entre le client et le serveur. Il peut effectivement arriver, dans certain cas, que la récupération des informations se fasse en plusieurs phases. Il faut alors pouvoir mémoriser les actions réalisées à chacune des étapes franchies. Le bean Stateful permet de résoudre ce problème. Il est effectivement capable, après l'appel d'une méthode, de conserver un état spécifique, et ceci pour un client en particulier. Du coup, il y a de grandes chances pour que ce bean possède un certain nombre d'attributs associés à la classe et qui vont donc représenter les différents états attendus. Le caddie virtuel est l'exemple le plus commun pour illustrer l'utilisation d'un bean session Stateful. Chaque objet (instance) d'un bean session Stateful est associé à un client unique. Ce type de composant maintien ainsi l'état conversationnel avec le client. Les attributs de chacun des objets sont alors liés au client et leurs valeurs sont concervées d'un appel de méthodes à un autre. Quand utiliser les beans sessions Stateful? Les beans sessions Stateful sert habituellement à gérer un processus s'effectuant en plusieurs étapes. Ils sont donc généralement appropriés si l'une des conditions suivantes (non exhaustive) est vrai : 1. L'état du bean représente l'interaction entre le bean et un client particulier. 2. Le bean doit conserver les informations concernant le client durant l'exécution des méthodes. 3. Le bean fait la liaison entre le client et d'autres composants de l'application, présentant une vue simplifiée au client. 4. En coulisse, le bean contrôle plusieurs autres beans. Application d'entreprise Afin d'illustrer cette introduction, je vous propose de réaliser une application d'entreprise qui permet de visualiser un ensemble de photos numériques qui sont stockées sur la même machine que le serveur d'application. Dans ce cas de figure, nous pouvons considérer ce serveur comme un serveur de photos. Ces photos pourront aussi bien être consultées depuis un navigateur depuis Intenet ou en réseau local à l'aide d'une application fenêtrée. Un problème se pose lorsque nous essayons de transiter une photo par une connexion à distance. Généralement, nous avons besoin de prendre la classe BufferedImage pour représenter une photo. Malheureusement, cette classe n'est pas sérialisable, et de ce fait, elle ne peut être propagée dans le réseau. Par contre, en local, il est tout à fait possible de l'utiliser. Pour une connexion à distance, nous devons donc procéder au découpage de cette classe en indiquant d'abord son type ainsi que ses dimensions et en envoyant ensuite un tableau de pixels. Côté client, cette image sera ainsi recontituée et affichée, toujours au travers de cette classe BufferedImage. Puisque nous avons besoin de passer par plusieurs phases, j'ai donc mis en oeuvre un bean session Stateful PhotoBean. Pour connaître l'emplacement de ces photos, ainsi que la liste de l'ensemble des photos stockées, un bean session Stateless FichiersPhotoBean a été également constitué. Il faut souligner, par ailleurs, que ce bean est utilisé par le bean session Stateful, ce qui nous permettra, entre parenthèses, de voir comment communiquer entre les EJB. Comme pour un bean session Stateless, un bean session Stateful peut être accessible aussi bien en local qu'à distance. Pour les deux EJB, j'ai donc développé les deux types d'interface.

Développement des EJB Pour cette application d'entreprise, nous avons donc besoin de deux EJB, un Stateless et un Stateful. photos.fichiersphotoremote.java package photos; import javax.ejb.remote; @Remote public interface FichiersPhotoRemote { String[] liste(); photos.fichiersphotolocal.java package photos; import javax.ejb.local; @Local public interface FichiersPhotoLocal { String repertoire(); String[] liste(); photos.fichiersphotobean.java package photos; import java.io.*; import java.util.*; import javax.ejb.stateless; @Stateless public class FichiersPhotoBean implements FichiersPhotoRemote, FichiersPhotoLocal { private final String répertoire = "D:/Photos/";

public String[] liste() { return new File(répertoire).list(); public String repertoire() { return répertoire; Le premier bean FichiersPhotoBean permet de spécifier les photos déjà présentes dans le serveur et propose ainsi la liste des noms des fichiers. Remarquez la présence d'un attribut constant répertoire qui indique ainsi la localisation du stockage des photos. Le choix d'un bean de type Stateless s'impose ici puisque nous n'avons pas à gérer la conservation d'un état particulier. L'information complète désirée est délivrée avec l'appel d'une seule méthode. photos.photoremote.java package photos; import java.io.ioexception; import javax.ejb.remote; @Remote public interface PhotoRemote { void choix(string nom) throws IOException; int getlargeur(); int gethauteur(); int gettype(); int[] getpixels(); photos.photolocal.java package photos; import java.awt.image.bufferedimage; import java.io.*; import javax.ejb.local; @Local public interface PhotoLocal { BufferedImage recuperer(string nom) throws IOException; BufferedImage recuperer(string nom, int taille) throws IOException; photos.photobean.java package photos; import java.awt.geom.*; import java.awt.image.*; import java.io.*; import javax.ejb.*; import javax.imageio.imageio; @Stateful public class PhotoBean implements PhotoRemote, PhotoLocal { private int largeur; private int hauteur; private int type; private int[] pixels; @EJB private FichiersPhotoLocal localisation; public void choix(string nom) throws IOException { BufferedImage photo = ImageIO.read(new File(localisation.repertoire()+nom)); largeur = photo.getwidth(); hauteur = photo.getheight(); type = photo.gettype(); pixels = photo.getrgb(0, 0, largeur, hauteur, null, 0, largeur); public int getlargeur() { return largeur; public int gethauteur() { return hauteur; public int gettype() { return type; public int[] getpixels() { return pixels; public BufferedImage recuperer(string nom) throws IOException { return ImageIO.read(new File(localisation.repertoire()+nom)); public BufferedImage recuperer(string nom, int taille) throws IOException { BufferedImage source = recuperer(nom); double ratio = source.getwidth()/(double)taille;

BufferedImage image = new BufferedImage((int)(source.getWidth()/ratio), (int)(source.getheight()/ratio), source.gettype ()); AffineTransform retailler = AffineTransform.getScaleInstance(1/ratio, 1/ratio); int interpolation = AffineTransformOp.TYPE_BICUBIC; AffineTransformOp retaillerimage = new AffineTransformOp(retailler, interpolation); retaillerimage.filter(source, image); return image; Le deuxième bean PhotoBean permet de restituer l'image de la photo d'un des fichiers stockés. Dans le cas d'une restitution à distance, nous l'avons déjà évoqué en préambule, nous sommes obligés de l'effectuer en plusieurs étapes. Le choix d'un bean Stateful s'impose donc ici. 1. Lorsqu'une application fenêtrée distante réclame une photo, elle doit d'abord faire appel à la méthode choix() en spécifiant ainsi le nom du fichier désiré. Cette méthode, en interne, récupère bien l'image et remplit ensuite chacun des attributs nécessaires au déploiement effectif de toutes les informations complètes d'une image, savoir : largeur, hauteur, modèle de couleur de l'image, et surtout l'ensemble des pixels constituant l'image. Une fois que ces attributs sont bien renseignés, l'application cliente devra faire appel successivement aux méthodes suivantes : getlargeur(), gethauteur(), gettype() et enfin getpixels(). Une fois que l'application fenêtrée a bien récupérée l'ensemble de ces informations, elle pourra ensuite reconstituer la photo désirée de son côté. 2. Dans le cas d'un appel local, au travers d'une application Web par exemple, nous n'avons pas du tout la même problématique. En effet, puisque nous ne passons pas par le réseau, vous pouvez restituer directement la photo grâce à un objet de type BufferedImage au moyen de la méthode recuperer(). Vous avez d'ailleurs le choix entre la taille normale de l'image ou construire une vignette en spécifiant ainsi la largeur désirée. 3. Remarquez au passage que ce bean utilise les compétences du bean FichiersPhotoBean. Nous avons effectivement besoin de connaître l'emplacement du répertoire de stockage de l'ensemble des photos. L'accès à ce bean s'effectue en local. Il suffit donc de proposer une injection à l'aide de l'annotation @EJB. Application cliente distante Nous mettons en oeuvre une application fenêtrée qui sert de visionneuse de photos. Vous avez le choix de la photo à afficher au moyen d'une ComboBox qui liste l'ensemble des fichiers présents sur le serveur. La photo s'affiche alors dans toute la partie restante de la fenêtre.

photos.client.java package photos; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.image.bufferedimage; import java.io.*; import javax.naming.*; public class Client extends JFrame implements ItemListener { private static FichiersPhotoRemote fichiers; private static PhotoRemote photo; private String[] liste; private Panneau panneau = new Panneau(); private JComboBox choix; public Client() throws Exception { liste = fichiers.liste(); choix = new JComboBox(liste); panneau.change(récupérer(choix.getselecteditem())); choix.additemlistener(this); setsize(500, 400); settitle("visionneuse de photos"); add(choix, BorderLayout.NORTH); add(panneau); setdefaultcloseoperation(exit_on_close); setvisible(true); private BufferedImage récupérer(object nom) throws IOException { photo.choix((string)nom); int largeur = photo.getlargeur(); int hauteur = photo.gethauteur(); int style = photo.gettype(); BufferedImage image = new BufferedImage(largeur, hauteur, style); image.setrgb(0, 0, largeur, hauteur, photo.getpixels(), 0, largeur); return image;

public static void main(string[] args) throws Exception { Context ctx = new InitialContext(); fichiers = (FichiersPhotoRemote) ctx.lookup(photos.fichiersphotoremote.class.getname()); photo = (PhotoRemote) ctx.lookup(photos.photoremote.class.getname()); new Client(); public void itemstatechanged(itemevent e) { try { panneau.change(récupérer(choix.getselecteditem())); catch (IOException ex) { settitle("problème avec le serveur"); class Panneau extends JComponent { private BufferedImage image; private double ratio; public void change(bufferedimage image) { this.image = image; ratio = (double)image.getwidth()/image.getheight(); repaint(); protected void paintcomponent(graphics surface) { if (image!=null) surface.drawimage(image, 0, 0, this.getwidth(), (int)(this.getwidth()/ratio), null); Nous avons besoin de travailler avec les deux beans sessions, d'une part pour connaître l'ensemble des photos présentes en ce moment sur le serveur et ensuite pour récupérer la photo désirée afin qu'elle soit affichée dans la fenêtre. Nous avons donc à mettre en place les deux interfaces distantes correspondantes. Vous remarquez la présence de la méthode récupérer() qui s'occupe effectivement de récupérer et de restituer l'image correspondante en spécifiant au départ le fichier concerné. Comme, je l'ai déjà évoqué plus haut, toute cette procédure s'effectue en plusieurs phases par l'appel de méthodes successives de l'objet distant PhotoBean au travers de son interface PhotoRemote. Application Web Nous mettons également en place une application Web de telle sorte qu'il soit possible de visualiser l'ensemble des photos présentes sur le serveur dans une page Web et sous forme de vignettes dont il est possible de choisir la taille. Côté application Web, j'utilise systématiquement la technologie JSF qui offre une façon de concevoir très séduisante puisqu'elle respecte l'ossature MVC. Dans le schéma ci-dessous, remarquez également la présence d'une servlet supplémentaire dont le rôle est de permettre la transformation d'une image sous forme de flux binaire qui est ensuite exploitée par la page JSP. C'est cette servlet, qui se nomme AfficherPhoto, qui s'occupe de récupérer la photo souhaitée et qui utilise donc les compétences du bean session Stateful PhotoBean en local au travers de l'interface PhotoLocal. Par contre, c'est le JavaBean Photos qui recense l'ensemble des photos présentes sur le serveur en se servant des compétences du bean session Stateless FichiersPhotoBean au travers de l'interface FichiersPhotoLocal.

Voici ci-dessous les fichiers de configuration de notre application Web : web.xml <?xml version="1.0" encoding="utf-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.facesservlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>afficherphoto</servlet-name> <servlet-class>servlet.afficherphoto</servlet-class> </servlet> <servlet-mapping> <servlet-name>faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>afficherphoto</servlet-name> <url-pattern>/afficherphoto</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>faces/photos.jsp</welcome-file> </welcome-file-list> </web-app> faces-config.xml <?xml version='1.0' encoding='utf-8'?> <faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"> <managed-bean> <managed-bean-name>photos</managed-bean-name> <managed-bean-class>bean.photos</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config> Voici ensuite le JavaBean bean.photos qui exploite donc les compétences du bean session Stateless FichiersPhotoBean. Je propose l'injection d'une instance de ce bean, grâce à l'annotation @EJB que je place juste avant l'interface correspondante : FichiersPhotoLocal. Je peux me permettre de réaliser cette injection puisque je suis en mode local. bean.photos.java

package bean; import javax.ejb.ejb; import photos.fichiersphotolocal; public class Photos { private int largeurvignette = 300; @EJB private FichiersPhotoLocal fichiers; public int getlargeurvignette() { return largeurvignette; public void setlargeurvignette(int largeurvignette) { this.largeurvignette = largeurvignette; public String[] getliste() { return fichiers.liste(); Je dois m'occuper également de faire une injection sur la servlet servlet.afficherphoto, mais cette fois-ci pour une instance du bean session Stateful PhotoBean, au travers de son interface PhotoLocal. Je le rappelle que, puisque je suis en mode local, je peux récupérer cette fois-ci directement un objet BufferedImage au travers de la méthode recuperer(). J'en profite pour spécifier la taille de la vignette que je désire, information que je récupère depuis le bean photos. servlet.afficherphoto.java package servlet; import bean.photos; import java.io.*; import java.net.*; import javax.ejb.ejb; import javax.imageio.imageio; import javax.servlet.*; import javax.servlet.http.*; import photos.photolocal; public class AfficherPhoto extends HttpServlet { @EJB private PhotoLocal photo; protected void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { response.setcontenttype("image/jpeg"); OutputStream out = response.getoutputstream(); String nom = request.getparameter("nom"); Photos photos = (Photos) request.getsession().getattribute("photos"); int taille = photos.getlargeurvignette(); ImageIO.write(photo.recuperer(nom, taille), "JPEG", out); out.close(); Pour la page Web Photos.jsp, je récupère la liste des photos présentes sur le serveur au moyen de la propriété liste du bean photos. A l'aide de cette liste, je prend le nom du fichier pour chaque image que je place ensuite en paramètre de l'appel de la servlet au travers de la balise <h:graphicsimage>. Photos.jsp <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <f:view> <html> <body bgcolor="black" text="yellow"> <h2><h:outputtext value="ensemble des photos"/></h2> <hr /> <h:form> <h:outputtext value="largeur des vignettes : " /> <h:inputtext value="#{photos.largeurvignette" size="5"/> <hr /> <h:datatable value="#{photos.liste" var="photo"> <h:column> <h:graphicimage value="afficherphoto?nom=#{photo" /> </h:column> </h:datatable> </h:form> </body> </html> </f:view> Cycle devie d'un bean session Afin de gérer la vie d'un bean session, le conteneur d'ejb doit procéder à différentes étapes, qui constitue le cycle de vie de celui-ci. Tout d'abord, pour exploiter un bean session, celui-ci doit être instancié, c'est-à-dire que le conteneur d'ejb fabrique l'objet correspondant à la classe du bean en question. De façon très simplifiée, le conteneur crée l'objet grâce à l'appel de la méthode newinstance(). Celle-ci est disponible à partir de l'objet Class lié à la classe du bean session : photos.photobean.class.newinstance();

Attention, comme tout JavaBean, qu'il soit entreprise ou pas, ceci implique que la classe d'implémentation du bean session dispose d'un constructeur public par défaut, c'est-à-dire sans argument. Le conteneur va ensuite analyser cet objet afin de déceler les éventuelles injections de dépendance à effectuer avec, par exemple, les annotations @EJB. Cet objet passe ensuite dans des phases particulières que nous pouvons éventuellement intercepter. Pour cela, vous pouvez donc mettre en place des méthodes prévues à cet effet qui seront sollicitées par le seul conteneur d'ejb. Il suffit alors d'indiquer la méthode à activer par rapport à la phase souhaitée, et ceci au travers d'annotations spécifiques. Dans ce cas là, ces méthodes sont appelées callback interceptors. Les beans sessions supportent les callback interceptors suivants : 1. @PostConstruct : qui intervient après toutes les dépendances d'injection effectuées par le conteneur et avant le premier appel de la méthode métier. 2. @PreDestroy : qui est appelé au moment où l'objet du bean est détruit (lors de la suppression du bean ou à l'arrêt de l'application). 3. @PrePassivate (Stateful uniquement) : qui permet de spécifier une méthode qui sera appelée par le conteneur d'ejb lorsque le bean a été inactif et qu'il a été jugé nécessaire de passer celui-ci dans un état sérialisable. La méthode en question doit s'assurer que les ressources maintenues en instance sont libérées ou sérialisables à leur tour. On parle de passivation. L'intérêt de ce principe est de libérer la mémoire employée par le Stateful inutilisée pour le moment. Le conteneur peut alors sérialiser l'objet sur un support externe (disque dur...). Cette passivation peut intervenir lorsque l'objet n'est plus sollicité depuis quelques temps ou parce que beaucoup d'autres EJB sont actifs par ailleurs. C'est le conteneur qui décide quand solliciter cette passivation au regard de la mémoire disponible à l'heure actuelle et suivant l'état des autres EJB en pleine activité. Effectivement, le serveur d'applications peut utiliser un système de tampon (swap) pour des soucis d'optimisation de mémoire (concept de passivation). 4. @PostActivate (Stateful uniquement) : qui joue le rôle inverse de @PrePassivate. Cette annotation permet de spécifier la méthode qui sera appelée par le conteneur lorsqu'un bean devra être réactivé de son état de passivation. Le bean devra retrouver son état opérationnel en récupérant les ressources éventuellement libérées lors de la passivation. On parle d'activation. Il existe une dernière annotation qui influe sur le cycle de vie. Il s'agit de l'annotation @Remove. Cette annotation permet de spécifier la méthode qui permet de faire en sorte que l'objet soit supprimé. Cette fois-ci, c'est le client qui appelle cette méthode et non plus le conteneur d'ejb. Effectivement, lorsque le client a fini avec un bean session Stateful, cela ne sert plus à rien de conserver cet EJB. Il peut alors appeler la méthode annotée avec @Remote. Le système devient plus performant en libérant toute la mémoire utilisée par ce bean session. L'état passivé est réservé au bean session stateful car c'est le seul à partager un état avec le client. Le cycle de vie pour ce type de composant commence au premier appel d'une méthode métier. 1. Dans ce cas là, le conteneur crée un objet via la méthode Class.newInstance(). 2. Il injecte ensuite les dépendances et appelle les méthodes annotées avec @PostConstruct. 3. La grande différence avec le type stateless est que le stateful intègre le concept de passivation. L'implémentation de ce concept est spécifique au fournisseur de conteneur et peut donc varier d'un serveur à l'autre. Dans tous les cas, le conteneur appelle les méthodes annotées avec @PrePassivate et @PostActivate au moment de la passivation et de l'activation du composant. 4. L'instance est supprimée lorsque le client appelle une méthode annotée @Remove ou lorsque le temps imparti pour la session est dépassé (timeout). Illustration du cycle de vie sur le serveur de photos Nous allons mettre en oeuvre le cycle de vie de l'objet distant à partir du projet précédent. Nous nous intéresserons donc ici uniquement à l'appel de méthodes distantes à l'aide de l'application fenêtrée et au travers du bean session stateful. Effectivement, une fois que l'application est terminée, et lorsque que la fenêtre se ferme définitivement, j'aimerais avertir le bean session stateful qu'il peut être libéré. Je rajoute donc une méthode arreter() à mon interface PhotoRemote qui va réaliser cette opération : photos.photoremote.java 1 package photos; 2 3 import java.io.ioexception; 4 import javax.ejb.remote; 5 6 @Remote 7 public interface PhotoRemote { 8 void choix(string nom) throws IOException; 9 int getlargeur(); 10 int gethauteur(); 11 int gettype(); 12 int[] getpixels(); 13 void arreter(); 14 Par ailleurs, je vais mettre en oeuvre le callback @PostConstruct afin que le bean session stateful puisse être au courant, le plus tôt possible, de la localisation du répertoire de stockage des photos. Par ailleurs, j'aimerais libérer le tableau de pixels correspondant à l'image récupérée, à l'aide du callback @PreDestroy, juste avant que le composant soit détruit. Il est vrai que ce tableau prend beaucoup de place en mémoire.

Ceci dit, ce tableau de pixels va être libéré sans tarder, puisque l'objet conteneur va lui-même être détruit. Normalement, nous n'avons donc pas besoin d'implémenter une telle méthode. En réalité, c'est pour voir comment faire dans le cas où notre objet utilise effectivement des ressources qui doivent être libérées. photos.photobean.java 1 package photos; 2 3 import java.awt.geom.*; 4 import java.awt.image.*; 5 import java.io.*; 6 import javax.annotation.*; 7 import javax.ejb.*; 8 import javax.imageio.imageio; 9 10 @Stateful 11 public class PhotoBean implements PhotoRemote, PhotoLocal { 12 private int largeur; 13 private int hauteur; 14 private int type; 15 private int[] pixels; 16 @EJB 17 private FichiersPhotoLocal localisation; 18 private String répertoire; 19 20 @PostConstruct 21 public void localiser() { 22 répertoire = localisation.repertoire(); 23 24 25 @PreDestroy 26 public void libérer() { 27 pixels = null; 28 29 30 @Remove 31 public void arreter() { 32 33 public void choix(string nom) throws IOException { 34 BufferedImage photo = ImageIO.read(new File(répertoire+nom)); 35 largeur = photo.getwidth(); 36 hauteur = photo.getheight(); 37 type = photo.gettype(); 38 pixels = photo.getrgb(0, 0, largeur, hauteur, null, 0, largeur); 39 40 41 public int getlargeur() { 42 return largeur; 43 44 45 public int gethauteur() { 46 return hauteur; 47 48 49 public int gettype() { 50 return type; 51 52 53 public int[] getpixels() { 54 return pixels; 55 56 57 public BufferedImage recuperer(string nom) throws IOException { 58 return ImageIO.read(new File(répertoire+nom)); 59 60 61 public BufferedImage recuperer(string nom, int taille) throws IOException { 62 BufferedImage source = recuperer(nom); 63 double ratio = source.getwidth()/(double)taille; 64 BufferedImage image = new BufferedImage((int)(source.getWidth()/ratio), (int)(source.getheight()/ratio), source.gettype()); 65 AffineTransform retailler = AffineTransform.getScaleInstance(1/ratio, 1/ratio); 66 int interpolation = AffineTransformOp.TYPE_BICUBIC; 67 AffineTransformOp retaillerimage = new AffineTransformOp(retailler, interpolation); 68 retaillerimage.filter(source, image); 69 return image; 70 71 Pour l'application cliente, il faut récupérer l'événement correspondant à la clôture de la session et solliciter ainsi la méthode arreter() du bean session stateful (lignes 27 à 31). Remarquez, par contre, que l'application cliente ne gère absolument pas les callback @PostConstruct et @PreDestroy. C'est le conteneur d'ejb qui s'en occupe tout seul, sans avoir besoin de s'en préoccuper par la suite. photos.client.java 1 package photos; 2 3 import javax.swing.*; 4 import java.awt.*; 5 import java.awt.event.*; 6 import java.awt.image.bufferedimage; 7 import java.io.*; 8 import javax.naming.*; 9 10 public class Client extends JFrame implements ItemListener {

11 private static FichiersPhotoRemote fichiers; 12 private static PhotoRemote photo; 13 private String[] liste; 14 private Panneau panneau = new Panneau(); 15 private JComboBox choix; 16 17 public Client() throws Exception { 18 liste = fichiers.liste(); 19 choix = new JComboBox(liste); 20 panneau.change(récupérer(choix.getselecteditem())); 21 choix.additemlistener(this); 22 setsize(500, 400); 23 settitle("visionneuse de photos"); 24 add(choix, BorderLayout.NORTH); 25 add(panneau); 26 setdefaultcloseoperation(exit_on_close); 27 addwindowlistener(new WindowAdapter() { 28 public void windowclosing(windowevent evt) { 29 photo.arreter(); 30 31 ); 32 setvisible(true); 33 34 35 private BufferedImage récupérer(object nom) throws IOException { 36 photo.choix((string)nom); 37 int largeur = photo.getlargeur(); 38 int hauteur = photo.gethauteur(); 39 int style = photo.gettype(); 40 BufferedImage image = new BufferedImage(largeur, hauteur, style); 41 image.setrgb(0, 0, largeur, hauteur, photo.getpixels(), 0, largeur); 42 return image; 43 44 45 public static void main(string[] args) throws Exception { 46 Context ctx = new InitialContext(); 47 fichiers = (FichiersPhotoRemote) ctx.lookup(photos.fichiersphotoremote.class.getname()); 48 photo = (PhotoRemote) ctx.lookup(photos.photoremote.class.getname()); 49 new Client(); 50 51 52 public void itemstatechanged(itemevent e) { 53 try { 54 panneau.change(récupérer(choix.getselecteditem())); 55 56 catch (IOException ex) { 57 settitle("problème avec le serveur"); 58 59 60 61 62 class Panneau extends JComponent { 63 private BufferedImage image; 64 private double ratio; 65 66 public void change(bufferedimage image) { 67 this.image = image; 68 ratio = (double)image.getwidth()/image.getheight(); 69 repaint(); 70 71 72 protected void paintcomponent(graphics surface) { 73 if (image!=null) 74 surface.drawimage(image, 0, 0, this.getwidth(), (int)(this.getwidth()/ratio), null); 75 76 La vie d'un bean session stateful démarre lors de la récupération de celui-ci par l'application cliente (via l'injection JNDI). Elle se termine si le temps imparti à la durée de vie est dépassée ou si le client appelle une méthode annotée avec @Remove. Attention : à chaque appel de la méthode Context.lookup(), une instance du bean session stateful est créée. Veuillez bien gérer la création et la suppression de ces instances pour ne pas surcharger la mémoire.

Interception de tous les appels de méthode - @AroundInvoke Il est également possible d'intercepter tous les appels de méthode du bean session invoqués par le client. Il suffit pour cela de définir une méthode qui va servir de callback interceptor au moyen de l'annotation @AroundInvoke. C'est le conteneur d'ejb qui lancera cette méthode particulière lorsque n'importe laquelle des méthodes du bean session est demandée par le client. Le nom de la méthode qui va servir d'interception est a votre libre arbitre, comme toutes les autres méthodes callback d'ailleurs. Par contre, la signature de la méthode est particulière et doit être respectée : @AroundInvoke public Object nomdeméthode(invocationcontext ctx) throw Exception {... Remarquez la présence du type InvocationContext qui est en réalité une interface dont voici la signature : package javax.ejb; public interface InvocationContext { public Object gettarget(); public java.lang.reflect.method getmethod(); public Object[] getparameters(); public void setparameters(object[] params); public EJBContext getejbcontext(); public java.util.map<string, Object> getcontextdata(); public Object proceed() throw Exception; 1. gettarget() : retourne l'objet correspondant au bean qui lance la méthode sollicitée par le client. 2. getmethod() : retourne la méthode qui est appelée par le client. 3. getparameters() : retourne les paramètres de la méthode appelée par le client. 4. setparameters() : modifie les paramètres de la méthode appelée par le client. 5. getejbcontext() : retourne les méthodes d'interception du bean (callback interceptor). 6. getcontextdata() : permet de passer des valeurs entre les méthodes d'invocation. 7. proceed() : lance l'intercepteur suivant s'il existe ou lance tout simplement la méthode d'appel souhaitée par le client. Cette méthode doit systématiquement être utilisée si nous désirons que la méthode appelée par le client soit effectivement lancée. A titre d'exemple, je reprends le projet précédent et je rajoute à mon bean session stateful une méthode gestiondesappelsdeméthode() qui va contrôler si la méthode choix() est appelée en premier par le client, avant donc toutes les autres méthodes : getlargeur(), gethauteur(), gettype() et getpixels(). J'en profite pour faire une trace au niveau du serveur afin d'afficher les méthodes qui seront effectivement appelées. J'ai mis une trace supplémentaire sur les callback @ PostConstruct et @PreDestroy. photos.photobean.java 1 package photos; 2 3 import java.awt.geom.*; 4 import java.awt.image.*; 5 import java.io.*; 6 import javax.annotation.*; 7 import javax.ejb.*; 8 import javax.imageio.imageio; 9 import javax.interceptor.*; 10

11 @Stateful 12 public class PhotoBean implements PhotoRemote, PhotoLocal { 13 private int largeur; 14 private int hauteur; 15 private int type; 16 private int[] pixels; 17 @EJB 18 private FichiersPhotoLocal localisation; 19 private String répertoire; 20 private boolean choixeffectué; 21 22 @PostConstruct 23 public void localiser() { 24 System.out.println("Objet construit"); 25 répertoire = localisation.repertoire(); 26 27 28 @PreDestroy 29 public void libérer() { 30 System.out.println("Objet détruit"); 31 pixels = null; 32 33 34 @AroundInvoke 35 public Object gestiondesappelsdeméthode(invocationcontext ctx) throws Exception { 36 String nomméthode = ctx.getmethod().getname(); 37 System.out.println("Invocation de la méthode : "+nomméthode); 39 if (nomméthode.equals("arreter")) return ctx.proceed(); 40 if (nomméthode.equals("choix")) choixeffectué = true; 41 else if (!choixeffectué) throw new Exception("Pas dans le bon ordre d'appel"); 42 return ctx.proceed(); 43 44 45 @Remove 46 public void arreter() { 47 48 public void choix(string nom) throws IOException { 49 BufferedImage photo = ImageIO.read(new File(répertoire+nom)); 50 largeur = photo.getwidth(); 51 hauteur = photo.getheight(); 52 type = photo.gettype(); 53 pixels = photo.getrgb(0, 0, largeur, hauteur, null, 0, largeur); 54 55 56 public int getlargeur() { 57 return largeur; 58 59 60 public int gethauteur() { 61 return hauteur; 62 63 64 public int gettype() { 65 return type; 66 67 68 public int[] getpixels() { 69 return pixels; 70 71 72 public BufferedImage recuperer(string nom) throws IOException { 73 return ImageIO.read(new File(répertoire+nom)); 74 75 76 public BufferedImage recuperer(string nom, int taille) throws IOException { 77 BufferedImage source = recuperer(nom); 78 double ratio = source.getwidth()/(double)taille; 79 BufferedImage image = new BufferedImage((int)(source.getWidth()/ratio), (int)(source.getheight()/ratio), source.gettype()); 80 AffineTransform retailler = AffineTransform.getScaleInstance(1/ratio, 1/ratio); 81 int interpolation = AffineTransformOp.TYPE_BICUBIC; 82 AffineTransformOp retaillerimage = new AffineTransformOp(retailler, interpolation); 83 retaillerimage.filter(source, image); 84 return image; 85 86 Maintenant, lorsque le client effectue un appel sur la méthode qu'il désire, c'est d'abord la méthode gestiondesappelsdeméthode() qui est appelée. Ensuite, grâce à la méthode proceed(), la méthode souhaitée par le client est effectivement exécutée, à moins qu'une exception soit lancée, bien entendu. Pour le code de l'application cliente, j'ai revu un certain nombre de chose. Notamment, je me suis rendu compte que grâce au traçage des appels effectués par le client, la gestion des événements avec ItemListener n'était pas la plus performante. Je préfère donc passer par un ActionListener. Par ailleurs, j'ai changé la méthode récupérer() en faisant exprès de lancer d'abord la méthode getlargeur() avant d'avoir choisi la photo souhaitée (lignes 37 et 38). photos.client.java 1 package photos; 2 3 import javax.swing.*; 4 import java.awt.*; 5 import java.awt.event.*; 6 import java.awt.image.bufferedimage; 7 import java.io.*; 8 import javax.naming.*; 9 10 public class Client extends JFrame implements ActionListener { 11 private static FichiersPhotoRemote fichiers;

12 private static PhotoRemote photo; 13 private String[] liste; 14 private Panneau panneau = new Panneau(); 15 private JComboBox choix; 16 17 public Client() { 18 liste = fichiers.liste(); 19 choix = new JComboBox(liste); 20 panneau.change(récupérer(choix.getselecteditem())); 21 choix.addactionlistener(this); 22 setsize(500, 400); 23 settitle("visionneuse de photos"); 24 add(choix, BorderLayout.NORTH); 25 add(panneau); 26 setdefaultcloseoperation(exit_on_close); 27 addwindowlistener(new WindowAdapter() { 28 public void windowclosing(windowevent evt) { 29 photo.arreter(); 30 31 ); 32 setvisible(true); 33 34 35 private BufferedImage récupérer(object nom) { 36 try { 37 int largeur = photo.getlargeur(); 38 photo.choix((string)nom); 39 int hauteur = photo.gethauteur(); 40 int style = photo.gettype(); 41 BufferedImage image = new BufferedImage(largeur, hauteur, style); 42 image.setrgb(0, 0, largeur, hauteur, photo.getpixels(), 0, largeur); 43 return image; 44 45 catch (Exception ex) { 46 settitle("problème avec le serveur"); 47 return null; 48 49 50 51 public static void main(string[] args) throws Exception { 52 Context ctx = new InitialContext(); 53 fichiers = (FichiersPhotoRemote) ctx.lookup(photos.fichiersphotoremote.class.getname()); 54 photo = (PhotoRemote) ctx.lookup(photos.photoremote.class.getname()); 55 new Client(); 56 57 58 public void actionperformed(actionevent e) { 59 panneau.change(récupérer(choix.getselecteditem())); 60 61 62 63 class Panneau extends JComponent { 64 private BufferedImage image; 65 private double ratio; 66 67 public void change(bufferedimage image) { 68 if (image!=null) { 69 this.image = image; 70 ratio = (double)image.getwidth()/image.getheight(); 71 repaint(); 72 73 74 75 protected void paintcomponent(graphics surface) { 76 if (image!=null) 77 surface.drawimage(image, 0, 0, this.getwidth(), (int)(this.getwidth()/ratio), null); 78 79 Dans ces conditions, voici ce que nous obtenons si nous exécutons l'application cliente. Par contre, si nous remettons les méthodes dans le bon ordre en permuttant les lignes 37 et 38, voici le traçage que nous obtenons sur le serveur d'application :

Classe d'interception L'utilisation de méthodes de type callback interceptors est une pratique facile à mettre en place. Toutefois, l'inconvénient principal de cette approche est le mélange qu'elle génère entre les méthodes métiers et celle du cycle de vie des beans sessions. Ce rassemblement peut porter à confusion si la gestion du cycle de vie est importante. L'idéal est de prévoir une séparation entre les méthodes métiers et les méthodes d'interception. Il est effectivement possible de définir cette gestion de cycle de vie dans des classes dédiées, appelée classe interceptor. L'association d'une classe d'intercepteur à un beans session se fait à l'aide de l'annotation @Interceptors qui prend en argument la classe d'interception. photos.photobean.java 1 package photos; 2 3 import java.awt.geom.*; 4 import java.awt.image.*; 5 import java.io.*; 6 import javax.annotation.*; 7 import javax.ejb.*; 8 import javax.imageio.imageio; 9 import javax.interceptor.*; 10 11 @Interceptors({CycleVie.class) 12 @Stateful 13 public class PhotoBean implements PhotoRemote, PhotoLocal { 14 private int largeur; 15 private int hauteur; 16 private int type; 17 18 int[] pixels; 19 String répertoire; 20 21 @Remove 22 public void arreter() { 23 24 public void choix(string nom) throws IOException { 25 BufferedImage photo = ImageIO.read(new File(répertoire+nom)); 26 largeur = photo.getwidth(); 27 hauteur = photo.getheight(); 28 type = photo.gettype(); 29 pixels = photo.getrgb(0, 0, largeur, hauteur, null, 0, largeur); 30 31 32 public int getlargeur() { 33 return largeur; 34 35 36 public int gethauteur() { 37 return hauteur; 38 39 40 public int gettype() { 41 return type; 42 43 44 public int[] getpixels() { 45 return pixels; 46 47 48 public BufferedImage recuperer(string nom) throws IOException { 49 return ImageIO.read(new File(répertoire+nom)); 50 51 52 public BufferedImage recuperer(string nom, int taille) throws IOException { 53 BufferedImage source = recuperer(nom); 54 double ratio = source.getwidth()/(double)taille; 55 BufferedImage image = new BufferedImage((int)(source.getWidth()/ratio), (int)(source.getheight()/ratio), source.gettype()); 56 AffineTransform retailler = AffineTransform.getScaleInstance(1/ratio, 1/ratio); 57 int interpolation = AffineTransformOp.TYPE_BICUBIC; 58 AffineTransformOp retaillerimage = new AffineTransformOp(retailler, interpolation); 59 retaillerimage.filter(source, image); 60 return image; 61 62 Revoici donc le bean session stateful. Vous remarquez en ligne 11, la déclaration de la classe d'interception qui s'appelle CycleVie. Nous n'avons plus les méthodes callback interceptors, ce qui allège considérablement le code source du bean. J'ai également enlevé l'injection du bean session stateless FichiersPhotoLocal qui sera plutôt placé sur la classe d'interception. Pour finir, les attributs pixels et répertoires ne sont plus privés afin qu'ils deviennent accessibles depuis la classe d'interception. photos.cyclevie.java