User: pra     
  Date: 01/03/01 13:58:24

  Modified:    src/docs jbossdocs.xml
  Added:       src/docs howtomdb.xml
  Log:
  Added chapter on MDB
  
  Revision  Changes    Path
  1.3       +2 -1      manual/src/docs/jbossdocs.xml
  
  Index: jbossdocs.xml
  ===================================================================
  RCS file: /products/cvs/ejboss/manual/src/docs/jbossdocs.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- jbossdocs.xml     2001/02/11 00:57:38     1.2
  +++ jbossdocs.xml     2001/03/01 21:58:24     1.3
  @@ -19,7 +19,7 @@
   <!ENTITY howtoejx.xml SYSTEM "howtoejx.xml">
   <!ENTITY howtojca.xml SYSTEM "howtojca.xml">
   <!ENTITY basicconfiguration.xml SYSTEM "basicconfiguration.xml">
  -
  +<!ENTITY howtomdb.xml SYSTEM "howtomdb.xml">
   ]>
   
   <book>
  @@ -38,6 +38,7 @@
   &cmp.xml;
   &customizingjaws.xml;
   &advconfig.xml;
  +&howtomdb.xml;
   &designnotes.xml;
   <chapter><title>Howto</title>
   
  
  
  
  1.1                  manual/src/docs/howtomdb.xml
  
  Index: howtomdb.xml
  ===================================================================
  <chapter>
       <title>Working with Message Driven Beans</title>
     <para>Author:
        <author><firstname>Peter</firstname><surname>Antman</surname></author>
        <email>[EMAIL PROTECTED]</email>
     </para>
     <section><title>Introduction</title>
  <para>This chapter describes how to use Message Driven Beans with JBoss. Message 
Driven Beans are a new bean type added in the EJB 2.0 specification. They are 
therefore still somewhat unknown and under utilized. Hopefully this documentation will 
lessen that a bit, since JBoss has full support for Message Driven Beans.</para>
    </section>
  
      <section><title>Six steps to MDB nirvana in JBoss</title>
  
  <para>If you don't need to do any special configuration and already know how to code 
a message driven bean (MDB), here are 6 easy step to get it up and working with JBoss. 
 (The easiest way to get up and working with Message Driven Beans in JBoss is the 
check out the jbosstest from cvs and look into the mdb test.)</para>
  
    <orderedlist>
  
       <listitem>
         <para>
  Check out the latest JBoss from cvs and compile.
         </para>
       </listitem>
       <listitem>
         <para>
       Write the source code for a Message Driven Bean.
         </para>
       </listitem>
       <listitem>
         <para>Write the ejb-jar.xml descriptor.</para>
  
       <para>Here is an example for a bean listening on a queue with bean managed 
transactions :</para>
  <programlisting><![CDATA[
       <?xml version="1.0"?>
       <!DOCTYPE ejb-jar>
       <!--
        PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" 
       "http://java.sun.com/dtd/ejb-jar_2_0.dtd"
       -->
       <ejb-jar>
           <enterprise-beans>
             <message-driven>
               <ejb-name>QueueBean</ejb-name>
               <ejb-class>org.jboss.test.mdb.bean.QueueBean</ejb-class>
               <message-selector></message-selector>
               <transaction-type>Bean</transaction-type>
               <acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
               <message-driven-destination>
                  <destination-type>javax.jms.Queue</destination-type>
                  <subscription-durability>NonDurable</subscription-durability>
               </message-driven-destination>
             </message-driven>
       </enterprise-beans>
       </ejb-jar>
  ]]></programlisting>
       <para>
       And here are one for a durable topic with container managed transactions:</para>
  <programlisting><![CDATA[
       <?xml version="1.0"?>
       <!DOCTYPE ejb-jar>
       <message-driven>
               <ejb-name>DurableTopicBean</ejb-name>
               <ejb-class>org.jboss.test.mdb.bean.TopicBean</ejb-class>
               <message-selector></message-selector>
                <transaction-type>Container</transaction-type>
               <message-driven-destination>
                   <destination-type>javax.jms.Topic</destination-type>
                   <subscription-durability>Durable</subscription-durability>
               </message-driven-destination>
             </message-driven>
           </enterprise-beans>
           <assembly-descriptor>
             <container-transaction>
              <method>
                <ejb-name>DurableTopicBean</ejb-name>
                <method-name>*</method-name>
              </method>
              <trans-attribute>Required</trans-attribute>
              </container-transaction>
          </assembly-descriptor>
       </ejb-jar>
  ]]></programlisting>
  </listitem>
  <listitem>
         <para>
       Write the jboss.xml deployment descriptor (this MUST always be done with MDB in 
 jboss), but for a MDB without special need the container configuration need not be 
