traitements asynchrones - ejb...

24
Traitements asynchrones - EJB Messages Chapitres traités Introduction Dans cette étude, nous nous intéresserons à la communication entre les applications clientes avec un système asynchrone de type JMS. JMS est un service proposé par Java EE qui intègre une messagerie et qui permet de stocker des messages par une application cliente. Ces messages sont ensuites délivrés à une autre application cliente qui les attend éventuellement (gestion événementielle). Toutefois, si cette dernière n'est pas encore active, les messages sont concervés par la messagerie. Les messages peuvent aussi être dédiés au serveur lui-même. Nous verrons alors qu'il existe un bean spécialisé dans la réception de message issu du service JMS, il s'agit du bean Message Driven Bean. Nous en profiterons également pour voir comment envoyer ou recevoir du courrier électronique. Et pour finir, nous verrons comment mettre en oeuvre le service de Timer intégré dans Java EE. Introduction 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 serveurs. Par communication, il faut comprendre un envoi de données directement interprétables et utilisables par les autres applications. Ce sont les beans messages qui permettent de traiter les messages venant d'autres applications. Avant de connaître ce type de bean, dans un premier temps, nous présenterons l'API JMS, qui permet la connexion avec un système de messagerie inter-applications. Le concept de bean message (MDB - Message Driven Bean) a été introduit avec les EJB 2.0 afin de traiter les messages venant d'un fournisseur JMS (Java Message Service). En réalité, avec EJB 3.0, les MDB supportent maintenant n'importe quel système de messagerie et sont donc indépendants de JMS et simplifie grandement le développement de ces composants. Toutefois, tous les serveurs d'applications compatibles EJB 3.0 doivent supporter JMS. Beaucoup d'éditeurs fournissent leur propre implémentation JMS. dans tous les cas, un fournisseur JMS est inévitable pour utiliser les MDB. Java Message Service - concepts JMS, ou Java Message Service, est une API d'échanges de messages pour permettre un dialogue entre applications via un fournisseur (Provider) de messages. L'application cliente envoie un message dans une liste d'attente (plutôt qu'à une autre application directement, ce qui permet de faire un bon découplage logiciel), sans se soucier de la disponibilité de cette application (chaque système possède son propre cycle de vie). Le client a, de la part du fournisseur de messages, une garantie de qualité de service (certitude de remise au destinataire, delai de remise, etc.). JMS est l'API utilisée pour l'accès à un système de messagerie d'entreprise. Ce système permet donc l'échange de messages entre différentes applications distantes. Les serveurs de messageries sont souvent méconnus (contrairement aux serveurs de base de données), bien qu'ils soient nombreux. On retrouve : 1. MQSeries d'IBM, 2. JBoss Messaging, 3. One Message Queue de Sun Microsystem, ... Les applications utilisant JMS sont indépendantes du type de serveur auquel elles se connectent du moment qu'elles utilisent l'API JMS. . Les applications utilisent généralement JMS dans les architectures de type B2B (Business to Business). En effet, cette API permet d'interconnecter n'importe quel système utilisant le principe de messagerie où l'envoi et la réception de message sont asynchrones. Cela signifie que les applications communiquant via JMS peuvent ne pas s'exécuter en même temps, sur le même principe que le système de courrier électronique (email). Lorsque l'expéditeur envoie une requête (via email), il ne reçoit pas directement la réponse. Il se peut qu'il ne reçoive jamais de réponse, ou seulement un accusé de réception. Architecture JMS L'architecture JMS est composée de différents éléments : 1. Un fournisseur :(Provider) : c'est l'élément qui a la charge de la livraison des messages entre les différents intervenants. Il s'occupe de traiter les envois et de faire en sorte qu'ils soient bien reçus. Il s'agit d'un service qui implémente l'API JMS pour échanger les messages entre deux clients. 2. Un client : c'est une application ou un composant d'application intervenant lors des échanges. Il envoie ou reçoit les messages. Il s'agit d'une classe Java qui utilise JMS pour émettre et/ou recevoir des messages. Un client envoie un message vers une file d'attente, et le client destinataire reste à l'écoute d'une file d'attente pour recevoir le message. Le transfert du message et sa persistance sont assurés par le fournisseur. 3. Un message : c'est, comme son nom l'indique, l'élément qui va transiter via une communication entre les clients. Un fournisseur sert toujours d'intermédiaire ; nous ne les envoyons donc pas directement d'un client à un autre. Un message est un ensemble de données échangées de manière asynchrone entre les composants. Il existe plusieurs types de messages (texte, objet, binaire, etc.). 4. Les destinations : ce sont des objets configurés au niveau du fournisseur qui sont à disposition des clients et qui seront utilisés par ces derniers pour l'envoi et la réception des messages. Pour schématiser, nous pouvons dire qu'il s'agit de boîtes à lettres dans lesquelles sont placées les messages en attendant qu'un client vienne les réclamer. Ce sont des ressources à rechercher dans l'annuaire JNDI du fournisseur.

Upload: dangduong

Post on 15-Sep-2018

229 views

Category:

Documents


0 download

TRANSCRIPT

Traitements asynchrones - EJB Messages

Chapitres traités Introduction

Dans cette étude, nous nous intéresserons à la communicatio n entre les applications clientes avec un système asynchron e de type JMS.JMS est un service proposé par Java EE qui intègre une messagerie et qui permet de stocker des messa ges par une application cliente.Ces messages sont ensuites délivrés à une autre application cliente qui les attend éventuellement (gestion événementi elle). Toutefois, sicette dernière n'est pas encore active, les messages sont co ncervés par la messagerie.

Les messages peuvent aussi être dédiés au serveur lui-même. Nous verrons alors qu'il existe un bean spécialisé dans la ré ception demessage issu du service JMS , il s'agit du bean Message Driven Bean .

Nous en profiterons également pour voir comment envoyer ou r ecevoir du courrier électronique. Et pour finir, nous verro ns comment mettre en oeuvre le service deTimer intégré dans Java EE.

Introduction

Au sein d'une application d'entreprise de grande ampleur, i l peut être intéressant de faire communiquer entre elles les différentes sous-applications clientes et serveurs. Parcommunication, il faut comprendre un envoi de données direc tement interprétables et utilisables par les autres applic ations.

Ce sont les beans messages qui permettent de traiter les mess ages venant d'autres applications. Avant de connaître ce ty pe de bean, dans un premier temps, nousprésenterons l'API JMS, qui permet la connexion avec un syst ème de messagerie inter-applications.

Le concept de bean message ( MDB - Message Driven Bean) a été introduit avec les EJB 2.0 afin de traiter les messages venant d'un fournisseur JMS (Java MessageService). En réalité, avec EJB 3.0, les MDB supportent maintenant n'importe quel système de messageri e et sont donc indépendants de JMS et simplifie grandementle développement de ces composants.

Toutefois, tous les serveurs d'applications compatibles EJB 3.0 doivent supporter JMS. Beaucoup d'éditeurs fournissent leur propre implémentat ion JMS. dans tousles cas, un fournisseur JMS est inévitable pour utiliser les MDB.

Java Message Service - concepts

JMS, ou Java Message Service, est une API d'échanges de messages pour permettre un dialogue entre applications via un fournisseur ( Provider ) de messages.L'application cliente envoie un message dans une liste d'at tente (plutôt qu'à une autre application directement, ce qu i permet de faire un bon découplage logiciel), sans sesoucier de la disponibilité de cette application (chaque sy stème possède son propre cycle de vie). Le client a, de la part du fournisseur de messages, une garantie de qualitéde service (certitude de remise au destinataire, delai de re mise, etc.).

JMS est l'API utilisée pour l'accès à un système de messagerie d'entreprise. Ce système permet donc l'échange de messages entre différentes applicationsdistantes. Les serveurs de messageries sont souvent méconnus (contrairement aux serveurs de base de données), bien qu'ils soient nombreux. On retrouve :

1. MQSeries d'IBM ,

2. JBoss Messaging ,

3. One Message Queue de Sun Microsystem , ...

Les applications utilisant JMS sont indépendantes du type de serveur auquel elles se connec tent du moment qu'elles utilisent l'API JMS..

Les applications utilisent généralement JMS dans les architectures de type B2B (Business to Business ). En effet, cette API permet d'interconnecter n'importe qu elsystème utilisant le principe de messagerie où l'envoi et la réception de message sont asynchrones. Cela signifie que le s applications communiquant via JMSpeuvent ne pas s'exécuter en même temps, sur le même principe que le système de courrier électronique ( email ). Lorsque l'expéditeur envoie une requête ( via email ),il ne reçoit pas directement la réponse. Il se peut qu'il ne re çoive jamais de réponse, ou seulement un accusé de réception .

Architecture JMS

L'architecture JMS est composée de différents éléments :

1. Un fournisseur : (Provider ) : c'est l'élément qui a la charge de la livraison des message s entre les différents intervenants. Il s'occupe de traiter les envois et de faireen sorte qu'ils soient bien reçus. Il s'agit d'un service qui implémente l'API JMS pour échanger les messages entre deux clients.

2. Un client : c'est une application ou un composant d'application inter venant lors des échanges. Il envoie ou reçoit les messages. I l s'agit d'une classe Java quiutilise JMS pour émettre et/ou recevoir des messages. Un client envoie u n message vers une file d'attente, et le client destinataire reste à l'écoute d'une filed'attente pour recevoir le message. Le transfert du message et sa persistance sont assurés par le fournisseur.

3. Un message : c'est, comme son nom l'indique, l'élément qui va transiter via une communication entre les clients. Un fournisseur ser t toujours d'intermédiaire ;nous ne les envoyons donc pas directement d'un client à un aut re. Un message est un ensemble de données échangées de manièr e asynchrone entre lescomposants. Il existe plusieurs types de messages (texte, o bjet, binaire, etc.).

4. Les destinations : ce sont des objets configurés au niveau du fournisseur qui s ont à disposition des clients et qui seront utilisés par ces d erniers pour l'envoi et laréception des messages. Pour schématiser, nous pouvons dir e qu'il s'agit de boîtes à lettres dans lesquelles sont placé es les messages en attendant qu'un clientvienne les réclamer. Ce sont des ressources à rechercher dan s l'annuaire JNDI du fournisseur.

Un point important dans cette architecture est qu'elle perm et une communication faiblement couplée entre les clients : un client ne se préoccupe pas de l'identité deson ou de ses correspondants ni de leur éventuel état. De plus ce système peut travailler en environnement hétérogène (ap plication C++, Java ...).

Modèle de messagerie

JMS offre deux modèles de messagerie point à point et publication/abonnement ou (publication/souscription ) :

1. Le mode point à point utilise les files d'attente ( javax.jms.Queue ) pour communiquer. Ce mode ( un émetteur, un récepteur ) s'apparente à l'envoi d'un e-mail .

2. Le mode publication/abonnement utilise des sujets ( javax.jsm.Topic ) pour échanger des messages. Ce mode ( un émetteur, multiples récepteurs ) correspond, parexemple, à une souscription auprès d'un serveur de news . Par défaut, seuls les récepteurs connectés au sujet (Topic ) sont alertés de l'arrivée du message. Pourque les messages soient concervés pour les récepteurs décon nectés, ils doivent être déclarés comme durable .

Nous pouvons considerer ces deux modes de la façon suivante : le modèle point à point représente une relation "One To One" entre un message et un destinatairealors que le modèle publication/souscription est représentée par une relation "One To Many" .

Chaque mode utilise une interface différente pour envoyer d es messages : javax.jms.QueueSender dans le mode point à point , et javax.jms.TopicPublisher pour lemode publication/abonnement . Tous deux héritent de la super interface javax.jms.MessageProducer .

Le mode point à point

Le mode point à point repose sur le concept de files d'attente ( Queue ). Cela signifie que chaque message est envoyé par un product eur dans une file d'attente, et est reçupar un seul consommateur. Une fois le message consommé, il di sparaît de la file d'attente. Dans ce principe, les messages sont envoyés et empilés au fur et à mesure.Lorsque l'application cliente consommatrice est libre, el le reçoit ainsi l'ensemble des messages empilés.

Tant qu'un message n'est pas consommé, ou qu'il n'a pas expir é, il reste stocké au sein du fournisseur. Dès que le client de vient actif, il peut alors consulter lemessage qui lui était destiné, et ceci sans aucun problème. C eci peut se faire à tout moment. C'est vraiment le même princi pe que la messagerie.

Le mode publication/abonnement

Dans ce modèle, un producteur peut envoyer un message à plusi eurs consommateurs par le biais d'un sujet (topic ). Chaque consommateur doit cependant êtrepréalablement inscrit à ce sujet sinon il ne reçoit rien. Dans ce mode, l'émetteur du message n e connait pas les destinataires qui se sont abonnés.

