JMS-Anbindung

Diese Methode ist veraltet (deprecated). Bitte benutzen Sie stattdessen die neue Methode.

Diese Anleitung erkärt, wie man ein JMS-System über den Message Service an Lobster_data anbindet. Hier wird die Anbindung auf Basis von Apache ActiveMQ beschrieben, es kann aber prinzipiell jedes JMS-konforme System verwendet werden.

Hinweis: Diese Messages sind nicht zu verwechseln mit dem internen Nachrichtentyp Message in Lobster_data. Das Prinzip ist hierbei zwar ähnlich, es handelt sich bei letzterem aber um eine internes und proprietäres Nachrichtenformat.

images/download/attachments/36578864/JMS_1_ActiveMQ_Web_interface-version-1-modificationdate-1563932526000-api-v2.png

Wichtiger Hinweis: Hierzu muss in ./etc/factory.xml und ./etc/message.xml statt der Klasse com.ebd.hub.services.message.MessageService die Klasse com.ebd.hub.services.message.JMSMessageService eingetragen werden!

In ./etc/message.xml muss die Anbindung an das JMS-System erstmal grundsätzlich konfiguriert werden. Die Jar-Dateien Ihres JMS-Providers kopieren Sie bitte in ./extlib (in unserem Fall activemq-all-5.10.0.jar). Für jede JMS Queue bzw. Topic müssen zusätzliche Einträge in der Datei ./etc/message.xml vorgenommen werden. In unserem Beispiel empfangen wir JMS-Nachrichten von der JMS Queue JMSReceiveQueue und senden Nachrichten auf die JMS Queue JMSSendQueue. In jedem Fall werden nur Byte- bzw. Text-Messages unterstützt.

