Beispiel: Message Driven Bean
Inhalt:
Anlegen der Message Driven Bean "Message"
MDB goes Server
Anlegen des Applicationclients
Mit Injection
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.
Das Beispiel besteht aus einem "EAR Application Project" mit dem Namen "Message", einem EJB-Projekt mit einer Message Driven Bean und einem Application Client-Projekt.
Anlegen der Message Driven Bean "Message"
Über "New" -> "Other..." wählen wir "EJB" -> "Message-Driven Bean" aus:
Die Klasse heißt "MessageBean" und liegt im Package "de.fhw.komponentenarchitekturen.knauf.mdb".
Als "Destination Name" wird ein Name angegeben, unter dem die zugehörige Queue ins Server-JNDI gebunden wird. Falls wir keine Queue im
Server konfigurieren (siehe weiter unten) wird hier beim Deploy eine neue angelegt.
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.
Im nächsten Schritt können wir alles bei den Defaults belassen.
Die generierte Klasse sieht so aus (bereinigt um Kommentare):
package de.fhw.komponentenarchitekturen.knauf.mdb;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;
@MessageDriven(
activationConfig =
{
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
},
mappedName = "queue/MessageBeanQueue")
public class MessageBean implements MessageListener
{
public MessageBean()
{
}
public void onMessage(Message message)
{
}
}
Leider müssen wir hier eine Anpassung vornehmen: das Attribut "mappedName" muss ersetzt werden durch eine weitere Property der "actionConfig", sonst führt dies zu folgender Exception:
org.jboss.deployers.spi.DeploymentException: Required config property RequiredConfigPropertyMetaData@1aec43[name=destination descriptions=[DescriptionMetaData@7f923d[language=de]]] for messagingType 'javax.jms.MessageListener' not found in activation config [ActivationConfigProperty(destinationType=javax.jms.Queue)] ra=jboss.jca:service=RARDeployment,name='jms-ra.rar'
at org.jboss.resource.deployment.ActivationSpecFactory.createActivationSpec(ActivationSpecFactory.java:95)
at org.jboss.resource.deployers.RARDeployment.createActivationSpec(RARDeployment.java:313)
at org.jboss.resource.deployers.RARDeployment.internalInvoke(RARDeployment.java:276)
at org.jboss.system.ServiceDynamicMBeanSupport.invoke(ServiceDynamicMBeanSupport.java:156)
at org.jboss.mx.server.RawDynamicInvoker.invoke(RawDynamicInvoker.java:164)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:668)
at org.jboss.ejb3.JmxClientKernelAbstraction.invoke(JmxClientKernelAbstraction.java:58)
at org.jboss.ejb3.mdb.inflow.JBossMessageEndpointFactory.createActivationSpec(JBossMessageEndpointFactory.java:287)
at org.jboss.ejb3.mdb.inflow.JBossMessageEndpointFactory.start(JBossMessageEndpointFactory.java:185)
...
Der Header sieht jetzt so aus:
@MessageDriven (activationConfig=
{
@ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination", propertyValue="queue/MessageBeanQueue")
})
In der Methode onMessage
steckt die Implementierung der Bean:
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 );
}
}
Schon sind wir kurz davor die MDB auf den Server zu packen.
MDB goes Server
Wenn wir jetzt das Projekt auf den Server legen würden, würde das zu folgender Fehlermeldung führten (wobei das Deploy trotzdem erfolgreich durchläuft!):
19:45:09,343 WARN [JmsActivation] Failure in jms activation org.jboss.resource.adapter.jms.inflow.JmsActivationSpec@8b7ac9(ra=org.jboss.resource.adapter.jms.JmsResourceAdapter@1c1a0cb destination=queue/MessageBeanQueue destinationType=javax.jms.Queue tx=true durable=false reconnect=10 provider=java:/DefaultJMSProvider user=null maxMessages=1 minSession=1 maxSession=15 keepAlive=60000 useDLQ=true DLQHandler=org.jboss.resource.adapter.jms.inflow.dlq.GenericDLQHandler DLQJndiName=queue/DLQ DLQUser=null DLQMaxResent=5)
javax.naming.NameNotFoundException: MessageBeanQueue not bound
at org.jnp.server.NamingServer.getBinding(NamingServer.java:771)
at org.jnp.server.NamingServer.getBinding(NamingServer.java:779)
at org.jnp.server.NamingServer.getObject(NamingServer.java:785)
at org.jnp.server.NamingServer.lookup(NamingServer.java:443)
at org.jnp.server.NamingServer.lookup(NamingServer.java:399)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:713)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:673)
at javax.naming.InitialContext.lookup(Unknown Source)
at org.jboss.util.naming.Util.lookup(Util.java:222)
at org.jboss.resource.adapter.jms.inflow.JmsActivation.setupDestination(JmsActivation.java:464)
at org.jboss.resource.adapter.jms.inflow.JmsActivation.setup(JmsActivation.java:352)
at org.jboss.resource.adapter.jms.inflow.JmsActivation$SetupActivation.run(JmsActivation.java:729)
at org.jboss.resource.work.WorkWrapper.execute(WorkWrapper.java:204)
at org.jboss.util.threadpool.BasicTaskWrapper.run(BasicTaskWrapper.java:260)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Also deklarieren wir die Queue. Hierfür gibt es drei Möglichkeiten (sowie die Möglichkeit des automatischen Erzeugens):
- Variante 1: Global
Dazu die Datei "%JBOSS_HOME%\server\default\deploy\messaging\destinations-service.xml" öffnen und im Element "server"
folgendes einfügen:
<mbean code="org.jboss.jms.server.destination.QueueService"
name="knauf:service=Queue,name=MessageBeanQueue"
xmbean-dd="xmdesc/Queue-xmbean.xml">
<attribute name="JNDIName">queue/MessageBeanQueue</attribute>
<depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends>
<depends>jboss.messaging:service=PostOffice</depends>
</mbean>
Wir geben der MBean einen Namen unter dem sie in der JMXConsole auftaucht, es werden diverse Abhängigkeiten definiert.
Außerdem wird über ein Attribut der JNDI-Name vergeben, an die Bean beim Deploy gebunden wird.
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:14:02,234 INFO [ConnectionFactory] org.jboss.jms.server.connectionfactory.ConnectionFactory@114ba77 started
19:14:02,234 INFO [QueueService] Queue[/queue/ExpiryQueue] started, fullSize=200000, pageSize=2000, downCacheSize=2000
19:14:02,234 INFO [QueueService] Queue[queue/MessageBeanQueue] started, fullSize=200000, pageSize=2000, downCacheSize=2000
19:14:02,343 INFO [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=JmsXA' to JNDI name 'java:JmsXA'
19:14:02,359 INFO [TomcatDeployment] deploy, ctxPath=/, vfsUrl=ROOT.war
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.messaging.connectionfactory:service=ConnectionFactory
aufrufen:
Wir erkennen in ihr zum Beispiel den JNDI-Namen, unter dem diese Connection Factory später für Clients verfügbar ist:
- Variante 2: Zur Laufzeit
Wir öffnen die JMXConsole unter http://localhost:8080/jmx-console und
gehen zum Objekt "jboss.messaging", Service "service=ServerPeer":
Wir gehen zur Operation "deployQueue" (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, auf der Konsole finden wir allerdings folgende Zeile:
19:47:56,656 INFO [QueueService] Queue[queue/MessageBeanQueue] started, fullSize=200000, pageSize=2000, downCacheSize=2000
Danach taucht die neue Queue im Attribut "Destinations" auf.
Wir finden sie auch in der JMXConsole unter dem Service "jboss.messaging.destination":
Die Queue verwschwindet allerdings nach einem Server-Restart wieder.
- Variante 3: Im EJB-Projekt
Jetzt kommt die eigenlich eleganteste Variante (die allerdings zu Deploy-Fehlern führt, siehe unten): wir fügen die Queue durch eine Konfigurations-Datei direkt im EJB-Projekt zu.
Die Config-Datei kommt in das Verzeichnis "MessageEJB\ejbModule\META-INF".
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"?>
<!DOCTYPE server
PUBLIC "-//JBoss//DTD MBean Service 5.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss-service_5_0.dtd">
<server>
<mbean code="org.jboss.jms.server.destination.QueueService"
name="knauf:service=Queue,name=MessageBeanQueue"
xmbean-dd="xmdesc/Queue-xmbean.xml">
<depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends>
<depends>jboss.messaging:service=PostOffice</depends>
<attribute name="JNDIName">queue/MessageBeanQueue</attribute>
</mbean>
</server>
Anmerkung: Man könnte die Datei auch direkt in das EJB-Jar hängen (im Project Explorer also in "MessageEJB\ejbModule").
In JBoss 5.0.0GA führte das zu einer Fehlermeldung, weil der Service scheinbar zweimal deployed wurde (die Queue hat trotzdem funktioniert), in 5.0.1GA ist dieser Bug gefixt.
Wichtig ist, dass wir die MessageDrivenBean mit der zusätzlichen Annotation @org.jboss.ejb3.annotation.Depends
versehen müssen:
@MessageDriven (activationConfig=
{
@ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination", propertyValue="queue/MessageBeanQueue")
})
@Depends(value = "knauf:service=Queue,name=MessageBeanQueue")
public class MessageBean implements MessageListener
{
Hier wird definiert, dass die Bean erst deployed werden darf, wenn die im value
der @Dependes
-Annotation eingetragene Managed Bean gestartet
worden ist. Würden wir dies nicht tun, würde bei jedem Deploy eine solche Fehlermeldung auftauchen:
20:52:31,000 WARN [JmsActivation] Failure in jms activation org.jboss.resource.adapter.jms.inflow.JmsActivationSpec@4dbba7(ra=org.jboss.resource.adapter.jms.JmsResourceAdapter@144f046 destination=queue/MessageBeanQueue destinationType=javax.jms.Queue tx=true durable=false reconnect=10 provider=java:/DefaultJMSProvider user=null maxMessages=1 minSession=1 maxSession=15 keepAlive=60000 useDLQ=true DLQHandler=org.jboss.resource.adapter.jms.inflow.dlq.GenericDLQHandler DLQJndiName=queue/DLQ DLQUser=null DLQMaxResent=5)
javax.naming.NameNotFoundException: MessageBeanQueue not bound
at org.jnp.server.NamingServer.getBinding(NamingServer.java:771)
at org.jnp.server.NamingServer.getBinding(NamingServer.java:779)
at org.jnp.server.NamingServer.getObject(NamingServer.java:785)
at org.jnp.server.NamingServer.lookup(NamingServer.java:443)
at org.jnp.server.NamingServer.lookup(NamingServer.java:399)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:713)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:673)
at javax.naming.InitialContext.lookup(Unknown Source)
at org.jboss.util.naming.Util.lookup(Util.java:222)
at org.jboss.resource.adapter.jms.inflow.JmsActivation.setupDestination(JmsActivation.java:464)
at org.jboss.resource.adapter.jms.inflow.JmsActivation.setup(JmsActivation.java:352)
at org.jboss.resource.adapter.jms.inflow.JmsActivation$SetupActivation.run(JmsActivation.java:729)
at org.jboss.resource.work.WorkWrapper.execute(WorkWrapper.java:204)
at org.jboss.util.threadpool.BasicTaskWrapper.run(BasicTaskWrapper.java:260)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
JBoss scheint aber in einer Schleife immer wieder zu versuchen, die Queue zu binden, und im zweiten Versuch ist dann wohl der "knaufmq-service.xml" gestartet
und das Binden der Queue funktioniert.
Sollte allerdings obige Exception mehrfach kommen, liegt wohl ein Tippfehler vor.
Die Wirkung der @Depends
-Annotation sehen wir beim Deploy in der Serverkonsole:
14:14:36,937 INFO [JBossASKernel] Created KernelDeployment for: MessageEJB.jar
14:14:36,937 INFO [JBossASKernel] installing bean: jboss.j2ee:ear=Message.ear,jar=MessageEJB.jar,name=MessageBean,service=EJB3
14:14:36,937 INFO [JBossASKernel] with dependencies:
14:14:36,937 INFO [JBossASKernel] and demands:
14:14:36,937 INFO [JBossASKernel] jboss.ejb:service=EJBTimerService
14:14:36,937 INFO [JBossASKernel] knauf:service=Queue,name=MessageBeanQueue
14:14:36,937 INFO [JBossASKernel] and supplies:
14:14:36,937 INFO [JBossASKernel] jndi:null
14:14:36,937 INFO [JBossASKernel] Class:javax.jms.MessageListener
14:14:36,937 INFO [JBossASKernel] Added bean(jboss.j2ee:ear=Message.ear,jar=MessageEJB.jar,name=MessageBean,service=EJB3) to KernelDeployment of: MessageEJB.jar
- Variante 4: Automatisch
Hierbei handelt es sich um ein neues Feature von JBoss 5.0GA: https://jira.jboss.org/jira/browse/JBAS-6013
Man benötigt dazu im EJB-Projekt folgende "jboss.xml" (in ejbModule\META-INF):
<?xml version="1.0" encoding="UTF-8"?>
<jboss xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss_5_0.xsd"
version="3.0">
<enterprise-beans>
<message-driven>
<ejb-name>MessageBean</ejb-name>
<create-destination>true</create-destination>
</message-driven>
</enterprise-beans>
</jboss>
Wichtig ist das Element "create-destination".
Im Moment führt eine solche "jboss.xml" zu Validierungsfehlern, da das Schema "http://www.jboss.org/j2ee/schema/jboss_5_0.xsd" ungültig ist. Ein Deploy funktioniert allerdings.
Eine korrigierte XSD hängt diesem JBoss-Bug an: https://jira.jboss.org/jira/browse/JBAS-6426
Um sie zu verwenden: irgendwo auf die Festplatte legen. Danach im XML-Katalog (siehe weiter unten im Abschnitt Anlegen des Applicationclients)
einen Eintrag zufügen mit:
- Location: lokaler Pfad zu jboss_5_0.xsd
- Key type: "Schema Location"
- Key: http://www.jboss.org/j2ee/schema/jboss_5_0.xsd
Hier gibt es eine Variante dieses Projekts: MessageCreateDestination.ear
Anlegen des Applicationclients
Der Applicationclient muss nicht die EJB-JARs referenzieren, da MessageDrivenBeans über keine
Remote/Local-Interfaces verfügen.
Es wird neue Klasse vom Typ javax.swing.JFrame
zugefügt.
Sie muss in "Manifest.mf" als Main-Class
eingetragen werden.
Auf ihr einen Button und ein Textfeld platzieren. 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:org.jnp.interfaces");
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 einmalig geschehen!) fügen wir die DTD "jboss-client_5_0.dtd"
in den XML-Katalog ein. Hierzu sollte eine Internetverbindung bestehen! Unter "Window" -> "Preferences" wird "XML" -> "XML Catalog"
gewählt. 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_5_0.dtd".
Aber wir referenzieren trotzdem die aus dem Internet.
Als "Location" wird die Adresse auf der JBoss-Homepage angegeben: http://www.jboss.org/j2ee/dtd/jboss-client_5_0.dtd
"Key Type" ist "Public ID".
"Key" ist -//JBoss//DTD Application Client 5.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 der Dateiname "jboss-client.xml" angegeben und des META-INF-Verzeichnis als
Ziel gewählt.
Im nächsten Schritt wird die Option "Create XML file from a DTD file" 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 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss-client_5_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.
Mit Injection
Hier gibt es ein EAR-Projekt mit für die Injection vorbereitetem Client: MessageInjection.ear
Der Code des Clients sieht so aus (die injizierten Variablen müssen static sein und in der Main Class stecken):
@Resource(name="jms/MBConnectionFactory")
private static QueueConnectionFactory queueConnectionFactory;
@Resource(name="jms/MBQueueRef")
private static Queue queue;
Für Queue
und QueueConnectionFactory
greife ich auf einen Eintrag aus dem ENC zu (siehe letzter Abschnitt).
Hier hätte auch ein Binden an den globalen JNDI-Namen ohne Verwendung des ENC geklappt:
@Resource(mappedName="ConnectionFactory")
private static QueueConnectionFactory queueConnectionFactory;
@Resource(mappedName="queue/MessageBeanQueue")
private static Queue queue;
Die Konfiguration des Environment Naming Context sieht so aus:
application-client.xml:
<?xml version="1.0" encoding="UTF-8"?>
<application-client id="Application-client_ID" version="5"
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/application-client_5.xsd">
<display-name>
MessageClient</display-name>
<resource-ref>
<res-ref-name>jms/MBConnectionFactory</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<message-destination-ref>
<message-destination-ref-name>jms/MBQueueRef</message-destination-ref-name>
<message-destination-type>javax.jms.Queue</message-destination-type>
</message-destination-ref>
</application-client>
jboss-client.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-client PUBLIC
"-//JBoss//DTD Application Client 5.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss-client_5_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>
<message-destination-ref>
<message-destination-ref-name>jms/MBQueueRef</message-destination-ref-name>
<jndi-name>queue/MessageBeanQueue</jndi-name>
</message-destination-ref>
</jboss-client>
Wichtig ist, dass die Queue nicht als resource-ref
eingetragen wird, sondern als message-destination-ref
!
Zum Starten des Client muss der mühsame Weg über den org.jboss.ejb3.client.ClientLauncher
gegangen werden:
- Die "MainClass" ist:
org.jboss.client.AppClientMain
-
Die "Program Arguments" sind:
-jbossclient de.fhw.komponentenarchitekturen.knauf.mdb.MessageFrame -launchers org.jboss.ejb3.client.ClientLauncher -j2ee.clientName MessageClient
- Die "VM Arguments" sind:
-Djava.naming.factory.initial=org.jnp.interfaces.NamingContextFactory -Djava.naming.provider.url=jnp://localhost:1099
Um Fehler bei der Injection zu erkennen, habe ich außerdem folgende "log4j.properties" im Verzeichnis "appClientModule" abgelegt (die zu ziemlich viel Konsolenausgabe
beim Starten des Clients führt). Log4j findet diese Datei automatisch, da sie auf dem Classpath liegt:
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%t][%c] - <%m>%n
Anmerkung: wenn die Logausgabe über Log4j nicht funktioniert, kann man die obigen Startparameter erweitern um -Dlog4j.debug=true
und sieht dadurch hoffentlich,
warum Log4j z.B. die Konfiguration nicht findet.
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.komponentenarchitekturen.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.
Für die Umsetzung @Depends
-Annotation (mit der wir die MDB vom Start der Queue abhängig machen), ist eine Datei "jboss.xml" nötig
(diesmal wieder die DTD-basierte Version):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC
"-//JBoss//DTD JBOSS 5.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss_5_0.dtd">
<jboss>
<enterprise-beans>
<message-driven>
<ejb-name>MessageBean</ejb-name>
<depends>knauf:service=Queue,name=MessageBeanQueue</depends>
</message-driven>
</enterprise-beans>
</jboss>
Auf Injection im Application Client habe ich hier verzichtet ;-)
Die modifizierte Version des Projekts gibt es hier: MessageNoAnnotation.ear.
ACHTUNG: Dieses Projekt kann nicht neben dem obigen Message-Beispiel existieren !
Stand 11.05.2009
Historie:
28.01.2009: Übernommen aus Vorjahresbeispiel und angepaßt an Eclipse 3.4 sowie JBoss 5.0 (neues Messaging-Framework, Injection im Client)
31.01.2009: @Depends
-Annotation zugefügt, "knaufmq-service.xml" umgeschoben
04.03.2009: Injection-Beispiel perfektioniert.
10.03.2009: Variante "Deklaration der Queue über ...-service.xml" an JBoss 5.0.1GA angepaßt (kann jetzt auch im EJB-Projekt in oberster Ebene liegen.
11.05.2009: Client-Start mit Injection: VM-Argument "Context.URL_PKG_PREFIXES" entfernt, weil unnötig.