1
2
3
4
5
6
7
8
9
10
11 package org.mule.routing.response;
12
13 import org.mule.config.i18n.CoreMessages;
14 import org.mule.routing.inbound.AbstractEventAggregator;
15 import org.mule.routing.inbound.EventGroup;
16 import org.mule.umo.UMOEvent;
17 import org.mule.umo.UMOMessage;
18 import org.mule.umo.routing.ResponseTimeoutException;
19 import org.mule.umo.routing.RoutingException;
20 import org.mule.util.MapUtils;
21 import org.mule.util.concurrent.Latch;
22
23 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
24 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
25 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
26
27
28
29
30
31
32
33
34
35
36 public abstract class AbstractResponseAggregator extends AbstractResponseRouter
37 {
38
39
40
41
42
43 protected final ConcurrentMap eventGroups = new ConcurrentHashMap();
44
45
46
47
48 protected final ConcurrentMap locks = new ConcurrentHashMap();
49
50
51
52
53
54 protected final ConcurrentMap responseMessages = new ConcurrentHashMap();
55
56 public void process(UMOEvent event) throws RoutingException
57 {
58
59 final Object groupId = this.getReplyAggregateIdentifier(event.getMessage());
60 if (groupId == null || groupId.equals("-1"))
61 {
62 throw new RoutingException(CoreMessages.noCorrelationId(), event.getMessage(), event
63 .getEndpoint());
64 }
65
66
67 boolean lookupMiss = false;
68
69
70 while (true)
71 {
72 if (lookupMiss)
73 {
74 try
75 {
76
77 Thread.sleep(1);
78 }
79 catch (InterruptedException interrupted)
80 {
81 Thread.currentThread().interrupt();
82 }
83 }
84
85
86 EventGroup group = this.getEventGroup(groupId);
87
88
89 if (group == null)
90 {
91
92 group = this.addEventGroup(this.createEventGroup(event, groupId));
93 }
94
95
96 synchronized (group)
97 {
98
99 if (group != this.getEventGroup(groupId))
100 {
101
102 lookupMiss = true;
103 continue;
104 }
105
106 if (logger.isDebugEnabled())
107 {
108 logger.debug("Adding event to response aggregator group: " + groupId);
109 }
110
111
112 group.addEvent(event);
113
114
115 if (this.shouldAggregateEvents(group))
116 {
117
118 UMOMessage returnMessage = this.aggregateEvents(group);
119
120
121
122 this.removeEventGroup(group);
123
124
125
126 UMOMessage previousResult = (UMOMessage) responseMessages.putIfAbsent(groupId,
127 returnMessage);
128 if (previousResult != null)
129 {
130
131
132
133 throw new IllegalStateException(
134 "Detected duplicate aggregation result message with id: " + groupId);
135 }
136
137
138
139
140 Latch l = (Latch) locks.get(groupId);
141 if (l == null)
142 {
143 if (logger.isDebugEnabled())
144 {
145 logger.debug("Creating latch for " + groupId + " in " + this);
146 }
147
148 l = new Latch();
149 Latch previous = (Latch) locks.putIfAbsent(groupId, l);
150 if (previous != null)
151 {
152 l = previous;
153 }
154 }
155
156 l.countDown();
157 }
158
159
160 break;
161 }
162 }
163 }
164
165
166
167
168 protected EventGroup createEventGroup(UMOEvent event, Object groupId)
169 {
170 if (logger.isDebugEnabled())
171 {
172 logger.debug("Creating new event group: " + groupId + " in " + this);
173 }
174
175 return new EventGroup(groupId);
176 }
177
178
179
180
181 protected EventGroup getEventGroup(Object groupId)
182 {
183 return (EventGroup) eventGroups.get(groupId);
184 }
185
186
187
188
189 protected EventGroup addEventGroup(EventGroup group)
190 {
191 EventGroup previous = (EventGroup) eventGroups.putIfAbsent(group.getGroupId(), group);
192
193
194 return (previous != null ? previous : group);
195 }
196
197
198
199
200 protected void removeEventGroup(EventGroup group)
201 {
202 eventGroups.remove(group.getGroupId());
203 }
204
205
206
207
208
209
210
211
212
213 public UMOMessage getResponse(UMOMessage message) throws RoutingException
214 {
215 Object responseId = this.getCallResponseAggregateIdentifier(message);
216
217 if (logger.isDebugEnabled())
218 {
219 logger.debug("Waiting for response for message id: " + responseId + " in " + this);
220 }
221
222 Latch l = (Latch) locks.get(responseId);
223 if (l == null)
224 {
225 if (logger.isDebugEnabled())
226 {
227 logger.debug("Got response but no one is waiting for it yet. Creating latch for "
228 + responseId + " in " + this);
229 }
230
231 l = new Latch();
232 Latch previous = (Latch) locks.putIfAbsent(responseId, l);
233 if (previous != null)
234 {
235 l = previous;
236 }
237 }
238
239 if (logger.isDebugEnabled())
240 {
241 logger.debug("Got latch for message: " + responseId);
242 }
243
244
245 UMOMessage result;
246
247
248
249 boolean resultAvailable = false;
250
251
252
253 boolean interruptedWhileWaiting = false;
254
255 try
256 {
257 if (logger.isDebugEnabled())
258 {
259 logger.debug("Waiting for response to message: " + responseId);
260 }
261
262
263 if (this.getTimeout() <= 0)
264 {
265 l.await();
266 resultAvailable = true;
267 }
268 else
269 {
270 resultAvailable = l.await(this.getTimeout(), TimeUnit.MILLISECONDS);
271 }
272 }
273 catch (InterruptedException e)
274 {
275 interruptedWhileWaiting = true;
276 }
277 finally
278 {
279 locks.remove(responseId);
280 result = (UMOMessage) responseMessages.remove(responseId);
281
282 if (interruptedWhileWaiting)
283 {
284 Thread.currentThread().interrupt();
285 }
286 }
287
288 if (!resultAvailable)
289 {
290 if (logger.isTraceEnabled())
291 {
292 logger.trace("Current responses are: \n" + MapUtils.toString(responseMessages, true));
293 }
294
295 throw new ResponseTimeoutException(
296 CoreMessages.responseTimedOutWaitingForId(
297 this.getTimeout(), responseId), message, null);
298 }
299
300 if (result == null)
301 {
302
303 throw new IllegalStateException("Response Message is null");
304 }
305
306 if (logger.isDebugEnabled())
307 {
308 logger.debug("remaining locks : " + locks.keySet());
309 logger.debug("remaining results: " + responseMessages.keySet());
310 }
311
312 return result;
313 }
314
315
316
317
318 protected abstract boolean shouldAggregateEvents(EventGroup events);
319
320
321
322
323 protected abstract UMOMessage aggregateEvents(EventGroup events) throws RoutingException;
324
325 }