View Javadoc

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      private final ConcurrentMap eventGroups = new ConcurrentHashMap();
34      private volatile Comparator comparator;
35  
36      public AbstractEventResequencer()
37      {
38          super();
39      }
40  
41      public Comparator getComparator()
42      {
43          return comparator;
44      }
45  
46      public void setComparator(Comparator eventComparator)
47      {
48          this.comparator = eventComparator;
49      }
50  
51      // //@Override
52      public MuleEvent[] process(MuleEvent event) throws MessagingException
53      {
54          MuleEvent[] result = null;
55  
56          if (this.isMatch(event))
57          {
58              // indicates interleaved EventGroup removal (very rare)
59              boolean miss = false;
60  
61              // match event to its group
62              final Object groupId = this.getEventGroupIdForEvent(event);
63  
64              // spinloop for the EventGroup lookup
65              while (true)
66              {
67                  if (miss)
68                  {
69                      try
70                      {
71                          // recommended over Thread.yield()
72                          Thread.sleep(1);
73                      }
74                      catch (InterruptedException interrupted)
75                      {
76                          Thread.currentThread().interrupt();
77                      }
78                  }
79  
80                  // check for an existing group first
81                  EventGroup group = this.getEventGroup(groupId);
82  
83                  // does the group exist?
84                  if (group == null)
85                  {
86                      // ..apparently not, so create a new one & add it
87                      group = this.addEventGroup(this.createEventGroup(event, groupId));
88                  }
89  
90                  // ensure that only one thread at a time evaluates this EventGroup
91                  synchronized (group)
92                  {
93                      // make sure no other thread removed the group in the meantime
94                      if (group != this.getEventGroup(groupId))
95                      {
96                          // if that is the (rare) case, spin
97                          miss = true;
98                          continue;
99                      }
100 
101                     // add the incoming event to the group
102                     group.addEvent(event);
103 
104                     if (this.shouldResequenceEvents(group))
105                     {
106                         result = this.resequenceEvents(group);
107                         this.removeEventGroup(group);
108                     }
109 
110                     // result or not: exit spinloop
111                     break;
112                 }
113             }
114         }
115 
116         return result;
117     }
118 
119     /**
120      * @see AbstractEventAggregator#createEventGroup(MuleEvent, Object)
121      */
122     protected EventGroup createEventGroup(MuleEvent event, Object groupId)
123     {
124         return new EventGroup(groupId);
125     }
126 
127     /**
128      * @see AbstractEventAggregator#getEventGroupIdForEvent(MuleEvent)
129      */
130     protected Object getEventGroupIdForEvent(MuleEvent event)
131     {
132         String groupId = event.getMessage().getCorrelationId();
133 
134         if (groupId == null)
135         {
136             groupId = NO_CORRELATION_ID;
137         }
138 
139         return groupId;
140     }
141 
142     /**
143      * @see AbstractEventAggregator#getEventGroup(Object)
144      */
145     protected EventGroup getEventGroup(Object groupId)
146     {
147         return (EventGroup) eventGroups.get(groupId);
148     }
149 
150     /**
151      * @see AbstractEventAggregator#addEventGroup(EventGroup)
152      */
153     protected EventGroup addEventGroup(EventGroup group)
154     {
155         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         return (previous != null ? previous : group);
159     }
160 
161     /**
162      * @see AbstractEventAggregator#removeEventGroup(EventGroup)
163      */
164     protected void removeEventGroup(EventGroup group)
165     {
166         eventGroups.remove(group.getGroupId());
167     }
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         if (events == null || events.size() == 0)
180         {
181             return EventGroup.EMPTY_EVENTS_ARRAY;
182         }
183 
184         MuleEvent[] result = events.toArray();
185         Comparator cmp = this.getComparator();
186 
187         if (cmp != null)
188         {
189             Arrays.sort(result, cmp);
190         }
191         else
192         {
193             logger.debug("MuleEvent comparator is null, events were not reordered");
194         }
195 
196         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 }