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

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.
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.
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.
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 (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 :

Ce que fournit les bases de l'implémentation d'une architecture client - serveur :
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.
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 :
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.

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 :
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 :
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) :
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 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.
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.
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.

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.

Voici quelques considérations concernant l'utilisation des beans sessions dans un système d'entreprise :
Les beans sessions Stateful sont appropriés si l'une des conditions suivantes (non exhaustive) est vrai :
Pour améliorer les performances, vous pouvez choisir d'utiliser un bean session Stateless s'il a une de ces caractéristiques :
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.
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, 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.
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()));
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 :
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.
.
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, 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.
.
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 :
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).
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).
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.
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.
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 :

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