Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
AbstractEventResequencer |
|
| 2.0;2 |
1 | /* | |
2 | * $Id: AbstractEventResequencer.java 7963 2007-08-21 08:53:15Z dirk.olmes $ | |
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.umo.MessagingException; | |
14 | import org.mule.umo.UMOEvent; | |
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 UMOEvent[] process(UMOEvent event) throws MessagingException | |
53 | { | |
54 | 12 | UMOEvent[] 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(UMOEvent, Object) | |
121 | */ | |
122 | protected EventGroup createEventGroup(UMOEvent event, Object groupId) | |
123 | { | |
124 | 4 | return new EventGroup(groupId); |
125 | } | |
126 | ||
127 | /** | |
128 | * @see AbstractEventAggregator#getEventGroupIdForEvent(UMOEvent) | |
129 | */ | |
130 | protected Object getEventGroupIdForEvent(UMOEvent 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 UMOEvent[] resequenceEvents(EventGroup events) | |
178 | { | |
179 | 4 | if (events == null || events.size() == 0) |
180 | { | |
181 | 0 | return EventGroup.EMPTY_EVENTS_ARRAY; |
182 | } | |
183 | ||
184 | 4 | UMOEvent[] 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("Event 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 | } |