Contrairement au mode point à point , dans un mode publication/abonnement un message envoyé va être donc reçu par plusieurs clients. Le message ne disparait duTopic que lorsque tous les abonnés l'ont lu et acquitté.

Il existe deux types de souscription : temporaire et durable . Dans le premier cas, les consommateurs reçoivent les messa ges tant qu'ils sont connectés au sujet.Dans le cas d'une souscription durable , on oblige le fournisseur à enregistrer les messages lors d' une déconnexion, et à les envoyer lors de la nouvelle connexi on duconsommateur.

Mise en place de tous les composants JMS afin d'établir la communication par messages asynchrones

Il existe un certain nombre de composants qui s'occupe de la gestion globale de JMS, et de permettre ainsi une communication asynchrone entre applicationsclientes.

ConnectionFactory et Destination

Pour travailler avec JMS, la première étape consiste d'abord à se connecter au fourni sseur JMS. Pour cela, nous devons récupérer un objet ConnectionFactory via JNDI quirend ainsi la connexion possible avec le fournisseur. Cet ob jet peut être assimilé à une DataSource (en JDBC). En effet, de la même façon qu'un DataSource fournit uneconnexion JDBC, une ConnectionFactory fournit une connexion JMS au service de routage de message. L'autre élément à récupére r est la destination. Les destinations(Destination ) sont des objets qui véhiculent les messages. JMS comporte deux types de destination, comme nous venons de le d écouvrir, les Queue et les Topic .

Voici l'écriture à proposer côté application cliente (plate-forme indépendante Java SE) :

Context ctx = new InitialContext();ConnectionFactory fabrique = (ConnectionFactory)ctx.lookup("ConnectionFactory");Destination destination = (Destination)ctx.lookup("queue/MaFile");

Voici une autre écriture où nous passons par un bean session qui nous permer d'utiliser l'injection. Il suffit alors de spécifier l'annotation @Resource :

@Resource(mappedName="ConnectionFactory")private ConnectionFactory fabrique;@Resource(mappedName="queue/MaFile")private Destination destination;

Pour obtenir une ConnectionFactory , une Queue , ou un Topic , il faut les rechercher par leur nom dans l'annuaire JNDI ou utiliser l'injection. Cela suppose donc queces ressources soient préalablement mis en oeuvre et qu'ell es soient recensées au travers du service d'annuaire JNDI.

Création du contexte JNDI

Les propriétés pour la création du contexte JNDI sont dépendantes du fournisseur utilisé, et à ce titre, nous devons utiliser la même démarche que pour la communicationpar les beans session. Il est donc nécessaire d'initialiser ce contexte avec tous les bons paramètres requis. Le plus fac ile, à mon avis (puisque portable), est de placer cesdifférents paramètres, dans un fichier de configuration do nt le nom est bien précis ( jndi.properties ) et qui doit être placé dans le répertoire racine du projet.

Voici les paramètres correspondant au serveur d'application Glassfish :

jndi.properties (Glassfish)

# Accès au serveur d'application Glassfishjava.naming.factory.initial =com.sun.enterprise.naming.SerialInitContextFactoryjava.naming.factory.url.pkgs =com.sun.enterprise.namingjava.naming.factory.state =com.sun.corba.ee.impl.presentation.rmi.JNDIStateFac toryImplorg.omg.CORBA.ORBInitialHost =portableorg.omg.CORBA.ORBInitialPort =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 JBossjava.naming.factory.initial =org.jnp.interfaces.NamingContextFactoryjava.naming.factory.url.pkgs =org.jboss.naming:org.jnp.interfacesjava.naming.provider.url =portable:1099

Archives à déployer sur chaque poste cliente

Pour que la communication puisse s'établir correctement av ec le serveur d'application, notamment dans le service JMS, vous devez impérativement récupérer les archivessuivantes pour toutes les applications qui seront à déploye r sur chaque poste client. Voici un exemple d'archives pour l e serveur d'application Glassfish :

Archives supplémentaires à déployer avec l'application cliente pour le serveur d'application Glassfish

# Archives à installerappserv-rt.jarjavaee.jarappserv-deployment-client.jarappserv-ext.jar# Archives supplémentaires pour JMSimqjmsra.jarappserv-admin.jarappserv-ws.jar

Connection et Session

L'objet ConnectionFactory permet de créer une connexion avec le fournisseur JMS. Une fois la connexion créée, elle est ensuite utilisée pour créer une session. La sessionsert à regrouper toutes les opérations d'envoi et de récepti on des messages. Dans la majorité des cas, une session unique est suffisante. La création de plusieurs sessionsest utile seulement dans le cas d'applications multi-tâche s qui produisent et reçoivent des messages en même temps. Eff ectivement, l'objet Session est mono-tâche, c'est-à-dire que ses méthodes n'autorisent pas l'accès concurren t. Généralement, le thread qui crée l'objet Session utilise le producteur et le consommateur de cette session.

Connection connexion = fabrique.createConnection();Session session = connexion.createSession(true, 0); // createSession(transaction, accuséRéception);

La méthode createSession() prend deux paramètres. Une session, je le rappelle, est un co ntexte transactionnel utilisé pour grouper un ensemble d'e nvois ou deréceptions de messages dans une même unité de travail. Comme avec les bases de données, une session transactionnelle n'e st validée qu'après un appel impliciteou explicite d'un ordre commit . Si donc, vous désirez pouvoir travailler avec plusieurs me ssages pour une même session, vous devez autoriser le modetransactionnel dans le premier argument de la fonction. Le d euxième est utile pour savoir si vous désirez qu'un accusé ré ception soit renvoyé afin de préciser quemessage est bien arrivé à sa destination. Dans l'affirmativ e, vous devez utilisez la constante Session.AUTO_ACKNOWLEDGE .

Toutefois, la spécification indique que la valeur de ces arg uments est ignorée au sein d'un conteneur EJB. En effet, celu i-ci gère les transactions et les accusésréceptions en fonction des paramètres de déploiement.

Les bonnes pratiques de développement incitent à fermer les connexions une fois le travail terminé.

Connection connexion = fabrique .createConnection ();...connexion .close();

MessageProduceur et MessageConsumer

La dernière étape nous sert à préciser le sens du transfert du message, est-ce pour envoyer ou est-ce pour recevoir ? Deux o bjets correspondent à ces deux situations,respectivement MessageProduceur et MessageConsumer :

MessageProduceur envoi = session.createProduceur(destination);MessageConsumer réception = session.createConsumer(destination);...envoi.send(message);

Chacune des méthodes de l'objet session prend en paramètre la destination sur laquelle l'objet est connecté. Une fois que la nature de l'échange est créée, vousn'avez plus qu'à utiliser la méthode correspondante, notamment la méthode send() pour envoyer le message (dans le cas de la réception, il faut mettre en placeune gestion d'événement que nous allons bientôt découvrir).

Ajustement possible des composants JMS suivant le mode de communication choisi

ConnectionFactory, Destination, MessageProduceur et MessageConsumer sont en réalité des interfaces génériques, que nous pouvons utiliser directement sans aucunproblème. Il est toutefois possible, dès le départ, de propo ser des interfaces plus spécifiques, qui héritent d'ailleu rs de ces interfaces, correspondant respectivement aumode point à point ou au mode publication/abonnement . Voici, en conséquence, les différentes interfaces que vou s pouvez choisir :

Générique point à point publication/abonnement

ConnectionFactory QueueConnectionFactory TopicConnectionFactory

Connection QueueConnection TopicConnection

Destination Queue Topic

Session QueueSession TopicSession

MessageProduceur QueueSender TopicPublisher

MessageConsumer QueueReceiver TopicSuscriber

A titre d'exemple, voici l'enchaînement des classes que nous pouvons prendre dans le cas spécifique d'un mode point à point :

Context ctx = new InitialContext();QueueConnectionFactory fabrique = (QueueConnectionFactory)ctx.lookup("QueueConnectionFactory");Queue destination = (Queue)ctx.lookup("queue/MaFile");QueueConnection connexion = fabrique.createConnection();QueueSession session = connexion.createSession(true, 0);QueueSender envoi = session.createProduceur(destination);ouQueueReceiver réception = session.createConsumer(destination);

Les messages

Venons en maintenant au point crucial, c'est-à-dire les mes sages. Bien évidemment, pour dialoguer, les clients JMS s'échangent des messages, c'est-à-dire qu'un clientexpédie un message vers une file d'attente, et qu'un client d estinataire exécutera un traitement à la réception de ce mes sage. Dans JMS, un message est un objet Java quidoit implémenter l'interface javax.jms.Message . Il est composé de trois parties :

1. L'en-tête (header ) : qui se compose des informations de destination, d'expira tion, de priorité, date d'envoi, etc.

2. Les propriétés (properties ) : qui représentent les caractéristiques fonctionnelles d u message.

3. Le corps du message (body ) : qui contient les données à transporter.

L'en-tête du message

L'en-tête du message contient un certain nombre de champs pr édéfinis permettant de l'identifier. Nous pouvons voir cet te section comme les métadonnées du message :qui a créé le message, date de création, durée de vie, accusé r éception demandé ou non, etc. Chacune de ces métadonnées pos sède des accesseurs getXxx() et setXxx()(définis dans l'interface javax.jms.Message ) qui permettent d'en modifier le contenu, mais la plupart so nt affectées automatiquement par le fournisseur.

Nom Description

JMSMessageID identifiant unique de message

JMSCorremationID Utilisé pour associer de façon applicative deux messages pa r leur identifiant.

JMSDeliveryModeIl existe deux modes d'envoi : persistant ( le message est dél ivré une et une seule fois au destinataire, c'est-à-dire que même au cas de panne dufournisseur, le message sera délivré) et non persistant (le message peut ne pas être délivré en cas de panne puisqu'il n'e st pas rendu persistant).

JMSDestination File d'attente destinataire du message.

JMSExpiration Date d'expiration du message.

JMSPriorityPriorité du message. Cet attribut indique la priorité de faç on croissante à partir de 0 (les messages de niveau 9 ont plus d e priorité que les messagesde niveau 0).

JMSRedelivered Booléen qui signifie que le message a été redélivré au destin ataire.

JMSReplyTo File d'attente de réponse du message.

JMSTimestamp L'heure d'envoi du message est affecté automatiquement par le fournisseur.

Les propriétés

Cette section du message est optionnelle et agit comme une ex tension des champs d'en-tête. Les propriétés d'un message JMS sont des couples (nom, valeur), où la valeurest un type de base du langage Java (entiers, chaînes de carac tères, booléens, etc.). L'interface javax.jms.Message définit des accesseurs pour manipuler ces valeurs. Cesdonnées sont généralement positionnées par le client avant l'envoi d'un message et, comme nous le verrons par la suite, p euvent être utilisées pour filtrer les messages.

Le corps du message

Le corps du message, bien qu'optionnel, est la zone qui conti ent les données. Ces données sont formatées selon le type du m essage qui est défini par les interfacessuivantes (qui héritent toutes de javax.jms.Message ) :

Interface Description

javax.jms.BytesMessage Pour les messages sous forme de flux d'octets.

javax.jms.TextMessage Echange de données de type texte.

javax.jms.ObjectMessage Messages composés d'objets Java sérialisés.

javax.jms.MapMessage Echange de données sous la forme clé/valeur . La clé doit être une String et la valeur de type primitif.

javax.jms.StreamMessage Echange de données en provenance d'un flux.

javax.jms.BytesMessage

Le premier type de message, BytesMessage , sur lequel nous pouvons travailler, permet d'échanger les tableaux d'octets ainsi que les types primitifs. Voici les m éthodesque nous pouvons prendre :

1. Les méthodes de lecture : readBoolean(), readByte(), readBytes(byte[]), readChar (), readDouble(), readFloat(), readInt(), readLong(), re adShort() et readUTF().

2. Les méthodes d'écriture : writeBoolean(), writeByte(), writeBytes(byte[]), write Char(), writeDouble(), writeFloat(), writeInt(), writeL ong(), writeObject(), writeShort() etwriteUTF().

Voici un exemple qui permet d'envoyer plusieurs messages qui comportent des valeurs primitives :

Session session = connexion.createSession(true, 0);MessageProduceur envoi = session.createProduceur(destination);BytesMessage message = session.createBytesMessage();message.writeInt(15);message.writeDouble(-6.78);message.writeBoolean(true);envoi.send(message);

javax.jms.TextMessage

Ce type de message, TextMessage , est certainement le plus simple puisqu'il ne comporte que d eux méthodes essentielles : getText() et setText() :

Session session = connexion.createSession(true, 0);MessageProduceur envoi = session.createProduceur(destination);TextMessage message = session.createTextMessage();message.setText("Bienvenue");

message.setText(" à tout le monde");envoi.send(message);

