Beispiele: ClaimSubmissionClient (novaris-claims-mq, Sender/Empfaenger der Antwort),
ClaimIntakeMDB.sendReply(...) (novaris-policy-core, Empfaenger der Anfrage/Sender der Antwort).
JMS ist von Natur aus asynchron und einseitig - eine Nachricht geht von A nach B, fertig. Trotzdem braucht man haeufig ein synchrones Anfrage/Antwort-Verhalten ueber JMS (der Aufrufer will wissen, ob und wie seine Schadensmeldung verarbeitet wurde, bevor er weitermacht). Das Request/Reply-Muster baut das aus JMS-Bordmitteln nach.
JMSReplyTo - der Sender traegt in die Anfrage ein, wohin die Antwort gehen soll:
java
request.setJMSReplyTo(replyQueue);
Der Empfaenger liest das aus und weiss dadurch, wohin er antworten soll, ohne dass diese
Adresse fest verdrahtet sein muss.
JMSCorrelationID - eine vom Sender erzeugte eindeutige ID (hier: eine UUID), die in
der Antwort unveraendert zurueckgespiegelt wird:
java
String correlationId = UUID.randomUUID().toString();
request.setJMSCorrelationID(correlationId);
// ... beim Empfaenger:
reply.setJMSCorrelationID(originalMessage.getJMSCorrelationID());
Notwendig, weil eine Antwort-Queue von vielen parallelen Anfragen gemeinsam genutzt
werden kann (siehe naechster Abschnitt) - ohne Korrelations-ID koennte ein Aufrufer die
Antwort auf die Anfrage eines anderen lesen.
Ein synchroner, selektiver Empfang - der Sender wartet aktiv (mit Timeout) auf genau
die Antwort mit seiner eigenen Korrelations-ID:
java
String selector = "JMSCorrelationID = '" + correlationId + "'";
MessageConsumer replyConsumer = session.createConsumer(replyQueue, selector);
// ... Anfrage senden ...
Message reply = replyConsumer.receive(replyTimeoutMillis);
Der Message-Selektor filtert bereits auf Provider-Seite - der Consumer sieht nur
Nachrichten mit genau dieser Korrelations-ID, alle anderen Antworten auf derselben Queue
bleiben fuer andere Consumer/Selektoren sichtbar.
ClaimSubmissionClient.submitClaim(...) legt den selektiven replyConsumer bewusst vor
dem Versenden der Anfrage an. Waere die Reihenfolge umgekehrt, koennte (bei einem sehr
schnell antwortenden Empfaenger, insbesondere bei einem im selben Prozess laufenden
Test-Broker) die Antwort theoretisch schon eintreffen, bevor der Consumer ueberhaupt
existiert - eine klassische Race Condition bei selbstgebautem Request/Reply.
TemporaryQueueDieses Projekt nutzt eine feste, administrierte Antwort-Queue
(jms/novaris/claimsReplyQueue) mit Korrelations-ID-Filterung. Die Alternative -
eine TemporaryQueue pro Anfrage - vermeidet die Notwendigkeit einer Korrelations-ID
(jede Anfrage hat ihre eigene, exklusive Antwort-Adresse), ist aber an die Lebensdauer der
erzeugenden Connection gebunden und in administrativ strengen WebSphere-/MQ-Landschaften
seltener anzutreffen, weil jede temporaere Queue eine dynamisch erzeugte
Verwaltungseinheit auf dem Queue-Manager ist. Siehe ClaimSubmissionClient-Javadoc fuer die
vollstaendige Abwaegung.
replyConsumer.receive(replyTimeoutMillis) blockiert hoechstens bis zum Timeout - antwortet
der Empfaenger gar nicht (System down, Nachricht verloren, Verarbeitung haengt), bekommt der
Aufrufer nach Ablauf null zurueck. ClaimSubmissionClient uebersetzt das in eine
LegacySystemUnavailableException (System Exception, siehe
docs/10-ejb-konzepte/06-exceptions.md). Das ist der Preis fuer synchrones Verhalten auf
einer asynchronen Grundlage: der Aufrufer muss explizit mit "keine Antwort" umgehen, ein
reiner Methodenaufruf haette dieses Problem nicht (wuerde dafuer aber die Entkopplung von
Sender und Empfaenger verlieren).