filled in. </para>
  
       <para>The destination-jndi-name element points to the queue.</para>
  
      <para> Here are the one for the queue bean:</para>
  <programlisting><![CDATA[
  
       <?xml version="1.0" encoding="Cp1252"?>
  
       <jboss>
            <enterprise-beans>
              <message-driven>
                <ejb-name>QueueBean</ejb-name>
                <configuration-name>Standard Message Driven Bean</configuration-name>
                <destination-jndi-name>queue/testQueue</destination-jndi-name>
              </message-driven>
            </enterprise-beans>
       </jboss>
  ]]></programlisting>
  
      <para> And here for the durable topic:</para>
  <programlisting><![CDATA[
       <?xml version="1.0" encoding="Cp1252"?>
  
       <jboss>
            <enterprise-beans>
              <message-driven>
                <ejb-name>DurableTopicBean</ejb-name>
                <configuration-name>Standard Message Driven Bean</configuration-name>
                <destination-jndi-name>topic/testDurableTopic</destination-jndi-name>
                <mdb-user>john</mdb-user>
                <mdb-passwd>needle</mdb-passwd>
                <mdb-client-id>DurableSubscriberExample</mdb-client-id>
              </message-driven>
       </jboss>
  ]]></programlisting>
  </listitem>
  <listitem>
         <para>
       Edit jbossmq.xml in conf/default and ad the queue or topic
         </para>
       <para>Eg:</para>
  <programlisting><![CDATA[
               <Queue>
                       <Name>testQueue</Name>
               </Queue>
  ]]></programlisting>
  
  <para>     For the queue, and </para>
  
  <programlisting><![CDATA[
               <Topic>
                       <Name>testDurableTopic</Name>
               </Topic>
  ]]></programlisting>
  
  <para>plus</para>
  <programlisting><![CDATA[
                        <User>
                               <Name>john</Name>
                               <Password>needle</Password>
                               <Id>DurableSubscriberExample</Id>
                               <DurableSubscription>
                                       <Name>DurableSubscriberExample</Name>
                                       <TopicName>testDurableTopic</TopicName>
                               </DurableSubscription>
                       </User>
  ]]></programlisting>
  
  <para>for the durable topic.</para>
  
  </listitem>
  <listitem>
         <para>
       Deploy the bean, for example by packing it in a jar and copy it into the jboss 
deploy directory.</para>
  </listitem>
  </orderedlist>
  
  <para>Start sending messages to your bean</para>
  </section>  
  
  <section><title>Writing Message Driven Bean</title>
  <para>Message Driven Beans are a new part of EJB 2.0. The reason these beast where 
added is that there was no way in EJB 1.1 to handle asynchronous invocation. The 
primary reason behind this is that an EJB bean may never be invoke from other objects 
other than through its remote interface. Therefore a bean could never set itself up as 
a listener for asynchronous invocation.</para>
  
  <para>With MDB this lack is filled. An MDB is a bean without a remote interface, 
where the container sets itself up as a listener for asynchronous invocation and 
handles the invocation of the concrete bean, which follows all the usual roles for EJB 
beans.</para>
  
  <para>Message Driven Beans are primarily focused on JMS. An MDB is either a topic or 
a queue subscriber. One nice feature with MDB is that one gets multithreaded 
subscribers (even for Topics) without having to care about the subtle difficulties to 
write multithreaded code and to write multithreaded JMS message consumers.</para>
  
  <para>What should you use your beans to then? Basically you would use MDB any time 
you are about to create a JMS subscriber. Typical conditions for doing this is:</para>
  
  <itemizedlist>
  <listitem>
  Decouple an invoker from the invoked code.
  </listitem>
  <listitem>
  Make it possible for multiple parties to get you messages.
  </listitem>
  <listitem>
  Get asynchronous behavior, i.e. start long running code without the need to wait for 
the call to return.
  </listitem>
  </itemizedlist>
  
  <para>Here we will write some typical MDB.</para>
  
  <section><title>Hello World MDB</title>
  <para>An MDB follows a typical EJB contract. It must implement the following two 
interfaces:</para>
  
  <itemizedlist>
  <listitem>
  javax.ejb.MessageDrivenBean
  </listitem>
  <listitem>
  javax.jms.MessageListener
  </listitem>
  </itemizedlist>
  
  <para>An MDB must therefore typically contain the following four methods:</para>
  <programlisting><![CDATA[
  public void setMessageDrivenContext(MessageDrivenContext ctx);
  public void ejbCreate();
  public void ejbRemove();
  public void onMessage(Message message);
  ]]></programlisting>
  
  <para>The full program listing of a simple Hello World bean could look like 
this:</para>
  <programlisting><![CDATA[
  
  package test.bean;
  
  import javax.ejb.MessageDrivenBean;
  import javax.ejb.MessageDrivenContext;
  import javax.ejb.EJBException;
  
  import javax.jms.MessageListener;
  import javax.jms.Message;
  
  public class MDB implements MessageDrivenBean, MessageListener{
      private MessageDrivenContext ctx = null;
      public MDB() {
        
      }
      public void setMessageDrivenContext(MessageDrivenContext ctx)
        throws EJBException {
        this.ctx = ctx;
      }
      
      public void ejbCreate() {}
  
      public void ejbRemove() {ctx=null;}
  
      public void onMessage(Message message) {
        System.err.println("Bean got message" + message.toString() );
      }
  } 
  ]]></programlisting>
  
  <para>To deploy this into JBoss we will have to write two deployment descriptors. 
One standard ejb-jar and one that is JBoss specific. We will chose to make this bean a 
Topic subscriber. Since we do not do anything we could typically chose to use 
container managed transaction with NotRequired (although this would in most cases not 
be the best thing to do)</para>
  
  <programlisting><![CDATA[
       <?xml version="1.0"?>
       <!DOCTYPE ejb-jar>
       <message-driven>
               <ejb-name>MDB</ejb-name>
               <ejb-class>test.bean.MDB</ejb-class>
               <message-selector></message-selector>
                <transaction-type>Container</transaction-type>
               <message-driven-destination>
                   <destination-type>javax.jms.Topic</destination-type>
                   <subscription-durability>NonDurable</subscription-durability>
               </message-driven-destination>
             </message-driven>
           </enterprise-beans>
           <assembly-descriptor>
             <container-transaction>
              <method>
                <ejb-name>DurableTopicBean</ejb-name>
                <method-name>*</method-name>
              </method>
              <trans-attribute>NotRequired</trans-attribute>
              </container-transaction>
          </assembly-descriptor>
       </ejb-jar>
  ]]></programlisting>
  
  <para>We also need to write a small deployment descriptor that is specific for 
