Java EE (Java Platform, Enterprise Edition) ist eine durch Schnittstellen definierte Architektur für Unternehmensanwendungen, bestehend aus verschiedenen Komponenten.
JMS (Java Message Service) stellt einen wichtigen Bestandteil von Java EE dar, um asynchrone Kommunikation (über einen Message Broker) zu ermöglichen, was auch unter dem Begriff MOM (Message Oriented Middleware) bekannt ist.
Die Vorteile von asynchroner Kommunikation per JMS sind:
+ Zeitliche Entkopplung zwischen Beauftragung und Bearbeitung
+ Lose Kopplung erleichtert Austausch einzelner Systeme
+ Flexibel konfigurierbare Kommunikation und Diensteverteilung
+ Plattform- und programmiersprachenunabhängige Kommunikation
+ Anbindung an Fremdsysteme, z.B. Hosts
+ Batchbetrieb ist möglich
+ Verteilte Anwendungen
+ Verteilte Transaktionen
+ Standardisierung, Wartungsfreundlichkeit
+ Definierte Sicherheitsmechanismen
+ Hohe Ausfallsicherheit erreichbar
+ Hohe Skalierbarkeit
Queue (Point-to-Point, "PTP") |
Topic (Publish-and-Subscribe, "pub-sub") |
|
InitialContext | InitialContext | InitialContext |
ConnectionFactory | QueueConnectionFactory | TopicConnectionFactory |
Connection | QueueConnection | TopicConnection |
Session | QueueSession | TopicSession |
Destination | Queue | Topic |
MessageConsumer | QueueReceiver | TopicSubscriber |
MessageProducer | QueueSender | TopicPublisher |
Requestor | QueueRequestor | TopicRequestor |
Message | Message | Message |
Message-Aufbau | |
---|---|
Header | Enthält vom JMS gesetzte allgemeine Nachrichteninformationen wie: JMSCorrelationID, JMSDeliveryMode, JMSDestination, JMSExpiration, JMSMessageID, JMSPriority, JMSRedelivered, JMSReplyTo, JMSTimestamp, JMSType. |
Properties | Enthält von den Anwendungen gesetzte Nachrichtenzusatzinformationen als Attribute in Form von Key/Value-Paaren, deren Values von verschiedenen Java-Typen sein können. Die Attribute werden gesetzt und gelesen über 'get<Type>Property()' und 'set<Type>Property()'. Ein möglicher Verwendungszweck dieser Attribute ist eine mögliche Filterung der Nachrichten bei den Empfängern. |
Body | Enthält die eigentliche Nachricht, die einem der folgenden fünf Message-Typen entspricht. |
Message-Typen | |
---|---|
TextMessage | Übermittelt einzelnen Text-String. |
MapMessage | Übermittelt (mehrere) Attribute als Key/Value-Paare. Dabei können die Values nicht nur Strings sein, sondern auch viele andere Java-Typen. |
ObjectMessage | Übermittelt ein Java-Object (welches 'Serializable' implementieren muss). |
StreamMessage | Übermittelt Streams ähnlich wie 'DataOutputStream'. Anders als 'BytesMessage' übergibt 'StreamMessage' auch Datentypen. |
BytesMessage | Übermittelt Streams ähnlich wie 'DataOutputStream'. Anders als 'StreamMessage' übergibt 'BytesMessage' nicht interpretierte Rohdaten. |
Für die folgenden Beispiel wird ein Java EE Application Server für die JNDI- und JMS-Dienste benötigt. Es wird im Folgenden davon ausgegangen, dass JBoss installiert wird. Mit kleinen Modifikationen sind die Beispiele auch mit anderen Java EE Application Servern lauffähig.
Installieren Sie JBoss wie beschrieben unter 'jee-ejb2.htm#InstallationJBoss'.
Wenn Sie nicht JBoss verwenden wollen, müssen Sie einen anderen Java EE Application Server für die JNDI- und JMS-Dienste installieren.
Legen Sie ein Projektverzeichnis an, z.B. 'D:\MeinWorkspace\MeinJmsProjekt' (im Folgenden '<MeinJmsProjekt>' genannt).
Legen Sie im Projektverzeichnis '<MeinJmsProjekt>' das Unterverzeichnis '<MeinJmsProjekt>\lib' an und kopieren Sie dort hinein die vom Client benötigten Libs. Für JBosss ist dies die Datei 'jbossall-client.jar' aus dem JBoss-Verzeichnis 'C:\JBoss\client'.
Legen Sie im Projektverzeichnis '<MeinJmsProjekt>' das Unterverzeichnis '<MeinJmsProjekt>\conf' und darin die folgende Datei 'jndi.properties' an (wird von 'InitialContext()' benötigt):
java.naming.provider.url=jnp://localhost:1099 java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory jnp.socket.Factory=org.jnp.interfaces.TimedSocketFactory
Wenn Sie nicht JBoss verwenden oder wenn JBoss auf einem anderen Rechner installiert ist, müssen Sie die Datei anpassen, wie zum Beispiel beschrieben unter: jee-jndi.htm#jndi.properties.
JMS-Queues und -Topics müssen beim Java EE Application Server bzw. JMS-Provider angemeldet werden. Für JBoss muss hierfür eine '*-service.xml'-Datei in das JBoss-Deploy-Verzeichnis kopiert werden.
Legen Sie im Unterverzeichnis '<MeinJmsProjekt>\conf' die folgende Datei 'wetter-service.xml' an:
<?xml version="1.0" encoding="UTF-8"?> <!-- Destination without a configured SecurityManager or without a SecurityConf will default to role guest with read=true, write=true, create=false. --> <server> <mbean code="org.jboss.mq.server.jmx.Topic" name="jboss.mq.destination:service=Topic,name=WetterTopic"> <depends optional-attribute-name="DestinationManager"> jboss.mq:service=DestinationManager </depends> <depends optional-attribute-name="SecurityManager"> jboss.mq:service=SecurityManager </depends> </mbean> </server>
Kopieren Sie diese Datei in das JBoss-Deploy-Verzeichnis:
copy D:\MeinWorkspace\MeinJmsProjekt\conf\wetter-service.xml C:\JBoss\server\default\deploy
Bei anderen Java EE Application Servern müssen entweder ähnliche Dateien erstellt werden oder JMS wird über die Server-Konfiguration (z.B. Web-Konsole) eingerichtet.
JNDI (Java Naming and Directory Interface) bietet einen Namens- und Verzeichnisdienst, über den Objekte und Dienste gefunden und verfügbar gemacht werden.
Besonders zu Beginn kann es sehr hilfreich sein, die genauen Namen aller JNDI-Einträge anzeigen zu lassen, da sie bei den verschiedenen Java EE Application Servern unterschiedlich vergeben werden können.
Führen Sie hierfür das Testprogramm unter jee-jndi.htm#Auslesen-Namen-im-JNDI aus.
QueueSender
Vergegenwärtigen Sie sich den Unterschied zwischen 'Queue' und 'Topic'.
Der folgende QueueSender sendet zehn Nachrichten an ein Queue.
Speichern Sie im Verzeichnis '<MeinJmsProjekt>\src\meinjmspackage' die folgende Datei 'MyQueueSender.java':
package meinjmspackage; import javax.jms.*; import javax.naming.*; public class MyQueueSender { public static void main( String[] args ) throws NamingException, JMSException { final String MEINE_QUEUE = "queue/testQueue"; Context ctx = null; QueueConnection connect = null; QueueSession session = null; Queue queue = null; QueueSender sender = null; try { ctx = new InitialContext(); QueueConnectionFactory fact = (QueueConnectionFactory) ctx.lookup( "ConnectionFactory" ); connect = fact.createQueueConnection(); session = connect.createQueueSession( false, Session.AUTO_ACKNOWLEDGE ); try { queue = (Queue) ctx.lookup( MEINE_QUEUE ); } catch( NameNotFoundException ex ) { queue = session.createQueue( MEINE_QUEUE ); ctx.bind( MEINE_QUEUE, queue ); } sender = session.createSender( queue ); connect.start(); for( int i=0; i<10; i++ ) { TextMessage msg = session.createTextMessage(); msg.setText( "Die " + (i+1) + ". Meldung des MyQueueSenders." ); sender.send( msg ); System.out.println( "Sending " + msg.getText() ); // System.out.println( "Sending " + msg.toString() ); } } finally { try { if( null != sender ) sender.close(); } catch( Exception ex ) {/*ok*/} try { if( null != session ) session.close(); } catch( Exception ex ) {/*ok*/} try { if( null != connect ) connect.close(); } catch( Exception ex ) {/*ok*/} try { if( null != ctx ) ctx.close(); } catch( Exception ex ) {/*ok*/} } } }
Queue-receive
Der folgende QueueReceiver empfängt maximal 20 Sekunden lang bis zu 20 Nachrichten von einer Queue.
Anders als ein Listener wartet 'QueueReceiver.receive(1000)' synchron auf Messages (mit vorgegebenem Timeout).
Speichern Sie im Verzeichnis '<MeinJmsProjekt>\src\meinjmspackage' die folgende Datei 'MyQueueReceive.java':
package meinjmspackage; import javax.jms.*; import javax.naming.*; public class MyQueueReceive { public static void main( String[] args ) throws Exception { final String MEINE_QUEUE = "queue/testQueue"; Context ctx = null; QueueConnection connect = null; QueueSession session = null; Queue queue = null; QueueReceiver receiver = null; try { ctx = new InitialContext(); QueueConnectionFactory fact = (QueueConnectionFactory) ctx.lookup( "ConnectionFactory" ); connect = fact.createQueueConnection(); session = connect.createQueueSession( false, Session.AUTO_ACKNOWLEDGE ); try { queue = (Queue) ctx.lookup( MEINE_QUEUE ); } catch( NameNotFoundException ex ) { queue = session.createQueue( MEINE_QUEUE ); ctx.bind( MEINE_QUEUE, queue ); } receiver = session.createReceiver( queue ); connect.start(); for( int i=0; i<20; i++ ) { TextMessage msg = (TextMessage) receiver.receive( 1000 ); if( null != msg ) { System.out.println( msg.getText() ); msg.acknowledge(); } } } finally { try { if( null != receiver ) receiver.close(); } catch( Exception ex ) {/*ok*/} try { if( null != session ) session.close(); } catch( Exception ex ) {/*ok*/} try { if( null != connect ) connect.close(); } catch( Exception ex ) {/*ok*/} try { if( null != ctx ) ctx.close(); } catch( Exception ex ) {/*ok*/} } } }
Queue-Listener
Der folgende Queue-Listener empfängt 20 Sekunden lang Nachrichten von einer Queue.
Anders als bei 'receive()' braucht er als Listener nicht auf Messages zu warten, sondern empfängt sie asynchron, indem er sich über 'QueueReceiver.setMessageListener(new MyQueueListener())' registriert, wodurch Messages an die 'onMessage(Message message)'-Callback-Methode geschickt werden.
Speichern Sie im Verzeichnis '<MeinJmsProjekt>\src\meinjmspackage' die folgende Datei 'MyQueueListener.java':
package meinjmspackage; import javax.jms.*; import javax.naming.*; public class MyQueueListener implements MessageListener { public static void main( String[] args ) throws Exception { final String MEINE_QUEUE = "queue/testQueue"; Context ctx = null; QueueConnection connect = null; QueueSession session = null; Queue queue = null; QueueReceiver receiver = null; try { ctx = new InitialContext(); QueueConnectionFactory fact = (QueueConnectionFactory) ctx.lookup( "ConnectionFactory" ); connect = fact.createQueueConnection(); session = connect.createQueueSession( false, Session.AUTO_ACKNOWLEDGE ); try { queue = (Queue) ctx.lookup( MEINE_QUEUE ); } catch( NameNotFoundException ex ) { queue = session.createQueue( MEINE_QUEUE ); ctx.bind( MEINE_QUEUE, queue ); } receiver = session.createReceiver( queue ); receiver.setMessageListener( new MyQueueListener() ); connect.start(); Thread.sleep( 20000 ); } finally { try { if( null != receiver ) receiver.close(); } catch( Exception ex ) {/*ok*/} try { if( null != session ) session.close(); } catch( Exception ex ) {/*ok*/} try { if( null != connect ) connect.close(); } catch( Exception ex ) {/*ok*/} try { if( null != ctx ) ctx.close(); } catch( Exception ex ) {/*ok*/} } } public void onMessage( Message message ) { try { TextMessage msg = (TextMessage) message; System.out.println( msg.getText() ); message.acknowledge(); } catch( JMSException ex ) { System.out.println( ex.getMessage() ); } } }
Achten Sie darauf, dass die 'Vorbereitungen' durchgeführt sind.
Prüfen Sie mit der 'JNDI-Context-Testroutine', ob unter 'queue' der Eintrag 'queue/testQueue' aufgelistet ist.
Starten Sie JBoss und öffnen Sie drei Kommandozeilenfenster.
Geben Sie im ersten Kommandozeilenfenster ein:
cd \MeinWorkspace\MeinJmsProjekt
set CLASSPATH=.;bin;conf;lib/jbossall-client.jar
javac -d bin src/meinjmspackage/*.java
java meinjmspackage.MyQueueListener
Geben Sie im zweiten Kommandozeilenfenster ein:
cd \MeinWorkspace\MeinJmsProjekt
set CLASSPATH=.;bin;conf;lib/jbossall-client.jar
java meinjmspackage.MyQueueReceive
Geben Sie im dritten Kommandozeilenfenster ein:
cd \MeinWorkspace\MeinJmsProjekt
set CLASSPATH=.;bin;conf;lib/jbossall-client.jar
java meinjmspackage.MyQueueSender
Starten Sie dabei das jeweils letzte 'java ...'-Kommando in der Reihenfolge der Kommandozeilenfenster, also 'MyQueueSender' zuletzt. Starten Sie diese drei Kommandos möglichst kurz hintereinander.
In allen drei Kommandozeilenfenstern sollen Nachrichten erscheinen. Im 'MyQueueSender'-Kommandozeilenfenster erscheinen alle zehn Nachrichten. Die beiden anderen Kommandozeilenfenster teilen sich die Nachrichten, jede Nachricht erscheint also in genau einem Empfänger-Kommandozeilenfenster.
TopicPublisher
Vergegenwärtigen Sie sich den Unterschied zwischen 'Queue' und 'Topic'.
Der folgende TopicPublisher sendet zehn Wetternachrichten an ein Topic.
Dabei gibt es eine Besonderheit: Während 'Wetterlage', 'Windrichtung' und 'Temperatur' normal über eine 'MapMessage' im Body der 'Message' übermittelt werden, wird die 'Stadt'-Information getrennt davon als 'Message'-Property gesetzt. Dies werden wir weiter unten im 'Topic-Listener'-Beispiel für einen Nachrichtenfilter ausnutzen.
Speichern Sie im Verzeichnis '<MeinJmsProjekt>\src\meinjmspackage' die folgende Datei 'MyTopicPublisher.java':
package meinjmspackage; import javax.jms.*; import javax.naming.*; public class MyTopicPublisher { public static void main( String[] args ) throws NamingException, JMSException { final String WETTER_TOPIC = "topic/WetterTopic"; final String[] STAEDTE = { "Aachen", "Berlin" }; final String[] WETTERLAGEN = { "Sonnig", "Wolkig", "Regen " }; final String[] WINDRICHTUNGEN = { "Nord", "West", "Sued", "Ost " }; Context ctx = null; TopicConnection connect = null; TopicSession session = null; Topic topic = null; TopicPublisher sender = null; try { ctx = new InitialContext(); TopicConnectionFactory fact = (TopicConnectionFactory) ctx.lookup( "ConnectionFactory" ); connect = fact.createTopicConnection(); session = connect.createTopicSession( false, Session.AUTO_ACKNOWLEDGE ); try { topic = (Topic) ctx.lookup( WETTER_TOPIC ); } catch( NameNotFoundException ex ) { topic = session.createTopic( WETTER_TOPIC ); ctx.bind( WETTER_TOPIC, topic ); } sender = session.createPublisher( topic ); connect.start(); for( int i=0; i<10; i++ ) { int rndm = (int) (Math.random() * 300); String stadt = STAEDTE[rndm % STAEDTE.length]; String wetterlage = WETTERLAGEN[rndm % WETTERLAGEN.length]; String windrichtung = WINDRICHTUNGEN[rndm % WINDRICHTUNGEN.length]; double temperatur = (double) (rndm - 50) / 10; MapMessage msg = session.createMapMessage(); msg.setStringProperty( "Stadt", stadt ); msg.setString( "Wetterlage", wetterlage ); msg.setString( "Windrichtung", windrichtung ); msg.setDouble( "Temperatur", temperatur ); sender.publish( msg ); System.out.println( "Sending Stadt=" + stadt + " Wl=" + wetterlage + " Wr=" + windrichtung + " T=" + temperatur + "C" ); // System.out.println( "Sending " + msg.toString() ); } } finally { try { if( null != sender ) sender.close(); } catch( Exception ex ) {/*ok*/} try { if( null != session ) session.close(); } catch( Exception ex ) {/*ok*/} try { if( null != connect ) connect.close(); } catch( Exception ex ) {/*ok*/} try { if( null != ctx ) ctx.close(); } catch( Exception ex ) {/*ok*/} } } }
Topic-receive
Der folgende Topic-Empfänger empfängt maximal 20 Sekunden lang bis zu 20 Wetternachrichten von einem Topic.
Anders als ein Listener wartet 'TopicSubscriber.receive(1000)' synchron auf Messages (mit vorgegebenem Timeout).
Speichern Sie im Verzeichnis '<MeinJmsProjekt>\src\meinjmspackage' die folgende Datei 'MyTopicReceive.java':
package meinjmspackage; import javax.jms.*; import javax.naming.*; public class MyTopicReceive { public static void main( String[] args ) throws Exception { final String WETTER_TOPIC = "topic/WetterTopic"; Context ctx = null; TopicConnection connect = null; TopicSession session = null; Topic topic = null; TopicSubscriber subscrib = null; try { ctx = new InitialContext(); TopicConnectionFactory fact = (TopicConnectionFactory) ctx.lookup( "ConnectionFactory" ); connect = fact.createTopicConnection(); session = connect.createTopicSession( false, Session.AUTO_ACKNOWLEDGE ); try { topic = (Topic) ctx.lookup( WETTER_TOPIC ); } catch( NameNotFoundException ex ) { topic = session.createTopic( WETTER_TOPIC ); ctx.bind( WETTER_TOPIC, topic ); } subscrib = session.createSubscriber( topic ); connect.start(); for( int i=0; i<20; i++ ) { MapMessage msg = (MapMessage)subscrib.receive( 1000 ); if( null != msg ) { System.out.println( "Receiving: Stadt=" + msg.getStringProperty( "Stadt" ) + " Wl=" + msg.getString( "Wetterlage" ) + " Wr=" + msg.getString( "Windrichtung" ) + " T=" + msg.getDouble( "Temperatur" ) + "C" ); msg.acknowledge(); } } } finally { try { if( null != subscrib ) subscrib.close(); } catch( Exception ex ) {/*ok*/} try { if( null != session ) session.close(); } catch( Exception ex ) {/*ok*/} try { if( null != connect ) connect.close(); } catch( Exception ex ) {/*ok*/} try { if( null != ctx ) ctx.close(); } catch( Exception ex ) {/*ok*/} } } }
Topic-Listener (mit Filter)
Der folgende Topic-Listener empfängt 20 Sekunden lang Wetternachrichten von einem Topic.
Anders als bei 'receive()' braucht er als Listener nicht auf Messages zu warten, sondern empfängt sie asynchron, indem er sich über 'TopicSubscriber.setMessageListener(new MyTopicListener())' registriert, wodurch Messages an die 'onMessage(Message message)'-Callback-Methode geschickt werden.
Dabei gibt es eine Besonderheit:
Oben im 'TopicPublisher'-Beispiel
wurden 'Wetterlage', 'Windrichtung' und 'Temperatur' normal über eine 'MapMessage' im Body der
'Message'
übermittelt, während die 'Stadt'-Information getrennt davon als
'Message'-Property
gesetzt wurde.
Dies wird jetzt für einen Nachrichtenfilter ausgenutzt.
Wird ein Stadtname als Kommandozeilen-Argument übergeben, wird
'createSubscriber()'
nicht mit
subscrib = session.createSubscriber( topic );
sondern stattdessen mit
subscrib = session.createSubscriber( topic, "Stadt='"+ args[0] + "'", true );
aufgerufen, und es werden nur Nachrichten für die gewählte Stadt ausfiltert und übermittelt.
Speichern Sie im Verzeichnis '<MeinJmsProjekt>\src\meinjmspackage' die folgende Datei 'MyTopicListener.java':
package meinjmspackage; import javax.jms.*; import javax.naming.*; public class MyTopicListener implements MessageListener { public static void main( String[] args ) throws Exception { final String WETTER_TOPIC = "topic/WetterTopic"; Context ctx = null; TopicConnection connect = null; TopicSession session = null; Topic topic = null; TopicSubscriber subscrib = null; try { ctx = new InitialContext(); TopicConnectionFactory fact = (TopicConnectionFactory) ctx.lookup( "ConnectionFactory" ); connect = fact.createTopicConnection(); session = connect.createTopicSession( false, Session.AUTO_ACKNOWLEDGE ); try { topic = (Topic) ctx.lookup( WETTER_TOPIC ); } catch( NameNotFoundException ex ) { topic = session.createTopic( WETTER_TOPIC ); ctx.bind( WETTER_TOPIC, topic ); } if( null != args && 0 < args.length ) subscrib = session.createSubscriber( topic, "Stadt='"+ args[0] + "'", true ); else subscrib = session.createSubscriber( topic ); subscrib.setMessageListener( new MyTopicListener() ); connect.start(); Thread.sleep( 20000 ); } finally { try { if( null != subscrib ) subscrib.close(); } catch( Exception ex ) {/*ok*/} try { if( null != session ) session.close(); } catch( Exception ex ) {/*ok*/} try { if( null != connect ) connect.close(); } catch( Exception ex ) {/*ok*/} try { if( null != ctx ) ctx.close(); } catch( Exception ex ) {/*ok*/} } } public void onMessage( Message message ) { try { MapMessage msg = (MapMessage)message; System.out.println( "Receiving: Stadt=" + msg.getStringProperty( "Stadt" ) + " Wl=" + msg.getString( "Wetterlage" ) + " Wr=" + msg.getString( "Windrichtung" ) + " T=" + msg.getDouble( "Temperatur" ) + "C" ); message.acknowledge(); } catch( JMSException ex ) { System.out.println( ex.getMessage() ); } } }
Achten Sie darauf, dass die 'Vorbereitungen' durchgeführt sind.
Prüfen Sie mit der 'JNDI-Context-Testroutine', ob unter 'topic' der Eintrag 'topic/WetterTopic' aufgelistet ist.
Starten Sie JBoss und öffnen Sie drei Kommandozeilenfenster.
Geben Sie im ersten Kommandozeilenfenster ein:
cd \MeinWorkspace\MeinJmsProjekt
set CLASSPATH=.;bin;conf;lib/jbossall-client.jar
javac -d bin src/meinjmspackage/*.java
java meinjmspackage.MyTopicListener
Geben Sie im zweiten Kommandozeilenfenster ein:
cd \MeinWorkspace\MeinJmsProjekt
set CLASSPATH=.;bin;conf;lib/jbossall-client.jar
java meinjmspackage.MyTopicReceive
Geben Sie im dritten Kommandozeilenfenster ein:
cd \MeinWorkspace\MeinJmsProjekt
set CLASSPATH=.;bin;conf;lib/jbossall-client.jar
java meinjmspackage.MyTopicPublisher
Starten Sie dabei das jeweils letzte 'java ...'-Kommando in der Reihenfolge der Kommandozeilenfenster, also 'MyTopicPublisher' zuletzt. Starten Sie diese drei Kommandos möglichst kurz hintereinander.
In allen drei Kommandozeilenfenstern erscheinen die gleichen Wetternachrichten (allerdings nicht immer in der gleichen Reihenfolge).
Geben Sie anschließend in einem Kommandozeilenfenster ein:
java meinjmspackage.MyTopicListener Aachen
Geben Sie sofort danach in einem anderen Kommandozeilenfenster ein:
java meinjmspackage.MyTopicPublisher
Jetzt werden nur noch Wetternachrichten für Aachen empfangen.
QueueRequestor
Der folgende QueueRequestor sendet zehn Nachrichten an ein Queue. Anders als die bisherigen Sender/Publisher erwartet er eine Antwortnachricht. Für jede Nachricht öffnet er einen temporären Rückkanal, macht diesen über das JMSReplyTo-Feld zugänglich, registriert sich als Empfänger und wartet auf eine Antwort.
Speichern Sie im Verzeichnis '<MeinJmsProjekt>\src\meinjmspackage' die folgende Datei 'MyQueueRequestor.java':
package meinjmspackage; import javax.jms.*; import javax.naming.*; public class MyQueueRequestor { public static void main( String[] args ) throws NamingException, JMSException { final String MEINE_QUEUE = "queue/testQueue"; Context ctx = null; QueueConnection connect = null; QueueSession session = null; Queue queue = null; QueueRequestor sender = null; try { ctx = new InitialContext(); QueueConnectionFactory fact = (QueueConnectionFactory) ctx.lookup( "ConnectionFactory" ); connect = fact.createQueueConnection(); session = connect.createQueueSession( false, Session.AUTO_ACKNOWLEDGE ); try { queue = (Queue) ctx.lookup( MEINE_QUEUE ); } catch( NameNotFoundException ex ) { queue = session.createQueue( MEINE_QUEUE ); ctx.bind( MEINE_QUEUE, queue ); } sender = new QueueRequestor( session, queue ); connect.start(); for( int i=0; i<10; i++ ) { TextMessage msg = session.createTextMessage(); msg.setText( "Die " + (i+1) + ". Anfrage des MyQueueRequestors." ); System.out.println( "Sending: " + msg.getText() ); TextMessage answer = (TextMessage) sender.request( msg ); System.out.println( "Reveived: " + answer.getText() ); } } finally { try { if( null != sender ) sender.close(); } catch( Exception ex ) {/*ok*/} try { if( null != session ) session.close(); } catch( Exception ex ) {/*ok*/} try { if( null != connect ) connect.close(); } catch( Exception ex ) {/*ok*/} try { if( null != ctx ) ctx.close(); } catch( Exception ex ) {/*ok*/} } } }
QueueRequestor-reply
Der folgende Nachrichtenbeantworter empfängt 20 Sekunden lang Nachrichten von einer Queue und beantwortet sie.
Speichern Sie im Verzeichnis '<MeinJmsProjekt>\src\meinjmspackage' die folgende Datei 'MyQueueRequestorAnswer.java':
package meinjmspackage; import javax.jms.*; import javax.naming.*; public class MyQueueRequestorAnswer implements MessageListener { final String MEINE_QUEUE = "queue/testQueue"; Context ctx = null; QueueConnection connect = null; QueueSession session = null; Queue queue = null; QueueReceiver receiver = null; QueueSender replySnd = null; public static void main( String[] args ) throws Exception { new MyQueueRequestorAnswer(); } public MyQueueRequestorAnswer() throws Exception { try { ctx = new InitialContext(); QueueConnectionFactory fact = (QueueConnectionFactory) ctx.lookup( "ConnectionFactory" ); connect = fact.createQueueConnection(); session = connect.createQueueSession( false, Session.AUTO_ACKNOWLEDGE ); try { queue = (Queue) ctx.lookup( MEINE_QUEUE ); } catch( NameNotFoundException ex ) { queue = session.createQueue( MEINE_QUEUE ); ctx.bind( MEINE_QUEUE, queue ); } receiver = session.createReceiver( queue ); receiver.setMessageListener( this ); connect.start(); Thread.sleep( 20000 ); } finally { try { if( null != replySnd ) replySnd.close(); } catch( Exception ex ) {/*ok*/} try { if( null != receiver ) receiver.close(); } catch( Exception ex ) {/*ok*/} try { if( null != session ) session.close(); } catch( Exception ex ) {/*ok*/} try { if( null != connect ) connect.close(); } catch( Exception ex ) {/*ok*/} try { if( null != ctx ) ctx.close(); } catch( Exception ex ) {/*ok*/} } } public void onMessage( Message message ) { try { TextMessage msg = (TextMessage) message; System.out.println( "Reveived: " + msg.getText() ); TextMessage answer = session.createTextMessage(); answer.setText( "Echo '" + msg.getText() + "'." ); System.out.println( "Answering: " + answer.getText() ); Queue reply = (Queue)msg.getJMSReplyTo(); replySnd = session.createSender( reply ); replySnd.send( answer ); replySnd.close(); replySnd = null; message.acknowledge(); } catch( JMSException ex ) { System.out.println( ex.getMessage() ); } } }
Achten Sie darauf, dass die 'Vorbereitungen' durchgeführt sind.
Prüfen Sie mit der 'JNDI-Context-Testroutine', ob unter 'queue' der Eintrag 'queue/testQueue' aufgelistet ist.
Starten Sie JBoss und öffnen Sie zwei Kommandozeilenfenster.
Geben Sie im ersten Kommandozeilenfenster ein:
cd \MeinWorkspace\MeinJmsProjekt
set CLASSPATH=.;bin;conf;lib/jbossall-client.jar
javac -d bin src/meinjmspackage/*.java
java meinjmspackage.MyQueueRequestorAnswer
Geben Sie im zweiten Kommandozeilenfenster ein:
cd \MeinWorkspace\MeinJmsProjekt
set CLASSPATH=.;bin;conf;lib/jbossall-client.jar
java meinjmspackage.MyQueueRequestor
Starten Sie dabei das jeweils letzte 'java ...'-Kommando in der Reihenfolge der Kommandozeilenfenster, also 'MyQueueRequestor' zuletzt. Starten Sie diese zwei Kommandos möglichst kurz hintereinander.
In den beiden Kommandozeilenfenstern können Sie das Anfrage-/Antwort-Spiel beobachten.
Erläuterungen und ein Programmierbeispiel zu MDB (Message Driven Bean) finden Sie in jee-ejb2.htm#Beispiel-MDB.