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