JBoss. The full version of this is quite big, and with it it is possible to configure 
the MDB container quite a bit. For most users this is not necessary, and the may use 
the standard configuration. The most important part of the descriptor is the 
specification of the destination. We chose the testTopic since it is always available 
in JBoss.</para>
  
  <programlisting><![CDATA[
  
       <?xml version="1.0" encoding="Cp1252"?>
  
       <jboss>
            <enterprise-beans>
              <message-driven>
                <ejb-name>MDB</ejb-name>
                <configuration-name>Standard Message Driven Bean</configuration-name>
                <destination-jndi-name>test/testTopic</destination-jndi-name>
              </message-driven>
            </enterprise-beans>
       </jboss>
  ]]></programlisting>
  
  <para>Then you will have to pack these into a jar. Here is one way to do it:</para>
  <programlisting><![CDATA[
    mkdir dist
    mkdir dist/META-INF
    mkdir dist/test
    cp MDB.class dist/test
    cp MDB-jar.xml dist/META-INF/ejb-jar.xml
    cp MDB-jboss.xml dist/META-INF/jboss.xml
    cd dist
    java jar -cvf mdb.jar .
  
  ]]></programlisting>
  <para>Copy the bean into the deploy directory of JBoss.</para>
  
  <para>To send stuff to your bean you need a JMS publisher. This is standard JMS 
