View Javadoc
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;
8   
9   import org.mule.DefaultMessageCollection;
10  import org.mule.DefaultMuleEvent;
11  import org.mule.DefaultMuleMessage;
12  import org.mule.api.MuleEvent;
13  import org.mule.api.MuleMessageCollection;
14  import org.mule.util.ClassUtils;
15  
16  import java.io.Serializable;
17  import java.util.ArrayList;
18  import java.util.Iterator;
19  import java.util.List;
20  
21  import org.apache.commons.collections.IteratorUtils;
22  
23  /**
24   * <code>EventGroup</code> is a holder over events grouped by a common group Id.
25   * This can be used by components such as routers to managed related events.
26   */
27  // @ThreadSafe
28  public class EventGroup implements Comparable<EventGroup>, Serializable
29  {
30      /**
31       * Serial version
32       */
33      private static final long serialVersionUID = 953739659615692697L;
34  
35      public static final MuleEvent[] EMPTY_EVENTS_ARRAY = new MuleEvent[0];
36  
37      private final Object groupId;
38      // @GuardedBy("this")
39      private final List<MuleEvent> events;
40      private final long created;
41      private final int expectedSize;
42  
43      public EventGroup(Object groupId)
44      {
45          this(groupId, -1);
46      }
47  
48      public EventGroup(Object groupId, int expectedSize)
49      {
50          super();
51          this.created = System.nanoTime();
52          this.events = new ArrayList<MuleEvent>(expectedSize > 0 ? expectedSize : 10);
53          this.expectedSize = expectedSize;
54          this.groupId = groupId;
55      }
56  
57      /**
58       * Compare this EventGroup to another one. If the receiver and the argument both
59       * have groupIds that are {@link Comparable}, they are used for the comparison;
60       * otherwise - since the id can be any object - the group creation time stamp is
61       * used as fallback. Older groups are considered "smaller".
62       * 
63       * @see java.lang.Comparable#compareTo(java.lang.Object)
64       */
65      @SuppressWarnings("unchecked")
66      public int compareTo(EventGroup other)
67      {
68          Object otherId = other.getGroupId();
69  
70          if (groupId instanceof Comparable<?> && otherId instanceof Comparable<?>)
71          {
72              return ((Comparable) groupId).compareTo(otherId);
73          }
74          else
75          {
76              long diff = created - other.getCreated();
77              return (diff > 0 ? 1 : (diff < 0 ? -1 : 0));
78          }
79      }
80  
81      /**
82       * Compares two EventGroups for equality. EventGroups are considered equal when
83       * their groupIds (as returned by {@link #getGroupId()}) are equal.
84       * 
85       * @see java.lang.Object#equals(Object)
86       */
87      @Override
88      public boolean equals(Object obj)
89      {
90          if (this == obj)
91          {
92              return true;
93          }
94  
95          if (!(obj instanceof EventGroup))
96          {
97              return false;
98          }
99  
100         final EventGroup other = (EventGroup) obj;
101         if (groupId == null)
102         {
103             return (other.groupId == null);
104         }
105 
106         return groupId.equals(other.groupId);
107     }
108 
109     /**
110      * The hashCode of an EventGroup is derived from the object returned by
111      * {@link #getGroupId()}.
112      * 
113      * @see java.lang.Object#hashCode()
114      */
115     @Override
116     public int hashCode()
117     {
118         return groupId.hashCode();
119     }
120 
121     /**
122      * Returns an identifier for this EventGroup. It is recommended that this id is
123      * unique and {@link Comparable} e.g. a UUID.
124      * 
125      * @return the id of this event group
126      */
127     public Object getGroupId()
128     {
129         return groupId;
130     }
131 
132     /**
133      * Returns an iterator over a snapshot copy of this group's collected events. If
134      * you need to iterate over the group and e.g. remove select events, do so via
135      * {@link #removeEvent(MuleEvent)}. If you need to do so atomically in order to
136      * prevent e.g. concurrent reception/aggregation of the group during iteration,
137      * wrap the iteration in a synchronized block on the group instance.
138      * 
139      * @return an iterator over collected {@link MuleEvent}s.
140      */
141     @SuppressWarnings("unchecked")
142     public Iterator<MuleEvent> iterator()
143     {
144         synchronized (events)
145         {
146             if (events.isEmpty())
147             {
148                 return IteratorUtils.emptyIterator();
149             }
150             else
151             {
152                 return IteratorUtils.arrayIterator(this.toArray());
153             }
154         }
155     }
156 
157     /**
158      * Returns a snapshot of collected events in this group.
159      * 
160      * @return an array of collected {@link MuleEvent}s.
161      */
162     public MuleEvent[] toArray()
163     {
164         synchronized (events)
165         {
166             if (events.isEmpty())
167             {
168                 return EMPTY_EVENTS_ARRAY;
169             }
170 
171             return events.toArray(EMPTY_EVENTS_ARRAY);
172         }
173     }
174 
175     /**
176      * Add the given event to this group.
177      * 
178      * @param event the event to add
179      */
180     public void addEvent(MuleEvent event)
181     {
182         synchronized (events)
183         {
184             events.add(event);
185         }
186     }
187 
188     /**
189      * Remove the given event from the group.
190      * 
191      * @param event the evnt to remove
192      */
193     public void removeEvent(MuleEvent event)
194     {
195         synchronized (events)
196         {
197             events.remove(event);
198         }
199     }
200 
201     /**
202      * Return the creation timestamp of the current group in nanoseconds.
203      * 
204      * @return the timestamp when this group was instantiated.
205      */
206     public long getCreated()
207     {
208         return created;
209     }
210 
211     /**
212      * Returns the number of events collected so far.
213      * 
214      * @return number of events in this group or 0 if the group is empty.
215      */
216     public int size()
217     {
218         synchronized (events)
219         {
220             return events.size();
221         }
222     }
223 
224     /**
225      * Returns the number of events that this EventGroup is expecting before
226      * correlation can proceed.
227      * 
228      * @return expected number of events or -1 if no expected size was specified.
229      */
230     public int expectedSize()
231     {
232         return expectedSize;
233     }
234 
235     /**
236      * Removes all events from this group.
237      */
238     public void clear()
239     {
240         synchronized (events)
241         {
242             events.clear();
243         }
244     }
245 
246     @Override
247     public String toString()
248     {
249         StringBuffer buf = new StringBuffer(80);
250         buf.append(ClassUtils.getSimpleName(this.getClass()));
251         buf.append(" {");
252         buf.append("id=").append(groupId);
253         buf.append(", expected size=").append(expectedSize);
254 
255         synchronized (events)
256         {
257             int currentSize = events.size();
258             buf.append(", current events=").append(currentSize);
259 
260             if (currentSize > 0)
261             {
262                 buf.append(" [");
263                 Iterator<MuleEvent> i = events.iterator();
264                 while (i.hasNext())
265                 {
266                     MuleEvent event = i.next();
267                     buf.append(event.getMessage().getUniqueId());
268                     if (i.hasNext())
269                     {
270                         buf.append(", ");
271                     }
272                 }
273                 buf.append(']');
274             }
275         }
276 
277         buf.append('}');
278 
279         return buf.toString();
280     }
281 
282     public MuleMessageCollection toMessageCollection()
283     {
284         MuleMessageCollection col;
285         synchronized (events)
286         {
287             if (events.isEmpty())
288             {
289                 col = new DefaultMessageCollection(null);
290             }
291             col = new DefaultMessageCollection(events.get(0).getMuleContext());
292             for (MuleEvent event : events)
293             {
294                 col.addMessage(event.getMessage());
295                 ((DefaultMuleMessage)col).copyInvocationProperties(event.getMessage());
296             }
297         }
298         return col;
299     }
300     
301     public MuleEvent getMessageCollectionEvent()
302     {
303         if (events.size() > 0)
304         {
305             return new DefaultMuleEvent(toMessageCollection(), events.get(events.size() -1));
306         }
307         else
308         {
309             return null;
310         }
311     }
312 }