1
2
3
4
5
6
7
8
9
10
11 package org.mule.routing.requestreply;
12
13 import org.mule.OptimizedRequestContext;
14 import org.mule.api.MuleEvent;
15 import org.mule.api.MuleException;
16 import org.mule.api.MuleMessageCollection;
17 import org.mule.api.construct.FlowConstruct;
18 import org.mule.api.processor.MessageProcessor;
19 import org.mule.api.processor.RequestReplyRequesterMessageProcessor;
20 import org.mule.api.routing.ResponseTimeoutException;
21 import org.mule.api.source.MessageSource;
22 import org.mule.config.i18n.CoreMessages;
23 import org.mule.context.notification.RoutingNotification;
24 import org.mule.processor.AbstractInterceptingMessageProcessor;
25 import org.mule.util.ObjectUtils;
26 import org.mule.util.concurrent.Latch;
27
28 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
29 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
30 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
31
32 import org.apache.commons.collections.buffer.BoundedFifoBuffer;
33
34 public abstract class AbstractAsyncRequestReplyRequester extends AbstractInterceptingMessageProcessor
35 implements RequestReplyRequesterMessageProcessor
36 {
37
38 public static final int MAX_PROCESSED_GROUPS = 50000;
39
40 protected volatile long timeout = -1;
41 protected volatile boolean failOnTimeout = true;
42 protected MessageSource replyMessageSource;
43 protected FlowConstruct flowConstruct;
44 private final MessageProcessor internalAsyncReplyMessageProcessor = new InternalAsyncReplyMessageProcessor();
45
46 protected final ConcurrentMap locks = new ConcurrentHashMap();
47 protected final ConcurrentMap responseEvents = new ConcurrentHashMap();
48 protected final Object processedLock = new Object();
49
50 protected final BoundedFifoBuffer processed = new BoundedFifoBuffer(MAX_PROCESSED_GROUPS);
51
52 public MuleEvent process(MuleEvent event) throws MuleException
53 {
54 if (replyMessageSource == null)
55 {
56 return processNext(event);
57 }
58 else
59 {
60 locks.put(getAsyncReplyCorrelationId(event), new Latch());
61
62 sendAsyncRequest(event);
63
64 return receiveAsyncReply(event);
65 }
66 }
67
68 public void setTimeout(long timeout)
69 {
70 this.timeout = timeout;
71 }
72
73 public void setFailOnTimeout(boolean failOnTimeout)
74 {
75 this.failOnTimeout = failOnTimeout;
76 }
77
78 public void setReplySource(MessageSource messageSource)
79 {
80 verifyReplyMessageSource(messageSource);
81 replyMessageSource = messageSource;
82 messageSource.setListener(internalAsyncReplyMessageProcessor);
83 }
84
85 protected void verifyReplyMessageSource(MessageSource messageSource)
86 {
87 }
88
89 protected String getAsyncReplyCorrelationId(MuleEvent event)
90 {
91 if (event.getMessage() instanceof MuleMessageCollection)
92 {
93 return event.getMessage().getCorrelationId();
94 }
95 else
96 {
97 return event.getFlowConstruct().getMessageInfoMapping().getCorrelationId(event.getMessage());
98 }
99 }
100
101 protected void sendAsyncRequest(MuleEvent event) throws MuleException
102 {
103 processNext(event);
104 }
105
106 protected MuleEvent receiveAsyncReply(MuleEvent event) throws ResponseTimeoutException
107 {
108 String asyncReplyCorrelationId = getAsyncReplyCorrelationId(event);
109 Latch asyncReplyLatch = (Latch) locks.get(asyncReplyCorrelationId);
110
111
112 boolean interruptedWhileWaiting = false;
113 boolean resultAvailable = false;
114 MuleEvent result = null;
115
116 try
117 {
118 if (logger.isDebugEnabled())
119 {
120 logger.debug("Waiting for async reply message with id: " + asyncReplyCorrelationId);
121 }
122
123 if (timeout <= 0)
124 {
125 asyncReplyLatch.await();
126 resultAvailable = true;
127 }
128 else
129 {
130 resultAvailable = asyncReplyLatch.await(timeout, TimeUnit.MILLISECONDS);
131 }
132 if (!resultAvailable)
133 {
134 postLatchAwait(asyncReplyCorrelationId);
135 resultAvailable = asyncReplyLatch.getCount() == 0;
136 }
137 }
138 catch (InterruptedException e)
139 {
140 interruptedWhileWaiting = true;
141 }
142 finally
143 {
144 locks.remove(asyncReplyCorrelationId);
145 result = (MuleEvent) responseEvents.remove(asyncReplyCorrelationId);
146 if (interruptedWhileWaiting)
147 {
148 Thread.currentThread().interrupt();
149 }
150 }
151
152 if (interruptedWhileWaiting)
153 {
154 Thread.currentThread().interrupt();
155 }
156
157 if (resultAvailable)
158 {
159 if (result == null)
160 {
161
162 throw new IllegalStateException("Response MuleEvent is null");
163 }
164
165
166
167
168 return OptimizedRequestContext.criticalSetEvent(result);
169 }
170 else
171 {
172 addProcessed(asyncReplyCorrelationId);
173
174 if (failOnTimeout)
175 {
176 event.getMuleContext()
177 .fireNotification(
178 new RoutingNotification(event.getMessage(), null,
179 RoutingNotification.ASYNC_REPLY_TIMEOUT));
180
181 throw new ResponseTimeoutException(CoreMessages.responseTimedOutWaitingForId((int) timeout,
182 asyncReplyCorrelationId), event, null);
183 }
184 else
185 {
186 return null;
187 }
188 }
189 }
190
191 protected void postLatchAwait(String asyncReplyCorrelationId)
192 {
193
194 }
195
196 protected void addProcessed(Object id)
197 {
198 synchronized (processedLock)
199 {
200 if (processed.isFull())
201 {
202 processed.remove();
203 }
204 processed.add(id);
205 }
206 }
207
208 protected boolean isAlreadyProcessed(Object id)
209 {
210 synchronized (processedLock)
211 {
212 return processed.contains(id);
213 }
214 }
215
216 class InternalAsyncReplyMessageProcessor implements MessageProcessor
217 {
218 public MuleEvent process(MuleEvent event) throws MuleException
219 {
220 String messageId = getAsyncReplyCorrelationId(event);
221
222 if (isAlreadyProcessed(messageId))
223 {
224 if (logger.isDebugEnabled())
225 {
226 logger.debug("An event was received for an event group that has already been processed, "
227 + "this is probably because the async-reply timed out. Correlation Id is: "
228 + messageId + ". Dropping event");
229 }
230
231 event.getMuleContext().fireNotification(
232 new RoutingNotification(event.getMessage(), event.getEndpoint()
233 .getEndpointURI()
234 .toString(), RoutingNotification.MISSED_ASYNC_REPLY));
235 return null;
236 }
237
238 addProcessed(messageId);
239 MuleEvent previousResult = (MuleEvent) responseEvents.putIfAbsent(messageId, event);
240 if (previousResult != null)
241 {
242
243
244
245 throw new IllegalStateException("Detected duplicate result message with id: " + messageId);
246 }
247 Latch l = (Latch) locks.get(messageId);
248 if (l != null)
249 {
250 l.countDown();
251 }
252 else
253 {
254 logger.warn("Unexpected message with id " + messageId
255 + " received. This message will be discarded.");
256 }
257 return null;
258 }
259 }
260
261 @Override
262 public String toString()
263 {
264 return ObjectUtils.toString(this);
265 }
266 }