programming, but here is one way to do it:</para>
  <programlisting><![CDATA[
  import javax.naming.*;
  
  import javax.jms.*;
  
  public class Main {
    public static void main(String arg[]) {
      try {
        // Get access to JNDI
        Context context = new InitialContext();
  
        // Lookup the managed connection factory for a topic
        TopicConnectionFactory topicFactory = 
           (TopicConnectionFactory)context.lookup(TOPIC_FACTORY);
  
        // Create a connection to the JMS provider
        TopicConnection topicConnection = topicFactory.createTopicConnection();
        
        // Creat a topic session
        TopicSession session = topicConnection.createTopicSession(
                                                   // No transaction
                                                   false, 
                                                   // Auto ack
                                                   Session.AUTO_ACKNOWLEDGE);
  
       // Lookup the destination you want to publish to
       Topic topic = (Topic)context.lookup("topic/testTopic");
  
       // Create a publisher
       TopicPublisher pub = session.createPublisher(topic);
  
       // Create a message
       TextMessage message = session.createTextMessage();
       message.setText("Hello World!");
  
       // Publish the message
       pub.publish(topic, message);
  
       // Close the stuff
       session.close();
       topicConnection.close();
      catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
  
  ]]></programlisting>
  
  <para>
  You will typically have to include the following jars in your classpath: 
jbossmq.client.jar, jms.jar, jnp-client.jar and jta-spec1_0_1.jar.
  </para>
  
  </section>
  
  <section><title>MDB as a listener</title>
  <para>We will here look at an example that does something more real. One nice way to 
use MDB is to have them act/emulate the listener pattern. The normal way with 
listeners is to have them register them self on the object emitting events. This is 
not possible with MDB, since the bean them self may only do some work when the receive 
an even (a message). Set up of MDB as a listener will therefore have to be done 
outside of the MDB. This could be as simple as defining a topic or queue and hardwire 
into the event generator to publish it's events/messages to that destination. It's 
also possible to create a more generic framework for message driven callback, 
something I have done with JMS and JMX. But that is for another document. Lets instead 
look on the MDB side.</para>
  
  <para>One way to partition the logic in EJB is to have one bean that does the real 
work (contains the logic), this could be a stateless session bean or an entity bean, 
and one bean acting as a listener. Lets write a working, but simplified version of 
this patter. We start with a very simple stateless session bean, with just one method: 
doWork. To make it easy we let it take a String as its mission. This is straight 
forward. First we need a home interface:</para>
  
  <programlisting><![CDATA[
  package msgbean.interfaces;
  
  import java.rmi.RemoteException;
  import javax.ejb.*;
  
  public interface WorkerHome  extends EJBHome {
     public Worker create() throws RemoteException, CreateException;
  } // WorkerHome
  ]]></programlisting>
  
  <para>We also need a remote interface</para>
  
  <programlisting><![CDATA[
  package msgbean.interfaces;
  
  import java.rmi.RemoteException;
  import javax.ejb.EJBObject;
  
  public interface Worker extends EJBObject {
      public void doWork(String work) throws RemoteException;
      
  } // Worker
  ]]></programlisting>
  
  <para>And finally we need the bean class:</para>
  
  <programlisting><![CDATA[
  package msgbean.server;
  import javax.ejb.*;
  import java.rmi.RemoteException;
  import javax.naming.*;
  
  public class WorkerBean implements SessionBean {
      private SessionContext ctx;
      public WorkerBean() {
        
      }
      public void setSessionContext(javax.ejb.SessionContext ctx) throws 
RemoteException { this.ctx = ctx; }
      public void unsetSessionContext() throws RemoteException { this.ctx = null; }
      public void ejbActivate() throws RemoteException {}
      public void ejbPassivate() throws RemoteException {}
      public void ejbRemove() throws RemoteException {}
      public void ejbCreate() throws CreateException {}
      public void doWork(String work) {
        System.out.println("WorkerBean doing work: " + work);
      }
  } // WorkerBean
  ]]></programlisting>
  
  <para>We will write the deployment descriptor for the bean later, since we will pack 
it with the listener.</para>
  
  <para>The next step is to write the listener bean. Here we will ad basically one 
thing: the ability to lookup the Worker bean and invoke it. We look up the bean 
through a an  JNDI reference defined via ejb-ref. This might be done in 
ejbCreate().</para>
  
  <programlisting><![CDATA[
  Context initCtx = new InitialContext();
            workerHome = (WorkerHome)initCtx.lookup("java:comp/env/ejb/worker");
  ]]></programlisting>
  
  <para>In the onMessage() method we get what we need out of the message. Here we 
could have sent an object of some kind, known to the listener. For simplicity we here 
chose to send a TextMessage and send the content of the message to the worker.</para>
  
  <programlisting><![CDATA[
            Worker worker = workerHome.create();
            if (message instanceof TextMessage) {
                TextMessage m = (TextMessage)message;
                worker.doWork(m.getText());
            }
  ]]></programlisting>
  
  <para>Here is the complete listing of the class:</para>
  
  <programlisting><![CDATA[
  package msgbean.server;
  
  import javax.ejb.*;
  
  import javax.naming.*;
  
  import javax.jms.*;
  
  import msgbean.interfaces.WorkerHome;
  import msgbean.interfaces.Worker;
  
  public class ListenerBean implements MessageDrivenBean, MessageListener{
      private MessageDrivenContext ctx = null;
      private WorkerHome workerHome = null;
  
      public ListenerBean() {
        
      }
      public void setMessageDrivenContext(MessageDrivenContext ctx)
        throws EJBException {
        this.ctx = ctx;
      }
      
      public void ejbCreate() throws CreateException {
        try {
            Context initCtx = new InitialContext();
            workerHome = (WorkerHome)initCtx.lookup("java:comp/env/ejb/worker");
        }catch(Exception ex) {
            throw new CreateException("Could not get worker: " + ex);
        }
  
      }
  
      public void ejbRemove() {ctx=null;}
  
      public void onMessage(Message message) {
        try {
            Worker worker = workerHome.create();
  
            // Get the message, here we could have an ObjectMessage containg
            // an object of a known class. We use Text here for simplicity
            if (message instanceof TextMessage) {
                TextMessage m = (TextMessage)message;
                worker.doWork(m.getText());
            }
            
        }catch(Exception ex) {
            throw new EJBException("Could not call worker " + ex);
        }
  
      }
  } 
  ]]></programlisting>
  
  <para>To deploy this into JBoss we need to write two deployment descriptors, one 
standard ejb, and one for JBoss. Lets begin with the standard one. For ease of use we 
include both beans into one jar. For the Message Driven Bean we have to decide if its 
a topic or not, and what kind of transactions it should run under. In this case we 
chose a topic and container managed transaction. We also have to specify an ejb-ref so 
that the listener can lookup the home of the WorkerBean:</para>
  
  <programlisting><![CDATA[
   <message-driven>
        <ejb-name>ListenerBean</ejb-name>
        <ejb-class>msgbean.server.ListenerBean</ejb-class>
          <message-selector></message-selector>
          <transaction-type>Container</transaction-type>
          <ejb-ref>
          <description>The Workers home</description>
          <ejb-ref-name>ejb/worker</ejb-ref-name>
          <ejb-ref-type>Session</ejb-ref-type>
            <ejb-link>WorkerBean</ejb-link>
          <home>msgbean.interfaces.WorkerHome</home>
          <remote>msgbean.interfaces.Worker</remote>
          </ejb-ref>
           <message-driven-destination>
              <destination-type>javax.jms.Topic</destination-type>
              <subscription-durability>NonDurable</subscription-durability>
          </message-driven-destination>
        </message-driven>
  ]]></programlisting>
  
  <para>We also have to ad an entry for the Worker bean:</para>
  
  <programlisting><![CDATA[
  <session>
          <description>Worker bean</description>
          <display-name>Worker</display-name>
          <ejb-name>WorkerBean</ejb-name>
          <home>msgbean.interfaces.WorkerHome</home>
          <remote>msgbean.interfaces.Worker</remote>
          <ejb-class>msgbean.server.WorkerBean</ejb-class>
          <session-type>Stateless</session-type>
          <transaction-type>Container</transaction-type>
        </session>
  ]]></programlisting>
  
  <para>Here is the complete deployment descriptor, including defintions of the 
transaction type.</para>
  
  <programlisting><![CDATA[
  <!DOCTYPE ejb-jar>
  <ejb-jar>
      <enterprise-beans>
  
        <message-driven>
        <ejb-name>ListenerBean</ejb-name>
        <ejb-class>msgbean.server.ListenerBean</ejb-class>
          <message-selector></message-selector>
          <transaction-type>Container</transaction-type>
          <ejb-ref>
          <description>The Workers home</description>
          <ejb-ref-name>ejb/worker</ejb-ref-name>
          <ejb-ref-type>Session</ejb-ref-type>
            <ejb-link>WorkerBean</ejb-link>
          <home>msgbean.interfaces.WorkerHome</home>
          <remote>msgbean.interfaces.Worker</remote>
          </ejb-ref>
           <message-driven-destination>
              <destination-type>javax.jms.Topic</destination-type>
              <subscription-durability>NonDurable</subscription-durability>
          </message-driven-destination>
        </message-driven>
        <session>
          <description>Worker bean</description>
          <display-name>Worker</display-name>
          <ejb-name>WorkerBean</ejb-name>
          <home>msgbean.interfaces.WorkerHome</home>
          <remote>msgbean.interfaces.Worker</remote>
          <ejb-class>msgbean.server.WorkerBean</ejb-class>
          <session-type>Stateless</session-type>
          <transaction-type>Container</transaction-type>
        </session>
      </enterprise-beans>
      <assembly-descriptor>
       <container-transaction>
         <method>
           <ejb-name>ListenerBean</ejb-name>
           <method-name>*</method-name>
         </method>
       <trans-attribute>Required</trans-attribute>
       </container-transaction>
       <container-transaction>
        <method>
        <ejb-name>WorkerBean</ejb-name>
        <method-intf>Remote</method-intf>
        <method-name>*</method-name>
        </method>
        <trans-attribute>Required</trans-attribute>
      </container-transaction>
      </assembly-descriptor>
    </ejb-jar>
  ]]></programlisting>
  
  <para>We also need to write a jboss.xml deployment descriptor. This is needed 
because it the destination JNDI name must be defined somewhere. Here we can not use on 
a default.</para>
  
  <programlisting><![CDATA[
  <?xml version="1.0" encoding="Cp1252"?>
  <jboss>
     <enterprise-beans>
         <message-driven>
           <ejb-name>ListenerBean</ejb-name>
                 <configuration-name>Standard Message Driven Bean</configuration-name>
           <destination-jndi-name>topic/testTopic</destination-jndi-name>
         </message-driven>
         <secure>false</secure>
     </enterprise-beans>
  </jboss>
  ]]></programlisting>
  
  <para>We then have to compile the beans. You will have to add several jar-files to 
your class-path to have success with this. Among the most important, and special for 
MDB, is that you will need ejb2.0 from lib/ext to be able to compile. You also need to 
pack them into a jar-file. Do this as described above. Deploy by copying the jar-file 
to deploy. You may use the example publisher above to publish messages to the 
bean.</para>
  </section>
  
  <section><title>The adapter pattern</title>
  <para>Another way to use MDB is as an adapter, for example between different 
messaging systems. This is rewarding, especially if you have  an J2EE Connector 
resource adapter for the other system, since you then will get a very effective, 
multithreaded and pooled system, without any advanced programing. Since I have written 
such a resource adapter for the messaging server XmlBlaster we will here look at one 
way to adapt between JMS and <ulink 
url="http://www.xmlblaster.org"><citetitle>XmlBlaster</citetitle></ulink>.</para>
  
  <para>The adapter uses an MDB to subscribe to a topic. It then republish it to an 
XmlBlaster server through the XmlBlasterK2 J2EE Connector adapter.The code is pretty 
straight forward. A J2EE Connector resource adapter must be deployed into the JBoss 
server. It is then referenced from within the bean the same way you would reference a 
JDBC resource. You would look it up this way:</para>
  
  <programlisting><![CDATA[
  factory = (BlasterConnectionFactory)new InitialContext ().lookup 
("java:comp/env/xmlBlaster");
  ]]></programlisting>
  
  <para>And use it this way:</para>
  
  <programlisting><![CDATA[
    con = factory.getConnection();
    // Construct Blaster Headers
    String key ="<key oid=\"" + message.getJMSMessageID() +"\" 
contentMime=\"text/xml\"></key>";
    String qos = "<qos></qos>";
    con.publish( new MessageUnit(key,msg.getBytes(),qos));
  ]]></programlisting>
  
  <para>The complete bean is pretty straight forward (almost the same code could be 
used to write directly to a database for example):</para>
  
  <programlisting><![CDATA[
  package javaclients.j2ee.k2;
  
  import javax.naming.InitialContext;
  import javax.naming.NamingException;
  
  import javax.ejb.MessageDrivenBean;
  import javax.ejb.MessageDrivenContext;
  import javax.ejb.EJBException;
  
  import javax.jms.MessageListener;
  import javax.jms.Message;
  import javax.jms.TextMessage;
  import javax.jms.JMSException;
  
  import javax.resource.ResourceException;
  
  import org.xmlBlaster.j2ee.k2.client.*;
  
  import org.xmlBlaster.util.XmlBlasterException;
  import org.xmlBlaster.engine.helper.MessageUnit;
  
  public class JmsAdapter implements MessageDrivenBean, MessageListener{
      private MessageDrivenContext ctx = null;
      private BlasterConnectionFactory factory = null;
      public JmsAdapter() {
        
      }
      public void setMessageDrivenContext(MessageDrivenContext ctx)
        throws EJBException {
        this.ctx = ctx;
        try {
            factory = (BlasterConnectionFactory)new InitialContext ().lookup 
("java:comp/env/xmlBlaster");
        } catch (NamingException ex) {
            throw new EJBException ("XmlBlaster not found: "+ex.getMessage ());
        }catch(Throwable th) {
            System.err.println("Throwable: " +th);
            th.printStackTrace();
            throw new EJBException("Throwable in setContext: " +th);
        }
  
      }
      
      public void ejbCreate() {}
  
      public void ejbRemove() {ctx=null;}
  
      public void onMessage(Message message) {
  
        BlasterConnection con = null;
        try {
            // Get message to handle
            System.err.println("Got message: " + message);
  
            if (message instanceof TextMessage) {
                 String msg = ((TextMessage)message).getText();
  
                 // Get connection
                 con = factory.getConnection();
                 
                 // Construct Blaster Headers - howto hanlde key here?
                 String key ="<key oid=\"" + message.getJMSMessageID() +"\" 
contentMime=\"text/xml\"></key>";
                 String qos = "<qos></qos>";
                 con.publish( new MessageUnit(key,msg.getBytes(),qos));
                 
            } else {
                System.err.println("Got message type I cant handle");
            }
            
        }catch(ResourceException re) {
            System.err.println("Resource ex: " +re);
            re.printStackTrace();
        } catch(XmlBlasterException be) {
            System.err.println("Blaster ex: " +be);
            be.printStackTrace();
        }catch(JMSException je) {
            System.err.println("JMSException ex: " +je);
            je.printStackTrace();
        }catch(Throwable th) {
            System.err.println("Throwable: " +th);
            th.printStackTrace();
            
        }finally {   
            try {
                if (con != null)
                    con.close ();
            }
            catch (Exception ex) {}
            
        }
      }
  } // MessageBeanImpl
  
  ]]></programlisting>
  
  <para>The deployment descriptors for this bean follows the normal way to write them, 
except that you will have to add entries for the resource-ref. Here is the ejb 
deployment descriptor:</para>
  
  <programlisting><![CDATA[
  <?xml version="1.0"?>
  <!DOCTYPE ejb-jar>
  <ejb-jar>
      <enterprise-beans>
        <message-driven>
        <ejb-name>JmsAdapter</ejb-name>
        <ejb-class>javaclients.j2ee.k2.JmsAdapter</ejb-class>
          <message-selector></message-selector>
           <transaction-type>Container</transaction-type>
          <resource-ref>
                 <res-ref-name>xmlBlaster</res-ref-name>
                 
<res-type>org.xmlBlaster.j2ee.k2.client.BlasterConnectionFactory</res-type>
                 <res-auth>Container</res-auth>
          </resource-ref>
          <message-driven-destination>
              <destination-type>javax.jms.Topic</destination-type>
              <subscription-durability>NonDurable</subscription-durability>
          </message-driven-destination>
        </message-driven>
      </enterprise-beans>
      <assembly-descriptor>
       <container-transaction>
         <method>
           <ejb-name>JmsAdapter</ejb-name>
           <method-name>*</method-name>
         </method>
         <trans-attribute>Required</trans-attribute>
       </container-transaction>
      </assembly-descriptor>
    </ejb-jar>
  ]]></programlisting>
  
  <para>And here is the jboss.xml descriptor:</para>
  
  <programlisting><![CDATA[
  <?xml version="1.0" encoding="Cp1252"?>
  
  <jboss>
      <resource-managers>
          <resource-manager>
            <res-name>xmlBlaster</res-name>
            <res-jndi-name>java:/XmlBlasterDS</res-jndi-name>
      </resource-manager>
      </resource-managers>
       <enterprise-beans>
         <message-driven>
           <ejb-name>JmsAdapter</ejb-name>
         <configuration-name>Standrad Message Driven Bean</configuration-name>
  
           <destination-jndi-name>topic/testTopic</destination-jndi-name>
            <resource-ref>
             <res-ref-name>xmlBlaster</res-ref-name>
             <resource-name>xmlBlaster</resource-name>
           </resource-ref>
         </message-driven>
          <secure>false</secure>
       </enterprise-beans>
   </jboss>
  ]]></programlisting>
  
  </section>
  </section>
  
  <section><title>Advanced MDB configuration</title>
  <para>The MDB implementation for JBoss have been written such that a user should not 
have to know a lot about the internals of JBoss. A part from the feew necessary lines 
in jboss.xml nothing else than standard knowledge of Message Driven Beans should 
suffice.</para>
  
  <para>Following the design of JBoss, the MDB implementation is also extremely 
configurable, if one want to tune or totally change the default configuration and 
implementation! Here follows some short notes on howto configure MDB for JBoss</para>
  
  <section><title>EJB deployment descriptor</title>
  <para>All MDB:s are quite configurable, apart from them being deployed in JBoss. 
Here are the basic choices that can be made:</para>
  
  <itemizedlist>
  <listitem>
  <para>A bean may be either a javax.jms.Topic or a javax.jms.Queue bean, which is 
decided in the stanza "destination-type", eg.</para>
  <programlisting><![CDATA[
           <message-driven-destination>
              <destination-type>javax.jms.Queue</destination-type>
              <subscription-durability>NonDurable</subscription-durability>
          </message-driven-destination>
  
  ]]></programlisting>
  </listitem>
  <listitem>
  <para>If a bean is a Topic it may be either NonDurable or Durable, wich is described 
in the stanza "subscription-durability".</para>
  <programlisting><![CDATA[
          <message-driven-destination>
              <destination-type>javax.jms.Topic</destination-type>
              <subscription-durability>Durable</subscription-durability>
          </message-driven-destination>
  ]]></programlisting>
  </listitem>
  <listitem>
  <para>A bean may be have transaction either as bean managed or container managed, 
which is described in the stanza "transaction-type", eg.</para>
  <programlisting><![CDATA[
  <transaction-type>Container</transaction-type>
  ]]></programlisting>
  </listitem>
  <listitem>
  <para>A bean managed bean may have an acknowledge type of either AUTO_ACKNOWLEDGE or 
DUPS_OK_ACKNOWLEDGE. This is currently not supported in JBoss since the container 
always are receiving messages under a transaction. But it is described in the stanza 
"acknowledge-mode".</para>
  <programlisting><![CDATA[
          <transaction-type>Bean</transaction-type>
          <acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
  ]]></programlisting>
  </listitem>
  <listitem>
  <para>A container managed bean may either specify transactions as Required or 
NotSupported, which is done in the container-transaction transaction part.</para>
  <programlisting><![CDATA[
      </container-transaction>
            <container-transaction>
         <method>
           <ejb-name>DurableTopicBean</ejb-name>
           <method-name>*</method-name>
         </method>
       <!-- May also be NotSupported -->
       <trans-attribute>Required</trans-attribute>
       </container-transaction>
  ]]></programlisting>
  </listitem>
  <listitem>
  <para>An MDB may also specify a selector which follows the JMS syntax for a 
selector. Which is specified in the stanza "message-selector".</para>
  <programlisting><![CDATA[
  <message-selector>JMSType='activityCompletion'</message-selector>
  ]]></programlisting>
  </listitem>
  </itemizedlist>
  
  <para>In a message-driven stanza one may also have the normal stuff that may be in a 
ejb deployment descriptor, such as "ejb-ref" and "resource-ref".</para>
  </section>
  
  <section><title>JBoss configuration</title>
  <para>The meat of the configuration options are in the jboss.xml deployment 
descriptor. This may be divided into two part. The necessary one that configures a 
particular bean. And the optional one (that will be taken from standardjboss.xml if 
not found and which configures the container. We will describe both here, but the 
container configuration will be broken into several parts since it involves stuff 
outside the jboss.xml file.</para>
  
  <para>In the bean part one always have to specify the JNDI for the JMS destination. 
In JBossMQ this always begins with either "topic/" or "queue/" followed by the name of 
the destination.</para>
  
  <programlisting><![CDATA[
  <destination-jndi-name>queue/testObjectMessage</destination-jndi-name>
  ]]></programlisting>
  
  <para>You also have to tell the beans name and the name of the container 
configuration. To use the one in standardjboss.xml you should give the name "Standard 
Message Driven Bean".</para>
  <programlisting><![CDATA[
   <message-driven>
           <ejb-name>QueueBean</ejb-name>
           <configuration-name>Standard Message Driven Bean</configuration-name>
           <destination-jndi-name>queue/testQueue</destination-jndi-name>
    </message-driven>
  ]]></programlisting>
  
  <para>It is also possible to use a name and a password to log in to JBossMQ. These 
may be used by them self. If you have specified a Durable Topic they are however 
required. Then one also have to specify a client-id. All these stuff are configurable 
in conf/default/jbossmq.xml. Using one of the default in that file we could have a 
deployment descriptor looking like this:</para>
  
  <programlisting><![CDATA[
    <message-driven>
       <ejb-name>DurableTopicBean</ejb-name>
       <configuration-name>Standard Message Driven Bean</configuration-name>
       <destination-jndi-name>topic/testDurableTopic</destination-jndi-name>
       <mdb-user>john</mdb-user>
       <mdb-passwd>needle</mdb-passwd>
       <mdb-client-id>DurableSubscriberExample</mdb-client-id>
     </message-driven>
  ]]></programlisting>
  
  <para>The container stuff for MDB are in standardjboss.xml. It is however possible 
to override this configuration by including it in the jboss.xml deployment descriptor. 
I will first give you the complete look if doing like this, and then go through the 
individual entries.</para>
  
  <programlisting><![CDATA[
  <?xml version="1.0" encoding="Cp1252"?>
  <jboss>
     <enterprise-beans>
         <message-driven>
           <ejb-name>ObjectMessageBean</ejb-name>
           <configuration-name>My Config</configuration-name>
           <destination-jndi-name>queue/testObjectMessage</destination-jndi-name>
         </message-driven>
         <secure>false</secure>
     </enterprise-beans>
     <container-configurations>
         <container-configuration>
              <container-name>My Config</container-name>
              <call-logging>false</call-logging>
              
<container-invoker>org.jboss.ejb.plugins.jms.JMSContainerInvoker</container-invoker>
              <container-interceptors>
                  <interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor>
                  <interceptor>org.jboss.ejb.plugins.SecurityInterceptor</interceptor>
                  <!-- CMT -->
                  <interceptor 
transaction="Container">org.jboss.ejb.plugins.TxInterceptorCMT</interceptor>
                  <interceptor transaction="Container" 
metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor>
                  <interceptor 
transaction="Container">org.jboss.ejb.plugins.MessageDrivenInstanceInterceptor</interceptor>
                  <!-- BMT -->
                  <interceptor 
transaction="Bean">org.jboss.ejb.plugins.MessageDrivenInstanceInterceptor</interceptor>
                  <interceptor 
transaction="Bean">org.jboss.ejb.plugins.MessageDrivenTxInterceptorBMT</interceptor>
                  <interceptor transaction="Bean" 
metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor>
              </container-interceptors>
              
<instance-pool>org.jboss.ejb.plugins.MessageDrivenInstancePool</instance-pool>
              <instance-cache></instance-cache>
              <persistence-manager></persistence-manager>
              <transaction-manager>org.jboss.tm.TxManager</transaction-manager>
              <container-invoker-conf>
                        
<JMSProviderAdapterJNDI>DefaultJMSProvider</JMSProviderAdapterJNDI>
                        
<ServerSessionPoolFactoryJNDI>StdJMSPool</ServerSessionPoolFactoryJNDI>
                        <MaximumSize>15</MaximumSize>
                        <MaxMessages>1</MaxMessages>
                <Optimized>True</Optimized>
              </container-invoker-conf>
              <container-pool-conf>
              <MaximumSize>100</MaximumSize>
              <MinimumSize>10</MinimumSize>
              </container-pool-conf>
          </container-configuration>
  
      </container-configurations>
      <resource-managers />
     </container-configurations>
  ]]></programlisting>
  
  <para>Here we go through some ways to configure the MDB container</para>
  <section><title>Container invoker</title>
  <para>The container invoker is what sends messages into the container system. It is 
this that is responsible for handling everything that has to do with JMS. The rest of 
the MDB container parts are basically agnostic to what kind of message system that the 
container invoker uses. It is therefore possible to write a new container invoker for 
other types of message system. Currently there are one limitation to this. The bean 
class still has to implement the MessageListener interface and the invoker therefore 
have to adopt to this interface. This will be made pluggable in a later release of 
MDB. </para>
  </section>
  
  <section><title>Container interceptor</title>
  <para>The container interceptor are an integral part of the container system and 
each does something particular to fulfill the EJB container contract. All of them are 
pluggable, meaning that it is possible to write new implementations of the and plug 
them in for a bean with a particular need. This should be considered a very advanced 
task.</para>
  </section>
  
  <section><title>Container invoker configuration</title>
  <para>This is probably the most interesting part to understand. Lets look at it in 
smaller pieces. First it defines a "JMSProviderAdapterJNDI":</para>
  <programlisting><![CDATA[
  <JMSProviderAdapterJNDI>DefaultJMSProvider</JMSProviderAdapterJNDI>
  ]]></programlisting>
  <para>There should be a JNDI name to a JMX bean where one might lookup a provider 
adapter class. This class is then used by the invoker to find the names of the 
connection factories, all IntitialContext lookups are done through this class, to make 
it possible to get access to a JMS provider outside of JBoss.</para>
  
  <para>The name is by default bound to the JbossMQProvider in jboss.jcml</para>
  <programlisting><![CDATA[
    <mbean code="org.jboss.jms.jndi.JMSProviderLoader" 
name=":service=JMSProviderLoader,name=JBossMQProvider">
      <attribute name="ProviderName">DefaultJMSProvider</attribute>
      <attribute 
name="ProviderAdapterClass">org.jboss.jms.jndi.JBossMQProvider</attribute>
    </mbean>
  ]]></programlisting>
  
  <para>It is however possible to add more JMSProviders to the jboss.jcml and use them 
in the container configuration. On  possible reason to do this is if one want to 
listen to a queue or topic in another JBoss server. The one could define another 
provider and configure its context. Say we have a JBoss server on a machine 
remote.com. We might then ad this to jboss.jcml:</para>
  
  <programlisting><![CDATA[
    <mbean code="org.jboss.jms.jndi.JMSProviderLoader" 
name=":service=JMSProviderLoader,name=RemoteJMSProvider">
      <attribute name="ProviderName">RemoteJMSProvider</attribute>
      <attribute 
name="ProviderAdapterClass">org.jboss.jms.jndi.JBossMQProvider</attribute>
      <attribute name="ProviderUrl">remote.com:1099</attribute>
    </mbean>
  ]]></programlisting>
  
  <para>Note how we added a "ProviderUrl" attribute. In jboss.xml we would write this 
in the JMSProviderAdapterJNDI element:</para>
  
  <programlisting><![CDATA[
  <JMSProviderAdapterJNDI>RemoteJMSProvider</JMSProviderAdapterJNDI>
  ]]></programlisting>
  
  <para>OBSERVE. I have this working for non transacted connections. It has not bean 
fully verified to work with the current JBossMQProvider.</para>
  
  <para>Another way to use this configuration option is to integrate another JMS 
provider into JBoss. This was actually how MDB was first implemented in JBoss through 
the OpenJMS implementation. To do this one have to implement the interface 
org.jboss.jms.jndi.JMSProviderAdapter. Be aware though that if the JMS provider does 
not support the full JMS ASF (chapter 8 in the JMS spec) you will have to write a full 
implementation of both the ProvuderAdapter and the ServerSession stuff.</para>
  
  <para>Next we have the "ServerSessionPoolFactoryJNDI" element</para>
  <programlisting><![CDATA[
  <ServerSessionPoolFactoryJNDI>StdJMSPool</ServerSessionPoolFactoryJNDI>
  ]]></programlisting>
  
  <para>This also points to a class loaded through jboss.jcml. It is the entry point 
to the ServerSessionPool. If one needs to write a provider specific pool or do some 
customization of the existing one, it would be possible to load that for a particular 
bean. The existing one is defined this way in jboss.jcml:</para>
  <programlisting><![CDATA[
    <mbean code="org.jboss.jms.asf.ServerSessionPoolLoader" 
name=":service=ServerSessionPoolMBean,name=StdJMSPool">
      <attribute name="PoolName">StdJMSPool</attribute>
      <attribute 
name="PoolFactoryClass">org.jboss.jms.asf.StdServerSessionPoolFactory</attribute>
    </mbean>
  ]]></programlisting>
  
  <para>The first implementation of MDB for JBoss was based on another 
ServerSessionPoolFactory, specially written for OpenJMS. This is currently not 
verified to work. What do work is the pluggability of ServerSessionPool 
factories.</para>
  
  <para>The last two entries looks like this:</para>
  
  <programlisting><![CDATA[
    <MaximumSize>15</MaximumSize>
    <MaxMessages>1</MaxMessages>
  ]]></programlisting>
  
  <para>The first of these - "MaximumSize" - defines how large the pool will be, i.e 
how many session it will have ready to serve incoming messages. The second one is used 
to configure the maximum number of messages a session is allowed to handle at once. I 
have never tweaked that one, and do not know if JBossMQ actually support that option. 
It might enhance performance if used.</para>
  </section>
  
  </section>
  
  </section>
  
  </chapter>
  
  
  
  
  
  
  
  
  
  

Reply via email to