Coverage Report - org.mule.routing.outbound.AbstractRoundRobinMessageSplitter
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractRoundRobinMessageSplitter
0%
0/53
0%
0/28
0
AbstractRoundRobinMessageSplitter$Counter
0%
0/6
0%
0/2
0
 
 1  
 /*
 2  
  * $Id: AbstractRoundRobinMessageSplitter.java 19191 2010-08-25 21:05:23Z tcarlson $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 5  
  *
 6  
  * The software in this package is published under the terms of the CPAL v1.0
 7  
  * license, a copy of which has been included with this distribution in the
 8  
  * LICENSE.txt file.
 9  
  */
 10  
 
 11  
 package org.mule.routing.outbound;
 12  
 
 13  
 import org.mule.DefaultMuleMessage;
 14  
 import org.mule.api.MuleMessage;
 15  
 import org.mule.api.endpoint.OutboundEndpoint;
 16  
 import org.mule.api.lifecycle.InitialisationException;
 17  
 import org.mule.api.processor.MessageProcessor;
 18  
 import org.mule.config.i18n.CoreMessages;
 19  
 
 20  
 import java.util.HashMap;
 21  
 import java.util.Iterator;
 22  
 import java.util.LinkedList;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 
 26  
 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
 27  
 
 28  
 /**
 29  
  * <code>FilteringListMessageSplitter</code> accepts a List as a message payload
 30  
  * then routes list elements as messages over an endpoint where the endpoint's filter
 31  
  * accepts the payload.
 32  
  */
 33  0
 public class AbstractRoundRobinMessageSplitter extends AbstractMessageSplitter
 34  
 {
 35  0
     private boolean deterministic = true;
 36  
     
 37  
     /* Users should only disable this if they have filters configured on the endpoint
 38  
      * that will control which endpoint will receive the message
 39  
      */
 40  0
     private boolean disableRoundRobin = false;
 41  
 
 42  0
     private static final AtomicInteger globalCounter = new AtomicInteger(0);
 43  0
     private boolean failIfNoMatch = true;
 44  
 
 45  
     @Override
 46  
     public void initialise() throws InitialisationException
 47  
     {
 48  0
         if (isDisableRoundRobin())
 49  
         {
 50  0
             setDeterministic(true);
 51  
         }
 52  0
         super.initialise();
 53  0
     }
 54  
 
 55  
     /**
 56  
      * Method used just to split the message into parts.  Each part should be an entry in the list.
 57  
      * The list can contain either {@link org.mule.api.MuleMessage} objects or just payloads (Mule will
 58  
      * automatically convert the payloads into messages).
 59  
      * <p/>
 60  
      * This method can be overridden by custom implementations of splitter router where the distribution of
 61  
      * the message parts will be done using either round robin or endpoint filtering.
 62  
      *
 63  
      * @param message the source message to split into parts
 64  
      * @return a list of payload objects or {@link org.mule.api.MuleMessage} objects. Usually, it is sufficient
 65  
      *         just to return payload objects
 66  
      */
 67  
     protected List splitMessage(MuleMessage message)
 68  
     {
 69  0
         if (message.getPayload() instanceof List)
 70  
         {
 71  0
             return new LinkedList((List) message.getPayload());
 72  
         }
 73  
         else
 74  
         {
 75  0
             throw new IllegalArgumentException(CoreMessages.objectNotOfCorrectType(
 76  
                     message.getPayload().getClass(), List.class).getMessage());
 77  
         }
 78  
     }
 79  
 
 80  
     /**
 81  
      * Retrieves a specific message part for the given endpoint. the message will then be
 82  
      * routed via the provider. <p/> <strong>NOTE:</strong>Implementations must provide
 83  
      * proper synchronization for shared state (payload, properties, etc.)
 84  
      *
 85  
      * @param message   the current message being processed
 86  
      * @param endpoints A list of targets that will be used to dispatch each of the parts
 87  
      * @return a {@link java.util.List} of message parts.  Each part will become the payload of the outgoing
 88  
      *         message.  Note that the parts will be dispatched to
 89  
      */
 90  
     @Override
 91  
     protected SplitMessage getMessageParts(MuleMessage message, List<MessageProcessor> endpoints)
 92  
     {
 93  0
         SplitMessage splitMessage = new SplitMessage();
 94  
 
 95  0
         List payloads = splitMessage(message);
 96  
         // Cache the properties here because for some message types getting the
 97  
         // properties can be expensive
 98  0
         Map props = new HashMap();
 99  0
         for (String propertyKey : message.getOutboundPropertyNames())
 100  
         {
 101  0
             Object value = message.getOutboundProperty(propertyKey);
 102  0
             if (value != null)
 103  
             {
 104  0
                 props.put(propertyKey, value);
 105  
             }
 106  0
         }
 107  
 
 108  0
         Counter counter = new Counter();
 109  
 
 110  0
         for (Iterator iterator = payloads.iterator(); iterator.hasNext();)
 111  
         {
 112  0
             Object payload = iterator.next();
 113  0
             MuleMessage part = new DefaultMuleMessage(payload, props, muleContext);
 114  0
             boolean matchFound = false;
 115  
 
 116  
             // If there is no filter assume that the endpoint can accept the
 117  
             // message. Endpoints will be processed in order to only the last
 118  
             // (if any) of the the targets may not have a filter
 119  
             //Try each endpoint in the list. If there is no match for any of them we drop out and throw an exception
 120  0
             for (int j = 0; j < endpoints.size(); j++)
 121  
             {
 122  0
                 MessageProcessor target =  endpoints.get(counter.next());
 123  0
                 OutboundEndpoint endpoint = target instanceof OutboundEndpoint ? (OutboundEndpoint) target : null;
 124  0
                 if (endpoint == null || endpoint.getFilter() == null || endpoint.getFilter().accept(part))
 125  
                 {
 126  0
                     if (logger.isDebugEnabled())
 127  
                     {
 128  0
                         logger.debug("Endpoint filter matched. Routing message over: "
 129  
                                 + endpoint.getEndpointURI().toString());
 130  
                     }
 131  0
                     iterator.remove();
 132  0
                     splitMessage.addPart(part, endpoint);
 133  0
                     matchFound = true;
 134  0
                     break;
 135  
                 }
 136  
             }
 137  0
             if (!matchFound)
 138  
             {
 139  0
                 if (isFailIfNoMatch())
 140  
                 {
 141  0
                     throw new IllegalStateException(CoreMessages.splitMessageNoEndpointMatch(endpoints, payload).getMessage());
 142  
                 }
 143  
                 else
 144  
                 {
 145  0
                     logger.info("No splitter match for message part. 'failIfNoMatch=false' ingoring message part.");
 146  
                 }
 147  
             }
 148  
 
 149  
             //start from 0 again
 150  0
             if (isDisableRoundRobin())
 151  
             {
 152  0
                 counter = new Counter();
 153  
             }
 154  
 //            if (enableCorrelation != ENABLE_CORRELATION_NEVER)
 155  
 //            {
 156  
 //                // always set correlation group size, even if correlation id
 157  
 //                // has already been set (usually you don't have group size yet
 158  
 //                // by this point.
 159  
 //                final int groupSize = payload.size();
 160  
 //                message.setCorrelationGroupSize(groupSize);
 161  
 //                if (logger.isDebugEnabled())
 162  
 //                {
 163  
 //                    logger.debug("java.util.List payload detected, setting correlation group size to "
 164  
 //                                    + groupSize);
 165  
 //                }
 166  
 //            }
 167  0
         }
 168  0
         return splitMessage;
 169  
 
 170  
     }
 171  
 
 172  
     /**
 173  
      * If this option is true (the default)
 174  
      * then the first message part is routed to the first endpoint, the
 175  
      * second part to the second endpoint, etc, with the nth part going to
 176  
      * the (n modulo number of targets) endpoint.
 177  
      * If false then the messages will be distributed equally amongst all
 178  
      * targets.
 179  
      * <p/>
 180  
      * The behaviour changes if the targets have filters since the message part will get routed
 181  
      * based on the next endpoint that follows the above rule AND passes the endpoint filter.
 182  
      *
 183  
      * @return true if deterministic has been set to true
 184  
      */
 185  
     public boolean isDeterministic()
 186  
     {
 187  0
         return deterministic;
 188  
     }
 189  
 
 190  
     /**
 191  
      * If this option is true (the default)
 192  
      * then the first message part is routed to the first endpoint, the
 193  
      * second part to the second endpoint, etc, with the nth part going to
 194  
      * the (n modulo number of targets) endpoint.
 195  
      * If false then the messages will be distributed equally amongst all
 196  
      * targets.
 197  
      * <p/>
 198  
      * The behaviour changes if the targets have filters since the message part will get routed
 199  
      * based on the next endpoint that follows the above rule AND passes the endpoint filter.
 200  
      *
 201  
      * @param deterministic the value to set
 202  
      */
 203  
     public void setDeterministic(boolean deterministic)
 204  
     {
 205  0
         this.deterministic = deterministic;
 206  0
     }
 207  
 
 208  
 
 209  
     /**
 210  
      * The default behaviour for splitter routers is to round-robin across
 211  
      * targets. When using filters on targets it is sometimes desirable to use only the filters to
 212  
      * control which endpoint the split message part goes too. For example, if you have 3 targets where
 213  
      * two have a filter but the last does not, you'll need to disable round robin since the 3rd endpoint
 214  
      * may end up routing a message that one of the other targets should have routed.
 215  
      * Generally it is good practice to either configure all targets with filters or none, in this case
 216  
      * there is not need to set this property.
 217  
      *
 218  
      * @return true if disabled
 219  
      */
 220  
     public boolean isDisableRoundRobin()
 221  
     {
 222  0
         return disableRoundRobin;
 223  
     }
 224  
 
 225  
     /**
 226  
      * The default behaviour for splitter routers is to round-robin across
 227  
      * targets. When using filters on targets it is sometimes desirable to use only the filters to
 228  
      * control which endpoint the split message part goes too. For example, if you have 3 targets where
 229  
      * two have a filter but the last does not, you'll need to disable round robin since the 3rd endpoint
 230  
      * may end up routing a message that one of the other targets should have routed.
 231  
      * Generally it is good practice to either configure all targets with filters or none, in this case
 232  
      * there is not need to set this property.
 233  
      *
 234  
      * @param disableRoundRobin true if disabled
 235  
      */
 236  
     public void setDisableRoundRobin(boolean disableRoundRobin)
 237  
     {
 238  0
         this.disableRoundRobin = disableRoundRobin;
 239  0
     }
 240  
 
 241  
     /**
 242  
      * If none of the targets match a split message part i.e. each endpoint has a
 243  
      * filter for a certain message part. This flag controls whether the part is ignorred or an
 244  
      * exceptin is thrown.
 245  
      *
 246  
      * @return true if an exception should be thrown when no match is found
 247  
      */
 248  
     public boolean isFailIfNoMatch()
 249  
     {
 250  0
         return failIfNoMatch;
 251  
     }
 252  
 
 253  
     /**
 254  
      * If none of the targets match a split message part i.e. each endpoint has a
 255  
      * filter for a certain message part. This flag controls whether the part is ignorred or an
 256  
      * exceptin is thrown.
 257  
      *
 258  
      * @param failIfNoMatch true if an exception should be thrown when no match is found
 259  
      */
 260  
     public void setFailIfNoMatch(boolean failIfNoMatch)
 261  
     {
 262  0
         this.failIfNoMatch = failIfNoMatch;
 263  0
     }
 264  
 
 265  0
     private class Counter
 266  
     {
 267  
 
 268  
         private AtomicInteger counter;
 269  
 
 270  
         public Counter()
 271  0
         {
 272  0
             if (isDeterministic())
 273  
             {
 274  0
                 counter = new AtomicInteger(0);
 275  
             }
 276  
             else
 277  
             {
 278  0
                 counter = globalCounter;
 279  
             }
 280  0
         }
 281  
 
 282  
         public int next()
 283  
         {
 284  0
             return counter.getAndIncrement() % getRoutes().size();
 285  
         }
 286  
 
 287  
     }
 288  
 }