Message Oriented Middleware (MOM) Java Message Service (JMS) Vorlesung: Applikationsserver Prof. Dr. Ch. Reich rch@fh furtwangen.de http://www.informatik.fh furtwangen.de/~reich
Message Oriented Middleware (MOM) Every DAD needs a MOM. DAD: Distributed Application Development. Indirekte asynchrone Kommunikation über Message-Queues. Kommunikation ohne direkte, logische Verbindung. =>enkoppelt, asynchron Allgemeine, zweckfreie Nachrichten (messages) können zwischen Applikationen, mittels Message Queueing, ausgetauscht werden.
MOM Client und Server können zu unterschiedlichsten Zeiten laufen. Zwischen Hineinstellen und Herausnehmen einer Nachricht können Stunden liegen. Message Queueing Teilnehmer kommunizieren über das Netz einfach durch Hineinstellen und Herausnehmen von Nachrichten in Queues. Hauptanwendung: verteilte, heterogene Geschäftssysteme
MOM-Entwicklungsgeschichte seit den 70er Jahren gibt es MOM- Produkte. 1999 entstand unter der Schirmherrschaft von Sun, eine Java API: JMS (Java Message Service), deren Einhaltung sich viele MOM-Hersteller verpflichtet haben. JMS ist eine einfache MOM-API. Verteilte Anwendungsentwicklung ist dadurch MOM-herstellerunabhängig möglich
JMS Providers SunONE Message Queue (SUN) JMS provider integriert im SunONE Application Server http://www.sun.com MQ JMS (IBM) MQSeries (weit verbreitete Messaging Technology) MQ kann als JMS Provider konfiguriert werden. http://www.ibm.com WebLogic JMS (BEA) enterprise-class messaging system integiert in den WebLogic Server http://www.bea.com
Andere JMS Vertreiber Fiorano Software http://www.fiorano.com JRUN Server http://www.allaire.com GemStone http://www.gemstone.com Nirvana http://www.pcbsys.com Oracle http://www.oracle.com Eine ausführlichere Liste können Sie bei: http://java.sun.com finden.
JMS unterstützte Kommunikationsmodelle Point-to-Point Warteschlangenprinzip Nachricht wird von einem konsumiert Publish/Subscribe Themenorientierte Nachrichtenverteilung Nachricht wird von vielen konsumiert
Warteschlangenmanagement Queue B Verbindung Queue A Client lookup logische Verbindung Queue Server verwaltet JNDI Name Server registriert Queues
Point-to-Point Kommunikationsmodell Ein-Wege-Kommunikation Msg Msg sends konsumiert Client 1 Queue Client 2 acknowledges
Zwei-Wege-Kommunikation Msg Msg konsumiert Queue A sends acknowledges Client 1 Client 2 acknowledges konsumiert Msg Queue B sends Msg
1:n Wege-Kommunikation (jedoch immer nur Einer) Client 2 Client 1 Msg sends Queue Msg konsumiert acknowledges Client 3... Client n
Point-to-Point Kommunikationsmodell Prinzip der Warteschlangen (Message Queues) zeitliche und örtliche Entkopplung von Anwendungen möglich. Nachrichten (messages), von Sender (producer) generiert, werden in eine oder mehrerer Warteschlangen (queues) geschickt. Persistente Speicherung der Nachrichten in den Warteschlangen, bis Empfänger (consumer) diese abholt. => zuverlässige Queues.
Point-to-Point Kommunikationsmodell First in, first out bei gleicher Priorität der Nachrichten Den Nachrichten können Prioritäten (priority) gesetzt werden. Eine Nachricht kann nur einmal konsumiert werden. Many-to-Many Queueing möglich.
Publish/Subscribe Kommunikationsmodell Topic Z Msg Msg konsumiert acknowledges Msg Client 2 Client 1 Msg sends konsumiert acknowledges Client 3 Msg konsumiert Client 4 acknowledges
Publish/Subscribe Kommunikationsmodell Indirekte Kommunikation über einen Message Broker Nachrichten werden zu sogenannten Themen (topics) zugeordnet. Produzenten generieren Nachrichten und publizieren (publish) diese unter einem gegebenen Thema. Alte Nachrichten werden von den neuen Nachrichten überschrieben. Komsumenten können Nachrichten abonieren (subscribe).
Publish/Subscribe Kommunikationsmodell Komsumenten erhalten alle Nachrichten, die unter dem entsprechenden Thema publisiert werden. An alle Konsumenten eines bestimmten Themas werden die Nachrichten übermittelt. Asynchrone Übertragung durch: polling Event getriggert.
Nachrichtenaufbau Kopf + Eigenschaften + Inhalt Kopf (Header): enthält Prioritäten, Lebensdauer, Ziel, etc. Eigenschaften (Properties): applikations-, provider-spezifisches, optinale Felder, etc. Inhalt (Body): enthält Nutzdaten; in JMS sind 5 Nutzdatentypen spezifiziert
JMS Message Header Felder Setter und Getter-Methoden des Message-Interfaces Message-Type JMSDestination JMSDeliveryMode JMSMessageID JMSTimeStamp JMSRedelivered JMSExpiration JMSPriority JMSCorrelationID JMSReplyTo JMSType Beschreibung Angabe des Empfängers (Queuename oder Topicname) Es gibt nicht-persistenter und p. Mode. ID (String) kann angegeben werden. Setzen eines Zeitstempels. könnte schon mal ausgeliefert worden sein Wie lange eine Nachricht lebt. Prioritäten (0-9); 0=low; 9=high Verlinken von Nachrichten möglich Wohin soll die Antwort geschickt werden. Symbolischer Type (String) kann gesetzt werden.
JMS Nachrichtentypen Message-Type enhält Methoden TextMessage String gettext, settext MapMessage Menge von setstring, setdouble, Name/Wert- setlong, getstring, Paaren getdouble, getlong BytesMessage StreamMessage ObjectMessage Strom aus nichtinterpretierte n Bytes Strom aus Primitivtypen serialisierte Objecte writebytes, readbytes writestring, writedouble, writelong, readstring, readdouble, readlong setobject, getobject
Nachrichten-Zustellmechanismen Persistente Nachrichten: Erst nach dem konsumieren werden Nachrichten vom Queue-Provider gelöscht. Empfangsbestätigung: implizite: durch MOM-Provider explizite: durch Konsument Lebenszeit: Nachricht wird gelöscht, wenn Zeit abgelaufen. Prioritäten: Nachrichten mit höheren Prioritäten werden zuerst ausgeliefert. Transaktionen: Nachrichten können zu Transaktionen zusammengefasst werden.
JMS API Programmiermodel Connection Factory creates Connection Message Producer sends to Destination creates creates Session creates Msg Message Consumer receives from Destination
Einfaches Point-to-Point Beispiel Einfaches standalone Program Client macht folgendes: Erzeugt eine Verbindung (connection) und eine Sitzung (session). Erzeugt einen Nachrichtensender (producer) und einen Nachrichtenempfänger (consumer). Senden und Empfangen von Nachrichten.
SimpleQueueSender.java 1. Benutze JNDI-lookup um queue und QueueConnectionFactory zu ermitteln. 2. Erzeuge eine Verbindung und eine Sitzung. 3. Erzeuge einen QueueSender 4. Erzeuge eine TextMessage 5. Schicke eine Nachricht in die queue. 6. Schließe Verbindung
1. Benutze JNDI-lookup um queue und QueueConnectionFactory zu ermitteln. InitialContext inicon = null; QueueConnectionFactory qconfact = null; Queue queue = null; inicon = new InitialContext(); qconfact =(QueueConnectionFactory) inicon.lookup ("QueueConnectionFactory"); queue = (Queue) inicon.lookup("qname");
2. Erzeuge eine Verbindung und eine Sitzung. QueueConnection qcon = null; QueueSession qses = null; qcon = qconfact.createqueueconnection(); qses = qcon.createqueuesession(false, Session.AUTO_ACKNOWLEDGE); 3. Erzeuge einen QueueSender QueueSender qsend = null; qsend = qses.createsender(queue);
4. Erzeuge eine TextMessage TextMessage message = null; message = qses.createtextmessage(); message.settext("eine Nachricht"); 5. Schicke eine Nachricht in die queue qsend.send(message); 6. Schließe Verbindung qcon.close();
SimpleQueueReceiver.java 1. Benutze JNDI-lookup um queue und QueueConnectionFactory zu ermitteln. (siehe SimpleQueueSender.java) 2. Erzeuge eine Verbindung und eine Sitzung. (siehe SimpleQueueSender.java) 3. Erzeuge einen QueueReceiver 4. Starte Verbindung => Nachrichten werden an Applikation übermittelt. 5. Lese eine Nachricht aus der queue. 6. Schließe Verbindung
3. Erzeuge einen QueueReceiver QueueReceiverer qreceiver = null; qreceiver = qses.createreceiver(); 4. Starte Verbindung qcon.start(); 5. Lese eine Nachricht aus der queue. TextMessage msg = (TextMessage) qreceiver.receive(); 6. Schließe Verbindung qcon.close();
Die receive()-methode ist blockierend, wenn: Message m = queuereceiver.receive(); Message m = queuereceiver.receive(0); ist nicht blockieren, wenn: Wert des receive-parameters > 0 oder Message m = qreceiver.receivenowait();
Einfaches Publish/Subscribe Beispiel Einfaches standalone Program Client macht folgendes: Erzeugt eine Verbindung (connection) und eine Sitzung (session). Erzeugt einen Nachrichtenpulizist (publisher) und einen Nachrichteninteressierten (subscriber). Senden und Empfangen von Nachrichten.
SimpleTopicPublisher.java 1. Benutze JNDI-lookup um Topic und TopicConnectionFactory zu ermitteln. (siehe SimpleTopicPublisher.java) 2. Erzeuge eine Verbindung und eine Sitzung. (siehe SimpleTopicPublisher.java) 3. Erzeuge einen TopicPublisher 4. Erzeuge eine TextMessage 5. Schicke eine Nachricht an das Topic. 6. Schließe Verbindung
1. Benutze JNDI-lookup um Topic und TopicConnectionFactory zu ermitteln. InitialContext inicon = null; TopicConnectionFactory tconfact = null; Topic topic = null; inicon = new InitialContext(); tconfact =(TopicConnectionFactory) inicon.lookup ("TopicConnectionFactory"); topic = (Topic) inicon.lookup("tname");
2. Erzeuge eine Verbindung und eine Sitzung. TopicConnection tcon = null; TopicSession tses = null; tcon = tconfact.createtopicconnection(); tses = tcon.createtopicsession(false, Session.AUTO_ACKNOWLEDGE); 3. Erzeuge einen TopicPublisher TopicPublisher tpub = null; tpub = tses.createpublisher(topic);
4. Erzeuge eine TextMessage TextMessage message = null; message = tses.createtextmessage(); message.settext("eine Nachricht"); 5. Schicke eine Nachricht an ein topic tpub.publish(message); 6. Schließe Verbindung tcon.close();
SimpleTopicSubscriber.java 1. Benutze JNDI-lookup um Topic und TopicConnectionFactory zu ermitteln. (siehe SimpleTopicPublisher.java) 2. Erzeuge eine Verbindung und eine Sitzung. (siehe SimpleTopicPublisher.java) 3. Erzeuge einen TopicSubscriber 4. Starte Verbindung => Nachrichten werden an Applikation übermittelt. 5. Lese eine Nachricht aus dem Topic. 6. Schließe Verbindung
3. Erzeuge einen TopicSubscriber TopicSubscriber tsub = null; tsub = tses.createsubscriber(topic); 4. Starte Verbindung tcon.start(); 5. Lese eine Nachricht aus dem Topic. TextMessage msg = (TextMessage) tsub.receive(); 6. Schließe Verbindung tcon.close();
Der MessageListener Sowohl für eine Queue als auch für ein Topic ist es möglich einen MessageListener zu setzen. QueueReceiver qr; qr.setmessagelistener(mymessagelistener); TopicSubsriber ts; ts.setmessagelistener(mymessagelistener); MyMessageListener muss die Methode onmessage() implementieren. Kommt eine Nachricht an, dann wird automatisch die onmessage()-methode aufgerufen.
JMS Interfaces Zusammenfassung der JMS-Interfaces: Eltern-Interface Point-to-Point Pub/Sub ConnectionFactory QueueConnectionFactory TopicConnectionFactory Connection QueueConnection TopicConnection Destination Queue Topic Session QueueSession TopicSession MessageProducer QueueSender TopicPublisher MessageConsumer QueueReceiver, TopicSubscriber QueueBrowser
4 Acknowledgement-Typen 1. ack durch commit nur bei Transaktionen möglich 2. ack durch Auto-Acknowledgment Session.AUTO_ACKNOWLEDGE Nachricht gilt als erfolgreich ausgeliefert, wenn der Client durch receive oder onmessage die Nachricht empfangen hat.
4 Acknowledgement-Typen 3. ack durch den Client Session.CLIENT_ACKNOWLEDGE Client muss auf das Message-Objekt die acknowledge()-methode aufrufen 4. ack durch Provider Session.DUPS_OK_ACKNOWLEDGE Message wird gleich acknowledged, sobald die Nachricht für die Konsumenten verfügbar ist. Achtung: Konsument bekommt Nachricht nie oder doppelt.
Setzen des Ack-Types Ein Acknowledgement Typ wird beim Erzeugen der Session durch einen geeigneten Parameter gesetzt QueueConnection.createQueueSession(..., AckTypeParam) TopicConnection.createTopicSession(..., AckTypeParam) Bsp: TopicSession session = topicconnection.createtopicsession(false, Session.CLIENT_ACKNOWLEDGE);
Robuste JMS Applikationen Persistente Nachrichten zu versenden machen Applikationen sehr robust, da Nachrichten nicht verloren gehen können. Die JMS-Provider haben per Default persistente Nachrichtenverarbeitung. Es ist auch möglich Nachrichten zu Transaktionen zusammenzufassen. Client-to-Messaging Transaktionssicht Client-to-Client Transaktionssicht (wird nicht von JMS unterstützt)
Nachrichten - Selector Empfänger (Receiver, Subscriber) benutzen Selektoren um nur solche Nachrichten herauszufiltern, die von Interesse sind. queuesession.createreceiver( queue, selectstr ); topicsession.createsubscriber( top, selectstr ); Seletor ist vom Typ: String und stellt ein SQL92-select dar. Es können nur auf Eigenschaften des JMS- Headers gefiltert werden.
Dauerhafte Nachrichten Bei TopicSubscribern gibt es die Möglichkeit, dass Nachrichten im Topic für den Subscriber aufgehoben werden, bis der Subscriber wieder online ist um diese Nachrichten zu konsumieren. Der Subscriber muss dafür eine ID (String) angeben, mit der er sich immer wieder meldet. topicsubscriber = topicsession.createdurablesubscriber( topic, id );
EJBs (Enterprise JavaBeans) Message Driven Beans
Message-Driven Beans Die J2EE Platform unterstützt Message- Driven Beans. Dies erlaubt JMS-Applikationen oder JMS-Beans mit der J2EE-Architektur zu kommunizieren. Anwendung findet dies vor allem bei der Anbindung von Altsystemen, da das einfach JMS-Interface vielen Programmiersprachen zur Verfügung steht.
JMS mit EJB-Message Driven Bean (MDB) App.Server Client 1 Msg sends Queue Msg konsumiert acknowledges MD Bean Container
Eigenschaften von Message- Driven Beans Sie haben keine home, local-home, remote, local Interfaces Sie implementieren nur die onmessage ()-Methode. Sie besitzen keinen Rückgabewert. Es können keine Exceptions an den Client zurückgeschickt werden. Sie sind stateless. Sie können durable oder non-durable Subscriber sein.
Lebenszyklus: Message-Driven Bean Bean existiert nicht 1. setmessagedrivencontext 2. ejbcreate ejbremove onmessage Bean bereit EJB-Container
Interfaces des Message- Driven Beans javax.jms.messagelistener public void onmessage(message msg); javax.ejb.messagedrivenbean extends javax.ejb.enterprisebean public void ejbremove() throws EJBException; public void ejbcreate() throws EJBException; public void setmessagedrivencontext (MessageDrivenContext ctx) throws EJBException;
Message-Driven Bean Deployment Descripter Felder <message-driven> <ejb-name>mdbean</ejb-name> <ejb-class>mypacket.mdbean</ejb-class> <transaction-type>container</transaction-type> <message-driven-destination> <!-- queue --> <destination-type>javax.jms.queue</destination-type> <!-- topic waere javax.jms.topic --> <!-- optional waeren die naechsten 2 Felder --> <message-selector>jmstype = 'gr1' </message-selector> <acknowledge-mode>auto-acknowledge </acknowledge-mode> </message-driven-destination> </message-driven>