Bevor Sie dieses Tutorial durcharbeiten, müssen Sie lokal einen Apache ActiveMQ Server laufen und dort beide oben genannten Queues eigerichtet haben (siehe http://activemq.apache.org/). Stellen Sie zudem manuell zwei Testnachrichten in die Queue JMSReceiveQueue. Für dieses Tutorial wurde ActiveMQ 5.10.0 Release verwendet.

Eingehende JMS-Nachrichten empfangen

Eingehende JMS Byte- oder Text-Messages müssen beim Empfang in eine für Lobster_data-konforme Nachricht umgewandelt werden. Diese umgewandelte Nachricht muss dann einem Profil mit Eingangsagent Message zugeführt werden. Wir benötigen eine Eintrag, um den allgemeinen Subscriber JMS2DW bekannt zu machen.

<!-- jms2message bridge/support -->
<Call name="setJMSManager"><Arg>
<New class="com.ebd.hub.services.message.jms.JMSManager">
<Arg>
<New class="com.ebd.hub.services.message.jms.JMSMapper">
<!-- Daten von JMS an DW durchreichen; kann beliebig oft wiederholt werden (für weitere Queues/Topics) -->
<Call name="addJMSMap">
<Arg>
<New class="com.ebd.hub.services.message.jms.JMSMap">
<!-- Map JMS Context/Queue auf Message-Service Context/Queue -->
<Arg>ConnectionFactory</Arg>
<Arg>JMSReceiveQueue</Arg>
<!-- hier steht dann JMS2DW dahinter, siehe unter "addConsumerQueue" -->
<Arg>jms</Arg>
<Arg>in</Arg>
<!-- Nächster Wert = false: es wird eine JMS Queue verwendet, ansonsten bei true: JMS Topic -->
<Arg type="boolean">false</Arg>
</New>
</Arg>
</Call>
<!-- Daten an JMS weiter durchreichen, siehe nächstes Kapitel, hat hier noch keine Bedeutung -->
<Call name="addMessageMap">
<Arg>
<New class="com.ebd.hub.services.message.jms.JMSMap">
<!-- Map JMS Context/Queue auf Message-Service Context/Queue -->
<Arg>ConnectionFactory</Arg>
<Arg>JMSSendQueue</Arg>
<!-- hier steht dann DW2JMS dahinter, siehe nächstes Kapitel -->
<Arg>DW2jms</Arg>
<Arg>DW2out</Arg>
<!-- Nächster Wert = false: es wird eine JMS Queue verwendet, ansonsten bei true: JMS Topic -->
<Arg type="boolean">false</Arg>
</New>
</Arg>
</Call>
</New>
</Arg>
<!-- Allgemeine Einstellungen, um das JMS-System zu erreichen -->
<!-- Mit "addJNDIProperty" kann jedes Property (key, value) an die Connection Factory mitgegeben werden -->
<Call name="addJNDIProperty">
<Arg>java.naming.factory.initial</Arg>
<Arg>org.apache.activemq.jndi.ActiveMQInitialContextFactory</Arg>
</Call>
<Call name="addJNDIProperty">
<Arg>java.naming.provider.url</Arg>
<Arg>tcp://localhost:61616</Arg>
</Call>
<!-- If set to true, JMS Header Properties are added as well -->
<Set name="addJmsPropertiesOfMessage">true</Set>
</New>
</Arg>
</Call>
<Call name="addConsumerQueue">
<Arg>
<New class="com.ebd.hub.services.message.ConsumerQueue">
<Arg>jms</Arg>
<Arg>in</Arg>
<!-- Klasse und Methode, die gerufen werden soll -->
<Call name="addConsumer">
<Arg>com.ebd.hub.datawizard.plugin.JMS2DW</Arg>
<Arg>consume</Arg>
</Call>
</New>
</Arg>
</Call>

Jetzt muss nur noch eine Konfigurationsdatei ./conf/jms2dw.properties erstellt werden. Der Inhalt muss <Kontext>.<Queue>=<Profilename> enthalten, damit das richtige Profil angesprochen wird. JMS Header Properties werden als Variable MSG_CALL_<Name-der-Property-in-Grossbuchstaben> übergeben, wenn addJmsPropertiesOfMessage auf true steht.

Beispiel:

jms.in=ProfileA


Zusammenfassung


Durch unsere Konfiguration in message.xml und ./conf/jms2dw.properties werden die empfangenen Daten der JMS Queue JMSReceiveQueue intern auf jms:in umgelenkt und als Lobster_data-Message an das Profil ProfileA per übergeben. Für ProfileA ist dies dann ein Aufruf, wie wenn es durch ein anderes Profil per Message gerufen worden wäre. Die Message ist immer vom Typ persistent mit einer Vorhaltezeit von 24h.

Abrufendes Profil

images/download/attachments/36578864/JMS_3-version-1-modificationdate-1563932526000-api-v2.png

Erstellen Sie ein neues Profil, mit einem Eingangsagenten des Typs Message, wie im obigen Screenshot und stellen Sie sicher, dass es aktiv ist. Spätestens beim nächsten Neustart des Integration Servers werden nun mit dem Profil alle Nachrichten von der Queue JMSReceiveQueue abgeholt.

Verbindungsfehler

Sollten die Nachrichten nicht vom ActiveMQ-Server abgeholt worden sein, dann könnte das an einem Verbindungsfehler liegen. Überprüfen können Sie das in der Datei ./logsservices/error.log. Dort könnten Sie im Fehlerfall zum Beispiel folgenden Ausschnitt vorfinden. Einen Fehler dieser Art würden Sie z. B. bekommen, wenn der Apache ActiveMQ-Server überhaupt nicht läuft.

14:27:42 SYSTEM:MESSAGE:JMSLISTENER Exception in listener for dynamicQueues/JMSReceiveQueue, restarting in 60s
javax.jms.JMSException: Could not connect to broker URL: tcp://localhost:61616. Reason: java.net.ConnectException: Connection refused: connect
at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:35)
at org.apache.activemq.ActiveMQConnectionFactory.createActiveMQConnection(ActiveMQConnectionFactory.java:283)
at org.apache.activemq.ActiveMQConnectionFactory.createActiveMQConnection(ActiveMQConnectionFactory.java:227)
at org.apache.activemq.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:175)
at com.ebd.hub.services.message.jms.JMSListener.init(JMSListener.java:140)
at com.ebd.hub.services.message.jms.JMSListener.run(JMSListener.java:73)
Caused by: java.net.ConnectException: Connection refused: connect

Hier ein weiterer Fehler.

javax.naming.NameNotFoundException: JMSReceiveQueue
at org.apache.activemq.jndi.ReadOnlyContext.lookup(ReadOnlyContext.java:225)
at javax.naming.InitialContext.lookup(InitialContext.java:411)
at com.ebd.hub.services.message.jms.JMSListener.handleQueue(JMSListener.java:166)
at com.ebd.hub.services.message.jms.JMSListener.init(JMSListener.java:158)
at com.ebd.hub.services.message.jms.JMSListener.run(JMSListener.java:73)

