View Javadoc

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