Enterprise Application Integration 8. Nachrichten-orientierte Middleware Kommunikation zwischen IS in Unternehmen eng Remote Procedure Calls/ Remote Method Invocation, z.B. bei: Kopplung CORBA EJB DCOM+ Web Services Nachrichten lose 2 Schnittstellen vs. Nachrichten Kommunikation über Schnittstellen (bei RPC/RMI) enge Kopplung der Systeme ermöglicht Typprüfung während Kompilierung und Laufzeit wenig Overhead Anpassung umfangreicher Systeme aufwändig Schnittstellen alter und neuer Systeme oft inkompatibel Schnittstelle int addiere(int x, int y); Implementierung int addiere(int x, int y) { return x+y; } 3 17 25 42 Aufrufende Klasse int result = schnittstelle.addiere(17, 25) Schnittstellen vs. Nachrichten Nachrichten lose Kopplung der Systeme bietet ggf. Zustellgarantie (bei Systemausfall später zugestellt) keine Typprüfung etwas größerer Overhead (z.B. durch Warteschlange, Metadaten) einfache Anpassung auch bei größeren Systemen für Kompatibilität mit Altsystemen alte Nachrichteninhalte beibehalten, neue ergänzen Sender 4 An: Empfänger Dienst: addiere x: 17 y: 25 Nachrichtenwarteschlange Empfänger Kommunikationsmodelle Synchrone Kommunikation Sender und Empfänger in Ablauf aneinander gekoppelt Sender blockiert, bis Empfänger antwortet Sender Empfänger Asynchrone Kommunikation Sender und Empfänger in Ablauf nicht gekoppelt Während Empfänger Antwort berechnet, kann Sender weiterarbeiten Sender 5 Empfänger Kommunikationsvarianten Synchrone Einwegkommunikation z.B. entfernter Methodenaufruf ohne Rückgabe 1. Sender sendet Anfrage an Empfänger und blockiert 2. Empfänger nimmt Nachricht entgegen, sendet Bestätigung ("Acknowledgement") und verarbeitet dann Nachricht 3. Sender erhält Bestätigung und kann direkt weiterarbeiten Sender Empfänger Ack 6 Kommunikationsvarianten Synchrones Polling Sender fragt periodisch bei Empfänger an, ob Resultate vorliegen Antwort entweder als Nachricht oder in gemeinsamem Speicher 1. Sender schickt Anfrage an Empfänger und arbeitet weiter 2. Empfänger startet Verarbeitung 3. Sender fragt regelmäßig nach Ergebnissen Falls keine vorhanden, wird weitergearbeitet und später erneut nachgefragt 4. Ergebnis liegt vor: Ergebnis wird geliefert, Empfänger kann weiterarbeiten 5. Sender arbeitet mit Ergebnis weiter Sender 7 Empfänger Kommunikationsvarianten Asynchrones Broadcasting Sender sendet Nachricht an mehrere Empfänger gleichzeitig, arbeitet weiter jeder Empfänger erhält Nachricht und kann reagieren Empfänger 1 Sender Empfänger 2 Empfänger 3 8 Kommunikationsvarianten Asynchrones Publish/Subscribe Ähnlich zum Broadcast, aber Empfänger abonnieren bei "Zusteller" Themen nur registrierte Empfänger erhalten Nachricht Abo Sender A Zusteller aB m e h eT nnier A Abonniere Thema A Empfänger 1 Empfänger 2 A Abon niere 9 Them a A Empfänger 3 Nachrichten-orientierte Middleware Middleware, die die Weitergabe von Nachrichten übernimmt Dienste zentriert auf Nachrichten Anlegen Weitergabe Auslieferung Speicherung (Persistierung) Transaktionssicherheit MoM als Vermittler zwischen Sender und Empfänger Message-Server / Message-Broker 10 Nachrichten-orientierte Middleware Vorteile: asynchrone Kommunikation sehr allgemein; ermöglicht Emulation anderer Modelle aufgrund allgemeinen Charakters hohes Maß an Interoperabilität zwischen heterogenen Systemen für lose gekoppelte Systeme sehr gut geeignet Nachteile: fehlende Typsicherheit Overhead durch Ver-/Entpacken und Übermittlung der Nachrichten 11 Nachrichten-orientierte Middleware Entweder Standalone… IBM Websphere MQ Sun Java System Message Queue MS Message Queue Server ObjectWeb JORAM BEA MessageQ TIBCO ActiveEnterprise … …oder als Bestandteil anderer Middleware-Systeme Java EE (JMS und Message-Driven Beans) z.B. JBoss … 12 Java Message Service Spezifikation, definiert Schnittstellen und Protokolle für Kommunikation durch Nachrichtenaustausch Kommunikationsvarianten asynchrones Senden (asynchrone Punkt-zu-Punkt-Kommunikation) asynchrones Publish/Subscribe asynchrones Request/Reply synchrones Request/Reply (blockierend) synchrone Einwegkommunikation ermöglicht Programmierung weiterer Kommunikationsvarianten 13 Java Message Service Zwei Arten von Nachrichtenkanälen Queues: einfache Warteschlangen für n:m-Kommunikation (einer empfängt) Topics: Publish/Subscribe-Kanäle für n:m-Kommunikation (alle empfangen) Queues und Topics sind zueinander inkompatibel Empfänger 1 Sender 1 xor xor Sender 2 Empfänger 2 Sender 1 Empfänger 1 xor Sender 2 14 Queue Topic und Empfänger 2 Java Message Service Queue: jede Nachricht wird nur einmal ausgeliefert eine Nachricht wird ggf. solange gespeichert, bis ein Empfänger sie abholt eine Übermittlungsreihenfolge wird nicht garantiert Topic: Variante 1: nur die Abonnenten zum Sendezeitpunkt empfangen Nachrichten können ggf. gar nicht empfangen werden Variante 2 (durable): auch die später hinzukommenden Empfänger erhalten die Nachricht 15 Java Message Service Nachrichten Bestehen aus Header, Properties und Body Header enthält Meta-Angaben (Empfänger, Lebensdauer, ...) Properties enthalten zusätzliche, frei definierbare Angaben (primitive Datentypen und Strings) Body enthält den eigentlichen Inhalt Nachrichtenarten (implementieren javax.jms.Message) TextMessage: zum Übermitteln eines Strings MapMessage: für Namen-Werte-Paare primitiver Datentypen ObjectMessage: zum Übermitteln eines serialisierbaren Objektes BytesMessage: liefert einen beschreibbaren Byte-Stream StreamMessage: für einen Stream primitiver Datentypen SpyMessage (JBoss-spezifisch): Nachricht ohne Inhalt ("Ping") 16 Header Properties Body Java Message Service Für Versand und Empfang existiert Interface-Hierarchie javax.jms.ConnectionFactory: Baut Verbindungen zwischen JMS Client und JMS Provider auf; wird von Java EE bereitgestellt javax.jms.Connection: Kapselt Verbindungen javax.jms.Session: Sitzung, innerhalb der Nachrichten gesendet und empfangen werden können javax.jms.Destination: Ziel einer Nachricht (z.B. Queue oder Topic), muss im Java EE-Kontext bereitliegen javax.jms.MessageProducer/MessageConsumer: Sender/Empfänger einer Nachricht, kommunizieren mit Destination Destinations können auch temporär sein 17 Java Message Service ConnectionFactory erzeugt Connection erzeugt MessageProducer erzeugt Session erzeugt MessageConsumer erzeugt sendet an Nachricht Destination 18 erhält von Java Message Service Grundsätzlicher Ablauf der Kommunikation 1. ConnectionFactory im JNDI-Kontext auffinden und referenzieren 2. mit Hilfe der Factory Connection erzeugen 3. Destination auffinden 4. Session erzeugen 5. Verbindung starten 6. MessageProducer und/oder MessageConsumer erzeugen 7. Nachrichten austauschen 8. Verbindung schließen 19 Java Message Service Implementierung: Grundlegendes Gerüst für JMS-Clients try { // alternativ: Properties in Datei jndi.properties hinterlegen Properties p = System.getProperties(); p.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); p.setProperty("java.naming.provider.url", "localhost:1099"); InitialContext ic = new InitialContext(); // ... } catch (JMSException e) {e.printStackTrace();} catch (NamingException e) {e.printStackTrace();} 20 Java Message Service Nachricht an Queue senden ConnectionFactory cf = (ConnectionFactory) ic.lookup("ConnectionFactory"); Connection con = cf.createConnection(); Session session = con.createSession(false,Session.AUTO_ACKNOWLEDGE); con.start(); Queue queue = (Queue) ic.lookup("queue/testQueue"); MessageProducer sender = session.createProducer(queue); MapMessage message = session.createMapMessage(); // message mit Inhalt füllen sender.send(message); con.close(); 21 Auslieferung an Message Server als Transaktion? Java Message Service Beispiel: Map-Nachricht mit Inhalt füllen message.setInt("anz",a.length); for(int i=0; i<a.length;i++) message.setDouble("arg"+i,a[i]); // Referenz auf temporäre Queue für die Antwort mitschicken message.setJMSReplyTo(temporaryQueue); 22 Java Message Service Nachricht an Topic senden ConnectionFactory cf = (ConnectionFactory) ic.lookup("ConnectionFactory"); Connection con = cf.createConnection(); Session sesssion = con.createSession(false,Session.AUTO_ACKNOWLEDGE); con.start(); Topic topic = (Topic) ic.lookup("topic/testTopic"); MessageProducer publisher = session.createProducer(topic); MapMessage message = session.createMapMessage(); // Nachricht mit Inhalt füllen publisher.send(message); con.close(); 23 Java Message Service Asynchrones Empfangen mit Message-Driven Bean Implementieren des Interfaces javax.jms.MessageListener und der Methode public void onMessage Registrieren des MessageListeners public class ExampleListener implements javax.jms.MessageListener { public void onMessage(Message message) { // message verarbeiten } } 24 Java Message Service Nachricht aus Queue synchron empfangen InitialContext ic = new InitialContext(); ConnectionFactory cf = (ConnectionFactory) ic.lookup("ConnectionFactory"); Connection con = cf.createConnection(); Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = (Queue) ctx.lookup("queue/testQueue"); MessageConsumer consumer = session.createConsumer(queue); con.start(); StreamMessage msg = (StreamMessage) consumer.receive(10000); // ggf. weitere Nachrichten empfangen qc.close(); bei Topic analog 25 Java Message Service Message-Selektoren Nachrichten können anhand ihrer Properties selektiert werden, z.B.: Sender: message.setStringProperty("Ergebnistyp","Median"); Empfänger: consumer = session.createConsumer("Ergebnistyp=´Median´"); msg = (StreamMessage) consumer.receive(0); “nicht passende” Nachrichten werden ignoriert 26 Message-Driven Beans Message-Driven Beans stellen JMS-Nachrichten-Empfänger dar können Queues oder Topics abfragen kapseln das Empfangen einer Nachricht nur Verarbeitung muss implementiert werden Interface MessageListener muss implementiert werden Verarbeitung über onMessage()-Methode 27 Message-Driven Beans Beantworten von Nachrichten MDBs schicken nicht automatisch eine Antwort dies muss ggf. explizit erfolgen (wie oben erläutert) hierzu wird oft eine temporäre Queue verwendet, die als Property der Nachricht mitgeschickt wurde Sender: Queue temp = session.createTemporaryQueue(); // … message.setJMSReplyTo(temp); MDB: Destination replyTo = message.getJMSReplyTo(); MessageProducer producer = session.getProducer(replyTo); // Antwortnachricht erstellen und verschicken … 28 Message-Driven Beans Verwendung von Annotationen: import import import import javax.ejb.ActivationConfigProperty; javax.ejb.Messagedriven; javax.jms.Message; javax.jms.MessageListener; @MessageDriven(activationConfig={ @ActivationConfigProperty(propertyName=“destinationType”, propertyValue=“javax.jms.Topic”), @ActivationConfigProperty(propertyName=“destination”, propertyValue=“queue/meinTopic”), @ActivationConfigProperty(propertyName=“messageSelector”, propertyValue=“Nachrichtentyp=´Storno´”), @ActivationConfigProperty(propertyName=“subscriptionDurability”, propertyValue=“Durable”)}) public class MeinMDB implements MessageListener{ public void onMessage(Message m){…} } 29 Literatur Roman, E., et. al.: Mastering Enterprise JavaBeans 3.0, Wiley 2006 http://www.theserverside.com/tt/books/wiley/masteringEJB3/index.tss (vollständiges Buch kostenlos herunterladbar) W. Eberling, J. Lessner: Enterprise JavaBeans 3, Hanser, 2007 http://www.hanser.de/3-446-41085-6 (JMS s. Leseprobe 1, Beispiele) D. Abts: Masterkurs Client/Server-Programmierung mit Java, vieweg http://www.vieweg.de/index.php;do=show/site=v/book_id=10777/sid=c099bd7834a500ad479cc8c845df07a0 (Beispiele s. ONLINEPLUS) Java Message Service Specification 1.1 http://java.sun.com/products/jms/docs.html 30