Coverage Report - org.mule.routing.inbound.AbstractEventResequencer
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractEventResequencer
83%
39/47
65%
13/20
2
 
 1  
 /*
 2  
  * $Id: AbstractEventResequencer.java 10489 2008-01-23 17:53:38Z dfeist $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.inbound;
 12  
 
 13  
 import org.mule.api.MessagingException;
 14  
 import org.mule.api.MuleEvent;
 15  
 
 16  
 import java.util.Arrays;
 17  
 import java.util.Comparator;
 18  
 
 19  
 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
 20  
 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
 21  
 
 22  
 /**
 23  
  * <code>AbstractEventResequencer</code> is used to receive a set of events,
 24  
  * resequence them and forward them on to their destination
 25  
  */
 26  
 
 27  
 // TODO MULE-841: much of the code here (like the spinloop) is *exactly* the same as in
 28  
 // AbstractEventAggregator, obviously we should unify this
 29  
 public abstract class AbstractEventResequencer extends SelectiveConsumer
 30  
 {
 31  
     protected static final String NO_CORRELATION_ID = "no-id";
 32  
 
 33  2
     private final ConcurrentMap eventGroups = new ConcurrentHashMap();
 34  
     private volatile Comparator comparator;
 35  
 
 36  
     public AbstractEventResequencer()
 37  
     {
 38  2
         super();
 39  2
     }
 40  
 
 41  
     public Comparator getComparator()
 42  
     {
 43  4
         return comparator;
 44  
     }
 45  
 
 46  
     public void setComparator(Comparator eventComparator)
 47  
     {
 48  2
         this.comparator = eventComparator;
 49  2
     }
 50  
 
 51  
     // //@Override
 52  
     public MuleEvent[] process(MuleEvent event) throws MessagingException
 53  
     {
 54  12
         MuleEvent[] result = null;
 55  
 
 56  12
         if (this.isMatch(event))
 57  
         {
 58  
             // indicates interleaved EventGroup removal (very rare)
 59  12
             boolean miss = false;
 60  
 
 61  
             // match event to its group
 62  12
             final Object groupId = this.getEventGroupIdForEvent(event);
 63  
 
 64  
             // spinloop for the EventGroup lookup
 65  
             while (true)
 66  
             {
 67  12
                 if (miss)
 68  
                 {
 69  
                     try
 70  
                     {
 71  
                         // recommended over Thread.yield()
 72  0
                         Thread.sleep(1);
 73  
                     }
 74  0
                     catch (InterruptedException interrupted)
 75  
                     {
 76  0
                         Thread.currentThread().interrupt();
 77  0
                     }
 78  
                 }
 79  
 
 80  
                 // check for an existing group first
 81  12
                 EventGroup group = this.getEventGroup(groupId);
 82  
 
 83  
                 // does the group exist?
 84  12
                 if (group == null)
 85  
                 {
 86  
                     // ..apparently not, so create a new one & add it
 87  4
                     group = this.addEventGroup(this.createEventGroup(event, groupId));
 88  
                 }
 89  
 
 90  
                 // ensure that only one thread at a time evaluates this EventGroup
 91  12
                 synchronized (group)
 92  
                 {
 93  
                     // make sure no other thread removed the group in the meantime
 94  12
                     if (group != this.getEventGroup(groupId))
 95  
                     {
 96  
                         // if that is the (rare) case, spin
 97  0
                         miss = true;
 98  0
                         continue;
 99  
                     }
 100  
 
 101  
                     // add the incoming event to the group
 102  12
                     group.addEvent(event);
 103  
 
 104  12
                     if (this.shouldResequenceEvents(group))
 105  
                     {
 106  4
                         result = this.resequenceEvents(group);
 107  4
                         this.removeEventGroup(group);
 108  
                     }
 109  
 
 110  
                     // result or not: exit spinloop
 111  12
                     break;
 112  0
                 }
 113  
             }
 114  
         }
 115  
 
 116  12
         return result;
 117  
     }
 118  
 
 119  
     /**
 120  
      * @see AbstractEventAggregator#createEventGroup(MuleEvent, Object)
 121  
      */
 122  
     protected EventGroup createEventGroup(MuleEvent event, Object groupId)
 123  
     {
 124  4
         return new EventGroup(groupId);
 125  
     }
 126  
 
 127  
     /**
 128  
      * @see AbstractEventAggregator#getEventGroupIdForEvent(MuleEvent)
 129  
      */
 130  
     protected Object getEventGroupIdForEvent(MuleEvent event)
 131  
     {
 132  12
         String groupId = event.getMessage().getCorrelationId();
 133  
 
 134  12
         if (groupId == null)
 135  
         {
 136  12
             groupId = NO_CORRELATION_ID;
 137  
         }
 138  
 
 139  12
         return groupId;
 140  
     }
 141  
 
 142  
     /**
 143  
      * @see AbstractEventAggregator#getEventGroup(Object)
 144  
      */
 145  
     protected EventGroup getEventGroup(Object groupId)
 146  
     {
 147  24
         return (EventGroup) eventGroups.get(groupId);
 148  
     }
 149  
 
 150  
     /**
 151  
      * @see AbstractEventAggregator#addEventGroup(EventGroup)
 152  
      */
 153  
     protected EventGroup addEventGroup(EventGroup group)
 154  
     {
 155  4
         EventGroup previous = (EventGroup) eventGroups.putIfAbsent(group.getGroupId(), group);
 156  
         // a parallel thread might have removed the EventGroup already,
 157  
         // therefore we need to validate our current reference
 158  4
         return (previous != null ? previous : group);
 159  
     }
 160  
 
 161  
     /**
 162  
      * @see AbstractEventAggregator#removeEventGroup(EventGroup)
 163  
      */
 164  
     protected void removeEventGroup(EventGroup group)
 165  
     {
 166  4
         eventGroups.remove(group.getGroupId());
 167  4
     }
 168  
 
 169  
     /**
 170  
      * Reorder collected events according to the configured Comparator.
 171  
      * 
 172  
      * @param events the EventGroup used for collecting the events
 173  
      * @return an array of events reordered according to the Comparator returned by
 174  
      *         {@link #getComparator()}. If no comparator is configured, the events
 175  
      *         are returned unsorted.
 176  
      */
 177  
     protected MuleEvent[] resequenceEvents(EventGroup events)
 178  
     {
 179  4
         if (events == null || events.size() == 0)
 180  
         {
 181  0
             return EventGroup.EMPTY_EVENTS_ARRAY;
 182  
         }
 183  
 
 184  4
         MuleEvent[] result = events.toArray();
 185  4
         Comparator cmp = this.getComparator();
 186  
 
 187  4
         if (cmp != null)
 188  
         {
 189  2
             Arrays.sort(result, cmp);
 190  
         }
 191  
         else
 192  
         {
 193  2
             logger.debug("MuleEvent comparator is null, events were not reordered");
 194  
         }
 195  
 
 196  4
         return result;
 197  
     }
 198  
 
 199  
     /**
 200  
      * Determines whether the events in the passed EventGroup are ready to be
 201  
      * reordered.
 202  
      * 
 203  
      * @see AbstractEventAggregator#shouldAggregateEvents(EventGroup)
 204  
      */
 205  
     protected abstract boolean shouldResequenceEvents(EventGroup events);
 206  
 
 207  
 }