EJB3 - Bean session

Chapitres traités   

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.

Choix du chapitre 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.

 

Choix du chapitre 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.

Choix du chapitre 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.

 

Choix du chapitre 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.

 

Choix du chapitrePremier 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.
.

 

Choix du chapitre 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 :

Attention pour le serveur JBoss : Il faut a tout pris retirer de l'archive de l'application la référence au jar qui contient l'Ejb. En effet par défaut cet librarie est incluse (sauf à l'enlever du packaging au niveau du project web)et cela a pour effet de provoquer une erreur de casting de la récupération de l'ejb. (il essaie de faire un cast de l'ejb récupéré mais ce dernier est alors un objet de type proxy).

 

Choix du chapitre Accès à la fois à distance et 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.

 

Choix du chapitre 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 conteneu