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