Datenbanken, Java - von am Sunday, September 28, 2008 16:36 - 0 Kommentare

JMS mit Oracle Advanced Queueing

1. Einleitung

JMS ist der Standard zum Austausch von Nachrichten zwischen unterschiedlichen (entfernten) Systemen in Java-Umfeld. Es ermöglicht eine lose Kopplung und ist damit Grundlage für eine serviceorientierte Architektur.

In jeder Installation einer Oracle-Datenbank ist seit der Version 9i, Release 2, ein so genanntes “Advanced Queueing” mit installiert. Mit “Advanced Queueing” (kurz AQ) wird das interne Messaging Vefahren von auf der Oracle-Datenbankebene bezeichnet. Oracle betreibt dafür intern einen sog. ESB (Enterprise Service Bus).

Anhand eines Praxisbeispiels wird der Einsatz eines Oracle Datenbanksystems 10g als Oracle JMS Provider gezeigt, so dass ohne weitere Systeme ein tragfähiges, hoch performantes JMS-System aufgebaut werden kann.

2. Einführung in JMS

JMS (Java Message System) stellt eine komfortable Möglichkeit dar, ein sog. “Fire-And-Forget-Konzept” umzusetzen, d.h. eine asynchrone Abarbeitung von Befehlen wird möglich. Dadurch kann beispielsweise erreicht werden, dass langlaufende Prozesse im Hintergrund ablaufen und die GUI für den Benutzer sofort wieder zur Verfügung steht.

Als Beispiel sei hier eine Bestellung genannt. Der Kunde erhält sofort eine Bestätigung, während der komplexe Prozess der Bestellabwicklung parallel und asynchron angestoßen wird.

Des weiteren wird dadurch die lose Kopplung von Systemen möglich. Während das GUI des Bestellsystems beispielsweise in einer “Tomcat-Umgebung” läuft, kann das Bestellsystem auf einem anderen weit entfernten System laufen.

Asynchrone Prozesse

Mittels JMS können nun Nachrichten über einen sog. “JMS-Provider” (im Prinzip eine Art Server) ausgetauscht werden. Dabei schickt das aufrufende System seine Nachricht an diesen Server. Das aufrufende System muss sich dabei nicht darum kümmern, wie und welches System die Nachricht wieder abholt. Für das sendende System ist die Prozessbearbeitung durch Absenden der Nachricht damit beendet. Das sendene System muss nur den “JMS-Provider” kennen und wissen wie es die Nachricht an diesen versendet.

Das empfangene System wiederum verbindet sich gegen den “JMS-Provider” und konsumiert die Nachricht. Auch hier muss das empfangende System nicht wissen, wer der Absender der Nachricht war und wie er dieser erreichen kann. Er muss lediglich die Adresse des “JMS Providers” kennen und wissen wie es sich gegen diesen verbindet.

Das empfangene oder das absendene System müssen dabei jeweils nicht gleichzeitig verfügbar sein. Der “JMS-Provider” sorgt für die Persistierung der entsprechenden Nachricht. Dabei ist einstellbar, wie lange dieser eine Nachricht verfügbar halten soll.

Grundsätzlich gibt es bei JMS zwei Prinzipen der Nachrichtenübermittlung. Zum einen das sog. “Punkt-zu-Punkt (Point-To-Point) Verfahren“, bei dem es einen Sender und einen Empfänger gibt.

Oracle JMS - Point-to-Point-Modell

Bei diesem Verfahren erhält derjenige Empfänger die Nachricht, der dieser als erstes anfordert. Danach wird die Nachricht innerhalb der Queue (also auf dem JMS-Provider) gelöscht. Eine weitere Zustellung an einen weiteren Empfänger derselben Nachricht ist dann nicht mehr möglich.

Bei dem “Publish-Subscribe-Modell” kann eine gesendete Nachricht mehrere Empfänger erreichen. Nachrichten werden dabei an ein sog. “Topic” versendet. Dabei können nur vorher an das Topic registrierte Empfänger die Nachricht auch erhalten. Sind diese nicht mit dem Topic verbunden, so erhalten diese die Nachricht nicht. Das interessante dabei ist, dass die Nachricht dabei solange im Topic (Queue / JMS-Provider) bestehen bleibt bis entweder das eingestellte Timeout erreicht wurde, oder aber die Nachricht an alle zuvor registrierten Empfänger verteilt wurde.

Oracle JMS - Publish-Subscriber-Modell

In dem hier vorliegenden Praxisbeispiel wird nur das „Point-To-Point“ Verfahren angewendet.

