Version 18

    This is just a "Quick Start" type guide to the 5.0 codebase as it stands today.

     

    Overview

    You can checkout the code from SVN at http://anonsvn.jboss.org/repos/labs/labs/jbossesb/workspace/skeagh.

     

    • To build the codebase, just execute the maven command "mvn clean install".
    • To build a project for your IDE, just execute the maven command "mvn eclipse:eclipse" (or "mvn idea:idea" if running IntelliJ).

     

    Some of the things we've been working on so far:

     

    • Making it easier to implement custom inbound and outbound routing components (connectors).
    • Making it easier to configure more complex routing operations by making condition based filtering (to and from services) a first class construct.
    • Providing a more simplified and flexible Service programming model, including a more simplified "Message" object, scoped context objects (InvocationContext, DeploymentContext) and more.
    • Impelmenting a true "Bus" based architecture for message aware routing, with service location transparency.
    • Impelmenting a "Bus" based architecture, where the Bus is an abstract concept, allowing ESB "deployments" exchange messages via a Bus (or Buses) without having any knowledge of the "backbone" transport on which the bus is based e.g. JMS, InVM, File, HTTP etc.
    • Having no core dependency on a physical Registry for Service Discovery.  Instead, ESB5 Services "discover" each other through the Buses their deployments are connected into.  In this way, ESB5 utilises an "Distributed, In-memory, Non-Persisted Registry".  This avoids/reduces a number common registry associated issues encountered on ESB4.  It also makes unit testing a lot more straightforward.
    • Supporting multiple deployment models.  Already has early support for running standalone (i.e. no container), JBoss MC and OSGi.  The deployment layers are thin layers on top of the ESB core i.e. MC/OSGi/etc are not hardwired into the core of the ESB.

    Flash Demo

    A short Flash Demo can be viewed here.

    Configuration

    The basic ESB Configuration is as follows:

     

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
    
    
        <resources>
            <!-- Deployment lifecycle managed resources e.g. Schedules. -->
        </resources>
    
    
        <routing>
            <!-- Global routing configurations.  InRouters and OutRouters. -->
        </routing>
    
    
        <services>
            <!-- Service definitions. -->
        </services>
    
    
    </jbossesb>
    

     

    Any given configuration instance can contain one or all of <resources>, <routing> and <services>.  Note that <routing> configurations can be made outside the scope of the <service> configurations.  This allows the user to define "global" routing configurations separate from the <service> configurations.

    Service Configuration

    Servce configuration is very easy.

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
    
        <services>   
    
         <service class="org.acme.ShoppingService" serviceCategory="Merchant" serviceName="Shopping" serviceDescription="Shopping Service" />
    
        </services>
    
    </jbossesb>
    

     

    Earlier ESB 5 code supported configuring of both inbound and outbound routing on a <service> element.  We are currently investigating what it feels like to only support Global routing configuration (see Routing Configuration sections below).  Global routing configuration allows you to support more service instances off a single inbound/outbound router through a more flexible condition based filtering of messages to and from services (<filterTo>, <filterFrom>).

    Global Routing Configuration

    <inRouter> and <outRouter> configurations can be made global by adding them to the <routing> section of the configuration.

     

    As stated earlier, earlier ESB 5 code also supported router configurations on the <service> configuration, but we are experimenting with only supporting global router configurations that filter message to (<filterTo>) and from (<filterFrom>)

    services.

     

    Note: At the moment however, the codebase still supports configuration of outbound routers on services.  Added to that, the global <outRouter> configs don't yet support the <filterFrom> config (that would make them consistent with the <filterTo> config on the <inRouter> config). 

     

    The <service> configuration would probably still need to have support for "message aware" routing in some form.  This could be done by supporting <filterTo> configs on the <service> element.  That would also be consistent with how messages are routed out of <inRouters> via the <filterTo> configuration.

     

    Example Configuration (current):

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
    
        <resources>
            <resource id="schedule1" class="org.jboss.esb.schedule.SimpleSchedule">
                <property name="frequency">2000</property>
            </resource>
        </resources>
    
        <routing>
            <inRouters>
                <inRouter name="fileRouter" class="org.jboss.esb.file.FileInboundRouter">
                    <filterTo serviceCategory="ESB1" serviceName="Service1">
                        <processors>
                            <processor class="com.acme.transformers.StringToCustomer" />
                        </processors>                    
                    </filterTo>
    
                    <property name="scheduleResourceId">schedule1</property>
                    <property name="fileSelectorPattern">../target/*.txt</property>
                </inRouter>
            </inRouters>
        </routing>
    
        <services>
            <service serviceCategory="ESB1" serviceName="Service1" serviceDescription="ESB1 Service1" class="org.jboss.esb.test.PrintlnService">
                <outRouter name="route-to-ESB2-Service" class="org.jboss.esb.invoke.ServiceRouter">
                    <property name="toCategory">ESB2</property>
                    <property name="toService">Service</property>
                    <property name="replyToCategory">ESB1</property>
                    <property name="replyToService">Service2</property>
                </outRouter>
            </service>
    
            <service serviceCategory="ESB1" serviceName="Service2" serviceDescription="ESB1 Service2" class="org.jboss.esb.test.PrintlnService">
                <outRouter name="fileOutboundRouter" class="org.jboss.esb.file.FileOutboundRouter">
                    <processors>
                        <processor class="com.acme.transformers.CustomerToString" />
                    </processors>
                    <property name="fileNamePattern">../target/service2-received/${timestamp}.out</property>
                </outRouter>
            </service>
        </services>
    
    </jbossesb>
    

     

    Possible "Global Only" routing configuration:

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
    
        <resources>
            <resource id="schedule1" class="org.jboss.esb.schedule.SimpleSchedule">
                <property name="frequency">2000</property>
            </resource>
        </resources>
    
        <routing>
        
            <inRouters>
                <inRouter name="fileRouter" class="org.jboss.esb.file.FileInboundRouter">
                    <filterTo serviceCategory="ESB1" serviceName="Service1">
                        <processors>
                            <processor class="com.acme.transformers.StringToCustomer" />
                        </processors>                    
                    </filterTo>
    
                    <property name="scheduleResourceId">schedule1</property>
                    <property name="fileSelectorPattern">../target/*.txt</property>
                </inRouter>
            </inRouters>
            
            <outRouters>
                <outRouter name="fileOutboundRouter" class="org.jboss.esb.file.FileOutboundRouter">
                    <filterFrom serviceCategory="ESB1" serviceName="Service2">
                        <processors>
                            <processor class="com.acme.transformers.CustomerToString" />
                        </processors>                    
                    </filterFrom>
                    <property name="fileNamePattern">../target/service2-received/${timestamp}.out</property>
                </outRouter>
            </outRouters>
            
        </routing>
    
        <services>
            <service serviceCategory="ESB1" serviceName="Service1" serviceDescription="ESB1 Service1" class="org.jboss.esb.test.PrintlnService">                 
                <filterTo serviceCategory="ESB2" serviceName="Service">
                    <replyTo serviceCategory="ESB1" serviceName="Service2" />
                </filterTo>            
            </service>
    
            <service serviceCategory="ESB1" serviceName="Service2" serviceDescription="ESB1 Service2" class="org.jboss.esb.test.PrintlnService" />
        </services>
    
    </jbossesb>
    

     

    Filter Configuration

    In the following example (pulled from the tests), we have a single <inRouter> and from it we route messages to either Service "A" or "B", depending on the contents of the message:

     

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
        
        <routing>
            <inRouters>
                <inRouter name="inrouter" class="org.jboss.esb.TestInboundRouter">
                    <!-- Route AAA message to Service:A -->
                    <filterTo serviceCategory="Service" serviceName="A">
                        <evaluator class="org.jboss.esb.filter.MVELEvaluator">
                            <property name="expression"><!-- message.payload == "AAA" --></property>
                        </evaluator>
                        <processors>
                            <processor class="org.jboss.esb.TestTransformer">
                                <property name="addToMessage">:added-by-serviceA-router</property>
                            </processor>
                        </processors>
                    </filterTo>
    
                    <!-- Route BBB message to Service:B -->
                    <filterTo serviceCategory="Service" serviceName="B">
                        <evaluator class="org.jboss.esb.filter.MVELEvaluator">
                            <property name="expression"><!-- message.payload == "BBB" --></property>
                        </evaluator>
                        <processors>
                            <processor class="org.jboss.esb.TestTransformer">
                                <property name="addToMessage">:added-by-serviceB-router</property>
                            </processor>
                        </processors>
                    </filterTo>
                </inRouter>
    
            </inRouters>
        </routing>
    
        <services>
    
            <service serviceCategory="Service" serviceName="A" serviceDescription="A Service" class="org.jboss.esb.TestService" />
            <service serviceCategory="Service" serviceName="B" serviceDescription="B Service" class="org.jboss.esb.TestService" />
    
        </services>
    
    </jbossesb>
    

     

    From this example you can see:

     

    1. Each <filterTo> configuration defines a target Service.  The <filterTo> service name can also be an outbound router, where no service of that name is deployed.
    2. Each <filterTo> configuration defines an <evaluator>.  The above example/test MVELEvaluator, but could also be configured to use an XPath, Regex, or whatever type of evaluator makes sense in the situation etc.
    3. Each <filterTo> configurationcan optionally defines a set of <processors> that are applied to the message before routing to the target Service/outRouter.

     

    So with the filtering functonality on the <inRouter> configurations, we can facilitate the following types of routing:

    [inRouter -> processors -> filterTo -> processors] -> [service] -> [processors -> outRouter]

    [inRouter -> processors -> filterTo -> processors] -> [service]

    [inRouter -> processors -> filterTo -> processors] -> [processors -> outRouter]

     

    So a more elaborate scenario including outbound filters might be:

    inRouter->processors->|
                          |->inFilter1->processors->|->serviceA
                          |
                          |->inFilter2->processors->|->serviceB->|
                                                                 |->outFilter1->processors->outRouter1
                                                                 |
                                                                 |->outFilter2->processors->outRouter2
    

    API

    The main component interfaces in the API are:

    public interface InboundRouter
    {
    
        /**
         * Set the {@link MessageDispatcher} for the router.
         * <p/>
         * Implementations use the dispatcher to dispatch messages to the
         * consuming Service.  The ESB runtime handles the details of delivering the
         * message to the interested Service.
         *
         * @param dispatcher The {@link MessageDispatcher} instance.
         */
        void setDispatcher(MessageDispatcher dispatcher);
    }
    
    public interface MessageDispatcher
    {
        /**
         * Dispatch the message to the target service.
         *
         * @param message           The message to be dispatched.
         * @param invocationContext The InvocationContext for the message dispatch.
         */
        void dispatch(Message message, InvocationContext invocationContext);
    
        /**
         * Is the dispatcher instance processing messages.
         * <p/>
         * This is required by the undeploy process.  After uninitializing
         * all {@link InboundRouter} instances, the undeploy waits untill
         * all dispatchers have completed processing all their messages before
         * continuing with the undeploy (uninitializing all other components
         * and resources).
         *
         * @return True if the dispatcher has active messages that it is
         *         processing, otherwise false.
         */
        boolean isProcessingMessages();
    }
    
    public interface FilterEvaluator
    {
        /**
         * Filter the message.
         *
         * @param message The message to be evaluated.
         * @return True if the message is to be filtered through.  False if the message is to be filtered out.
         */
        boolean filterMessage(Message message);
    }
    
    public interface MessageProcessor
    {
        /**
         * Process the message.
         *
         * @param message The message to be processed.
         * @return Message The message object.
         * @throws MessageProcessingException An exception occured while processing the message.
         */
        Message process(final Message message) throws MessageProcessingException;
    }
    
    /**
     * A Service is just a specialized MessageProcessor.
     */
    public interface Service extends MessageProcessor
    {
        /**
         * Service process method.
         * @param message The message to be processed.
         * @return The resultant {@link Message}.
         * @throws ServiceException Service exception processing the message.
         */
        Message process(final Message message) throws ServiceException;
    }
    
    public interface OutboundRouter
    {
        /**
         * Route the message.
         *
         * @param message The message to be routed.
         * @throws RoutingException An exception occured while routing the message.
         */
        void route(Message message) throws RoutingException;
    }
    

     

    The Message API is (without getters and setters):

    public class Message implements Serializable
    {
        /**
         * Primary Payload.
         */
        private Object payload;
        /**
         * Attachments.
         */
        private Map<String, Object> attachments = new LinkedHashMap<String, Object>();
    }
    

    Current Implementation

    The current implementation is quite simple.  It's geared around the concept of a "DeploymentUnit" and a "DeploymentRuntime".  A DeploymentUnit is created from a configuration instance and is added to a DeploymentRuntime instance.  The DeploymentRuntime instance can then be deployed and undeployed.

    Runtime Creation

    The following Sequence Diagram illustrates the high level process.  "Deployer" can be a container level deployer (MC, OSGi etc), a standalone application, a JUnit test etc.

    create-runtime-sequence.png

    See DeploymentUtil.createRuntime.  This utility method wraps up steps 1 to 4 above:

    DeploymentRuntime runtime = DeploymentUtil.createRuntime(configStream);
    
    runtime.deploy();
    

    And the undeploy process is simply:

    runtime.undeploy();
    

    Component Configuration

    All ESB component (InboundRouters, Transformers (Processors), Services, OutboundRouters etc) configurations are reflectively injected.  Taking a Service as an example (but the same applies for the other components):

     

    Configuration:

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
    
        <services>
            <service class="org.jboss.esb.ServiceA" serviceCategory="Service" serviceName="A" serviceDescription="A Service">
                <property name="stringProperty">MyService</property>
                <property name="intProperty">1234</property>
                <property name="longProperty">1234567</property>
                <property name="booleanProperty">true</property>
                <property name="commandProperty">createOrder</property>
            </service>
        </services>
    
    </jbossesb>
    

     

    Component:

    public class ServiceA implements Service
    {
        @Property
        private String stringProperty;
    
        @Property(defaultVal = "-1")
        private int intProperty;
    
        @Property(use = Property.Use.OPTIONAL)
        private Long longProperty;
    
        @Property(defaultVal = "false")
        private boolean booleanProperty;
    
        @Property(choice = {"createOrder", "updateOrder", "shipOrder"})
        private String commandProperty;
    
        public final Message process(final Message message) throws ServiceException
        {
           // Service implementation code...
        }
    }
    

    As you can see, the component simply needs to annotate the Class property with the @Property annotation.

    Runtime Deployment

    Deploying the DeploymentRuntime instance involves:

    deploy-runtime.png

    Calling DeploymentRuntime.deploy results in all the components being "Initialized" in the oder outlined above.  DeploymentRuntime.undeploy basically executes the same process in reverse order, "Uninitializing" all the components.

    Component Initialization/Uninitialization

    Component implementations (InboundRouters, Transformers (Processors), Services, OutboundRouters etc) specify intitialization/uninitialization code by specifying one or more public void zero arg methods and annotating them with the @Initialize and @Uninitialize annotations e.g.

     

    public class ServiceA implements Service
    {
    
        @Initialize
        public void initialize()
        {
            // Initialization code...
        }
    
        @Uninitialize
        public void uninitialize()
        {
            // Uninitialization code...
        }
        
        public final Message process(final Message message) throws ServiceException
        {
           // Service implementation code...
        }
    }
    

    Routing Into the ESB (InboundRouter)

    Routing messages into the ESB is straightforward enough.  You simply implement the InboundRouter interface.

     

    InboundRouters can be split (bloadly) into two categories, depending on the type of transport they add support for:

    1. Event Driven (Push) e.g. JMS.
    2. Schedule Driven (Pull) e.g. File.

     

    InboundRouter implementations are configured and initialized/uninitialized in the same way as any other ESB component.  This is described in Runtime Creation and Runtime Deployment sections (I would link them if I knew how to do internal links in Clearspace).

    Event Driven InboundRouter

    The following is a simple example of an InboundRouter implementation for a hypothetical Event based "XXXX" transport:

    public class XXXXInboundRouter implements InboundRouter, TransportXXXXListener
    {
        private MessageDispatcher dispatcher;
    
        public void setDispatcher(MessageDispatcher dispatcher)
        {
            this.dispatcher = dispatcher;
        }
    
        public void onXXXMessage(XXXX transportMessage)
        {
            InvocationContext invocationContext = new InvocationContext();
            Message esbMessage;
    
            esbMessage = toESBMessage(transportMessage);
            mapInvocationParams(invocationContext);        
            
            dispatcher.dispatch(esbMessage, invocationContext);    }
    }
    

    This InboundRouter could then be configured to route messages into a service as follows:

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
        
        <services>
            <service class="com.acme.AcmeService" serviceCategory="cat" serviceName="A" serviceDescription="A Service">
                <inRouter name="xxxxRouter" class="com.acme.XXXXInboundRouter" />
            </service>
        </services>
    
    </jbossesb>
    

    Alternatively, it can be configured globally, to filter messages to multiple services:

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
        
        <routing>
            <inRouters>
                <inRouter name="xxxxRouter" class="com.acme.XXXXInboundRouter" />
    
                <filters>
                    <!-- Route to "A" if the "fromAddress" equals "source1" -->
                    <filter inRouter="xxxxRouter">
                        <evaluator class="org.jboss.esb.filter.MVELEvaluator">
                            <property name="expression">invParams.fromAddress == "source1"</property>
                        </evaluator>
                        <to serviceCategory="Service" serviceName="A" />
                    </filter>
    
                    <!-- Route to "B" if the "fromAddress" equals "source2" -->
                    <filter inRouter="xxxxRouter">
                        <evaluator class="org.jboss.esb.filter.MVELEvaluator">
                            <property name="expression">invParams.fromAddress == "source2"</property>
                        </evaluator>
                        <to serviceCategory="Service" serviceName="B" />
                    </filter>
                </filters>
    
            </inRouters>
        </routing>
    
        <services>
    
            <service serviceCategory="Service" serviceName="A" serviceDescription="A Service" class="com.acme.AcmeServiceA" />
            <service serviceCategory="Service" serviceName="B" serviceDescription="B Service" class="com.acme.AcmeServiceB" />
    
        </services>
    
    </jbossesb>
    

    Note that the InboundRouters and Services can be defined on separate ESB deploments, so routing to a Service can be defined in isolation from the Service itself (if that is a style you like).

    Schedule Driven InboundRouter

    A Schedule Driven InboundRouter implementation is quite similar to its Event Driven equivalent.  An example implementation for a hypothetical  Schedule based "YYYY" transport might be:

    public class YYYYInboundRouter implements InboundRouter extends AbstractScheduleListener
    {
        private MessageDispatcher dispatcher;
    
        public void setDispatcher(MessageDispatcher dispatcher)
        {
            this.dispatcher = dispatcher;
        }
    
        public void onSchedule() throws SchedulingException
        {
            YYYY transportMessage;
    
            // Message is not pushed in... need to go out and get it...
            transportMessage = pollForMessage();
                    
            if(transportMessage != null)
            {
                InvocationContext invocationContext = new InvocationContext();
                Message esbMessage;
    
                esbMessage = toESBMessage(transportMessage);
                mapInvocationParams(invocationContext);        
            
                dispatcher.dispatch(esbMessage, invocationContext);
            }
        }
    }
    

    As can be seen, the Schedule based listener needs to implement the AbstractScheduleListener class.  It could implement the ScheduleListener interface directly, but implementing it via the AbstractScheduleListener class is easier since it takes care of hooking the listener to the Schedule resource.

     

    Configuring the above Schedule Driven listener is basically similar to its Event Driven equivalent, with the additional configuration of the Schedule Resource:

    <jbossesb xmlns="http://www.jboss.org/jbossesb/xsd/jbossesb-5.0.xsd">
        
        <resources>
            <resource id="schedule1" class="org.jboss.esb.schedule.SimpleSchedule">
                <property name="frequency">2000</property>
            </resource>
        </resources>
        
        <services>
            <service class="com.acme.AcmeService" serviceCategory="Service" serviceName="A" serviceDescription="A Service">
                <inRouter name="yyyyInrouter" class="org.acme.YYYYInboundRouter">
                    <property name="scheduleResourceId">schedule1</property>
                </inRouter>
            </service>
        </services>
    
    </jbossesb>
    

    Note the SimpleSchedule <resource> configuration and how it is referenced from the <inRouter> configuration.

    Routing Out of the ESB (OutboundRouter)

    TODO

    Routing On the ESB (Bus)

    TODO

    Testing

    The current codebase is very easy to write functional tests against.

    Deployment Options

    TODO

    OSGi

    TODO

    JBoss Microcontainer

    Integration with the JBoss5 Application Server is accomplished by a set of deployers. This section will describe the deployer that currently exist.

     

    Deploying a JBossESB application is simply a matter of dropping a .jar or a .esb archive in the deploy directory (or sub directory). Doing so will trigger an invokation of the EsbConfigParser.

     

    EsbConfigParser

    This class is responsible for parsing the jboss-esb.xml file and producing a EsbMetaData instance.

     

    EsbMetaData

    This class only contains two fields, one is the name of the archive that this deployment came from and the other is the Esb DeploymentUnit (not to be confused with the DeploymentUnit in MicroContainer).

    The EsbMetaData is what the EsbDeployer deals with and this way there can be many different types of ParserDeployers that produce EsbMetaData instances but using different sources (not xml files). A deployment parser could read an xml configuration file, or it could gather the same information in another way, perhaps using annotations on class files, another language, etc.

     

    EsbDeployer

    This deployer only deals with EsbMetaData and is responsible for creating a BeanMetaData instance that describes a EsbDeployment. So it does not create any ESB objects at this stage only BeanMetaData that describes such objects. MC will take care of the creating instances and handling the lifecycle of the EsbDeployment.

     

    EsbDeployment

    This is the result of the deployment chain and is created and its lifecycle managed by the MicroContainer. It implements the create, start, and stop MC lifecyle callbacks. These methods create the DeploymentRuntime (create), deploy(start) and undeploy(stop). This class implements EsbDeploymentMBean to expose the stop and start operations.

     

    Deployment

    The deployers are located in the <servername>/deployers directory. The directory 'esb.deployer' contains the JBossESB deployers.