Looking for Computer Science  & Information Technology online courses ?
Check my new web site: https://www.yesik.it !

Session beans.png

Session bean — Un des rôles de JBoss AS est d'aiguiller les requêtes des clients vers le session bean chargé d'y répondre.

Dans le cas d'un stateful session bean un client est lié à un bean: toutes les requêtes d'un même client vont sur le même session bean. Et celui-ci ne reçoit des requêtes que de ce client là.

Dans le cas d'un stateless session bean JBoss choisit le premier bean disponible: rien ne garantit que toutes les requêtes d'un client ne soient traitées par le même bean. De la même manière, le bean peut recevoir des requêtes de différents clients.


Un session bean est un objet métier Java destiné à être déployé sur un serveur d'application. Il existe deux sortes de session beans:

stateful session beans
session bean avec état – ceux-ci conservent leur état entre deux appels de méthodes.
stateless session beans
session beans sans état – leur état n'est pas garanti entre deux appels de méthodes.

Dans cet article, nous allons voir comment créer un session bean sans état géré par JBoss AS, et y accéder à partir d'une application Java distante.

L'arborescence du projet


Arborescence des fichiers à l'issue du projet: Si à un moment vous avez un problème, n'hésitez pas à vous reporter à cette illustration pour vérifier que vos fichiers sont bien placés au bon endroit.

Tout d'abord, nous allons créer les premiers éléments de l'arborescence de notre projet. Celui-ci contiendra notamment 3 sous-répertoires:

  1. un pour les sources;
  2. un pour le session bean compilé;
  3. et un pour le client compilé.
# Création du répertoire pour le projet
sh$ mkdir session-bean-project
sh$ cd session-bean-project

# Et ses sous-répertoires
sh$ mkdir -p src/fr/chicoree/sample
sh$ mkdir session-bean
sh$ mkdir client

Le bean

Pour respecter les spécifications EJB, un session bean doit se conformer à certaines règles. En particulier, il doit être défini en deux étapes: tout d'abord en créant une interface regroupant les services offerts par le bean, puis en créant une classe qui met en oeuvre cette interface.

Dans cet exemple, notre bean sera chargé de calculer le montant maximum de crédit autorisé pour un client en fonction de ses revenus annuels. Ce qui se traduit ainsi dans l'interface Java:

package fr.chicoree.sample;
 
import javax.ejb.Remote;
 
@Remote
public interface CreditManager {
    double  getMaximumCredit(double annualIncome);
}

Reste à mettre en oeuvre cette interface:

package fr.chicoree.sample;
 
import javax.ejb.Stateless;
 
@Stateless
public class CreditManagerBean implements CreditManager {
    public double getMaximumCredit(double annualIncome) {
	return annualIncome * 0.30;
    }
}

A ce stade, votre projet devrait correspondre à l'arborescence ci-dessous:

sh$ find .
.
./client
./src
./src/fr
./src/fr/chicoree
./src/fr/chicoree/sample
./src/fr/chicoree/sample/CreditManagerBean.java
./src/fr/chicoree/sample/CreditManager.java
./session-bean

Si tout est bon, nous pouvons passer à la compilation. Ici, le compilateur a besoin de certaines classes founies avec la distribution de JBoss. D'où l'option -classpath:

sh$ javac -classpath ${JBOSS_HOME}/common/lib/jboss-javaee.jar:. \
          -d session-bean                                     \
          src/fr/chicoree/sample/CreditManager*.java

# Si vous n'avez pas d'erreur, les fichiers compilés doivent se retrouver dans le dossier session-bean:
sh$ find session-bean/
session-bean/
session-bean/fr
session-bean/fr/chicoree
session-bean/fr/chicoree/sample
session-bean/fr/chicoree/sample/CreditManager.class
session-bean/fr/chicoree/sample/CreditManagerBean.class