Wichtiger Hinweis: Diesen Fehlerfall wollen wir gleich für einen Hinweis nutzen. In neueren ActiveMQ-Versionen muss das Präfix dynamicQueues/ (bzw. dynamicTopics/) dem Queue-Namen vorangestellt werden, also z. B. dynamicQueues/JMSSendQueue und dynamicQueues/JMSSendQueue. Bitte passen Sie diese Namen in der Konfigurationsdatei ./etc/message.xml an. Nun sollte der Empfang, sofern alle anderen Einstellungen korrekt sind, auch funktionieren. Zum Verständnis können Sie sich hier das Thema "dynamic contexts" in ActiveMQ ansehen.

Erfolgreicher Verbindungsaufbau

images/download/attachments/36578864/1-version-1-modificationdate-1563932526000-api-v2.png

War der Verbindungsaufbau erfolgreich und wurden die Nachrichten abgeholt, dann werden Sie auf der Web-Oberfläche des ActiveMQ-Servers obiges Bild sehen.

Daten an JMS übergeben

Um Lobster_data-Messages an JMS zu übergeben, muss der Abschnitt addMessageMap enthalten sein (haben Sie bereits im Kapitel davor eingetragen).
Weiterhin müssen wir zwei weitere Consumer Queues in der Konfigurationsdatei ./etc/message.xml eintragen.

<Call name="addConsumerQueue">
<Arg>
<New class="com.ebd.hub.services.message.ConsumerQueue">
<Arg>jms</Arg>
<Arg>out</Arg>
<!-- Klasse und Methode, die gerufen werden soll -->
<Call name="addConsumer">
<Arg>com.ebd.hub.datawizard.plugin.DW2JMS</Arg>
<Arg>consume</Arg>
</Call>
</New>
</Arg>
</Call>
 
 
<Call name="addConsumerQueue">
<!-- make context and queue visible -->
<Arg>
<New class="com.ebd.hub.services.message.ConsumerQueue">
<Arg>DW2jms</Arg>
<Arg>DW2out</Arg>
</New>
</Arg>
</Call>

Lobster_data-Messages mit Kontext jms und Queue-Namen out sollen an die JMS Queue (oder Topic) JMSSendQueue überführt werden. Mittels Konfigurationsdatei ./conf/dw2jms.properties kann bestimmt werden, ob die JMS-Nachricht eine Byte- oder Text-Nachricht wird. Der Inhalt muss <Kontext>.<Queue>.type=byte oder string enthalten, damit das richtige Zielformat angesprochen wird. Beim Type string muss ebenfalls das Encoding bekannt sein, hierzu bitte einen Eintrag in der Form <Kontext>.<Queue>.encoding=8859_1 anfügen. Alternativ können Sie auch eine Variable JMS_MSG_TYPE und JMS_MSG_ENCODING im Profil anlegen und dort die Werte eintragen. Sind diese Variablen definiert, wird der Inhalt der Konfigurationsdatei für dieses Profil ignoriert. Alle definierte Variablen, die mit JMS_ beginnen, werden als JMS Header Property gesetzt, jedoch ohne JMS_ als Präfix. Die Variable JMS_InvoiceNo wird also als Header Property InvoiceNo gesetzt.

Beispiel

jms.out.type=string
jms.out.encoding=8859_1

Erklärung zum Ablauf anhand des Kontext jms und Queue out


Im Lobster_data erstellen Sie ein Profil mit Antwortweg vom Typ Message (1). Der Inhalt (3) ist z. B. CSV mit Encoding 8859_1. Der verwendete Kontext ist jms und der Queue-Name ist out (2). Hinweis: Der Profil-Name wird zwar ignoriert, da es ein Pflichtfeld ist, muss hier aber ein beliebiger Wert eingetragen werden.

Die Lobster_data-Message wird zunächst vom Subscriber com.ebd.hub.datawizard.plugin.DW2JMS unter jms:out entgegengenommen und zu einer JMS -Text-Message umgebaut und dann auf den Kontext DW2jms mit Queue DW2out gelegt (also gleiche Werte mit Präfix DW2). Dort greift unser Eintrag aus addMessageMap und wird in die JMS-Queue (oder Topic) JMSSendQueue eingestellt.