3. Die Oracle-Datenbank als JMS-Provider

In jeder Installation einer Oracle-Datenbank ist seit der Version 9i, Release 2, ein so genanntes “Advanced Queueing” mit installiert.

Oracle JMS - Advanced Queueing

Das interessante dabei ist, dass Oracle dabei intern alle Nachrichten mit etablierten Standardverfahren über Datenbanktabellen und Datenbankusern abwickelt. Dies bedeutet, dass es sich hierbei um ein robustes, stabiles und seit Jahren etabliertes Verfahren handelt. Des weiteren hat man, sofern man eine Oracle Datenbank betreibt, immer auch gleich einen JMS-Provider zur Hand, so dass man kein weiteres System aufsetzen muss um JMS nutzen zu können.

Grundsätzlich gibt es zwei Verfahren um über das Advanced Queueing von Oracle Nachrichten zu versenden. Zum einen können die Nachrichten über sog. “PL/SQL-Packages” versendet werden, welches jedoch kein JMS darstellt. Eine andere Möglichkeit ist der Versand per Nachrichten über den sog. “JMS-Adapter“. In diesem Artikel wird nur der Versand per JMS aus Java-Applikationen ohne “Enterprise Java Beans (EJB)” behandelt.

4. Use-Case des Praxissystems

Von einem Vorsystem werden Daten per FTP im XML-Format auf den Datenbankserver geliefert. Diese werden gemäß einer vorgegebenen Fachlogik verarbeitet. Sowohl die Verarbeitung im Ladeprozess als auch die Änderung von Daten in einer vom Benutzer zu bedienenden Anwendung führen zu Trigger-Auslösungen auf der Datenbank, welche dann eine Nachricht vom Typ XMLType (ein PL/SQL Type) in eine Queue stellen. Diese Information wird mit Hilfe eines externen Java-Programms entsprechend ausgelesen, bewertet und verschlüsselt als serialisiertes Java-Objekt über eine Queue einem externen Zielsystem zur Verfügung gestellt.

Dies führt zu der folgenden Architektur:

Oracle JMS - Zielarchitektur

5. Einrichtung der Datenbank zur Nutzung als JMS-Provider

5.1 Datenbank-User-Anlage

Da alle notwendigen Packages zum Betrieb der Oracle Datenbank als JMS-Provider bereits in der Grundinstallation einer Oracle Datenbank installiert sind, werden nur noch die notwendigen User und die entsprechenden Queues benötigt. Wie bereits beschrieben, so erfolgt die Verbindung zum Oracle-JMS-Providers mittels normaler Datenbankbenutzer. Oracle benötigt hierfür zwei dedizierte Datenbank-Benutzer. Einen für die Verwaltung der Queues und ihrer darunter liegenden Tabellen und einen für die eigentliche Nutzung des JMS-Providers, d.h. das Versenden und Empfangen der Nachrichten. Wir legen nun einen User “aqadm” als Verwalter und einen User “aquser” als Nutzer an und statten diese User mit den notwendigen Rechten aus:

  1. CONNECT system/<YOUR_PASSWORD>
  2.  
  3. -- Die Rolle für den AQ Admin User
  4. CREATE ROLE atc_aq_adm_role;
  5. GRANT CONNECT, RESOURCE, aq_administrator_role, CREATE ANY PROCEDURE TO atc_aq_adm_role;
  6.  
  7. -- Die Rolle für den normalen AQ User, der die Nachrichten in die Queue stellt
  8. CREATE ROLE atc_aq_user_role;
  9. GRANT CREATE SESSION, aq_user_role TO atc_aq_user_role;
  10.  
  11. -- Berechtigung zum Stellen einer Nachricht in die Queue
  12. EXEC dbms_aqadm.grant_system_privilege(privilege => 'ENQUEUE_ANY', grantee => 'atc_aq_user_role', admin_option => TRUE);
  13.  
  14. -- Berechtigung zum sog. "Dequeuen", d.h. nach Erhalt der Nachricht wird diese aus der Queue gelöscht
  15. EXEC dbms_aqadm.grant_system_privilege(privilege => 'DEQUEUE_ANY', grantee => 'atc_aq_user_role', admin_option => TRUE);
  16.  
  17. -- Der Admin-User für die Queues
  18. CREATE USER atc_aqadm IDENTIFIED BY atc_aqadm;
  19. GRANT atc_aq_adm_role TO atc_aqadm;
  20. GRANT EXECUTE ON dbms_aq TO atc_aqadm;
  21.  
  22. -- Der Nutzer der Queues
  23. CREATE USER atc_aquser IDENTIFIED BY atc_aquser;
  24. GRANT CONNECT, RESOURCE, CREATE SESSION TO atc_aquser;
  25. ALTER USER atc_aquser;
  26. GRANT atc_aq_user_role TO atc_aquser;
  27. GRANT EXECUTE ON dbms_aq TO atc_aquser;

