View Javadoc

1   /*
2    * $Id: AbstractOutboundRouter.java 20125 2010-11-09 00:38:43Z mike.schilling $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
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   * <code>AbstractOutboundRouter</code> is a base router class that tracks statistics about message processing
57   * through the router.
58   */
59  public abstract class AbstractOutboundRouter implements OutboundRouter
60  {
61      /**
62       * These properties are automatically propagated by Mule from inbound to outbound
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       * logger used by this class
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       * Determines if Mule stamps outgoing message with a correlation ID or not.
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             // if replyTo is set we'll probably want the correlationId set as
218             // well
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                 // buf.append(", ").append("Seq=").append(seq);
272                 // buf.append(", ").append("Group Size=").append(group);
273                 logger.debug(buf.toString());
274             }
275             message.setCorrelationId(correlation);
276             // message.setCorrelationGroupSize(group);
277             // message.setCorrelationSequence(seq);
278         }
279     }
280 
281     public List<MessageProcessor> getRoutes()
282     {
283         return routes;
284     }
285 
286     /*
287      * For spring access
288      */
289     // TODO Use spring factory bean
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      * @param name the route identifier
412      * @return the route or null if the endpoint's Uri is not registered
413      * @see org.mule.api.routing.InboundRouterCollection
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      * Send message event to destination.
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      * Propagates a number of internal system properties to handle correlation, session, etc. Note that in and
473      * out params can be the same message object when not dealing with replies.
474      * 
475      * @see #magicProperties
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 }