images/download/attachments/36578864/JMS_4-version-1-modificationdate-1563932526000-api-v2.png images/download/attachments/36578864/JMS_5-version-1-modificationdate-1563932526000-api-v2.png

Versenden Sie eine kleine Textdatei als Testnachricht über einen Ausgangsweg, wie oben im Screenshot dargestellt.

images/download/attachments/36578864/2-version-1-modificationdate-1563932526000-api-v2.png

In Ihrer Web-Oberfläche des ActiveMQ-Servers sollten Sie dann obiges Bild sehen.

Massendatenverarbeitung

Falls eine große Anzahl von Nachrichten über eine Queue verarbeitet werden soll, kann die Queue auf Dateiverarbeitung umgestellt werden. Es ist ein Patch verfügbar, der eine zusätzliche Option in der Queuedefinition ermöglicht. Bei Interesse wenden Sie sich bitte an unsere Mitarbeiter im Support.

<!-- jms2message bridge/support -->
<Call name="setJMSManager"><Arg>
<New class="com.ebd.hub.services.message.jms.JMSManager">
<Arg>
<New class="com.ebd.hub.services.message.jms.JMSMapper">
<!-- Daten von JMS an Lobster_data durchreichen; kann beliebig oft wiederholt werden (für weitere Queues/Topics) -->
<Call name="addJMSMap">
<Arg>
<New class="com.ebd.hub.services.message.jms.JMSMap">
<!-- Map JMS Context/Queue auf Message-Service Context/Queue -->
<Arg>ConnectionFactory</Arg>
<Arg>JMSReceiveQueue</Arg>
<!-- hier steht dann JMS2DW dahinter, siehe unter "addConsumerQueue" -->
<Arg>jms</Arg>
<Arg>in</Arg>
<!-- Nächster Wert = false: es wird eine JMS Queue verwendet, ansonsten bei true: JMS Topic -->
<Arg type="boolean">false</Arg>
<Arg type="boolean">true</Arg><!-- write payload to file -->
...

Wird dieser Parameter auf true gesetzt, so werden die Messages nicht mehr über den internen Messagebus weiterverarbeitet, sondern im Dateisystem unter Ordner ./jms/in/<Queuename> als *.dat ausgegeben. Siehe folgender Screenshot.

images/download/attachments/36578864/image2017-8-14_12_57_23-version-1-modificationdate-1563932526000-api-v2.png

Profil für Massendatenverarbeitung

Die dort ausgegebenen Dateien können dann über ein Profil mit zeitgesteuertem Eingangsagenten mit Datenquelle Datei abgearbeitet werden.

1. Eingangsagent von Message auf einen zeitgesteuerten Eingangsagenten mit Datenquelle Datei umstellen mit

  • Dateimuster *.dat (1)

  • Verzeichnis ./jms/in/<Queuename> (3)

  • Paralellverarbeitung auslassen, falls die Profile die Daten seriell nach dem FIFO-Prinzip abarbeiten sollen

  • Sortierung umstellen auf Dateiname (2)

images/download/attachments/36578864/JMS_6-version-1-modificationdate-1563932526000-api-v2.png

2. Nun stellen wir noch die Prüfzeiten ein.

images/download/attachments/36578864/JMS_7-version-1-modificationdate-1563932526000-api-v2.png

3. Die Option Profil darf nur in einer Instanz laufen darf nicht gesetzt sein.

Schreibverzögerung

Es gibt eine JVM-Umgebungsvariable hub.jms.writeDelay, die es ermöglicht die JMS-Verarbeitung mit einem Intervall zu verzögern. Siehe auch System-Properties des Integration Servers.

Falls die Serverperformance nicht ausreichend ist, um die vom externen System abgeholten Nachrichten zu verarbeiten und der Ordner mit den Payloadmessages voll läuft, kann diese Variable zur Laufzeit über die Admin-Konsole gesetzt bzw. angepasst werden.

System-Variablen

Die System-Variablen JMS_JMSCorrelationID, JMS_JMSPriority, JMS_JMSExpiration können verwendet werden, um die entsprechenden JMS-Header JMSCorrelationID, JMSPriority (0-9) und JMSExpiration (Timstamp in ms) zu setzen.