5.2 Anlegen der Queue

Zum Versenden der Nachrichten werden nun die entsprechenden Queues definiert. Diese beiden Queues unterscheiden sich durch die Angabe ihrer Nutzdaten (Payload). Für die Java-Objekte wird als Nutzart “AQ$_JMS_OBJECT_MESSAGE” und für die Nachricht mit dem PL/SQL Datentyp aus den PL/SQL-Routinen die Nutzart “SYS.XMLTYPE” gewählt.

Da wie zuvor beschrieben die gesamte Persistierung der Nachrichten über Tabellen geschieht, wird zunächst mittels “DBMS_AQADM.CREATE_QUEUE_TABLE” eine entsprechende Tabelle erzeugt, die dann für das Halten der entsprechenden Nutzdaten vorbereitet wird. Danach wird die entsprechende Queue mit dem gewünschten Namen erzeugt (z. Bsp. “DEV_ATCBPPMESSAGEQ“) und diese dann auf die entsprechend zuvor angelegte Tabelle gemappt. Danach wird die eigentliche Queue noch gestartet. Wird die entsprechende Queue nicht gestartet, so kann an diese keine Nachricht gesendet werden bzw. keine Nachricht von dieser abgeholt werden.

Der entsprechende Name der Queue ist wichtig, da hierüber aus den jeweiligen Applikationen der Zugriff auf diese erfolgt.

Nachfolgend die Anlage der Queue zur Aufnahme von serialisierten Java-Objekten:

  1. EXEC DBMS_AQADM.CREATE_QUEUE_TABLE(QUEUE_TABLE => 'DEV_ATCBPPMESSAGET',
  2. QUEUE_PAYLOAD_TYPE => 'SYS.AQ$_JMS_OBJECT_MESSAGE', MULTIPLE_CONSUMERS => FALSE);
  3.  
  4. EXEC DBMS_AQADM.CREATE_QUEUE(QUEUE_NAME => 'DEV_ATCBPPMESSAGEQ', QUEUE_TABLE => 'DEV_ATCBPPMESSAGET');
  5.  
  6. EXEC DBMS_AQADM.START_QUEUE(QUEUE_NAME => 'DEV_ATCBPPMESSAGEQ');
  7.  
  8. -- Grant the aq_user the needed rights to inserts and modify  -- lobs (this is needed only for JMS-Queues)
  9. GRANT SELECT, DELETE, INSERT, UPDATE ON DEV_ATCBPPMESSAGET TO atc_aq_user_role;

Als Nachrichten-Typ gibt es im JMS-Umfeld noch unter anderem die folgenden wichtigen Typen:

  • AQ$_JMS_TEXT_MESSAGE – für den Versand von reinen Strings
  • AQ$_JMS_MAP_MESSAGE – für den Versand von Maps
  • AQ$_JMS_BYTES_MESSAGE – für den Versand von Bytecode

Nachfogend nun die Anlage der Queue für den Versand einer PL/SQL-Nachricht aus einer PL/SQL-Routine heraus:

  1. EXEC DBMS_AQADM.CREATE_QUEUE_TABLE(QUEUE_TABLE => 'DEV_ATCBPPINT', QUEUE_PAYLOAD_TYPE => 'SYS.XMLType', MULTIPLE_CONSUMERS => FALSE);
  2.  
  3. EXEC DBMS_AQADM.CREATE_QUEUE(QUEUE_NAME => 'DEV_ATCBPPINT', QUEUE_TABLE => 'DEV_ATCBPPINT');
  4.  
  5. EXEC DBMS_AQADM.START_QUEUE(QUEUE_NAME => 'DEV_ATCBPPINT');

Für eine sog. „PL/SQL-Queue“ kann jeder beliebige Datentyp angegeben werden, der auch im PL/SQL-Umfeld möglich ist (beispielsweise VARCHAR2, NUMBER, etc.).

6. Nutzung der Datenbank als JMS-Provider zum Senden und Empfangen von Nachrichten

6.1 Versand von Nachrichten in eine PL/SQL-Queue aus PL/SQL

