Version 8

    Micro Container Integration

     

    This task is two fold:

     

    1. To Integrate with the JBoss MicroContainer.

    2. To POJOify the current MBeans used within JBM.

     

    The MicroContainer

     

    The JBoss MicroContainer allows the deployment of POJO's outside of the JBoss Application Server. It supports direct IOC Style dependency injection along with life cycle management. JBM can leverage this functionality to allow standalone deployment to allow developers to embed JMS within their own code.

     

    Tasks

     

    POJOification

     

    The first task is to change the current beans within JBM to POJO's and allow the MicroContainer to enforce the dependencies. Currently when one Object needs access to another it will  look up the service using JMX. i.e. the ServerPeer will look up the PersistenceManager. This relationship will be inverted and the MicrContainer will inject the PersistenceManager into ServerPeer.  This will involve changing the objects to have setters and getters instead of multi parameter constructors and would be defined within the MicroContainer configuration file something similar to the following:

     

    <bean name="PersistenceManager" class="org.jboss.messaging.core.impl.JDBCPersistenceManager">
            <property name="maxParams">500</property>
          <property name="reaperPeriod">5000</property>
    </bean>
    
    <bean name="ServerPeer" class="org.jboss.jms.server.ServerPeer">
            <property name="serverPeerID">0</property>
            <property name="clusterPullConnectionFactoryName">jboss.messaging.connectionfactory:service=ClusterPullConnectionFactory</property>
            <property name="persistenceManager">
                <inject bean="PersistenceManager"></inject>
            </property>
    </bean>
    

    The following MBeans within JBM will either be POJOified or deprecated if they are merely a wrapper around the actual implementation, i.e. MessagingPostOfficeService and MessagingPostOffice.

    1. ServerPeer

    2. MessagingPostOfficeService

    3. JDBCServiceSupport

    4. TopicService

    5. QueueService

    6. DestinationServiceSupport

    7. ConnectionFactory

    8. BridgeService

     

    • This may change anyway with 2.0 because of the work Tim is doing around the persistence mechanism.

     

    The life cycle of these objects will remain unchanged as the MicroContainer will call the appropriate methods, i.e. starting and stopping each object.

     

    External Dependencies

     

    Obviously JBM currently has JBoss dependencies that it currently needs to run. These will be either looked via JMX or JNDI.

     

    The JMX dependencies includes things like the Transaction Manager and the Security Service. It should be transparent as to whether we are running embedded or not so we will need have some sort of Service Locater to take care of looking up services. The configuration for this will be something like as follows:

    <bean name="PersistenceManager" class="org.jboss.messaging.core.impl.JDBCPersistenceManager">
            <property name="tm">
                <inject bean="ServiceLocator" property="transactionManager"></inject>
            </property>
            <property name="maxParams">500</property>
          <property name="reaperPeriod">5000</property>
    </bean>
    

     

     

    In this example the Service Locater will have a method called getTransactionManager() that will be called and the return value injected into the PersistenceManager Object. If we are running with the JBoss Application Server then the Object will already exist and a simple call to the MBeanServer will locate it:

    TransactionManagerServiceMBean tms =
                        (TransactionManagerServiceMBean) MBeanServerInvocationHandler.
                                newProxyInstance(mBeanServer, transactionManager, TransactionManagerServiceMBean.class, false);
    
                return tms.getTransactionManager();
    

     

    When running embedded we can provide a seperate configuration to create the Object, the following will also create an MBeanServer and register the Transaction Manager Service with it.This means that the Service Locater would always find the Object registered with the MBeanServer and the above code would work with both.

        <bean name="MBeanServer" class="java.lang.Object">
            <constructor factoryClass="org.jboss.jms.server.microcontainer.factory.mBeanServerCreator"
                         factoryMethod="createMBeanServer"></constructor>
        </bean>
        
        <aop:lifecycle-configure xmlns:aop="urn:jboss:aop-beans:1.0"
                                 name="DependencyAdvice"
                                 class="org.jboss.aop.microcontainer.aspects.jmx.JMXLifecycleCallback"
                                 classes="@org.jboss.aop.microcontainer.aspects.jmx.JMX">
            <property name="mbeanServer">
                <inject bean="MBeanServer"></inject>
            </property>
        </aop:lifecycle-configure>
    
        <bean class="com.arjuna.ats.jbossatx.jta.TransactionManagerService"
              name="TransactionManager">
            <property name="transactionTimeout">300</property>
            <property name="objectStoreDir">tx-object-store</property>
            <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss:service=TransactionManager",exposedInterface=com.arjuna.ats.jbossatx.jta.TransactionManagerServiceMBean.class)
            </annotation>
        </bean>
    

    Alternatively, we could just create the Transaction Manager as a simple bean and either inject it into the Service Locater or look it up via the MicroContainer Kernel.

     

    <bean name="TransactionManager" class="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple"></bean>
    

     

    The JNDI depencencies include things like the DataSource. The EJB3 project has integrated with the MicroContainer and they provide a local naming server and a helper binder class to register objects with. I'm not sure what the EJB3 team have for this code, it is currently within their codebase.

     

    It works as follows, The local naming server is defined as a bean, and then an object can be mapped into it by using the binder:

     

       <bean name="Naming" class="org.jnp.server.SingletonNamingServer"></bean>
    
       <bean name="InitialContextProperties" class="java.util.Hashtable">
          <constructor>
          <parameter class="java.util.Map">
                <map keyClass="java.lang.String" valueClass="java.lang.String">
                   <entry>
                      <key>java.naming.factory.initial</key>
                      <value>org.jnp.interfaces.LocalOnlyContextFactory</value>
                   </entry>
                   <entry>
                       <key>java.naming.factory.url.pkgs</key>
                       <value>org.jboss.naming:org.jnp.interfaces</value>
                   </entry>
                </map>
          </parameter>
          </constructor>
       </bean>
    
       <bean name="b1" class="org.jboss.ejb3.embedded.JndiBinder">
          <property name="jndiProperties"><inject bean="InitialContextProperties"></inject></property>
          <property name="target"><inject bean="aDatasource"></inject></property>
          <property name="bindTo">java:/DefaultDS</property>
          <property name="serializable">true</property>
       </bean>
    

     

    If we arent running embedded then the binder will bind to the JBossAS naming server as normal.

     

    If we decide not to use the EJB3 code then we could implement something along the similar lines.

     

    Connection Factories and Destinations

     

    As mention earlier these Objects will be POJO's instead of MBeans, however if we inject these using the normal methods then the configuration file may become overly complex. For isntance, every queue created would need to injected with a PersistanceManager amongst other things, this may make the configuration of new Destinations and Factories convoluted. An alternative method could be to provide a seperate configuration just for these. We would scan specific directories and look for say -JBM.xml, which could look something like this:

        <connection-factory name="ConnectionFactory">
            <bindings>
                <binding>/ConnectionFactory</binding>
                <binding>/XAConnectionFactory</binding>
                <binding>java:/ConnectionFactory</binding>
                <binding>java:/XAConnectionFactory</binding>
            </bindings>
        </connection-factory>
    
        <queue name="QueueWithOwnDLQAndExpiryQueue">
            <post-office>PostOffice</post-office>
            <DLQ>PrivateDLQ</DLQ>
            <ExpiryQueue>PrivateExpiryQueue</ExpiryQueue>
            <bindings>
                <binding>/QueueWithOwnDLQAndExpiryQueue</binding>
            </bindings>
        </queue>
    

     

     

    We would have a utility class that had access to the PostOffice and the ServerPeer that would take care of creating the destinations and Connection Factories, I think this would make configuration for the average user a lot simpler.

     

    Running the server embedded

     

    The JBoss MicroContainer comes with some Bootstrap classes for starting the server, we could build upon these to allow a user to embed JBM. The following code shows how to run JBM:

        public static void main(String[] args) throws Exception
        {
            JBMBootstrapServer bootstrap = new JBMBootstrapServer(args);
            bootstrap.run();
    
            //do some JMS stuff
    
    
        }
    

     

    Administration

     

    Currently a user has access to administration utilities such as creating destinations dynamically by JMX. If we remove this functionality then we need to replace it. I think some discussion is needed around this area. I would like as a user to have access to a fully fledged admin API, so you could do something like:

         InitialContext ic = new InitialContext();
         Admin admin = ic.lookup("JBM/admin");
         admin.connect("user","password");
         admin.doSomeStuff();