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