Gemäß des oben genannten User-Cases wird eine Nachricht nach der Modifikation eines Datensatzes in eine PL/SQL-Queue gesendet. Zunächst wird das als VARCHAR empfangene XML in ein XMLType gewandelt. Danach wird dieser XMLType in die zuvor definierte Queue abgelegt:

  1. [...] ENQUEUE_OPTIONS DBMS_AQ.ENQUEUE_OPTIONS_T;
  2. MESSAGE_PROPERTIES DBMS_AQ.MESSAGE_PROPERTIES_T;
  3. MESSAGE_HANDLE RAW(16); [...] v_XML_T := XMLType.createXML(v_XML);
  4. DBMS_AQ.ENQUEUE(    queue_name=>'ATC_AQADM.DEV_ATCBPPINT', enqueue_options=>ENQUEUE_OPTIONS,    message_properties=> MESSAGE_PROPERTIES,    PAYLOAD=>v_XML_T,   MSGID=>MESSAGE_HANDLE);

6.2 Abholen der Nachricht aus der PL/SQL-Queue mittels Java

Die Nachricht wird nun wieder mittels eines Java-Programmes aus einer PL/SQL-Queue abgeholt. Da der Zugriff auf eine Queue wie ein normaler Datenbank-Connect über den JDBC-Thin-Driver erfolgt, reicht es aus, sich mit dem zuvor angelegten User atc_aquser gegen die entsprechenden Datenbank zu konnektieren und die Nachricht abzuholen. Hierfür werden nur die folgenden externen JARs benötigt:

Für die Verwendung des Oracle XMLTypes in einem Java-Programm wird zusätzlich noch die Java-Bibliothek xdb.jar benötigt.

Als “connectString” wird ein normaler JDBC-Thin-Connection-String verwendet (z.B. jdbc:oracle:thin:@:1521:ATC).

Nachfolgend der entsprechende Code – stark vereinfacht und ohne Fehlerhandling:

  1. Class.forName("oracle.jdbc.driver.OracleDriver");
  2. conn = DriverManager.getConnection(connectString, userName, userPassword);
  3. Class.forName("oracle.AQ.AQOracleDriver");
  4. AQSession aq_sess = AQDriverManager.createAQSession(conn);
  5. AQQueue aqQueue = aq_sess.getQueue(DB_AQ_ADMIN_NAME, queueName);
  6. AQDequeueOption dqOption = new AQDequeueOption();
  7. dqOption.setNavigationMode (AQDequeueOption.NAVIGATION_NEXT_MESSAGE);
  8. AQMessage msg = ((AQOracleQueue)aqQueue).dequeue(dqOption,XMLType.getORADataFactory());  AQObjectPayload payload = msg.getObjectPayload();
  9. XMLType xmlType = (XMLType) payload.getPayloadData();

6.3 Versand von Nachrichten in eine JMS-Queue

Nachdem die Nachricht durch das Java-Programm abgeholt wurde und fachliche Routinen ausgeführt wurden, erzeugt das Java-Programm ein verschlüsseltes Java-Objekt, welches dann auf eine JMS-Queue gelegt wird, die zuvor – wie oben dargestellt – entsprechend definiert wurde. Auch hier erfolgt der Zugriff auf die Queue mittels einer normalen JDBC-Connection und dem zuvor definierten Usernamen “atc_aquser”. Diese wird im unten stehenden Codebeispiel über den “connectString” bereitgesellt (z.B. jdbc:oracle:thin:@:1521:ATC).

  1. // Übergabe: Ein „Serializable“
  2. JMSQueue jmsQueue = new JMSQueue();
  3. Properties info = new Properties();
  4. info.put(userName, userPassword);
  5.  
  6. // ConnectString : JDBC-ConnectString
  7. QueueConnectionFactory queueConnectionFactory =       AQjmsFactory.getQueueConnectionFactory(connectString, info);
  8. jmsQueue.connection = queueConnectionFactory.createQueueConnection(userName, userPassword);
  9.  
  10. /* If a session is transacted, message acknowledgment is handled automatically by commit and recovery is handled automatically by rollback. In the following code the session is transacted and acknowledged by commit and rollback. */
  11. jmsQueue.session = aq.connection.createQueueSession(true, Session.CLIENT_ACKNOWLEDGE);
  12.  
  13. // Acknowledges by commit and rollback
  14. jmsQueue.connection.start();
  15. Queue queue = ((AQjmsSession)jmsQueue.session).getQueue(DB_AQ_ADMIN_NAME, "DEV_ATCBPPMESSAGEQ");
  16.  
  17. AQjmsObjectMessage objectData = new AQjmsObjectMessage();
  18. objectData.setObject(serializable);
  19. QueueSender queueSender = jmsQueue.session.createSender(queue);
  20. queueSender.send(objectData);
  21.  
  22. jmsQueue.session.commit();
  23. jmsQueue.session.close();
  24. jmsQueue.connection.close();