javax.jms.ObjectMessage

Avec Java, nous pouvons envoyer un objet sérialisable à l'ai de de ObjectMessage . Là aussi, c'est très simple puique cette interface dispose également de deux méthodesgetObject() et setObject() :

class Personne implements Serializable { ... }...Personne moi = new Personne();Personne toi = new Personne();...Session session = connexion.createSession(true, 0);MessageProduceur envoi = session.createProduceur(destination);ObjectMessage message = session.createObjectMessage();message.setText(moi);message.setText(toi);envoi.send(message);

javax.jms.MapMessage

Ce type de message, MapMessage , permet d'envoyer et de recevoir des informations suivant l e système clé/valeur . Ainsi, nous retrouvons les mêmes méthodes que pour letype BytesMessage , mais à chaque fois, nous devons préciser la clé sous forme de chaîne de caractères. Par ailleurs, les méthodes sont plutô t des accesseurs getXxx() etsetXxx() :

Session session = connexion.createSession(true, 0);MessageProduceur envoi = session.createProduceur(destination);MapMessage message = session.createMapMessage();message.setInt("nombre", 15);message.setDouble("débit", -6.78);message.setBoolean("acquitement", true);envoi.send(message);

javax.jms.StreamMessage

Ce type de message, StreamMessage , est vu cette fois-ci comme un flux. Il ressemble beaucoup d' ailleurs au type DataInputStream ou DataOutputStream . A ce titre nousretrouvons exactement les mêmes méthodes que BytesMessages , mais avec en plus quelques méthodes supplémentaires, savo ir : readObject(), readString(), writeObject()et writeString().

Session session = connexion.createSession(true, 0);MessageProduceur envoi = session.createProduceur(destination);StreamMessage message = session.createStreamMessage();message.writeInt(15);message.writeDouble(-6.78);message.writeBoolean(true);envoi.send(message);

Comment envoyer un message

Pour envoyer un message, nous avons déjà tout recensé. Nous c onnaissons tout. Nous avons juste à revoir l'ensemble des él éments à mettre en oeuvre.

1. Tout d'abord, la fabrique de connexion ( ConnectionFactory ) et la destination ( Destination ) doivent être connues par le client JMS.

2. Une fois la référence de la ConnectionFactory obtenue, on se connecte au provider (fournisseur) JMS via l' objet Connection .

3. A partir de cette connexion, nous devons obtenir une sessi on (Session ).

4. A partir de cette session, nous devons créer un MessageProducer qui va permettre d'envoyer des messages auprès d'une destin ation.

5. La session permet également de créer le message suivant le type choisi.

Context ctx = new InitialContext();ConnectionFactory fabrique = (ConnectionFactory)ctx.lookup("ConnectionFactory");Destination destination = (Destination)ctx.lookup("queue/MaFile");Connection connexion = fabrique.createConnection();Session session = connexion.createSession(false, Session.AUTO_ACKNOWLEDGE);MessageProduceur envoi = session.createProduceur(destination);TextMessage message = session.createTextMessage();message.setText("Bienvenue");message.setText(" à tout le monde");envoi.send(message);connexion.close();

Comment recevoir un message