Reste à packager notre bean dans un JAR (Java ARchive – Un format d'archive utilisé dans le monde Java pour regrouper des fichiers (classes compilées, méta-données, etc.).) , et à le déployer sur le serveur:

sh$ jar cf CreditManager.jar -C session-bean fr
sh$ cp CreditManager.jar ${JBOSS_HOME}/server/default/deploy

Si vous observez le journal de déploiement du serveur (dans ${JBOSS_HOME}/server/default/log/server.log), vous devriez voir le message suivant qui indique que votre bean est bien déployé sur le serveur. Au passage, ce message donne aussi le nom par lequel les applications distantes pourront y accéder:

[...]
2009-08-26 15:26:52,895 INFO  [org.jboss.ejb3.session.SessionSpecContainer]
(HDScanner) Starting jboss.j2ee:jar=CreditManager.jar,name=CreditManagerBean,service=EJB3
2009-08-26 15:26:52,896 INFO  [org.jboss.ejb3.EJBContainer]
(HDScanner) STARTED EJB: fr.chicoree.sample.CreditManagerBean ejbName: CreditManagerBean
2009-08-26 15:26:52,914 INFO  [org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase]
(HDScanner) Binding the following Entries in Global JNDI:

	CreditManagerBean/remote - EJB3.x Default Remote Business Interface
	CreditManagerBean/remote-fr.chicoree.sample.CreditManager - EJB3.x Remote Business Interface

Nous en avons maintenant terminé avec la partie serveur. Reste à écrire le client.

Le client

Paradoxalement (?), le code du client est un peu plus complexe. Tout simplement parce qu'il doit localiser le bean avant de pouvoir y accéder. Et comme en l'occurrence notre client va être une application Java SE ordinaire, celle-ci ne peut avoir de support direct de la part du serveur d'application dans cette tâche.

package fr.chicoree.sample;
 
import javax.naming.InitialContext;
 
public class CreditClient {
    public static void main(String[] args) throws Exception {
        // Recupère une référence sur le ''bean''
	InitialContext	ctx = new InitialContext();
	CreditManager	cm  = (CreditManager)ctx.lookup("CreditManagerBean/remote");
 
        // Dorénavant, le ''bean'' peut être utilisé comme un objet de l'application
	double income = 120000;
	System.out.printf("income = %8.0f - Max. credit: %8.0f%n",
			    income,
			    cm.getMaximumCredit(income));
    }
}

Le code chargé de récupérer la référence sur le session bean est concentré dans les deux lignes suivantes:

/* ... */
	InitialContext	ctx = new InitialContext();
	CreditManager	cm  = (CreditManager)ctx.lookup("CreditManagerBean/remote");
Bean et proxy.png

Bean proxy — Le bean n'existe que sur le serveur. Tout ce qu'obtient le client, c'est une référence sur celui-ci (en fait un proxy). Autrement dit, à chaque fois qu'on invoque une méthode sur cet objet, cela implique un échange avec le serveur d'application.

Comme vous le constater, on utilise le contexte de l'application pour localiser le bean par son nom. Or, il faut bien à un moment ou un autre indiquer que c'est JBoss qui héberge cet objet. Cela se fait via un fichier de configuration appelé jndi.properties à placer à la racine de votre application (dans notre cas, dans le répertoire client):

sh$ cat client/jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost:1099

Reste à compiler notre projet. Ici encore, le compilateur a besoin qu'on lui fournisse certaines bibliothèques non incluses dans Java SE. Par ailleurs, le client a à la fois besoin de son code, mais aussi de l'interface CreditManager au travers de laquelle il accédera au bean. Ce qui se traduit par la ligne de commande suivante:

sh$ javac -classpath ${JBOSS_HOME}/client/jbossall-client.jar:. \
          -d client                                          \
          src/fr/chicoree/sample/CreditManager.java          \
          src/fr/chicoree/sample/CreditClient.java

Quand à l'exécution, celle-ci se fait comme pour un programme Java ordinaire, à ceci près qu'il faut fournir à la JVM (Java Virtual Machine – Machine Virtuelle Java.
Un logiciel capable d'interpréter le byte-code Java pour exécuter un programme. )
les JAR des bibliothèques clients de JBoss:

sh$ cd client
sh$ java -classpath ${JBOSS_HOME}/client/jbossall-client.jar:. \
         fr.chicoree.sample.CreditClient
log4j:WARN No appenders could be found for logger (org.jnp.interfaces.TimedSocketFactory).
log4j:WARN Please initialize the log4j system properly.
income =   120000 - Max. credit:    36000

Remarque:

JBoss utilise log4j pour journaliser les événements. Si comme moi vous avez des messages d'avertissement à ce propos, c'est que log4j n'est pas convenablement configuré. Si vous voulez configurer log4j, rajoutez le fichier client/log4j.properties:

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

log4j.rootLogger=INFO, stdout

Et à quoi ça sert?

Finalement, la mise en oeuvre d'un session bean ne se révèle "pas si compliqué que ça". A condition de rester organisé. Mais la question qui peut se poser, c'est de savoir "à quoi ça sert, tout ça"?

Un usage typique des session beans est de pouvoir centraliser sur un serveur l'ensemble des logiques métiers d'une application. Et celle-ci peuvent parfois être très complexes. Si les centraliser offre l'avantage d'éviter d'avoir à les reproduire dans chaque application, l'intérêt majeur est surtout de pouvoir les modifier facilement (dans notre exemple, si les conditions de crédit changent). Ainsi, seul le bean sur le serveur est à modifier, et automatiquement, toutes les applications utilisant ce service bénéficieront des dernières modifications – où qu'elles soient situées dans le monde et sans intervention locale.

Un autre intérêt est de pouvoir concevoir des applications distribuées utilisant des beans fournis par différents prestataires. Ceux-ci gardent le contrôle des services qu'ils fournissent et sont susceptibles de les mettre à jour à tout moment. Tout en permettant à leurs clients de bâtir des applications reposant sur ces services.