6.4 Abholen einer Nachricht aus einer JMS-Queue

Das externe Drittsystem holt die Daten aus der JMS-Queue dann entsprechend wie nachfolgende dargestellt ab. Auch hier wird wieder über einen normalen JDBC-Connect gearbeitet.

  1. JMSQueue jmsQueue = new JMSQueue();
  2.  
  3. Properties info = new Properties();
  4. info.put(userName, userPassword);
  5.  
  6. QueueConnectionFactory queueConnectionFactory = AQjmsFactory.getQueueConnectionFactory(connectString, info);
  7. jmsQueue.connection = queueConnectionFactory.createQueueConnection(userName, userPassword);
  8.  
  9. jmsQueue.session = jmsQueue.connection.createQueueSession(true, Session.CLIENT_ACKNOWLEDGE);  // Session is transacted. Acknowledges by commit and rollback
  10.  
  11. jmsQueue.connection.start();
  12.  
  13. Queue queue = ((AQjmsSession) jmsQueue.session).getQueue(DB_AQ_ADMIN_NAME, "DEV_ATCBPPMESSAGEQ“);
  14.  
  15. QueueReceiver queueReceiver = jmsQueue.session.createReceiver(queue);
  16. AQjmsObjectMessage objectMessage = (AQjmsObjectMessage)  queueReceiver.receive(5000);
  17. Serializable serializable = objectMessage.getObject();
  18.  
  19. jmsQueue.session.commit();
  20. jmsQueue.session.close();
  21. jmsQueue.connection.close();

Das abholende System hat danach das empfangende Java-Objekt als ,Serializable‘ entsprechend zur Verfügung.

7. Fazit

Mit Hilfe der Möglichkeiten von “Oracle Advanced Queueing” lässt sich auf einfache Art und Weise ein JMS-Service etablieren. In jedem Umfeld in dem sich eine Oracle Datenbank befindet, ist diese Funktionalität im Prinzip “Out-of-the-Box” vorhanden und einsatzbereit.

Die hier vorliegende Abhandlung zeigt natürlich nur ein kleinen Ausschnitt aus den mächtigen Möglichkeiten, die Oracle in Zusammenhang mit dem Austausch von Nachrichten bietet. Entsprechend komplexer wird die Thematik beispielsweise bei dem sog. “Publish Subscribe Messaging Modell”, bei dem es mehrere Sender und Empfänger geben kann.

Auch die Thematik der Fehlerbehandlung ist selbstverständlich nicht zu vernachlässigen. Oracle bietet hier ein durchdachtes Konzept bei auftretenden Fehlern in der Nachrichtenübertragung. Kombiniert mit dem “Java Exception Handling” ergeben sich etablierte Möglichkeiten der Fehlerbehandlung, bis hin zum Zurückrollen einer Transaktion auf der sendenden Seite. Mit sog. “Message Driven Beans” im Rahmen der “Enterprise Java Beans” stehen dem Entwickler noch weitere komplexe Möglichkeiten zur Verfügung.

Die Erwartungen an das Advanced Queuing wurden voll uns ganz erfüllt. Derzeit arbeitet das System mit ca. 10.000 Nachrichten täglich ohne Probleme.

Für den Einsatz von Oracle als JMS-Provider sprechen folgende Gründe:

  • vollwertiger JMS-Provider
  • Out-Of-The-Box Einsatz
  • Nutzung von etablierten Technologien zur Persistierung, Backup und Recovery
  • einfache Administration mit bewährten Tools (TOAD, SQL*Plus, etc.)
  • keine Adminstration eines weiteren Systems notwendig
  • leichtes Ansprechen aus Drittprogrammen heraus über den Standard-Port 1521, der inhouse in den meisten Firewalls freigegeben ist
  • Zugriff mittels Standard-Treibern (keine weitere Connectivity-Software notwendig)

8. Weiterführende Links:

- Besprechung von JMS auf sun.com.
- Introducing Oracle JMS auf oracle.com.

9. Weiterführende Literatur:

Bücher zu fortgeschrittenen Oracle-Themen finden Sie auf Amazon.de in der Kategorie Oracle, im speziellen empfehlen wir für Oracle Datenbankadministration das umfassende Werk Oracle Database 11g – DBA-Handbuch von Bob Bryla und Kevin Loney.

Autor: Christian Hartmann

Be Sociable, Share!


Kommentare

Kommentieren

Weitere Empfehlungen: