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