Beispiel: Message Driven Bean
Inhalt:
Anlegen der Application
Anlegen der Message Driven Bean "Message"
MDB goes Server
Anlegen des Applicationclients
Ohne Annotations
Beispiel für eine Message Driven Bean, die ihre Messages per Application Client erhält.
Hier gibt es das Projekt zum Download (dies ist ein EAR-Export, die Importanleitung findet
man im Stateless-Beispiel): Message.ear
Aufbau des Beispieles
a) Message Driven Bean, die an einer Queue hängt.
b) Application Client der die Nachrichten abschickt.
Anlegen der Application
Ein "EAR Application Project" mit dem Namen "Message" erstellen.
Zu erzeugende Module definieren. Dieses Beispiel benötigt ein EJB-Projekt und
ein Anwendungsclientprojekt.
Jetzt die Deployment-Deskriptoren "application.xml", "ejb-jar.xml" und "application-client.xml" auf JavaEE5 umstellen.
Anlegen der Message Driven Bean "Message"
Wir fügen eine Klasse namens "MessageBean" im Package "de.fhw.swtvertiefung.knauf.mdb" zu. Diese
Klasse soll das Interface "javax.jms.MessageListener" implementieren
Der Code sieht so aus:
@MessageDriven (activationConfig=
{
@ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination", propertyValue="queue/MessageBeanQueue")
})
public class MessageBean implements MessageListener
{
private static final Logger logger = Logger.getLogger ( MessageBean.class.getName() );
public void onMessage(Message message)
{
try
{
MessageBean.logger.info("onMessage: Message vom Typ " + message.getClass().toString() + " erhalten");
if (message instanceof TextMessage)
{
TextMessage textMessage = (TextMessage) message;
MessageBean.logger.info("TextMessage enthält diesen Text: " + textMessage.getText() );
}
else
MessageBean.logger.info("Sonstige Message. toString() = " + message.toString() );
}
catch (JMSException jex)
{
MessageBean.logger.log( Level.SEVERE, "Fehler beim Verarbeiten der Message: " + jex.getMessage(), jex );
throw new EJBException ("Fehler beim Verarbeiten der Message: " + jex.getMessage(), jex );
}
}
}
Die Annotation "@MessageDriven" muss zwei Pflicht-Properties deklarieren: Der destination-type
gibt an ob
es sich bei dieser Bean um eine Queue
(Punkt-zu-Punkt-Verbindung, der Client schickt die Nachricht an einen bestimmten
Empfänger, wie eine Telefonverbindung) oder ein Topic
(es gibt eine unbekannte Anzahl von Empfängern,
vergleichbar mit dem Radio-Senden) handelt.
Die Property destination
gibt den JNDI-Namen einer Queue im Server an, an der diese Bean lauscht.
Falls wir keine Queue im Server konfigurieren (siehe weiter unten) wird hier beim Deploy eine neue angelegt.
Schon sind wir kurz davor die MDB auf den Server zu packen.
MDB goes Server
Bevor wir die MDB auf den Server schieben deklarieren wir die Queue. Hier gibt es drei Möglichkeiten:
- Variante 1: Global
Dazu die Datei "%JBOSS_HOME%\server\default\deploy\jms\jbossmq-destinations-service.xml" öffnen und im Element "server"
folgendes einfügen:
<mbean code="org.jboss.mq.server.jmx.Queue"
name="knauf:service=Queue,name=MessageBeanQueue">
<depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
<attribute name="JNDIName">queue/MessageBeanQueue</attribute>
</mbean>
Wir geben der MBean einen Namen unter dem sie in der JMXConsole auftaucht. Außerdem wird über ein Attribut der
JNDI-Name vergeben.
Jetzt können wir den Server starten und unsere Bean publizieren. Im folgenden ein Auszug aus dem Serverlog bei
Serverstart (Verarbeitung der in "jbossmq-destinations-service.xml" deklarierten Queue):
19:53:37,031 INFO [A] Bound to JNDI name: queue/A
19:53:37,031 INFO [B] Bound to JNDI name: queue/B
19:53:37,031 INFO [C] Bound to JNDI name: queue/C
19:53:37,031 INFO [D] Bound to JNDI name: queue/D
19:53:37,031 INFO [ex] Bound to JNDI name: queue/ex
19:53:37,031 INFO [MessageBeanQueue] Bound to JNDI name: queue/MessageBeanQueue
19:53:37,046 INFO [testTopic] Bound to JNDI name: topic/testTopic
19:53:37,046 INFO [securedTopic] Bound to JNDI name: topic/securedTopic
19:53:37,046 INFO [testDurableTopic] Bound to JNDI name: topic/testDurableTopic
19:53:37,046 INFO [testQueue] Bound to JNDI name: queue/testQueue
Wir starten die JMX-Console (http://localhost:8080/jmx-console).
Am untersten Ende finden wir die MBean für unsere Queue wieder:
Hier können wir diverse Attribute einsehen, z.B. den JNDI-Namen der Queue.
Anschließend geht es in die JNDIView. Dort rufen wir die Operation java.lang.String list()
auf und sehen
im Ergebnis unsere Bean sowie die JMS-ConnectionFactory:
Die ConnectionFactory für die Message Queues (an die sich auch unsere MDB hängt) steckt unter dem Namen "ConnectionFactory" im JNDI.
Informationen über diese ConnectionFactory erhalten wir, wenn wir, zurück in der JNDIView, die MBean jboss.jms:alias=QueueConnectionFactory
aufrufen:
Wir erkennen in ihr zum Beispiel den JNDI-Namen:
- Variante 2: Zur Laufzeit
Wir öffnen die JMXConsole unter http://localhost:8080/jmx-console und
gehen zum Service "jboss.mq":
Wir gehen zur Operation "createQueue" (mit zwei Parametern) und geben für den 1. Parameter den Queue-Namen
"MessageBeanQueue" ein, für den 2. Parameter den JNDI-Namen "queue/MessageBeanQueue".
Die Statusausgabe nach dem Aufruf ist eher unspektakulär. Wir finden die Queue in der JMXConsole unter dem Service "jboss.mq.destination":
Die Queue ist scheinbar persistent angelegt und überlebt auch Server-Restarts. Wenn wir sie wieder loswerden wollen
rufen wir die Operation "destroyQueue" mit dem Namen der Queue als Parameter (also "MessageBeanQueue") auf.
- Variante 3: Im EJB-Projekt
Jetzt kommt die eleganteste Variante: wir fügen die Queue durch eine Konfigurations-Datei direkt im EJB-Projekt zu.
Die Config-Datei kommt in das Verzeichnis "MessageEJB\ejbModule\" (NICHT ins Unterverzeichnis "META-INF" hängen).
Der Name ist egal, im Beispiel heißt sie "knaufmq-service.xml". Ihr Inhalt ist identisch mit der Konfiguration
in der globalen Datein "jbossmq-service.xml":
<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="org.jboss.mq.server.jmx.Queue"
name="knauf:service=Queue,name=MessageBeanQueue">
<depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
<attribute name="JNDIName">queue/MessageBeanQueue</attribute>
</mbean>
</server>
Anlegen des Applicationclients
Der Applicationclient muss nicht die EJB-JARs referenzieren, da MessageDrivenBeans über keine
Remote/Local-Interfaces verfügen.
Es sind folgende Schritte nötig:
-Klasse "MessageApplication" zufügen, die nur den MessageFrame angezeigt.
Sie muss in "Manifest.mf" als Main-Class
eingetragen werden.
-Eine neue Klasse vom Typ "Java" -> "Swing" -> "JFrame Visual Class" zufügen. Auf ihr einen Button und
ein Textfeld plazieren. Beim Button-Klick soll die Eingabe aus dem Textfeld an die MessageBean geschickt werden.
Der Code dazu sieht so aus:
private void sendMessage()
{
try
{
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming.client");
props.setProperty(Context.PROVIDER_URL, "jnp://localhost:1099");
props.setProperty("j2ee.clientName", "MessageClient");
//JMS initialisieren. Die ConnectionFactory aus dem JNDI holen:
InitialContext initialContext = new InitialContext(props);
QueueConnectionFactory queueConnectionFactory =
(QueueConnectionFactory) initialContext.lookup("java:comp/env/jms/MBConnectionFactory");
//Die konfigurierte Queue holen:
Queue queue = (Queue) initialContext.lookup("java:comp/env/jms/MBQueueRef");
//Verbindung erzeugen:
QueueConnection queueConnection = queueConnectionFactory.createQueueConnection();
QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender queueSender = queueSession.createSender(queue);
//Senden der Nachricht:
TextMessage textMessage = queueSession.createTextMessage();
textMessage.setText(this.jTextFieldMessage.getText());
queueSender.send(textMessage);
//Fertig.
JOptionPane.showMessageDialog(this, "Nachricht wurde gesendet und sollte im Serverlog stehen !");
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
-Jetzt kommt die Hauptarbeit: die Resourcen-Referenzen müssen deklariert werden:
In "application-client.xml" wird folgendes eingefügt:
<resource-ref>
<res-ref-name>jms/MBConnectionFactory</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-ref>
<res-ref-name>jms/MBQueueRef</res-ref-name>
<res-type>javax.jms.Queue</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Die eigentliche Verknüpfung mit den globalen JNDI-Namen der JBoss-Resourcen erfolgt
über die Datei "jboss-client.xml". Wir könnten natürlich (wie in den bisherigen Beispielen)
den XML-Inhalt direkt zufügen, aber um den WebTools-Plugin ein bißchen besser kennen zu lernen machen wir das hier über
den umständlichen Weg:
Zuerst einmal (dies muss nur einmal geschehen !) fügen wir die DTD "jboss-client_4_0.dtd"
in den XML-Katalog ein. Dazu unter "Window" -> "Preferences" -> "Web and XML" -> "XML Catalog"
wählen. Hier einen neuen Eintrag in der Kategorie "User Specified Entries" zufügen. Nebenbei: die DTD liegt auch im JBoss-VErzeichnis
unter "\docs\dtd\jboss-client_4_0.dtd" (aus dieser habe ich auch die Werte geklaut).
Aber wir referenzieren trotzdem die aus dem Internet.
Als URI wird die Adresse auf der JBoss-Homepage angegeben: http://www.jboss.org/j2ee/dtd/jboss-client_4_0.dtd
"Key Type" ist "Public ID".
"Key" ist -//JBoss//DTD Application Client 4.0//EN
.
Das Ergebnis sieht so aus:
Jetzt können wir "jboss-client.xml" anlegen. Dazu Rechtsklick auf "META-INF" im Client-Projekt und
"New" -> "Other..." wählen. Wir wählen "XML" -> "XML" aus.
Im ersten Schritt wird die Option "Create XML file from a DTD file" gewählt.
Im nächsten Schritt wird der Dateiname "jboss-client.xml" angegeben und des META-INF-Verzeichnis als
Ziel gewählt.
Die DTD-Datei steckt im XML-Katalog, wir wählen sie aus:
Im letzten Schritt können wir die Defaults belassen:
Das erzeugt eine Rumpf-XML-Datei mit sauberer DTD-Deklaration.
Jetzt fügen wir die Resourcen-Referenzen zu. Wichtig ist dass die Elemente res-ref-name
in "jboss-client.xml" den gleichen Wert haben wie die zugehörigen Element in
"application-client.xml" !
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-client PUBLIC "-//JBoss//DTD Application Client 4.0//EN" "http://www.jboss.org/j2ee/dtd/jboss-client_4_0.dtd" >
<jboss-client>
<jndi-name>MessageClient</jndi-name>
<resource-ref>
<res-ref-name>jms/MBConnectionFactory</res-ref-name>
<jndi-name>ConnectionFactory</jndi-name>
</resource-ref>
<resource-ref>
<res-ref-name>jms/MBQueueRef</res-ref-name>
<jndi-name>queue/MessageBeanQueue</jndi-name>
</resource-ref>
</jboss-client>
Jetzt die Anwendung per Rechtsklick -> "Run as" -> "Run as Java Application" ausführen. Nach dem Klick auf den
Button sollte die Message im Serverlog bzw. auf der Console erscheinen.
Ohne Annotations
"ejb-jar.xml" sieht so aus:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar id="ejb-jar_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<display-name>MessageEJB</display-name>
<enterprise-beans>
<message-driven>
<display-name>MessageBean</display-name>
<ejb-name>MessageBean</ejb-name>
<ejb-class>de.fhw.swtvertiefung.knauf.mdb.MessageBean</ejb-class>
<messaging-type>javax.jms.MessageListener</messaging-type>
<transaction-type>Container</transaction-type>
<message-destination-type>javax.jms.Queue</message-destination-type>
<activation-config>
<activation-config-property>
<activation-config-property-name>destinationType</activation-config-property-name>
<activation-config-property-value>javax.jms.Queue</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>destination</activation-config-property-name>
<activation-config-property-value>queue/MessageBeanQueue</activation-config-property-value>
</activation-config-property>
</activation-config>
</message-driven>
</enterprise-beans>
</ejb-jar>
Die Elemente "messaging-type", "transaction-type" und "message-destination-type" sind scheinbar optional,
die Anwendung hat jedenfalls auch ohne funktioniert.
Die modifizierte Version des Projekts gibt es hier: MessageNoAnnotation.ear.
ACHTUNG 1: Dieses Projekt kann nicht neben dem obigen Message-Beispiel existieren !
Stand 09.10.2006
Historie:
09.10.2006: Übernommen aus WS 2005/2006 und angepaßt an EJB3