1 2 Previous Next 24 Replies Latest reply on Dec 2, 2006 5:56 PM by kurtstam Go to original post
      • 15. Re: Design of a Content Based Routing Service
        kurtstam

        Thanks Dave I'll give that a try.

        • 16. Re: Design of a Content Based Routing Service
          kurtstam

          I have been working on the CBR configuration, and this is what we have so far:

          The CBR can be started as a service by putting a fragment like this in your deployment-configuration.xml (taken from the qa/junit/src/org/jboss/soa/esb/listeners/message/ContentBasedRouting.xml):

          <ContentBasedRouter
           service-category="MessageRouting"
           service-name="ContentBasedRoutingService"
           service-description="This CBR routs messages to different service destination based whether the messageType is XML or Serializable"
           listenerClass="org.jboss.soa.esb.listeners.message.CbrJmsQueueListener"
           connection-factory="ConnectionFactory"
           destination-type="queue"
           destination-name="queue/A"
           jndi-type="jboss"
           jndi-URL="localhost"
           message-selector="service='ContentBasedRouting-SerializableOrXml'"
           >
           <cbr ruleSet="JBossESBRules.drl" />
           </ContentBasedRouter>
          


          This will bring up a CBR with serviceName ContentBasedRoutingService, and it is of type CbrJmsQueueListener (which extends JmsQueueListener).

          The message-selector is set to 'ContentBasedRouting-SerializableOrXml' and the section you can see the ruleset it will use is 'JBossESBRules.drl'. So if you send your message to this destination it will route this message based on that ruleSet.

          Now, the outcoming of the routing is one or more destinations. Based on the setting of the flag 'MessageRouter.DELIVER_MESSAGE' in the message
          the Router itself will take care of the delivery (if set to true, which is the default behavior) or if set to false it will simply return the Collection of destinationServices. This part becomes interesting when the CBR is called from an action. Let's say you have the following section in your deployment-config.xml:

          <CreditAgencyJMSOutput
           listenerClass="org.jboss.soa.esb.listeners.JmsQueueListener"
          
           actions="CreditCheckResponse-To-BankQuote, CBR-To-Banks,
          BankQuote-Transform,CBR-Deliver"
          
           maxThreads="1"
           queueConnFactoryClass="ConnectionFactory"
           listenJndiType="jboss"
           listenJndiURL="localhost"
           listenQueue="queue/A"
           listenMsgSelector="sample_loanbroker_servicecode='creditResponse'"
          >
          
          </CreditAgencyJMSOutput>
          
          <Action name="CBR-To-Banks" processor="addDestinationListToMessage" />
          <Action name="CBR-Deliver" processor="deliverToDestinations" />
          


          The idea here is the Credit-Agency response is send to the CBR, which obtains the list of destinationServices. Which gets set on the message. Now before delivering them you may want to execute another action before sending it out for delivery. The action I m ade up here is the 'BankQuote-Transform'. The the next action 'CBR-Deliver', sends the message on it's merry way.

          Note that if you have multiple ruleSets, you can define a Listener for each and you can then chain these routers, for example sort out the XML-based messages first (and route those with XPATH). It seems a nice way to break the ruleSets up manageable sets. The JRulesBasedRouter keeps a static HashMap of each of these ruleSets.

          Well so far the update, I'm opening the floor for suggestions :).

          --Kurt

          • 17. Re: Design of a Content Based Routing Service
            kurtstam

            The XPath based DSL worked like a charm. It's all checked in. If you have time you should look at the ContentBasedRoutingUnitTest to see it in action.

            Also I now have a real example for the CbrProxyAction configuration. (Taken from qa: CbrProxyActionUnitTest

            So if your service need to use the CBR you can add it into the action pipeline like this

            <action name="TestDefaultRouteAction"
             process="route"
             class="org.jboss.soa.esb.actions.CbrProxyAction"
             service-category="MessageRouting"
             service-name="ContentBasedRoutingService" />
            


            Where process set to:

            1. routeAndDeliver (the default), this fires the rules, obtains destinations and delivers the message to this destination service.
            2. route, files the rules and adds a collection of destination Services into the message, then at a later time you can call
            3. deliver, which obtains the destinations from the message and delivers them.

            For now the rules are jared up into a jbossesb-rules.jar. We'll have to see if there is need to make this more dynamic.

            Thanks for your help Dave! And it's not too late for some last minute suggestions or comments.

            --Kurt


            • 18. Re: Design of a Content Based Routing Service

              I have a few concerns with the router. Hopefully these aren't issues and it's just me being nieve.

              Is the method of caching WorkingMemories thread safe? I assume (maybe falsely?) that the ESB is multi-threaded. That is, it can process multiple messages at once by using separate threads. I see two problems here: if two messages come in for routing at once, both with the same ruleset, the two threads could walk all over each other. If both threads are sharing the same WorkingMemory object, how can you be sure which message or destinationServices list the rules are operating on? Below is a patch that addresses the issue by using synchronization. One alternative would be to pool WorkingMemory objects.

              I also fixed what I think is a memory leak with the Message being retained in the WorkingMemory forever.

              Index: F:/dev/jbossesb-workspace/JBossESB/product/core/services/src/org/jboss/internal/soa/esb/services/routing/cbr/JBossRulesRouter.java
              ===================================================================
              --- F:/dev/jbossesb-workspace/JBossESB/product/core/services/src/org/jboss/internal/soa/esb/services/routing/cbr/JBossRulesRouter.java (revision 7517)
              +++ F:/dev/jbossesb-workspace/JBossESB/product/core/services/src/org/jboss/internal/soa/esb/services/routing/cbr/JBossRulesRouter.java (working copy)
              @@ -25,12 +25,13 @@
               import java.io.InputStreamReader;
               import java.io.Reader;
               import java.util.ArrayList;
              -import java.util.HashMap;
               import java.util.List;
               import java.util.Map;
              +import java.util.concurrent.ConcurrentHashMap;
              
               import org.apache.log4j.Logger;
               import org.apache.log4j.Priority;
              +import org.drools.FactHandle;
               import org.drools.RuleBase;
               import org.drools.RuleBaseFactory;
               import org.drools.WorkingMemory;
              @@ -49,7 +50,7 @@
               */
               public class JBossRulesRouter extends ContentBasedRouter
               {
              - private static Map<String,WorkingMemory> workingMemories=new HashMap<String,WorkingMemory>();
              + private static Map<String,WorkingMemory> workingMemories=new ConcurrentHashMap<String,WorkingMemory>();
               private static Logger logger = Logger.getLogger(JBossRulesRouter.class);
               /**
               * Route the message, where the routing rules are supplied as part of
              @@ -87,20 +88,22 @@
               {
               List<String> destinationServices = new ArrayList<String>();
               try {
              - if (!workingMemories.containsKey(ruleSet)) {
              + WorkingMemory workingMemory = workingMemories.get(ruleSet);
              + if (workingMemory == null) {
               logger.log(Priority.INFO, "Reading ruleSet from file=" + ruleSet);
               RuleBase ruleBase = readRuleBase(ruleSet, ruleLanguage);
              - WorkingMemory workingMemory = ruleBase.newWorkingMemory();
              + workingMemory = ruleBase.newWorkingMemory();
               workingMemories.put(ruleSet, workingMemory);
               }
               logger.log(Priority.DEBUG, "Obtained message=" + message + " with ruleSet=" + ruleSet);
              - WorkingMemory workingMemory = workingMemories.get(ruleSet);
              - workingMemory.setGlobal("destinationServices", destinationServices);
              - workingMemory.assertObject(message);
              - logger.log(Priority.INFO, "Fire the rules engine");
              - workingMemory.fireAllRules();
              + synchronized (workingMemory) {
              + workingMemory.setGlobal("destinationServices", destinationServices);
              + FactHandle factHandle = workingMemory.assertObject(message);
              + logger.log(Priority.INFO, "Fire the rules engine");
              + workingMemory.fireAllRules();
              + workingMemory.retractObject(factHandle);
              + }
               //Now route there, later we will implement an option to place a callback.
              - destinationServices = (List) workingMemory.getGlobal("destinationServices");
               logger.log(Priority.DEBUG, "Destination Services List: " + destinationServices);
               Boolean deliverMessages = (Boolean) message.getProperties().getProperty(MessageRouter.DELIVER_MESSAGES);
               //Only actuall deliver the message if this is set in the message
              


              • 19. Re: Design of a Content Based Routing Service

                Uh, naive != nieve. Oops.

                • 20. Re: Design of a Content Based Routing Service
                  kurtstam

                  Thanks for the feedback Dave. I liked all the changes in your patch and have just checked them in.

                  --Kurt

                  • 21. Re: Design of a Content Based Routing Service
                    tirelli

                    Hi all,

                    Interesting thread you got here.

                    I would like to ask if you tried, and if not, to suggest you to try caching the RuleBase instead of the working memories.

                    The reason is we design JBRules to support rulebase sharing, but working memory should be so cheap to create that no synchronization should be needed... just create a new working memory every time you need one (when working with stateless working memories).

                    This would allow you to avoid the synchronization code and the cost of retracting the fact from the working memory.

                    Let me know if you tried that already or want me to help with it.

                    []s
                    Edson

                    • 22. Re: Design of a Content Based Routing Service
                      kurtstam

                      Hi Edson,

                      I would love to prevent synchronization, and no I didn't not try caching the RuleBase. I don't really have time to look into it now :(. But if you can give me a hand we may get those changes into the GA release a few weeks after.

                      Cheers,

                      --Kurt

                      • 23. Re: Design of a Content Based Routing Service

                        Kurt, I made the changes and I'll email you the patch.

                        • 24. Re: Design of a Content Based Routing Service
                          kurtstam

                          Thanks Dave,

                          The patch made into RC1. Edson maybe you can take a look at new new codebase? I think that for GA I want to pull the destinations out of the rules and put them into the configuration, passing them into the rules when the rule base is constructed. Maybe an hashmap where the rules reference them by key. This so one can see the possible routing destinations by looking at the config, and you don't have to drop into the rules themselves. Note that the config gets reloaded periodically so we'd have to destroy the ruleBases at that point? Let me know what you think.

                          thx,

                          --Kurt

                          1 2 Previous Next