Le consommateur du message est le client capable d'être à l'é coute d'une file d'attente (ou d'un sujet), et de traiter les messages à leur réception. En effet, le client doit êtreconstamment à l'écoute ( listener ) et, à l'arrivée d'un nouveau message, il doit pouvoir le tra iter. Pour cela, l'application doit appeler la méthode onMessage() de l'interfacejavax.jms.MessageListener . Celle-ci est très spécialisée et permet ainsi la réception asynchrone des messages. charge au développeur d'implémen ter cette interface pourréaliser le traitement adéquat lors de la réception d'un mes sage. Voici la procédure à suivre :

1. Tout d'abord, comme l'envoi d'un message, la fabrique de c onnexion ( ConnectionFactory ) et la destination ( Destination ) doivent être connues par le client JMS.

2. Une fois la référence de la ConnectionFactory obtenue, le consommateur doit se connecter au provider (fou rnisseur) JMS via l'objet Connection .

3. A partir de cette connexion, nous devons obtenir une sessi on (Session ).

4. A partir de la session, on crée un MessageConsumer qui va permettre de consommer les messages. Pour ce faire, no us associons un listener MessageListenerpour traiter les messages de façon asynchrone. Ainsi, à chaq ue réception d'un nouveau message, la méthode onMessage() est automatiquement invoquée et peuteffectuer le traitement désiré.

5. Attention : à ce stade, il ne faut surtout pas oublier de démarrer la conn exion avec la méthode start() sinon aucun message ne sera reçu.

public class Réception implements MessageListener { private Panneau panneau = new Panneau(); public Réception() throws Exception { Context ctx = new InitialContext(); ConnectionFactory fournisseur = (ConnectionFactory) ctx.lookup("ConnectionFactory"); Destination destination = (Destination)ctx.lookup("queue/maFile"); Connection connexion = fournisseur.createConnection(); Session session = connexion.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer réception = session.createConsumer(destination); réception.setMessageListener(this); connexion.start(); }

public static void main(String[] args) throws Exception { new Réception(); } public void onMessage(Message arg) { try { TextMessage message = (TextMessage) arg; System.out.println(message.getText()); System.out.println(message.getText()); } catch (Exception ex) { } }}

Deux applications clientes en communication avec JMS

Nous allons mettre en oeuvre nos nouvelles connaissances su r une communication asynchrone entre deux applications cli entes au travers du service JMS. Pour illustrerces différents mécanismes d'échange, nous allons créer deu x applications clientes fenêtrées.

1. La première doit récupérer des photos présentes sur le pos te local et les afficher ensuite dans la zone principale de la fenêtre. Ainsi, vous avez la possibilité dechoisir la photo qui vous plait afin de l'envoyer au service d e messagerie asynchrone JMS.

2. La deuxième, sur un autre poste client, en attente d'évent uels messages venant du même fournisseur de messagerie, aff iche la photo envoyée par la premièreapplication.

Application cliente qui envoi les photos ................ ............................. Application cliente qui r eçoit les photos

Réglage du serveur d'application Glassfish pour exploiter le service JMS

Nous avons deux réglages spécifiques pour mettre en oeuvre l e service JMS, d'une part ce qui correspond à la connexion du fournisseur ( ConnectionFactory ), d'autre partle type de destination ( Destination ) : le mode point à point ou le mode publication/abonnement . Comme pour la base de données, vous devez effectuer ces diff érentsréglages dans la rubrique Resources :

Ressources supplémentaires à proposer avec chaque application cliente

Pour que chaque client puisse se connecter au bon serveur d'a pplication et pour que lecomportement soit bien compris par ce dernier, nous devons d 'une part régler le fichierjndi.properties et ensuite déployer l'ensemble des archives correspondant au serveurutilisé.

Nous voyons ici les archives à prendre en compte pour le serveur d'applicationGlassfish.

Architecture globale des postes clientes avec le serveur d'applications

Application clientes

Après ces différents réglages, nous pouvons maintenant pas ser dans le vif du sujet, c'est-à-dire le codage des deux appl ications clientes, d'une part celle qui envoie lesmessages photos.EnvoyerPhotos et ensuite celle qui reçoit les messages photos.Visionneuse .

photos.EnvoyerPhotos.java

1 package photos ; 2 3 import javax . swing .*; 4 import java . awt .*; 5 import java . awt . event .*; 6 import java . awt . image . BufferedImage ; 7 import java . io .*; 8 import javax . imageio .*; 9 import javax . naming .*; 10 import javax . jms .*; 11 12 public class EnvoyerPhotos extends JFrame implements ActionListener { 13 private String répertoire = "J:/Stockage/" ; 14 private String [] liste ; 15 private Panneau panneau = new Panneau (); 16 private JComboBox choix ; 17 private JButton envoyer = new JButton ( "Envoyer la photo" ); 18 19 private static ConnectionFactory fournisseur ; 20 private static Destination destination ; 21 22 public EnvoyerPhotos () { 23 liste = new File ( répertoire ). list (); 24 choix = new JComboBox( liste ); 25 panneau . change ( récupérer ()); 26 choix . addActionListener ( this ); 27 envoyer . addActionListener ( this ); 28 setSize ( 500, 400); 29 setTitle ( "Stockage de photos" ); 30 add( choix , BorderLayout . NORTH); 31 add( envoyer , BorderLayout . SOUTH); 32 add( panneau ); 33 setDefaultCloseOperation ( EXIT_ON_CLOSE); 34 setVisible ( true ); 35 } 36 37 private BufferedImage récupérer () { 38 try { 39 BufferedImage photo = ImageIO . read ( new File ( répertoire +choix . getSelectedItem ())); 40 return photo ; 41 } 42 catch ( Exception ex ) { 43 setTitle ( "Problème de localisation des photos" ); 44 return null ; 45 }

46 } 47 48 public static void main ( String [] args ) throws Exception { 49 Context ctx = new InitialContext (); 50 // ctx.addToEnvironment("SECURITY_PRINCIPAL", "guest"); 51 // ctx.addToEnvironment("SECURITY_CREDENTIALS ", "guest"); 52 fournisseur = ( ConnectionFactory ) ctx. lookup ( "JmsFournisseurPhotos" ); 53 destination = ( Destination )ctx. lookup ( "JmsPointVisionneuse" ); 54 new EnvoyerPhotos (); 55 } 56 57 public void actionPerformed ( ActionEvent e) { 58 if ( e. getSource ()== choix ) { 59 panneau . change ( récupérer ()); 60 } 61 else if ( e. getSource ()== envoyer ) { 62 try { 63 File fichier = new File ( répertoire +choix . getSelectedItem ()); 64 byte [] octets = new byte [( int ) fichier . length ()]; 65 FileInputStream photo = new FileInputStream ( fichier ); 66 photo . read ( octets ); 67 envoyer ( octets ); 68 } 69 catch ( IOException ex ) { 70 setTitle ( "Problème avec le fichier" ); 71 } 72 } 73 } 74 75 private void envoyer ( byte [] octets ) { 76 try { 77 Connection connexion = fournisseur . createConnection (); 78 Session session = connexion . createSession ( false , Session . AUTO_ACKNOWLEDGE); 79 MessageProducer envoi = session. createProducer ( destination ); 80 StreamMessage message = session. createStreamMessage (); 81 message. writeString (( String ) choix . getSelectedItem ()); 82 message. writeInt ( octets . length ); 83 message. writeBytes ( octets ); 84 envoi. send ( message); 85 connexion . close (); 86 } 87 catch ( JMSException ex ) { 88 setTitle ( "Problème avec le serveur" ); 89 } 90 } 91 } 92 93 class Panneau extends JComponent { 94 private BufferedImage image ; 95 private double ratio ; 96 97 public void change ( BufferedImage image ) { 98 if ( image != null ) { 99 this . image = image ;100 ratio = ( double ) image . getWidth ()/ image . getHeight (); 101 repaint ();102 }103 }104 105 protected void paintComponent ( Graphics surface ) {106 if ( image != null )107 surface . drawImage ( image , 0, 0, this . getWidth (), ( int )( this . getWidth ()/ ratio ), null ); 108 } 109 }

Rappelez-vous que nous avons un certain nombre d'objets à mettre en oeuvre et ceci dans un ordre bien précis. Nous devons :

1. Connaître le fournisseur JMS - ligne 52 .

2. Choix de la destination - ligne 53 .

3. Etablir réellement la connexion avec le fournisseur - ligne 77 .

4. Prévoir une session sans transaction avec accusé récepti on - ligne 78 .

5. Création d'un objet pour l'envoi de messages dans la desti nation choisie - ligne 79 .

6. Création d'un message adapté pour envoyer la photo désiré e avec son nom de fichier et sa taille - lignes 80 à 83 .

7. Envoi du message structuré - ligne 84 .

8. Clôture de la connexion avec le fournisseur JMS - ligne 85 .

Sur les lignes 50 et 51 , vous remarquez la présence de commentairesqui expliquent comment agir sur un fournisseur qui propose u neauthentification. Par défaut, l'identification se fait au tomatiquementavec les valeurs guest . Voici d'ailleurs les propriétés qui sont placéesautomatiquement lorsque vous mettez en oeuvre laConnectionFactory au niveau de votre serveur d'applications.

photos.Visionneuse.java

1 package photos ; 2 3 import java . util . logging .*; 4 import javax . swing .*; 5 import java . awt .*; 6 import java . awt . event .*; 7 import java . awt . image .*;

8 import java . io .*; 9 import javax . imageio .*;10 import javax . naming .*;11 import javax . jms .*;12 13 public class Visionneuse extends JFrame implements MessageListener {14 private Panneau panneau = new Panneau ();15 16 public Visionneuse () throws Exception {17 Context ctx = new InitialContext ();18 ConnectionFactory fournisseur = ( ConnectionFactory ) ctx. lookup ( "JmsFournisseurPhotos" );19 Destination destination = ( Destination ) ctx . lookup( "JmsPointVisionneuse" );20 Connection connexion = fournisseur . createConnection();21 Session session = connexion . createSession ( false , Session . AUTO_ACKNOWLEDGE);22 MessageConsumer réception = session . createConsumer( destination );23 réception. setMessageListener ( this );24 connexion . start ();25 setSize ( 500, 400);26 setTitle ( "Visionneuse" );27 add( panneau );28 setDefaultCloseOperation ( EXIT_ON_CLOSE);29 setVisible ( true );30 }31 32 public static void main ( String [] args ) throws Exception {33 new Visionneuse ();34 }35 36 public void onMessage ( Message arg ) {37 try {38 StreamMessage message = ( StreamMessage ) arg ;39 setTitle ( message. readString ());40 byte [] octets = new byte [ message. readInt ()];41 message. readBytes ( octets );42 ByteArrayInputStream fluxImage = new ByteArrayInputStream ( octets );43 BufferedImage photo = ImageIO . read ( fluxImage );44 panneau . change ( photo ); 45 } 46 catch ( Exception ex ) {47 Logger . getLogger ( "global" ). log ( Level . SEVERE, null , ex );48 }49 }50 }

Sur l'application cliente qui reçoit les messages, nous retrouvons un certain nombre d'objets qui sont identiques à l'application précédente. La grosse différencevient de la mise en place d'une gestion d'événements avec un écouteur de message adapté. Voici l'ensemble de la procédure à suivre :

1. La classe doit implémenter l'interface MessageListener - ligne 13 .

2. Il faut également connaître le fournisseur JMS - ligne 18 .

3. Choisir la destination - ligne 19 .

4. Etablir réellement la connexion avec le fournisseur - ligne 20 .

5. Prévoir une session sans transaction avec accusé récepti on - ligne 21 .

6. Créer un objet pour la réception de messages par rapport à l a destination choisie - ligne 22 .

7. Mettre en place un système d'écoute pour être en permanenc e en attente d'un nouveau message - ligne 23 .

8. Et surtout activer et démarrer la connexion avec le fourni sseur JMS pour effectivement recevoir les messages - ligne 24 .

...............................................................................................

9. Traitement des messages lorsqu'ils sont reçus - lignes 36 à 49 .

10. Adaptation du message reçu suivant le type choisi dans le protocole d'échange - ligne 38 .

11. Récupération des différents éléments constituant le me ssage - lignes 39 à 41 .

Il est possible de sélectionner les messages à récupérer à pa rtir de certains critères. Cette partie sera expliquée à la f in du chapitre suivant.Si vous souhaitez vous y rendre directement .

Message Driven Bean - MDB

Pour l'instant, nous venons d'utiliser les compétences de JMS, qui est un service délivré par Java EE, sans passer par un bean quelconque. Cette messagerie est tr èsperfomante et très intéressante puisqu'elle permet une com munication entre applications clientes, même si l'une d'el le n'est pas encore en service. Le message délivré n'estpas perdu pour autant. Dès que l'application concernée rent re en activité, elle reçoit le message qui lui était destiné.

Pour mettre en place ce système, nous avons besoin, comme nous l'avons découvert, de faire des recherches JNDI, d'une part pour se connecter au fournisseurJMS, et d'autre part pour choisir la destination souhaitée. Cela peut prendre un certain temps. Par ailleurs, chaque application cliente doit posséder des archivessupplémentaires uniquement pour la partie JMS, qui sont au nombre de trois.

Nous avons toutefois la possibilité, pour le client qui envo ie un message, de passer par un bean session. Effectivement, nous avons vu qu'il est possible d'utiliserl'injection au travers de l'annotation @Resource pour mettre en place la connexion au fournisseur et désigner la destination. Dans ce cas de figure, côté applicationcliente, nous pourrons nous passer des archives supplément aires, et nous aurons un seul appel au service JNDI pour retrouver le bean session. Par contre,

l'inconvénient de cette structure, c'est que nous devons dé velopper l'interface correspondante au bean session afin q u'elle soit déployée avec le client et qu'ellepermette ainsi la communication entre le client et le serveu r Java EE.

Toutefois, il n'est pas possible de le faire pour l'applicat ion qui reçoit le message. En effet, nous sommes obligé de con cerver la gestion d'événements propre à laréception de message, et du coup nous avons besoin de tous les éléments nécessaires à la construction de cette réception, comme la ConnectionFactory et laDestination .

Dans certaines situations, il arrive que les messages ne soi ent pas destinés à un client en particulier, mais que ce soit p lutôt le serveur d'applications qui doit s'enoccuper. L'exemple le plus parlant et d'envoyer un mail pour un certain nombre d'événements qui se produisent : réceptio n de nouvelles photos, suppression dequelques unes, etc. Nous pourrions développer une applicat ion cliente tierce pour résoudre ce problème, mais cela récl ame beaucoup d'énergie et de ressource.Heureusement, il existe un composant, un EJB qui est spécial isé pour la réception de messages côté serveur, et qui plus es t, est très facile à développer et à mettreen oeuvre. Il s'agit du troisième type de bean qui se nomme Message Driven Bean ou MDB.

Définition du Message Driven Bean ou MDB

Un Message Driven Bean ou MDB est un EJB qui se comporte comme un listener JMS , c'est-à-dire qui reçoit des messages et les traite de maniè re asynchrone. Les MDBse rapprochent des EJB stateless car ils sont, eux aussi, sans état. Ils s'exécutent à l'intér ieur du conteneur EJB qui assure donc le multithreading, la s écurité ou la gestiondes transactions.

Les MDB sont à l'écoute (listener) d'une file d'attente et se réveillent à chaque arrivée de messages. En fait, il faut garder à l'esprit que c'est le conteneur qui est levéritable listener JMS et qu'il délègue au MDB le traitement du message, et plus particulièrement à la méthode onMessage() que nous avons déjà utilisée. Commeles autres EJB, le MDB peut accéder à tout type de ressources : EJB, JDBC, JavaMail, etc.

Attention : un MDB ne possède pas d'interface distante ou locale puisqu'il n'e st pas utilisé par un client. Il est constitué d'une seule cla sse Java qui doit être annotéepar javax.ejb.MessageDriven . Pour réagir à l'arrivée d'un message, il doit implémenter l a méthode onMessage( javax.jms.Message ) définie dans l'interfacejavax.jms.MessageListener . Il est associé à une destination JMS , c'est-à-dire à une Queue pour les communications point à point ou à un Topic pour lescommunications publication/souscription . Avec un MDB, vous n'avez plus à vous préoccuper du fournisseur, donc pas de ConnectionFactory . La méthodeonMessage() est activée à la réception d'un message envoyé par un client JMS .

Mise en oeuvre au travers du projet de stockage à distance de photos numériques

Nous allons mettre en place, à la fois le bean session statele ss qui s'occupe d'envoyer les messages, et le bean MDB qui va traiter ces messages. Pour cela, nous allonsreprendre le projet qui permet de stocker, sur un seul serveu r, des photos qui se trouvent sur différents postes clients. Les différents traitements ont été largementdéveloppés dans les études précédentes, je passerais donc p lus de temps sur la partie gestion et traitement des messages .

Nous avons :

Côté client Côté serveur

Une application fenêtrée qui permet à la fois de visualiser les photos dudisque dur du poste client et les photos qui sont archivées sur le serveur. Vouspouvez stocker de nouvelles photos ou supprimer celles qui sont déjàprésentes :

Une fenêtre apparaît automatiquement dès qu'un client se connecte au serveurd'applications. Lorsqu'une nouvelle photo est archivée, elle est automatiquementaffichée dans la zone principale de la fenêtre. Dans la barre de titre, vous voyezapparaître l'ensemble des événements qui se produisent en relation avecl'activité du client :

L'affichage de ces événements sont également répercutés tout simplement dansla console de visualisation du serveur :

Architecture du projet

Fichier de propriétés jndi.properties et archives à déployer sur chaque poste client

Cette fois-ci, la communication entre le client et le serveu r se fait au travers du bean session stateless ArchivagePhotosRemote . Nous n'avons donc plus besoin desarchives supplémentaires relatives à JMS. En effet, c'est l e bean session qui s'occupe d'envoyer les messages.

Voici les paramètres correspondant au serveur d'applications Glassfish :

jndi.properties (Glassfish)

# Accès au serveur d'application Glassfishjava.naming.factory.initial =com.sun.enterprise.naming.SerialInitContextFactoryjava.naming.factory.url.pkgs =com.sun.enterprise.namingjava.naming.factory.state =com.sun.corba.ee.impl.presentation.rmi.JNDIStateFac toryImplorg.omg.CORBA.ORBInitialHost =portableorg.omg.CORBA.ORBInitialPort =3700

Archives à déployer avec l'application cliente pour le serveur d'application Glassfish

# Archives à installerappserv-rt.jarjavaee.jarappserv-deployment-client.jarappserv-ext.jar

Codage des sources côté client

Pour dialoguer et archiver les photos sur le serveur d'appli cations, nous passons par l'interface ArchivagePhotosRemote . Celle-ci est en relation directe et à distance avecle bean session stateless ArchivagePhotosBean . Elle propose les méthodes suivantes :

1. stocker() : permet de stocker la photo choisie par le client en précisan t le nom du fichier et la suite des octets constituant la photo .

2. liste() : délivre la liste des fichiers photos déjà archivées.

3. supprimer() : détruit la photo archivée dans le serveur dont le nom du fich ier est spécifié en argument.

4. getPhoto() : restitue la photo archivée dans le serveur dont le nom du fic hier est spécifié en argument.

Pour le code de l'application cliente, nous n'avons rien de particulier, si ce n'est de faire appel au service du bean session représenté par l'interfaceArchivagePhotosRemote :

photos.ArchivagePhotosRemote.java

package photos ;

import java . io . IOException ;import javax . ejb . Remote;import javax . jms .*;

@Remote

public interface ArchivagePhotosRemote { void stocker ( String intitulé , byte [] octets ) throws IOException ; String [] liste (); void supprimer ( String nom); byte [] getPhoto ( String nom) throws IOException ;}

photos.EnvoyerPhotos.java

1 package photos ; 2 3 import javax . swing .*; 4 import java . awt .*; 5 import java . awt . event .*; 6 import java . awt . image . BufferedImage ; 7 import java . io .*; 8 import javax . imageio .*; 9 import javax . naming .*; 10 import javax . jms .*; 11 12 public class EnvoyerPhotos extends JFrame implements ActionListener { 13 private String répertoire = "J:/Stockage/" ; 14 private static ArchivagePhotosRemote archivage; 15 16 private String [] listeLocal ; 17 private String [] listeServeur ; 18 private Panneau panneauPhoto = new Panneau (); 19 private JComboBox choixLocal ; 20 private JComboBox choixServeur ; 21 private JButton envoyer = new JButton ( "Stocker" ); 22 private JButton supprimer = new JButton ( "Supprimer" ); 23 private JPanel panneauNord = new JPanel (); 24 private JPanel panneauSud = new JPanel (); 25 26 public EnvoyerPhotos () throws IOException { 27 listeLocal = new File ( répertoire ). list (); 28 listeServeur = archivage. liste (); 29 choixLocal = new JComboBox( listeLocal ); 30 choixServeur = new JComboBox( listeServeur ); 31 panneauPhoto . change ( ImageIO . read ( new File ( répertoire + choixLocal . getSelectedItem ()))); 32 choixLocal . addActionListener ( this ); 33 envoyer . addActionListener ( this ); 34 choixServeur . addActionListener ( this ); 35 supprimer . addActionListener ( this ); 36 setSize ( 500, 500); 37 setTitle ( "Stockage de photos" ); 38 panneauNord . add( new JLabel ( "Photos en local : " )); 39 panneauNord . add( choixLocal ); 40 panneauNord . add( envoyer ); 41 add( panneauNord , BorderLayout . NORTH); 42 panneauSud . add( new JLabel ( "Sur le serveur : " )); 43 panneauSud . add( choixServeur ); 44 panneauSud . add( supprimer ); 45 add( panneauSud , BorderLayout . SOUTH); 46 add( panneauPhoto ); 47 setDefaultCloseOperation ( EXIT_ON_CLOSE); 48 setVisible ( true ); 49 } 50 51 public static void main ( String [] args ) throws Exception { 52 Context ctx = new InitialContext (); 53 archivage = ( ArchivagePhotosRemote ) ctx . lookup ( ArchivagePhotosRemote . class . getName()); 54 new EnvoyerPhotos (); 55 } 56 57 public void actionPerformed ( ActionEvent e) { 58 if ( e. getSource ()== choixLocal ) { 59 try { 60 panneauPhoto . change ( ImageIO . read ( new File ( répertoire + choixLocal . getSelectedItem ()))); 61 } 62 catch ( IOException ex ) { 63 setTitle ( "Problème de localisation des photos" ); 64 } 65 } 66 else if ( e. getSource ()== envoyer ) { 67 try { 68 File fichier = new File ( répertoire +choixLocal . getSelectedItem ()); 69 byte [] octets = new byte [( int ) fichier . length ()]; 70 FileInputStream photo = new FileInputStream ( fichier ); 71 photo . read ( octets ); 72 archivage. stocker (( String ) choixLocal . getSelectedItem (), octets ); 73 choixServeur . addItem ( choixLocal . getSelectedItem ()); 74 } 75 catch ( IOException ex ) { 76 setTitle ( "Problème avec le fichier" ); 77 } 78 } 79 else if ( e. getSource ()== choixServeur ) { 80 try { 81 ByteArrayInputStream fluxImage = new ByteArrayInputStream (archivage. getPhoto (( String ) choixServeur . getSelectedItem ())); 82 panneauPhoto . change ( ImageIO . read ( fluxImage )); 83 } 84 catch ( IOException ex ) { 85 setTitle ( "Problème avec le serveur" ); 86 } 87 } 88 else if ( e. getSource ()== supprimer ) {

89 archivage. supprimer (( String ) choixServeur . getSelectedItem ()); 90 choixServeur . removeItem ( choixServeur . getSelectedItem ()); 91 } 92 } 93 } 94 95 class Panneau extends JComponent { 96 private BufferedImage image ; 97 private double ratio ; 98 99 public void change ( BufferedImage image ) {100 if ( image != null ) {101 this . image = image ;102 ratio = ( double ) image . getWidth ()/ image . getHeight (); 103 repaint ();104 }105 }106 107 protected void paintComponent ( Graphics surface ) {108 if ( image != null )109 surface . drawImage ( image , 0, 0, this . getWidth (), ( int )( this . getWidth ()/ ratio ), null ); 110 } 111 }

Codage des sources côté serveur

Nous nous intéressons tout d'abord au bean session stateles s ArchivagePhotosBean . Cette fois-ci, contrairement à une application cliente, n ous pouvons utiliser l'injectionpour désigner le fournisseur JMS et la destination au travers de l'annotation @Resource (ligne 13 à 16 ) en spécifiant la valeur du paramètre mappedName . De cette façon,c'est beaucoup plus facile à mettre en oeuvre !

photos.ArchivagePhotosBean.java

1 package photos ; 2 3 import java . io .*; 4 import java . util . logging .*; 5 import javax . annotation .*; 6 import javax . ejb .*; 7 import javax . interceptor .*; 8 import javax . jms .*; 9 10 @Stateless11 public class ArchivagePhotosBean implements ArchivagePhotosRemote {12 13 @ Resource ( mappedName="JmsFournisseurPhotos" )14 private ConnectionFactory fournisseur ;15 @ Resource ( mappedName="JmsPointVisionneuse" )16 private Queue destination ;17 18 private final String répertoire = "J:/Photos/" ;19 20 public void stocker ( String intitulé , byte [] octets ) throws IOException {21 File fichier = new File ( répertoire + intitulé );22 FileOutputStream stockage = new FileOutputStream ( fichier );23 stockage . write ( octets );24 stockage . close ();25 }26 27 public String [] liste () {28 return new File ( répertoire ). list ();29 }30 31 public void supprimer ( String nom) {32 File fichier = new File ( répertoire +nom);33 fichier . delete ();34 }35 36 public byte [] getPhoto ( String nom) throws IOException {37 File fichier = new File ( répertoire +nom);38 byte [] octets = new byte [( int ) fichier . length ()];39 FileInputStream photo = new FileInputStream ( fichier );40 photo . read ( octets );41 photo . close ();42 return octets ;43 }44 45 @ AroundInvoke46 private Object messagerie ( InvocationContext ctx ) throws Exception {47 String nomMéthode = ctx . getMethod (). getName();48 String nomFichier = "" ;49 if ( ctx . getParameters ()!= null ) nomFichier = ( String ) ctx . getParameters ()[ 0];50 try {51 Connection connexion = fournisseur . createConnection ();52 Session session = connexion. createSession ( false , Session . AUTO_ACKNOWLEDGE);53 MessageProducer envoi = session. createProducer ( destination );54 MapMessage message = session. createMapMessage ();55 message. setLong ( "date" , System . currentTimeMillis ());56 message. setString ( "commande" , nomMéthode );57 message. setString ( "nom" , nomFichier );58 envoi . send ( message);59 connexion. close ();60 } 61 catch ( JMSException ex ) {62 Logger . getLogger ( "global" ). log ( Level . SEVERE, null , ex );63 }64 return ctx . proceed ();65 }66 }

Les quatre méthodes que nous venons d'évoquer s'occupent de la logique métier et rend les services demandés par le client . Vous remarquez la présence de laméthode supplémentaire messagerie() qui est une méthode dénommée, je le rappelle, callback interceptor . Comme son nom l'indique, cette méthode particulière estactivée par le conteneur d'EJB lui-même, et dans le cas qui no us préoccupe, elle est lancée à chaque fois qu'une méthode es t activée par le client ( interception desappels de méthode). Pour avoir un tel comportement, il faut s igner la méthode désirée avec l'annotation @ArroundInvoke .

Le but de cette méthode messagerie() et d'envoyer un message au service JMS . Ce message est composé de la date et de l'heure actuelle, du n om de la méthodesollicité par le client et éventuellement du nom du fichier p hoto désiré. Nous proposons donc une trace des événements pr ovoqués par les différentes actionsdemandées par le client.

Les messages envoyés sont ensuite traités par le bean spécialisé pour la réception des messages, le bean MDB MessagerieArchivageBean.

Attention , je le rappelle : un MDB ne possède pas d'interface distante ou locale puisqu'il n'e st pas utilisé par un client. Il est constitué d'une seule cla sse Java qui doitêtre annotée par @MessageDriven . Pour réagir à l'arrivée d'un message, il doit implémenter l a méthode onMessage( Message ) définie dans l'interfaceMessageListener . Il est associé à une destination JMS , c'est-à-dire à une Queue pour les communications point à point ou à un Topic pour les communicationspublication/souscription . Avec un MDB, vous n'avez pas à vous préoccuper du fournisseur, donc pas d e ConnectionFactory . La méthode onMessage() est activée àla réception d'un message envoyé par un client JMS

photos.MessagerieArchivageBean.java

1 package photos ; 2 3 import java . awt . Graphics ; 4 import java . awt . image .*; 5 import java . io .*; 6 import java . text . MessageFormat ; 7 import java . util . Date ; 8 import java . util . logging .*; 9 import javax . annotation .*;10 import javax . ejb .*;11 import javax . imageio .*;12 import javax . jms .*;13 import javax . swing .*;14 15 @MessageDriven ( mappedName="JmsPointVisionneuse" )16 public class MessagerieArchivageBean extends JFrame implements MessageListener {17 private String répertoire = "J:/Photos/" ;18 private Panneau panneau = new Panneau ();19 20 @ PostConstruct21 private void démarrer () {22 System . out . println ( "Démarrage Bean Message" );23 }24 25 @ PreDestroy26 private void fin () {27 System . out . println ( "Bean Message terminé" );28 }29 30 public MessagerieArchivageBean () {31 setSize ( 500, 400);32 setTitle ( "Visionneuse" );33 add( panneau ); 34 setVisible ( true ); 35 }36 37 public void onMessage ( Message message) {38 try {39 MapMessage msg = ( MapMessage) message;40 String motif = "{0, date, full} - {0, time, medium} : {1} : {2}" ;41 Date date = new Date ( msg. getLong ( "date" ));42 String commande = msg. getString ( "commande" ); 43 String nom = msg. getString ( "nom" );44 String affiche = MessageFormat . format ( motif , date , commande, nom);45 System . out . println ( affiche );46 setTitle ( affiche );47 if ( commande. equals ( "stocker" )) afficherPhoto ( nom);48 } 49 catch ( JMSException ex ) {50 Logger . getLogger ( "global" ). log ( Level . SEVERE, null , ex );51 }52 } 53 54 private void afficherPhoto ( String fichier ) {55 try {56 panneau . change ( ImageIO . read ( new File ( répertoire + fichier )));57 } 58 catch ( IOException ex ) {59 Logger . getLogger ( "global" ). log ( Level . SEVERE, null , ex );60 }61 }62 63 class Panneau extends JComponent {64 private BufferedImage image ;65 private double ratio ;66 67 public void change ( BufferedImage image ) {68 if ( image != null ) {69 this . image = image ;70 ratio = ( double ) image . getWidth ()/ image . getHeight (); 71 repaint ();72 }73 }74 75 @ Override76 protected void paintComponent ( Graphics surface ) {77 if ( image != null )

78 surface . drawImage ( image , 0, 0, this . getWidth (), ( int )( this . getWidth ()/ ratio ), null ); 79 } 80 }81 }

Pour avoir un bean MDB :

1. Ligne 15 : Nous devons donc en premier lieu utiliser l'annotation @MessageDriven et spécifier le paramètre mappedName dont la valeur correspond à ladestination du service JMS .

2. Ligne 16 : Une fois que vous avez réalisé cette opération, vous pouvez choisir le nom de votre classe. Celle-ci peut même hériter d' une autre classe, comme c'estle cas ici. Ainsi, en héritant de JFrame , j'ai la possibilité d'afficher une fenêtre automatiqueme nt à la création du bean, ce qui me permettra de visualiser les photosqui viennent juste d'être archivées.

3. Lignes 16 et 37 : Il faut que le MDB implémente l'interface MessageListener et redéfinisse donc la méthode associée onMessage() qui sera donc automatiquementsollicité à chaque arrivée d'un nouveau message.

4. Lignes 37 à 52 : Cette méthode onMessage() réalise le traitement associé au message qui, dans le cas pré sent, met en forme l'affichage des informations dans labarre de titre de la fenêtre et affiche éventuellement, dans la zone principale, la photo qui vient juste d'être archivée , mais uniquement si le client sollicite laméthode stocker() .

5. Remarquez qu'avec ce MDB, vous n'avez plus à vous préoccup er des objets annexes comme Connection, Session, MessageConsumer . Tout se faitautomatiquement en tâche de fond.

6. Lignes 20 à 28 : Comme tous les autres beans, le MDB possède des méthodes callback interceptor qui sont appelées respectivement à la suite de la constructi onde l'objet et juste avant sa destruction. Ces méthodes possè dent alors les annotations suivantes : @PostConstruct et @PreDestroy .

En fonctionnement, vous remarquerez qu'éventuellement pl usieurs fenêtres apparaissent, ce qui démontre bien que le MDB s'exécutent à l'intérieur du conteneurEJB et assure donc le multithreading.

L'annotation @MessageDriven et son attribut activationConfig

Nous venons de voir l'annotation @MessageDriven . Cette annotation possède également un attribut activationConfig qui permet de configurer convenablement lespropriétés du MDB. Comme nous pouvons avoir à réger plusieurs propriétés, nou s devons donc utiliser un tableau de @ActivationConfigProperty précisant le nom de lapropriété propertyName et sa valeur propertyValue .

Nous pouvons, par exemple stipuler que la destination est vraiment du type point à point :

@MessageDriven(mappedName="JmsPointVisionneuse", activationConfig={ @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue")})public class MessagerieArchivageBean extends JFrame implements MessageListener {

Tolérance de la déconnexion à un Topic

Le modèle de messagerie de type "abonnement" oblige les applications clientes à être connectées au sujet (Topic ) pour recevoir les messages de celui-ci. Si un problèmesurvient, les clients déconnectés perdent les messages émi s durant leur déconnexion. Cependant, le mode Topic offre la possibilité d'utiliser un abonnement durable.L'intérêt est donc de pouvoir recevoir les messages émis dep uis la dernière déconnexion.

L'utilisation de ce genre d'abonnement doit être précisée au niveau des propriétés du MDB. La propriété à utiliser est subscriptionDurability. Les valeurs prises parcelle-ci sont : Durable ou NonDurable. Par défaut, une souscription est NonDurable :

@MessageDriven(mappedName="JmsSujetVisionneuse", activationConfig={ @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Topic"), @ActivationConfigProperty(propertyName="subscriptionDurability", propertyValue="Durable")})public class MessagerieArchivageBean extends JFrame implements MessageListener {

Lorsque la destination est de type Queue , le principe d'abonnement durable n'a aucun sens. Par natur e, pour ce genre de destination, le facteur "durable" n'a pasd'importance car les messages sont automatiquement stocké s et doivent être consommés par un client unique.

Sélecteur de message

Il est possible de préciser certains critères permettant de ne pas recevoir l'ensemble des messages d'une destination. Le sélecteur de message utilise les propriétés dumessage en tant que critère dans les expressions conditionn elles. Ces conditions utilisent des expressions booléenne s afin de déterminer les messages à recevoir.

Nous pouvons par exemple récupérer uniquement les messages qui correspondent à l'utilisation de la méthode stocker() par le client :

@MessageDriven(mappedName="JmsPointVisionneuse", activationConfig={ @ActivationConfigProperty(propertyName="messageSelector", propertyValue="commande='stocker'")})public class MessagerieArchivageBean extends JFrame implements MessageListener {

Cette fois-ci, le MDB s'active uniquement que lorsque le client demande à archiver une nouvelle photo

Ces sélecteurs se basent sur les propriétés des messages. Ce lles-ci se situent dans l'en-tête du message, donc dépendan t du contenu, et sont asignées par lecréateur du message. Tous les types de message intègrent les méthodes de lecture et d'écriture de propriétés. En effet, c es méthodes sont décrites dans la super-interface javax.jms.Message . Les types de propriétés se basent sur les primitives Java : boolean, int, short, char ...

Pour définir les valeurs des propriétés, il faut utiliser le s méthodes setXxxProperty(Xxx) où Xxx représente les types byte, float, String, Object ... Les méthodesgetXxxProperty() sont également proposées pour récupérer les valeurs.

Pour revenir à notre exemple, voici comment régler la propriété "commande" qui est utilisée par le MDB :

@AroundInvokeprivate Object messagerie(InvocationContext ctx) throws Exception { String nomMéthode = ctx.getMethod().getName(); String nomFichier = ""; if (ctx.getParameters()!=null) nomFichier = (String) ctx.getParameters()[0]; try { Connection connexion = fournisseur.createConnection(); Session session = connexion.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer envoi = session.createProducer(destination); MapMessage message = session.createMapMessage(); message.setLong("date", System.currentTimeMillis()); message.setString("commande", nomMéthode); message.setString("nom", nomFichier); message.setStringProperty("commande", nomMéthode); envoi.send(message); connexion.close(); }

Les principaux protocoles de messagerie

catch (JMSException ex) { Logger.getLogger("global").log(Level.SEVERE, null, ex); } return ctx.proceed();}

Le système repose sur les mêmes concepts que la sélection des enregistrements avec SQL. Vous pouvez utiliser les opérateurs NOT, AND, OR, <, > ... D'autresfonctionnalités sont possibles. Prenons le cas où nous souhaitons traiter dans le MDB tous les messages concernant le stockage, la suppression et la lecture desphotos :

@MessageDriven(mappedName="JmsPointVisionneuse", activationConfig={ @ActivationConfigProperty(propertyName="messageSelector", propertyValue="commande IN ('stocker', 'supprimer', 'getPhoto')")})public class MessagerieArchivageBean extends JFrame implements MessageListener {

Vous pouvez également penser à un système d'alerte, dans le cas où le résultat d'une valeur n'est pas situé entre 10% et -10%, pour prévenir qu'il s'agit d'un casqui sort de l'ordinaire :

résultat NOT BETWEEN -10 AND 10

Voici juste un dernier exemple qui permet de gérer les messages qui correspondent à un archivage de photo ou à une lecture :

@MessageDriven(mappedName="JmsPointVisionneuse", activationConfig={ @ActivationConfigProperty(propertyName="messageSelector", propertyValue="commande NOT LIKE '%i%'")})public class MessagerieArchivageBean extends JFrame implements MessageListener {

Cette fonctionnalité est utile lorsque nous souhaitons tri er et répartir les messages situés dans la même destination v ers différents MDB, afin de leur appliquer destraitements différents.

Sélection de message dans une application cliente

Si la réception des messages est traitée par une application cliente et non plus dans le serveur au moyen d'un MDB, il est également possible de choisir les messages àtraiter. Cela se fait tout simplement lorsque nous créons le MessageConsumer au travers de la méthode Session .createConsumer() . En effet, cette méthode est surdéfinie.Nous pouvons prendre celle qui possède un deuxième argument correspondant au critère de sélections. Le critère de sélec tion est tout à fait du même ordre que celui quenous avons utilisé dans la rubrique précédente :

MessageConsumer réception = session.createConsumer(destination, "commande NOT LIKE '%i%'");

Accusé de réception

L'inconvénient d'un traitement asynchrone des messages es t qu'il n'y a pas de valeur de retour à l'expéditeur. Il est don c difficile pour lui de savoir si le message a bien ététransmis (lorsqu'il souhaite le savoir bien entendu). Il ex iste différentes solutions à ce problème.

La première consiste à utiliser des accusés de réception, mécanisme transparent géré par le fournisseur et le conteneur MDB. Cela permet à l'application cliente designaler que le message a bien été reçu. Sans cet accusé réception, le message continuera d'être envoyé. Ce mécanisme repose sur les transactions au niveau duMDB. Lorsque celle-ci est gérée par le conteneur, l'accusé est envoyé à la suite du commit ou non si la transaction échoue.

Il existe deux modes d'accusé réception :

1. Auto-acknowledge : L'accusé de réception doit être envoyé dès que le message a é té transféré au MDB. Il s'agit d'un envoi automatique lors du commit de latransaction.

@MessageDriven(mappedName="JmsPointVisionneuse", activationConfig={ @ActivationConfigProperty(propertyName="acknowledgeMode", propertyValue="Auto-acknowledge")})public class MessagerieArchivageBean extends JFrame implements MessageListener {

2. Dups-ok-acknowledge : L'accusé de réception est repoussé à un instant indétermin é et le conteneur choisi ensuite le moment où il a peu de tâches à traiter pourl'envoyer. Cela permet évidemment d'économiser les ressou rces. Nous pouvons cependant déconseiller cette dernière v aleur car le fournisseur pourrait croire quele message n'a pas été traité et déciderait de le transmettre à nouveau (ce qui pourrait entraîner des disfonctionnement s). De plus, le coût d'envoi d'un accusé deréception est négligeable que ce soit en capacité de calcul o u en charge réseau.

@MessageDriven(mappedName="JmsPointVisionneuse", activationConfig={ @ActivationConfigProperty(propertyName="acknowledgeMode", propertyValue="Dups-ok-acknowledge")})public class MessagerieArchivageBean extends JFrame implements MessageListene

La deuxième solution consiste à spécifier la valeur JMSReplyTo dans les paramètres d'en-tête du message. Cela permet au destinataire d'envoyer une réponse versla destination paramétrée. Du côté de l'expéditeur, nous définissons la destination de réponse de la manière suivante :

Destination destination = (Destination)ctx.lookup("queue/MaFile");...message.setJMSReplyTo(destination);message.setText("Bienvenue");message.setText(" à tout le monde");...

Le MDB recevant le message peut ensuite récupérer cette propriété et envoyer à son tour un message :

Destination destination = message.getReplyTo();

Cette méthode diffère de la précédente car elle permet un rée l retour d'information concernant le traitement du message . Cette solution est typiquement utilisée dansun système d'expédition de commandes. Le message de retour a lerte le système lorsque l'objet désiré est préparé et epédi é. Cela n'aurait pas pu être implémentéavec la première solution. Cette solution est également int éressante pour remonter des rapports d'erreurs dans le proc essus métier.

JavaMail

Nous avons très souvent besoin que le système envoie un e-mai l récapitulatif au client, lors d'une transaction. JavaMailest l'API qui nous permet d'utiliser le courrier électroniq ue.

SMTP (Simple Mail Transport Protocole),protocole qui permet l'envoi d'e-mails versun serveur.

POP3 (Post Office Protocole), protocole quipermet la réception d'e-mails. Protocole trèspopulaire sur Internet, il définit une boîteaux lettres unique pour chaque utilisateur.

IMAP (Internet Message Acces Protocole),protocole qui permet la réception d'e-mails.Ce protocole est plus complexe car il apportedes fonctionnalités supplémentaires :plusieurs répertoires par utilisateur, partagede répertoires entre plusieurs utilisateurs,maintient des messages sur le serveur, etc.

NNTP (Network News Transport Protocol),protocole utilisé par les forums dediscussion (news).

Le type MIME

Le type MIME (Multipurpose Internet MailExtensions) est un standard permettantd'étendre les possibilités du courrierélectronique, comme la possibilité d'insérerdes documents (images, sons, texte, etc.)dans un courrier.

Les destinataires

Lorsque nous envoyons un e-mail, l'adressedu destinataire peut être typée :

RecipientType.TO : destinataire directRecipientType.CC : copie conformeRecipientType.BCC : copie cachée

Attention au pare-feu

Si vous avez un firewall (pare-feu) sur votremachine, vérifiez bien qu'il autorise leprotocole SMTP sur le port 25. Sinon, les e-mails seront bloqués et ne pourrons pas êtreenvoyés.

Le courrier électronique repose sur le concept de clients et de serveurs. Les clients mails (tel que Outlook ouFirebird, etc.) s'appuient sur un serveur de messagerie pour obtenir ou envoyer des e-mails. Ces échanges sontnormalisés par des protocoles particuliers (SMTP, POP3, etc.).

L'API JavaMail permet de s'abstraire de tout système de mail et d'utiliser l a plupart des protocoles decommunication de façon transparente. Ce n'est pas un serveu r d'e-mails, mais un outil pour interagir avec leserveur de messagerie. Les applications développées avec JavaMail sont en réalité comparables aux différentesmessagerie clientes telle que Outlook, Firebird, etc. Cett e API propose donc des méthodes pour lire ou envoyerdes e-mails, rechercher un message, etc. Les classes et les i nterfaces de cette API sont regroupées dans lepaquetage javax.mail .

Pour ce chapitre, nous n'utiliserons pas toute la panoplie d es classes et des interfaces de l'API, mais juste lesprincipales :

La classe Session

A la manière de JMS, JavaMail possède une classe javax.mail.Session qui établit la connexion avec le serveur demessagerie. C'est elle qui encapsule les données liées à la c onnexion (options de configuration, login, mot de passe,nom du serveur) et à partir de laquelle les actions sont réali sées :

Properties propriétés = new Properties();propriétés.put("mail.smtp.host", "smtp.orange.fr");propriétés.put("mail.smtp.auth", "true");Session session = Session.getInstance(propriétés, null);

Pour créer une session, nous utilisons la méthode getInstance() à laquelle nous passons les paramètres d'initialisation.

La classe Message

La classe javax.mail.Message est une classe abstraite qui encapsule le contenu du courrie r électronique. Un message est composé d'un en-tête qui cont ient l'adresse del'auteur et du destinataire, le sujet, etc. et d'un corps qui contient les données à envoyer ou à recevoir. JavaMail fournit en standard une classe fille nomméejavax.mail.internet.MimeMessage pour les messages possédant le type MIME.

La classe Message possède de nombreuses méthodes pour initialiser les données du message :

Méthode Description

Message(session ) Créer un nouveau message.

Message(Folder , int ) Créer un message à partir d'un message existant.

void addFromAdress( Adress[] ) Ajouter des émetteurs au message.

void addRecipient( RecepientType , Address[] ) Ajouter des destinataires à un type d'envoi (direct, en copi e ou en copie cachée).

Flags getFlags() Retourne les états du message.

Adress[] getFrom() Retourne les émetteurs.

int getLineCount() Retourne le nombre ligne du message.

Address[] getRecipients( RecepientType ) Retourne les destinataires du type fourni en paramètre.

int getSize() Retourne la taille du message.

String getSubject() Retourne le sujet du message.

Address getReplyTo() Renvoie les mails pour la réponse.

Message reply( boolean ) Créer un message pour la réponse : le booléen indique si la rép onse ne doit être faite qu'à l'emetteur.

void setContent( Object , String ) Mettre à jour le contenu du message en précisant son type MIME .

void setFrom( Address ) Mettre à jour l'émetteur.

void setRecipients( RecepientsType , Address[] ) Mettre à jour les destinataires d'un type.

void setSentDate( Date) Mettre à jour la date d'envoi.

void setText( String ) Mettre à jour le contenu du message avec le type MIME « text/plain »

void setReply( Address ) Mettre à jour le destinataire de la réponse.

void writeTo( OutputStream )Envoie le message au format RFC 822 dans un flux. Très pratiqu e pour visualiser le message sur la console enpassant en paramètre ( System.out )

Exemple de céation d'un message :

Message message = new MimeMessage(session);message.setFrom(new InternetAddress("adresse@émetteur.fr"));message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]"));message.setSubject("Confirmation de la commande");message.setText("La commande n°34256 a été bien envoyée.");message.setSentDate(new Date());

La classe InternetAddress

La classe javax.mail.internet.InternetAddress est nécessaire pour chaque émetteur et destinataire d'e-ma il. Elle hérite de la classe javax.mail.Address et représente uneadresse e-mail au format [email protected] . Pour créer une adresse e-mail, il suffit de passer une chaîn e de caractères au constructeur :

message.setFrom(new InternetAddress("adresse@émetteur.fr"));message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]"));

La classe Transport

La classe javax.mail.Transport se charge d'envoyer le message avec le protocole adéquat. Da ns notre cas, pour SMTP, il faut obtenir un objet Transport dédié à ce protocoleen utilisant la méthode getTransport( "smtp" ) d'un objet Session . Il faut ensuite établir la connexion en passant le nom du ser veur de messagerie, le nom de l'utilisateur etson mot de passe. Pour envoyer le message que l'on a créé antér ieurement, il faut utiliser la méthode sendMessage() en lui passant la liste des destinatairesgetAllRecipients() . Enfin, il faut fermer la connexion à l'aide de la méthode close() :

Transport transport = session.getTransport("smtp");transport.connect("smtp.orange.fr", "utilisateur", "mot-de-passe");transport.sendMessage(message, message.getAllRecipients());transport.close();

Les classes Store et Folder

La classe abstraite Store représente un système de stockage de messages ou "messagerie" . Pour se connecter à cette "messagerie" et ainsi pouvoir consulter vosmessages, vous devez obtenir une instance de la classe Store avec la méthode getStore() de votre session, en lui donnant comme paramètre le protocol e utilisé. Ensuitevous n'avez plus qu'a vous connecter avec la méthode connect() , en lui précisant le nom du serveur, le nom d'utilisateur et l e mot de passe. La méthode close() permet delibérer la connexion avec le serveur.

Store store = session.getStore("pop3");store.connect("smtp.orange.fr", "utilisateur", "mot-de-passe");...store.close();

La classe abstraite Folder représente un répertoire dans lequel les messages sont stoc kés. Pour obtenir un instance de cette classe, il faut utilis er la méthodegetFolder() d'un objet de type Store en lui précisant le nom du répertoire. Avec le protocole "POP3" qui ne gère qu'un seul répertoire, le seul possible est "INBOX" .Ensuite, nous appelons la méthode open() en précisant le mode d'utilisation : READ_ONLY ou READ_WRITE.

Folder folder = store.getFolder("INBOX");folder.open(Folder.READ_ONLY);

Si vous utilisez le protocole “IMAP” , vous pouvez alors avoir d'autres répertoires..

Pour obtenir les messages contenus dans le répertoire, il fa ut appeler la méthode getMessages() . Cette méthode renvoie un tableau de Message qui peut être null siaucun message n'est renvoyé. Une fois les opérations termin ées, il faut fermer le répertoire en utilisant la méthode close() .

Message[] messages = folder.getMessages();folder.close();

En définitive, voici toute la procédure à suivre pour récupérer l'ensemble des messages stockées dans votre messagerie :

Store store = session.getStore("pop3");store.connect("smtp.orange.fr", "utilisateur", "mot-de-passe");Folder folder = store.getFolder("INBOX");folder.open(Folder.READ_ONLY);Message[] messages = folder.getMessages();folder.close();store.close();

Pièces jointes

Il est possible de joindre avec le mail des ressources sous fo rme de pièces jointes (attachments). Pour cela, nous devons passer par un objet de type MultiPart . Cet objetcontient à son tour des objets BodyPart . La structure d'un objet BodyPart ressemble à celle d'un simple objet Message . Donc chaque objet BodyPart contient des attributset un contenu.

Ainsi, pour mettre en oeuvre les pièces jointes, vous devez :

1. Instancier un objet de type MimeMessage .

2. Renseigner les éléments qui composent l'en −−−−tête : émetteur, destinataire, sujet ...

3. Instancier un objet de type MimeMultiPart .

4. Instancier un objet de type MimeBodyPart et alimenter le contenu de l'élément.

5. Ajouter cet objet à l'objet MimeMultiPart grâce à la méthode addBodyPart() .

6. Répéter l'instanciation et l'alimentation pour chaque r essource à ajouter.

7. Utiliser la méthode setContent() du message en passant en paramètre l'objet MimeMultiPart pour associer le message et les pièces jointes au mail.

Exemple :

Multipart multipart = new MimeMultipart();// creation de la partie principale du messageBodyPart messageBodyPart = new MimeBodyPart();messageBodyPart.setText("Texte du courrier électronique");multipart.addBodyPart(messageBodyPart);// creation et ajout de la piece jointemessageBodyPart = new MimeBodyPart();DataSource source = new FileDataSource("image.gif");messageBodyPart.setDataHandler(new DataHandler(source));messageBodyPart.setFileName("image.gif");multipart.addBodyPart(messageBodyPart);// ajout des éléments au mailmessage.setContent(multipart);

Les classes DataSource et FileDataSource sont des classes issues du paquetage javax.activations et sont prévues pour une utilisation spécifique de l'API JavaMail .Elles permettent de gérer les différentes données de type MIME associées au courrier électronique.

Mise en oeuvre

Afin d'illustrer ces différents éléments, nous allons nous servir du projet de l'application précédente. Toutefois, l e MDB MessagerieArchivageBean va cette fois-ci jouer unrôle différent. En effet, à chaque fois qu'une nouvelle phot o est archivée, au lieu de l'afficher dans une fenêtre, nous a llons l'envoyer par courrier électronique dans lamessagerie de l'administrateur. Voici donc l'architectur e correspondante :

Et voici le nouveau code du MDB :

photos.MessagerieArchivageBean.java

package photos ;

import java . io .*;import java . text . MessageFormat ;import java . util . Date ;import java . util . Properties ;import java . util . logging .*;import javax . activation .*;import javax . annotation .*;import javax . ejb .*;import javax . jms .*;import javax . mail . internet .*;

@MessageDriven ( mappedName="JmsPointVisionneuse" )public class MessagerieArchivageBean implements MessageListener { @PostConstruct private void démarrer () { System . out . println ( "Démarrage Bean Message" ); } @PreDestroy private void fin () { System . out . println ( "Bean Message terminé" ); } public void onMessage ( Message message) { try { MapMessage msg = ( MapMessage) message; String motif = "{0, date, full} - {0, time, medium} : {1} : {2}" ; Date date = new Date ( msg. getLong ( "date" )); String commande = msg. getString ( "commande" ); String nom = msg. getString ( "nom" ); String affiche = MessageFormat . format ( motif , date , commande, nom); System . out . println ( affiche ); if ( commande. equals ( "stocker" )) envoyerCourrier ( affiche , nom, date ); } catch ( Exception ex ) { Logger . getLogger ( "global" ). log ( Level . SEVERE, null , ex ); } } private void envoyerCourrier ( String affiche , String nom, Date date ) throws Exception { Properties propriétés = new Properties (); propriétés . put ( "mail.smtp.host" , "smtp.orange.fr" );// propriétés.put("mail.smtp.auth", "true"); javax.mail.Session session = javax . mail . Session . getInstance ( propriétés , null ); javax.mail.Message courrier = new MimeMessage( session );

courrier . setFrom ( new InternetAddress ( "[email protected]" )); courrier . setRecipient ( javax . mail . Message. RecipientType . TO, new InternetAddress ( "[email protected]" )); courrier . setSubject ( "Stockage de photos" ); courrier . setSentDate ( date ); javax.mail.Multipart multipart = new MimeMultipart (); javax.mail.BodyPart corpsCourrier = new MimeBodyPart (); corpsCourrier . setText ( affiche ); multipart. addBodyPart ( corpsCourrier ); corpsCourrier = new MimeBodyPart (); DataSource photo = new FileDataSource ( "J:/Photos/" +nom); corpsCourrier . setDataHandler ( new DataHandler ( photo )); corpsCourrier . setFileName ( "J:/Photos/" +nom); multipart. addBodyPart ( corpsCourrier ); courrier . setContent (multipart); javax . mail . Transport transport = session . getTransport ( "smtp" ); transport . connect ( "smtp.wanadoo.fr" , "emmanuel.remy" , "mot-de-passe" ); transport . sendMessage ( courrier , courrier . getAllRecipients ()); transport . close (); }}

Nous voyons ici l'intérêt d'utiliser un MDB intermédiaire. Effectivement, la construction d'un mail a vec une image comme pièce jointe prend pas mal de temps. Il estpréférable que le bean session soit le plus rapidement opéra tionnel et délèque ce type de tâche à quelqu'un d'autre. Ains i, chacun s'occupe de son propre travail.D'un côté, le bean session s'occupe de la logique métier et pr end juste un tout petit peu de temps pour envoyer le message JM S qui donne les informationsnécessaires. De l'autre le MDB réalise le traitement souhaité avec le temps qu'il faut pour envoyer ce mail. Rien ne presse.

Le service Timer

Le service Timer permet à un bean d'entreprise, comme un bean session ou un MDB, d'être sollicité à une date précise, lorsqu'un temps est éc oulé ou bien à partir d'unecertaine périodicité. Pour utiliser ce service particulie r, le bean d'entreprise doit implémenter l'interface javax.ejb.TimedObject qui définie une seule méthode ( callback ) quise nomme ejbTimeout() :

package javax.ejb;public interface TimedObject { public void ejbTimeout(Timer timer);}

Avec les EJB 3, nous avons également la possibilité d'utilis er une annotation spécifique @javax.ejb.Timeout sur la méthode de votre choix qui ne retourne rien et quidoit posséder en paramètre le type javax.ejb.Timer .

Ainsi, voici les deux solutions pour implémenter la prise en compte d'un événement temporel sur un bean session :

@Statelesspublic class QuelconqueBean implements QuelconqueRemote, javax.ejb.TimedObject { public void ejbTimeout(javax.ejb.Timer timer) { // Logique métier associé à cet événement }}

@Statelesspublic class QuelconqueBean implements QuelconqueRemote { @Timeout public void méthode(javax.ejb.Timer timer) { // Logique métier associé à cet événement }}

Notification du temps, de la durée ou de la période

Pour que l'événement temporel puisse être pris en compte, il faut également activer le service Timer en proposant la notification correspondante en spécifiant , soit une dateprécise, soit un temps écoulé, soit une périodicité. Cette n otification s'établit à partir de l'interface TimerService au travers de l'annotation @Resource . Le bean d'entreprisegère ainsi la notification et l'événement associé à cette no tification.

Voici un exemple qui permet d'effectuer un traitement particulier au bout de 30 jours :

@Statelesspublic class QuelconqueBean implements QuelconqueRemote { @Resource javax.ejb.TimerService serviceTimer; public void notification() { Calendar temps = Calendar.getInstance(); temps.add(Calendar.DATE, 30); serviceTimer.createTimer(temps.getTime(), null); } @Timeout private void traitement(javax.ejb.Timer timer) { // traitement particulier au bout des trentes jours }}

L'interface TimerService

L'interface TimerService permet à un bean entreprise d'accéder au conteneur du service de Timer qui nous permet de recensés les timers déjà en service ou d'en créer denouveau. Voici ci-dessous l'ensemble de méthodes qui compo sent cette interface. Vous remarquerez la présence de quatr e méthodes createTimer() qui permettent de créerle timer suivant la notification désirée. Lorsque le timer a rrive à terme, le service de Timer appelle la méthode ejbTimeout() ou la méthode callback associée à l'[email protected] .

package javax.ejb;import java.util.Date;import java.io.Serializable;public interface TimerService { public Timer createTimer(Date dateDésirée, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException; public Timer createTimer(long tempsEcoulé, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException; public Timer createTimer(Date expiration, long intervale,Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException; public Timer createTimer(long écoulé, long intervale,Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException; public java.util.Collection getTimers() throws IllegalStateException, EJBException;}

Voici la description de chacune des méthodes de l'interface TimerService :

Timer createTimer(Date dateDésirée, Serializable info)

Crée un timer qui s'actice une seule fois. Le timer arrive à terme suivant la date spécifiée en argument de la méthode. Ainsi, si vous désirez que le timers'active à la date du 13 août 2007, voici la procédure à suivre :

Calendar date = Calendar.getInstance();date.set(2007, Calendar.AUGUST, 13);serviceTimer.createTimer(date.getTime(), null);

Timer createTimer(long tempsEcoulé, Serializable info)

Crée un timer qui s'active une seule fois. Le timer arrive à terme après le temps écoulé spécifiée en argument de la méthode (exprimé en miliseconde). Ainsi,si vous désirez activer le timer après 90 jours, voici la valeur numérique que vous devez introduire : 1000*60*60*24*90.

long durée = 1000*60*60*24*90;serviceTimer.createTimer(durée, null);

Timer createTimer(Date expiration, long intervalle,Serializable info)

Crée un timer qui s'active plusieurs fois suivant un intervalle de temps. Le timer s'active la première fois à la date spécifiée dans le premier argument. Ensuite,il s'active de nouveau successivement suivant l'intervalle de temps spécifié au deuxième argument de la méthode (exprimé en miliseconde). Ainsi, nouspouvons activer le timer le 13 août 2007 et ensuite tous les trois jours :

Calendar date = Calendar.getInstance();date.set(2007, Calendar.AUGUST, 13);long intervalle = 1000*60*60*24*3;serviceTimer.createTimer(date.getTime(), intervalle, null);

Timer createTimer(long écoulé, long intervalle,Serializable info)

Crée un timer qui s'active plusieurs fois suivant un intervalle de temps. Le timer s'active la première suivant le temps écoulé spécifié dans le premier argumentde la méthode (exprimé en miliseconde). Ensuite, il s'active de nouveau successivement suivant l'intervalle de temps spécifié au deuxième argument de laméthode (exprimé en miliseconde). Ainsi, nous pouvons activer le timer au bout de 10 minutes et ensuite toutes les heures :

long début = 1000*60*10;long intervalle = 1000*60*60;serviceTimer.createTimer(début, intervalle, null);

java.util.Collection getTimers()

Retourne l'ensemble des timers créés pour un bean entreprise en particulier. Cette méthode permet de gérer les timers existants et ainsi de pouvoir à termeles arrêter, surtout ceux qui fonctionnent constamment à cause de la mise en oeuvre de l'intervalle de temps. Cela peut se faire au moyen de la méthodeTimer.cancel(). Pour annuler tous les timers d'un bean entreprise, voici la procédure à suivre :

for(Object élément : serviceTimer.getTimers()) { javax.ejb.Timer timer = (Timer) élément; timer.cancel();}

L'objet Timer

Le Timer est un objet qui implémente l'interface javax.ejb.Timer . Il représente un événement temporel injecté dans un bean en treprise qui utilise les compétences du servicede Timer . Les objets Timers sont délivrés par les méthodes TimerService .createTimer() et TimerService .getTimers() que nous venons d'étudier. Egalement, l'objet Timer estle seul paramètre de la méthode TimedObject .ejbTimeout() ou de la méthode callback associée à l'annotation @javax.ejb.Timeout .

package javax.ejb;

public interface Timer { public void cancel() throws NoSuchObjectLocalException, IllegalStateException, EJBException; public java.io.Serializable getInfo() throws IllegalArgumentException, IllegalStateException, EJBException; public java.util.Date getNextTimeout() throws IllegalArgumentException, IllegalStateException, EJBException; public long getTimeRemaining() throws IllegalArgumentException, IllegalStateException, EJBException; public TimerHandle getHandle() throws IllegalArgumentException, IllegalStateException, EJBException;}

Un objet Timer représente exactement un événement de temps et peut être uti lisé, grâce à ces méthodes, pour annuler un timer, obtenir un objet sérialisable,récupérer les infos le concernant, connaître le temps resta nt et obtenir les références du timer suivant.

void cancel()

Nous nous sommes déjà servi de cette méthode cancel() dans la rubrique précédente. Elle est utilisée pour annuler un timer spécifique dans le service deTimer lorsque ce dernier ne doit jamais expirer. C'est très utile lorsque un timer particulier doit être supprimer complètement ou simplement réactivé. Pourréactiver un timer, nous devons en réalité le supprimer et en créer un de nouveau :

@Statelesspublic class QuelconqueBean implements QuelconqueRemote { @Resource javax.ejb.TimerService serviceTimer; public void notification(int numéro) { String item = "Timer n°"+numéro; for(Object élément : serviceTimer.getTimers()) { javax.ejb.Timer timer = (Timer) élément; String schéduleur = (String)timer.getInfo(); if (schéduleur.equals(item)) timer.cancel(); } serviceTimer.createTimer(new Date(), item); } @Timeout private void traitement(javax.ejb.Timer timer) { // traitement particulier au bout des trentes jours }}

java.io.Serializable getInfo()

Le dernier argument de toutes les méthodes TimerService.createTimer() correspond à un objet info. Jusqu'à présent, nous n'avons pas utilisé cettepossibilité, et nous avons souvent proposé la valeur null. Il est possible de préciser la valeur que vous voulez avec le type que vous désirez, à la condition quecet objet soit sérialisable. Nous pouvons dès lors récupérer cette information au moyen de la méthode getInfo(). L'objet info est enregistré par le service deTimer et délivré ensuite au bean entreprise lorsque la méthode callback Timeout est invoquée. L'objet info peut être utilisé pour des situations biendifférentes, mais il est généralement requis pour identifier un timer.

java.util.Date getNextTimeout()

Résultats du test

Démarrage du bean sessionAppel de la méthode : liste -Appel de la méthode : getPhoto - Mésangebleu.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:12:36 2007Timer remaining :24079Appel de la méthode : getPhoto -Papillon.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:12:36 2007Timer remaining :20454Appel de la méthode : supprimer -Papillon.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:12:36 2007Timer remaining :16266Appel de la méthode : getPhoto - Mésangebleu.jpgAppel de la méthode : stocker - Papillon.jpgAppel de la méthode : getPhoto -Chardonneret élégant.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:13:02 2007Timer remaining :25407Appel de la méthode : supprimer -Chardonneret élégant.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:13:02 2007Timer remaining :15891Appel de la méthode : getPhoto - Mésangebleu.jpgAppel de la méthode : supprimer - Mésangebleu.jpgAppel de la méthode : getPhoto -Papillon.jpgAppel de la méthode : supprimer -Papillon.jpgAppel de la méthode : getPhoto - nullAppel de la méthode : getPhoto - nullAppel de la méthode : stocker - Papillon.jpgAppel de la méthode : getPhoto -Papillon.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:13:21 2007Timer remaining :29985Appel de la méthode : stocker - Mésangebleu.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:13:21 2007Timer remaining :27016Appel de la méthode : getPhoto - Mésangebleu.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:13:21 2007Timer remaining :21266Timer info : 1Timer Timeout : Wed Aug 15 10:13:24 2007Timer remaining :24250Appel de la méthode : stocker -Chardonneret élégant.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:13:21 2007Timer remaining :13516Timer info : 1Timer Timeout : Wed Aug 15 10:13:24 2007Timer remaining :16484Appel de la méthode : getPhoto -Papillon.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:13:21 2007Timer remaining :8391Timer info : 2Timer Timeout : Wed Aug 15 10:13:38 2007Timer remaining :24891Timer info : 1Timer Timeout : Wed Aug 15 10:13:24 2007Timer remaining :11375Le temps du timer n°0 est écoulé.Le temps du timer n°1 est écoulé.Le temps du timer n°0 est écoulé.Le temps du timer n°1 est écoulé.Le temps du timer n°0 est écoulé.Le temps du timer n°2 est écoulé.Le temps du timer n°1 est écoulé.Le temps du timer n°0 est écoulé.Le temps du timer n°2 est écoulé.Le temps du timer n°1 est écoulé.Appel de la méthode : supprimer -Papillon.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:14:06 2007Timer remaining :5375Timer info : 2Timer Timeout : Wed Aug 15 10:14:08 2007Timer remaining :6875Timer info : 1Timer Timeout : Wed Aug 15 10:14:09 2007Timer remaining :8359Appel de la méthode : getPhoto - Mésangebleu.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:14:06 2007

Cette méthode retourne la date correspondant à son expiration ou la date du prochain intervalle de temps.

long getTimeRemaining()

Cette méthode indique tout simplement le nombre de milisecondes avant que le timer expire.

Mise en oeuvre

A titre d'exercices, nous allons nous servir du bean session ArchivagePhotosBean pour implémenter plusieurs timers . Ils'agit d'un petit test qui, il faut bien le reconnaître, ne se rt absolument à rien si ce n'est en comprendre les différentsfonctionnements.

Nous profitons ainsi des méthodes déjà présentes pour gérer ces différents timers. Les méthodes :

1. liste() : crée le premier timer dès l'activation du bean session, puisque cette méthode est toute de suite sollicité.Ce timer débute après trente secondes et se relance toutes les quinze s secondes.

2. stocker() : crée un autre timer avec les mêmes caractéristiques.

3. supprimer() : enlève le dernier timer .

photos.ArchivagePhotosBean.java

package photos ;

import java . io .*;import java . util . logging .*;import javax . annotation .*;import javax . ejb .*;import javax . interceptor .*;import javax . jms .*;

@Statelesspublic class ArchivagePhotosBean implements ArchivagePhotosRemote { @Resource ( mappedName="JmsFournisseurPhotos" ) private ConnectionFactory fournisseur ; @Resource ( mappedName="JmsPointVisionneuse" ) private Queue destination ; @Resource TimerService serviceTimer; private final String répertoire = "J:/Photos/" ; private final long départ = 1000 * 30; private final long intervalle = 1000 * 15; private int numéro ; public void stocker ( String intitulé , byte [] octets ) throws IOException { File fichier = new File ( répertoire + intitulé ); FileOutputStream stockage = new FileOutputStream ( fichier ); stockage . write ( octets ); stockage . close (); serviceTimer. createTimer ( départ , intervalle , numéro ++); }

public String [] liste () { serviceTimer. createTimer ( départ , intervalle , numéro ++); return new File ( répertoire ). list (); }

public void supprimer ( String nom) { File fichier = new File ( répertoire +nom); fichier . delete (); for ( Object obj : serviceTimer. getTimers ()) { Timer timer = ( Timer ) obj ; if ( timer . getInfo (). equals ( numéro - 1)) { timer . cancel (); numéro --; } } } public byte [] getPhoto ( String nom) throws IOException { File fichier = new File ( répertoire +nom); byte [] octets = new byte [( int ) fichier . length ()]; FileInputStream photo = new FileInputStream ( fichier ); photo . read ( octets ); photo . close (); return octets ; } @PostConstruct private void début () { System . out . println ( "Démarrage du bean session" ); } @Timeout public void tempsEcoulé ( Timer timer ) { System . out . println ( "Le temps du timer n°" +timer . getInfo ()+ " est écoulé." ); } @PreDestroy private void fin () { System . out . println ( "Fin du bean session" ); } @AroundInvoke

Timer remaining :5360Appel de la méthode : supprimer - Mésangebleu.jpgTimer info : 0Timer Timeout : Wed Aug 15 10:14:06 2007Timer remaining :4313Appel de la méthode : getPhoto -Chardonneret élégant.jpgAppel de la méthode : supprimer -Chardonneret élégant.jpgAppel de la méthode : getPhoto - nullAppel de la méthode : getPhoto - null

private Object messagerie ( InvocationContext ctx ) throws Exception { String nomMéthode = ctx . getMethod (). getName(); String nomFichier = "" ; if ( ctx . getParameters ()!= null ) nomFichier = ( String ) ctx . getParameters ()[ 0]; System . out . println ( "Appel de la méthode : " +nomMéthode +" - " +nomFichier ); for ( Object obj : serviceTimer. getTimers ()) { Timer timer = ( Timer ) obj ; System . out . println ( "Timer info : " +timer . getInfo ()); System . out . println ( "Timer Timeout : " +timer . getNextTimeout()); System . out . println ( "Timer remaining :" +timer . getTimeRemaining ()); } return ctx . proceed (); }}

Timer sur un MDB

Il est tout à fait possible de prévoir un timer sur bean MDB. Il suffit alors de suivre la même démarche. Voici juste un pe tit exemple, qui là aussi ne sert à rien, et qui permetde voir comment faire. Nous nous servons du bean MDB MessagerieArchivageBean :

photos.MessagerieArchivageBean.java

package photos ;

import ... ;

@MessageDriven ( mappedName="JmsPointVisionneuse" )public class MessagerieArchivageBean implements MessageListener { @Resource TimerService serviceTimer; @PostConstruct private void démarrer () { System . out . println ( "Démarrage Bean Message" ); } @PreDestroy private void fin () { System . out . println ( "Bean Message terminé" ); } public void onMessage ( Message message) { try { MapMessage msg = ( MapMessage) message; String motif = "{0, date, full} - {0, time, medium} : {1} : {2}" ; Date date = new Date ( msg. getLong ( "date" )); String commande = msg. getString ( "commande" ); String nom = msg. getString ( "nom" ); String affiche = MessageFormat . format ( motif , date , commande, nom); System . out . println ( affiche ); serviceTimer. createTimer ( 15* 1000 , null ); } catch ( Exception ex ) { Logger . getLogger ( "global" ). log ( Level . SEVERE, null , ex ); } } @Timeout private void tempsEcoulé ( Timer timer ) { System . out . println ( "Les 15 secondes sont écoulées" ); }}