Java Messaging Service Matthieu EXBRAYAT Master 2 RIA Université Louis Pasteur 1
Messagerie Mécanisme permettant de faire communiquer deux programmes Il existe de nombreux systèmes de messagerie???
Middleware orienté message MOM concerne une infrastructure indépendante permettant de mettre en œuvre un support de messagerie Les architectures de MOM doivent permettre :??? Il existe trois systèmes de messagerie
Architecture orientées MOM Les SI construits à base de MOM ont : Des possibilités d'échanges de messages vers de multiples clients à travers des systèmes hétérogènes Un potentiel élevé d'accroissement Une réduction des risques Un temps de développement réduit Une maintenance facile Standardisation des échanges interprocess détails protocolaires, keep-alive, fabrique de messages, format binaires propriétaires des messages, modes de livraison...
Avantages des MOM Intégration de multiples protocoles et des multiples plateformes Messages définis par les utilisateurs GMD : Guaranteed Message Delivery Equilibrage de charge Tolérance de pannes Support pour plateformes hétérogènes Gestion et configuration sur interfaces graphiques
Exemple : monitoring Solution traditionnelle : scrutation Logiciel d administration Interrogation régulière des composants Doit connaître toute la configuration Solution messages Les composants (éléments administrés) émettent des messages (changement d état, alertes ) Un ou plusieurs démons reçoivent ces mesages et les agrègent, puis émettent à leur tour des messages pertinents.
Solution traditionnelle A. Freyssinet 98
Solution Messages
Les types de MOM Les logiciels de MOM peuvent fonctionner dans trois catégories (Elles définissent quels clients reçoivent un message) Point-To-Point (PTP) Publish-Subscribe(Pub/Sub) Request-Reply(RR) émetteur message mom récepteur? récepteur? récepteur?
Le domaine Point à Point Met en relation un client (le producteur) qui envoie un message vers un autre client (le receveur) émetteur récepteur? émetteur récepteur?
Queues PTP Plusieurs producteurs peuvent placer les messages pour divers destinataires dans une queue Queue de distribution récepteur producteur récepteur producteur Gestionnaire de files(serveur MOM) ==> Exemples d'utilisation? récepteur
Le domaine P/S Les producteurs de messages (appelés publishers) génèrent des données pour de multiples clients (subscribers) Abonné Editeur Abonné ==> Mécanisme similaire? Abonné
Sujets de Pub/Sub La publication et l'abonnement à un sujet découple le producteur et le consommateur Le cinéma contemporain récepteur producteur récepteur producteur Gestionnaire de sujets (Serveur MOM) ==> Exemple d utilisation récepteur
Le domaine Request/Reply Le domaine R/R définit un programme qui envoie un message et attend une réponse immédiatement Ce domaine modélise : l'approche client/serveur l'approche des systèmes distribués EJB (session / entity) CORBA DCOM
JMS Java Messaging Service Messages (et donc spec MOM) pour Java
Que fournit JMS JMS est un ensemble d'interfaces (et de leurs sémantiques associées) qui définissent comment un client utilise les fonctionnalités offertes par un système de messagerie JMS définit les API : du domaine PTP du domaine Pub/Sub http://java.sun.com/products/jms/index.htm l
Une application JMS c est : Des clients JMS Des clients non JMS Des messages Un fournisseur de service de messagerie Des objets administrés standards Messages préfabriqués Destinataires standards Client JMS MOM Implantation JMS Client non-jms Objets administrés standards Objets Destination et Usine de connexion préfabriqués
Couches logicielles
Objets JMS
Factory, Connection, Session Factory : fabrique de connexions Abstraction pour fabrication des connections Connection Lien entre le client et l infrastructure JMS Connection «physique» (socket) Gère l authentification si besoin Objet «lourd» >> potentiellement lié à des ressources hors-java Ne reste ouvert que lorsque c est nécessaire Démarre en mode arrêté (stopped) : permet la configuration
Factory, Connection, Session Session Fabrique de producteurs et consommateurs Fabrique pour destinations temporaires Fournit une notion de session et d ordre (partiel) des messages (produits et consommés) Garantit l ack des messages consommés Objet «léger», pur java Fermeture de session entraîne fermeture des producteurs et consommateurs
Modèle Point à point Création de queues : dépend de l infrastructure Queue / TemporaryQueue QueueConnectionFactory QueueConnection QueueSession QueueSender / QueueReceiver Plusieurs receivers sur une même queue? Receiver peut utiliser un selector QueueBrowser : consulter sans retirer QueueRequestor : Une classe pour faire du request/reply MessageListener
Modèle publication / abonnement Création de topic : dépend de l infrastructure Topic / TemporaryTopic TopicConnectionFactory TopicConnection TopicSession TopicPublisher/TopicSubscriber Possibilité de selector TopicRequestor Pas de Browser (pourquoi?) Abonnement durable Permet de lire tous les messages Suppose identification (même client) et unicité de l instance
Request / Reply Domaine non défini dans JMS, Mais Des classes fournies permettent de l'émuler avec P2P!
Structure des Messages Entête : Identification, destinataire, etc. Exemple : JMSDestination, JMSReplyTo, JMSType,JMSPriority,JMSExpiration Propriétés : Infos spécifiques / optionnelles Paires nom / valeur; Types simples + String Corps du Message StreamMessage (Flux de de variables de type simple) MapMessage (paires nom /valeur) TextMessage ObjectMessage BytesMessage
Sélection de messages Selector = chaîne au format SQL92 (condition). Peut porter sur (une partie des) infos de l entête et les propriétés Comparaisons, between, in/not in, like, priorités ( )
Une petite classe pratique package exemplejms.util; import java.util.properties; import javax.naming.*; public class JBossContext { public static Context getinitialcontext() throws NamingException{ Properties prop=new Properties(); prop.put(context.initial_context_factory,"org.jnp.interfaces.namingcontextfactory"); prop.put(context.url_pkg_prefixes,"org.jboss.naming:org.jnp.interfaces"); prop.put(context.provider_url,"jnp://localhost:1099"); return new InitialContext(prop); } }
Exemple Sender package exemplejms.p2p; import javax.jms.*; import javax.naming.*; import exemplejms.util.jbosscontext; public class Sender { public static void main(string[] args) { ConnectionFactory connectionfactory=null; Connection connection=null; Queue queue=null; Session session=null; MessageProducer messageproducer=null; TextMessage message=null; Context ctx=null;
try { ctx = JBossContext.getInitialContext(); connectionfactory=(connectionfactory)ctx.lookup("connectionfactory"); queue=(queue)ctx.lookup("queue/a"); connection=connectionfactory.createconnection(); session=connection.createsession(false,session.auto_acknowledge); messageproducer=session.createproducer(queue); message=session.createtextmessage(); message.settext("bonjour"); messageproducer.send(message); System.out.println("message envoyé"); connection.close(); } catch (NamingException e) {e.printstacktrace(); } catch (JMSException e) {e.printstacktrace(); } } }
Exemple Receiver package exemplejms.p2p; import javax.jms.*; import javax.naming.*; import exemplejms.util.jbosscontext; public class Receiver { public static void main(string[] args) { ConnectionFactory connectionfactory=null; Connection connection=null; Queue queue=null; Session session=null; MessageConsumer messageconsumer=null; TextMessage message=null; Context ctx=null;
try { ctx = JBossContext.getInitialContext(); connectionfactory=(connectionfactory)ctx.lookup("connectionfactory"); queue=(queue)ctx.lookup("queue/a"); connection=connectionfactory.createconnection(); session=connection.createsession(false,session.auto_acknowledge); messageconsumer=session.createconsumer(queue); connection.start(); message=(textmessage)messageconsumer.receive(); System.out.println("message reçu :"+message.gettext()); connection.close(); } catch (NamingException e) {e.printstacktrace(); } catch (JMSException e) {e.printstacktrace(); } } }
Exemple Publisher public class Publisher { public static void main(string[] args) { ConnectionFactory connectionfactory=null; Connection connection=null; Topic topic=null; Session session=null; MessageProducer messageproducer=null; TextMessage message=null; Context ctx=null; try { ctx = JBossContext.getInitialContext(); connectionfactory=(connectionfactory)ctx.lookup("connectionfactory"); EJB et Java EE Matthieu topic=(topic)ctx.lookup("topic/testtopic"); EXBRAYAT Master 2 RIA / ULP 2007
connection=connectionfactory.createconnection(); session=connection.createsession(false,session.auto_acknowledge); messageproducer=session.createproducer(topic); message=session.createtextmessage(); message.settext("bonjour"); messageproducer.send(message); System.out.println("message diffusé"); connection.close(); } catch (NamingException e) { e.printstacktrace(); } catch (JMSException e) { e.printstacktrace(); } } }
Exemple Subscriber public class Subscriber { public static void main(string[] args) { ConnectionFactory connectionfactory=null; Connection connection=null; Topic topic=null; Session session=null; MessageConsumer messageconsumer=null; TextMessage message=null; Context ctx=null; try { ctx = JBossContext.getInitialContext(); connectionfactory=(connectionfactory)ctx.lookup("connectionfactory"); topic=(topic)ctx.lookup("topic/testtopic");
connection=connectionfactory.createconnection(); session=connection.createsession(false,session.auto_acknowledge); messageconsumer=session.createconsumer(topic); connection.start(); message=(textmessage)messageconsumer.receive(); System.out.println("message reçu : "+message.gettext()); connection.close(); } catch (NamingException e) { e.printstacktrace(); } catch (JMSException e) { e.printstacktrace(); } } }
Exemple Requestor «à l'ancienne» public class Requestor { public static void main(string[] args) { QueueConnectionFactory queueconnectionfactory=null;... try { ctx = JBossContext.getInitialContext(); queueconnectionfactory=(queueconnectionfactory)ctx.lookup("connectionfactory"); queue=(queue)ctx.lookup("queue/a"); queueconnection=queueconnectionfactory.createqueueconnection(); queuesession=queueconnection.createqueuesession(false,session.auto_acknowledge); queuerequestor=new QueueRequestor(queueSession,queue); queueconnection.start();
message=queuesession.createtextmessage(); message.settext("requete"); messageretour=(textmessage)queuerequestor.request(message); System.out.println("message reçu :"+message.gettext()); String replyid = new String(messageRetour.getJMSCorrelationID()); if (replyid.equals(message.getjmsmessageid())) { System.out.println("REQUEST: OK: Reply matches sent message"); } else { System.out.println("REQUEST: ERROR: Reply does not match sent message"); } queueconnection.close();...
Exemple Replyer «à l'ancienne» public class Replyer { public static void main(string[] args) { QueueConnectionFactory queueconnectionfactory=null;... try { ctx = JBossContext.getInitialContext(); queueconnectionfactory=(queueconnectionfactory)ctx.lookup("connectionfactory"); queue=(queue)ctx.lookup("queue/a"); queueconnection=queueconnectionfactory.createqueueconnection(); queuesession=queueconnection.createqueuesession(false,session.auto_acknowledge); queuereceiver=queuesession.createreceiver(queue);
Requestor en «1.1» + file tempo public class Requestor2 { public static void main(string[] args) { ConnectionFactory connectionfactory=null; Connection connection=null; Queue queue=null; TemporaryQueue tempoqueue=null; Session session=null; MessageProducer messageproducer=null; MessageConsumer messageconsumer=null; TextMessage message=null; TextMessage messageretour=null; Context ctx=null;
try { ctx = JBossContext.getInitialContext(); connectionfactory=(queueconnectionfactory)ctx.lookup("connectionfactory"); queue=(queue)ctx.lookup("queue/a"); connection=connectionfactory.createconnection(); session=connection.createsession(false,session.auto_acknowledge); messageproducer=session.createproducer(queue); tempoqueue=session.createtemporaryqueue(); messageconsumer=session.createconsumer(tempoqueue); connection.start();
message=session.createtextmessage(); message.settext("requete"); message.setjmsreplyto(tempoqueue); messageproducer.send(message); messageretour=(textmessage)messageconsumer.receive(); System.out.println("message reçu :"+message.gettext()); String replyid = new String(messageRetour.getJMSCorrelationID()); if (replyid.equals(message.getjmsmessageid())) { System.out.println("REQUEST: OK: Reply matches sent message"); } else { System.out.println("REQUEST: ERROR: Reply does not match sent message"); } connection.close();...
queueconnection.start(); message=(textmessage)queuereceiver.receive(); QueueSender replysender=queuesession.createsender((temporaryqueue) message.getjmsreplyto()); TextMessage reply = queuesession.createtextmessage(); reply.settext("retour de "+message.gettext()); reply.setjmscorrelationid(message.getjmsmessageid()); replysender.send(reply); System.out.println("message reçu :"+message.gettext()); queueconnection.close();...
Approche Listener Réception des messages gérée dans un thread Créer une classe implémentant Message Listener Implanter la méthode OnMesssage
Exemple : Rcv avec Listener public class Consumer implements MessageListener { public Consumer() { Connection connection=null;... try { ctx = JBossContext.getInitialContext();... consumer=session.createconsumer(queue); consumer.setmessagelistener(this); connection.start(); } catch (NamingException e) {e.printstacktrace(); } catch (JMSException e) {e.printstacktrace(); } }
public void onmessage(message msg) { try { TextMessage message=(textmessage)msg; System.out.println("message reçu :"+message.gettext()); } catch (JMSException jmse) { jmse.printstacktrace(); } } public static void main(string[] args) { Consumer c=new Consumer(); } }
Jboss et JMS : ressources Approche fichier Dans server/all/deploy/jms/jbossmq-destinations-service.xml <mbean code="org.jboss.mq.server.jmx.queue" name="jboss.mq.destination:service=queue,name=d"> </mbean> <mbean code="org.jboss.mq.server.jmx.topic" name="jboss.mq.destination:service=topic,name=testtopic"> </mbean> Approche Console Web Dans la console : localhost:8080/jmx-console Service=DestinationManager / createqueue (nom du mbean/ nom de la file) Files non permanentes (disparaissent au shutdown)