1
2
3
4
5
6
7 package org.mule.routing.outbound;
8
9 import org.mule.DefaultMuleEvent;
10 import org.mule.DefaultMuleMessage;
11 import org.mule.api.MessagingException;
12 import org.mule.api.MuleContext;
13 import org.mule.api.MuleEvent;
14 import org.mule.api.MuleException;
15 import org.mule.api.MuleMessage;
16 import org.mule.api.config.MuleProperties;
17 import org.mule.api.construct.FlowConstruct;
18 import org.mule.api.construct.FlowConstructAware;
19 import org.mule.api.context.MuleContextAware;
20 import org.mule.api.endpoint.OutboundEndpoint;
21 import org.mule.api.lifecycle.Disposable;
22 import org.mule.api.lifecycle.Initialisable;
23 import org.mule.api.lifecycle.InitialisationException;
24 import org.mule.api.lifecycle.Startable;
25 import org.mule.api.lifecycle.Stoppable;
26 import org.mule.api.processor.MessageProcessor;
27 import org.mule.api.routing.OutboundRouter;
28 import org.mule.api.routing.RouterResultsHandler;
29 import org.mule.api.routing.RoutingException;
30 import org.mule.api.transaction.TransactionCallback;
31 import org.mule.api.transaction.TransactionConfig;
32 import org.mule.api.transport.DispatchException;
33 import org.mule.config.i18n.CoreMessages;
34 import org.mule.management.stats.RouterStatistics;
35 import org.mule.processor.AbstractMessageProcessorOwner;
36 import org.mule.routing.CorrelationMode;
37 import org.mule.routing.DefaultRouterResultsHandler;
38 import org.mule.transaction.TransactionTemplate;
39 import org.mule.util.StringMessageUtils;
40 import org.mule.util.SystemUtils;
41
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.List;
45
46 import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
47 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
48
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51
52
53
54
55
56 public abstract class AbstractOutboundRouter extends AbstractMessageProcessorOwner implements OutboundRouter
57 {
58
59
60
61 protected static List<String> magicProperties = Arrays.asList(
62 MuleProperties.MULE_CORRELATION_ID_PROPERTY, MuleProperties.MULE_CORRELATION_ID_PROPERTY,
63 MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY,
64 MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY, MuleProperties.MULE_SESSION_PROPERTY);
65
66
67
68
69 protected transient Log logger = LogFactory.getLog(getClass());
70
71 @SuppressWarnings("unchecked")
72 protected List<MessageProcessor> routes = new CopyOnWriteArrayList();
73
74 protected String replyTo = null;
75
76
77
78
79 protected CorrelationMode enableCorrelation = CorrelationMode.IF_NOT_SET;
80
81 protected TransactionConfig transactionConfig;
82
83 protected RouterResultsHandler resultsHandler = new DefaultRouterResultsHandler();
84
85 private RouterStatistics routerStatistics;
86
87 protected AtomicBoolean initialised = new AtomicBoolean(false);
88 protected AtomicBoolean started = new AtomicBoolean(false);
89
90 public MuleEvent process(final MuleEvent event) throws MuleException
91 {
92 TransactionTemplate<MuleEvent> tt = new TransactionTemplate<MuleEvent>(getTransactionConfig(),
93 muleContext);
94
95 TransactionCallback<MuleEvent> cb = new TransactionCallback<MuleEvent>()
96 {
97 public MuleEvent doInTransaction() throws Exception
98 {
99 return route(event);
100 }
101 };
102 try
103 {
104 return tt.execute(cb);
105 }
106 catch (RoutingException e)
107 {
108 throw e;
109 }
110 catch (Exception e)
111 {
112 throw new RoutingException(event, this, e);
113 }
114 }
115
116 protected abstract MuleEvent route(MuleEvent event) throws MessagingException;
117
118 protected final MuleEvent sendRequest(final MuleEvent routedEvent,
119 final MuleMessage message,
120 final MessageProcessor route,
121 boolean awaitResponse) throws MuleException
122 {
123 if (awaitResponse && replyTo != null)
124 {
125 logger.debug("event was dispatched synchronously, but there is a ReplyTo route set, so using asynchronous dispatch");
126 awaitResponse = false;
127 }
128
129 setMessageProperties(routedEvent.getSession().getFlowConstruct(), message, route);
130
131 if (logger.isDebugEnabled())
132 {
133 if (route instanceof OutboundEndpoint)
134 {
135 logger.debug("Message being sent to: " + ((OutboundEndpoint) route).getEndpointURI());
136 }
137 logger.debug(message);
138 }
139
140 if (logger.isTraceEnabled())
141 {
142 try
143 {
144 logger.trace("Request payload: \n"
145 + StringMessageUtils.truncate(message.getPayloadForLogging(), 100, false));
146 if (route instanceof OutboundEndpoint)
147 {
148 logger.trace("outbound transformer is: " + ((OutboundEndpoint) route).getTransformers());
149 }
150 }
151 catch (Exception e)
152 {
153 logger.trace("Request payload: \n(unable to retrieve payload: " + e.getMessage());
154 if (route instanceof OutboundEndpoint)
155 {
156 logger.trace("outbound transformer is: " + ((OutboundEndpoint) route).getTransformers());
157 }
158 }
159 }
160
161 MuleEvent result;
162 try
163 {
164 result = sendRequestEvent(routedEvent, message, route, awaitResponse);
165 }
166 catch (MessagingException me)
167 {
168 throw me;
169 }
170 catch (Exception e)
171 {
172 throw new RoutingException(routedEvent, null, e);
173 }
174
175 if (getRouterStatistics() != null)
176 {
177 if (getRouterStatistics().isEnabled())
178 {
179 getRouterStatistics().incrementRoutedMessage(route);
180 }
181 }
182
183 if (result != null)
184 {
185 MuleMessage resultMessage = result.getMessage();
186 if (logger.isTraceEnabled())
187 {
188 if (resultMessage != null)
189 {
190 try
191 {
192 logger.trace("Response payload: \n"
193 + StringMessageUtils.truncate(resultMessage.getPayloadForLogging(), 100,
194 false));
195 }
196 catch (Exception e)
197 {
198 logger.trace("Response payload: \n(unable to retrieve payload: " + e.getMessage());
199 }
200 }
201 }
202 }
203
204 return result;
205 }
206
207 protected void setMessageProperties(FlowConstruct service, MuleMessage message, MessageProcessor route)
208 {
209 if (replyTo != null)
210 {
211
212
213 message.setReplyTo(replyTo);
214 message.setOutboundProperty(MuleProperties.MULE_REPLY_TO_REQUESTOR_PROPERTY, service.getName());
215 if (logger.isDebugEnabled() && route instanceof OutboundEndpoint)
216 {
217 logger.debug("Setting replyTo=" + replyTo + " for outbound route: "
218 + ((OutboundEndpoint) route).getEndpointURI());
219 }
220 }
221 if (enableCorrelation != CorrelationMode.NEVER)
222 {
223 boolean correlationSet = message.getCorrelationId() != null;
224 if (correlationSet && (enableCorrelation == CorrelationMode.IF_NOT_SET))
225 {
226 if (logger.isDebugEnabled())
227 {
228 logger.debug("CorrelationId is already set to '" + message.getCorrelationId()
229 + "' , not setting it again");
230 }
231 return;
232 }
233 else if (correlationSet)
234 {
235 if (logger.isDebugEnabled())
236 {
237 logger.debug("CorrelationId is already set to '" + message.getCorrelationId()
238 + "', but router is configured to overwrite it");
239 }
240 }
241 else
242 {
243 if (logger.isDebugEnabled())
244 {
245 logger.debug("No CorrelationId is set on the message, will set a new Id");
246 }
247 }
248
249 String correlation;
250 correlation = service.getMessageInfoMapping().getCorrelationId(message);
251 if (logger.isDebugEnabled())
252 {
253 logger.debug("Extracted correlation Id as: " + correlation);
254 }
255
256 if (logger.isDebugEnabled())
257 {
258 StringBuffer buf = new StringBuffer();
259 buf.append("Setting Correlation info on Outbound router");
260 if (route instanceof OutboundEndpoint)
261 {
262 buf.append(" for endpoint: ").append(((OutboundEndpoint) route).getEndpointURI());
263 }
264 buf.append(SystemUtils.LINE_SEPARATOR).append("Id=").append(correlation);
265
266
267 logger.debug(buf.toString());
268 }
269 message.setCorrelationId(correlation);
270
271
272 }
273 }
274
275 public List<MessageProcessor> getRoutes()
276 {
277 return routes;
278 }
279
280
281
282
283
284 @Deprecated
285 public void setMessageProcessors(List<MessageProcessor> routes) throws MuleException
286 {
287 setRoutes(routes);
288 }
289
290 public void setRoutes(List<MessageProcessor> routes) throws MuleException
291 {
292 this.routes.clear();
293 for (MessageProcessor route : routes)
294 {
295 addRoute(route);
296 }
297 }
298
299 public synchronized void addRoute(MessageProcessor route) throws MuleException
300 {
301 if (initialised.get())
302 {
303 if (route instanceof MuleContextAware)
304 {
305 ((MuleContextAware) route).setMuleContext(muleContext);
306 }
307 if (route instanceof FlowConstructAware)
308 {
309 ((FlowConstructAware) route).setFlowConstruct(flowConstruct);
310 }
311 if (route instanceof Initialisable)
312 {
313 ((Initialisable) route).initialise();
314 }
315 }
316 if (started.get())
317 {
318 if (route instanceof Startable)
319 {
320 ((Startable) route).start();
321 }
322 }
323 routes.add(route);
324 }
325
326 public synchronized void removeRoute(MessageProcessor route) throws MuleException
327 {
328 if (started.get())
329 {
330 if (route instanceof Stoppable)
331 {
332 ((Stoppable) route).stop();
333 }
334 }
335 if (initialised.get())
336 {
337 if (route instanceof Disposable)
338 {
339 ((Disposable) route).dispose();
340 }
341 }
342 routes.remove(route);
343 }
344
345 public String getReplyTo()
346 {
347 return replyTo;
348 }
349
350 public void setReplyTo(String replyTo)
351 {
352 this.replyTo = replyTo;
353 }
354
355 public CorrelationMode getEnableCorrelation()
356 {
357 return enableCorrelation;
358 }
359
360 public void setEnableCorrelation(CorrelationMode enableCorrelation)
361 {
362 this.enableCorrelation = enableCorrelation;
363 }
364
365 public void setEnableCorrelationAsString(String enableCorrelation)
366 {
367 if (enableCorrelation != null)
368 {
369 if (enableCorrelation.equals("ALWAYS"))
370 {
371 this.enableCorrelation = CorrelationMode.ALWAYS;
372 }
373 else if (enableCorrelation.equals("NEVER"))
374 {
375 this.enableCorrelation = CorrelationMode.NEVER;
376 }
377 else if (enableCorrelation.equals("IF_NOT_SET"))
378 {
379 this.enableCorrelation = CorrelationMode.IF_NOT_SET;
380 }
381 else
382 {
383 throw new IllegalArgumentException("Value for enableCorrelation not recognised: "
384 + enableCorrelation);
385 }
386 }
387 }
388
389 public TransactionConfig getTransactionConfig()
390 {
391 return transactionConfig;
392 }
393
394 public void setTransactionConfig(TransactionConfig transactionConfig)
395 {
396 this.transactionConfig = transactionConfig;
397 }
398
399 public boolean isDynamicRoutes()
400 {
401 return false;
402 }
403
404
405
406
407
408 public MessageProcessor getRoute(String name)
409 {
410 for (MessageProcessor route : routes)
411 {
412 if (route instanceof OutboundEndpoint)
413 {
414 OutboundEndpoint endpoint = (OutboundEndpoint) route;
415 if (endpoint.getName().equals(name))
416 {
417 return endpoint;
418 }
419 }
420 }
421 return null;
422 }
423
424 public RouterResultsHandler getResultsHandler()
425 {
426 return resultsHandler;
427 }
428
429 public void setResultsHandler(RouterResultsHandler resultsHandler)
430 {
431 this.resultsHandler = resultsHandler;
432 }
433
434
435
436
437 protected MuleEvent sendRequestEvent(MuleEvent routedEvent,
438 MuleMessage message,
439 MessageProcessor route,
440 boolean awaitResponse) throws MuleException
441 {
442 if (route == null)
443 {
444 throw new DispatchException(CoreMessages.objectIsNull("Outbound Endpoint"), routedEvent, null);
445 }
446
447 MuleEvent event = createEventToRoute(routedEvent, message, route);
448
449 if (awaitResponse)
450 {
451 int timeout = message.getOutboundProperty(MuleProperties.MULE_EVENT_TIMEOUT_PROPERTY, -1);
452 if (timeout >= 0)
453 {
454 event.setTimeout(timeout);
455 }
456 }
457
458 return route.process(event);
459 }
460
461
462
463
464 protected MuleEvent createEventToRoute(MuleEvent routedEvent, MuleMessage message, MessageProcessor route)
465 {
466 MuleEvent event = new DefaultMuleEvent(message, routedEvent.getEndpoint(), routedEvent.getSession(), routedEvent.getProcessingTime());
467 return event;
468 }
469
470
471
472
473 protected MuleMessage cloneMessage(MuleMessage message)
474 {
475 MuleMessage clonedMessage = new DefaultMuleMessage(message.getPayload(), message, muleContext);
476 return clonedMessage;
477 }
478
479
480
481
482
483
484
485
486 protected MuleMessage cloneMessage(MuleEvent event, MuleMessage message) throws MessagingException
487 {
488 assertNonConsumableMessage(event, message);
489 return cloneMessage(message);
490 }
491
492
493
494
495
496
497
498 protected void propagateMagicProperties(MuleMessage in, MuleMessage out)
499 {
500 for (String name : magicProperties)
501 {
502 Object value = in.getInboundProperty(name);
503 if (value != null)
504 {
505 out.setOutboundProperty(name, value);
506 }
507 }
508 }
509
510 public void initialise() throws InitialisationException
511 {
512 synchronized (routes)
513 {
514 super.initialise();
515 initialised.set(true);
516 }
517 }
518
519 public void dispose()
520 {
521 synchronized (routes)
522 {
523 super.dispose();
524 routes = Collections.<MessageProcessor> emptyList();
525 initialised.set(false);
526 }
527 }
528
529 public void start() throws MuleException
530 {
531 synchronized (routes)
532 {
533 super.start();
534 started.set(true);
535 }
536 }
537
538 public void stop() throws MuleException
539 {
540 synchronized (routes)
541 {
542 super.stop();
543 started.set(false);
544 }
545 }
546
547 public MuleContext getMuleContext()
548 {
549 return muleContext;
550 }
551
552 public void setRouterStatistics(RouterStatistics stats)
553 {
554 this.routerStatistics = stats;
555 }
556
557 public RouterStatistics getRouterStatistics()
558 {
559 return routerStatistics;
560 }
561
562 @Override
563 protected List<MessageProcessor> getOwnedMessageProcessors()
564 {
565 return routes;
566 }
567
568
569
570
571
572
573
574
575
576 protected void assertNonConsumableMessage(MuleEvent event, MuleMessage message) throws MessagingException
577 {
578 DefaultMuleMessage defaultMuleMessage = (DefaultMuleMessage) message;
579 if (defaultMuleMessage.isConsumable())
580 {
581 throw new MessagingException(CoreMessages.cannotCopyStreamPayload(defaultMuleMessage.getPayload().getClass().getName()), event);
582 }
583 }
584 }