View Javadoc

1   /*
2    * $Id: ChainingRouter.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.config.i18n.CoreMessages;
14  import org.mule.providers.NullPayload;
15  import org.mule.umo.UMOException;
16  import org.mule.umo.UMOMessage;
17  import org.mule.umo.UMOSession;
18  import org.mule.umo.endpoint.UMOEndpoint;
19  import org.mule.umo.routing.CouldNotRouteOutboundMessageException;
20  import org.mule.umo.routing.RoutePathNotFoundException;
21  import org.mule.umo.routing.RoutingException;
22  
23  /**
24   * <code>ChainingRouter</code> is used to pass a Mule event through multiple
25   * endpoints using the result of the first as the input for the second.
26   */
27  
28  public class ChainingRouter extends FilteringOutboundRouter
29  {
30  
31      public UMOMessage route(UMOMessage message, UMOSession session, boolean synchronous)
32          throws RoutingException
33      {
34          UMOMessage resultToReturn = null;
35          if (endpoints == null || endpoints.size() == 0)
36          {
37              throw new RoutePathNotFoundException(CoreMessages.noEndpointsForRouter(), message, null);
38          }
39  
40          final int endpointsCount = endpoints.size();
41          if (logger.isDebugEnabled())
42          {
43              logger.debug("About to chain " + endpointsCount + " endpoints.");
44          }
45  
46          // need that ref for an error message
47          UMOEndpoint endpoint = null;
48          try
49          {
50              UMOMessage intermediaryResult = message;
51  
52              for (int i = 0; i < endpointsCount; i++)
53              {
54                  endpoint = getEndpoint(i, intermediaryResult);
55                  // if it's not the last endpoint in the chain,
56                  // enforce the synchronous call, otherwise we lose response
57                  boolean lastEndpointInChain = (i == endpointsCount - 1);
58  
59                  if (logger.isDebugEnabled())
60                  {
61                      logger.debug("Sending Chained message '" + i + "': "
62                                   + (intermediaryResult == null ? "null" : intermediaryResult.toString()));
63                  }
64  
65                  if (!lastEndpointInChain)
66                  {
67                      UMOMessage localResult = send(session, intermediaryResult, endpoint);
68                      // Need to propagate correlation info and replyTo, because there
69                      // is no guarantee that an external system will preserve headers
70                      // (in fact most will not)
71                      if (localResult != null &&
72                          // null result can be wrapped in a NullPayload
73                          localResult.getPayload() != NullPayload.getInstance() &&
74                          intermediaryResult != null)
75                      {
76                          processIntermediaryResult(localResult, intermediaryResult);
77                      }
78                      intermediaryResult = localResult;
79  
80                      if (logger.isDebugEnabled())
81                      {
82                          logger.debug("Received Chain result '" + i + "': "
83                                       + (intermediaryResult != null ? intermediaryResult.toString() : "null"));
84                      }
85  
86                      if (intermediaryResult == null || intermediaryResult.getPayload() == NullPayload.getInstance())
87                      {
88                          // if there was an error in the first link of the chain, make sure we propagate back
89                          // any exception payloads alongside the NullPayload
90                          resultToReturn = intermediaryResult;
91                          logger.warn("Chaining router cannot process any further endpoints. "
92                                      + "There was no result returned from endpoint invocation: " + endpoint);
93                          break;
94                      }
95                  }
96                  else
97                  {
98                      // ok, the last call,
99                      // use the 'sync/async' method parameter
100                     if (synchronous)
101                     {
102                         resultToReturn = send(session, intermediaryResult, endpoint);
103                         if (logger.isDebugEnabled())
104                         {
105                             logger.debug("Received final Chain result '" + i + "': "
106                                          + (resultToReturn == null ? "null" : resultToReturn.toString()));
107                         }
108                     }
109                     else
110                     {
111                         // reset the previous call result to avoid confusion
112                         resultToReturn = null;
113                         dispatch(session, intermediaryResult, endpoint);
114                     }
115                 }
116             }
117 
118         }
119         catch (UMOException e)
120         {
121             throw new CouldNotRouteOutboundMessageException(message, endpoint, e);
122         }
123         return resultToReturn;
124     }
125 
126     public void addEndpoint(UMOEndpoint endpoint)
127     {
128         if (!endpoint.isRemoteSync())
129         {
130             logger.debug("Endpoint: "
131                          + endpoint.getEndpointURI()
132                          + " registered on chaining router needs to be RemoteSync enabled. Setting this property now");
133             endpoint.setRemoteSync(true);
134         }
135         super.addEndpoint(endpoint);
136     }
137 
138     /**
139      * Process intermediary result of invocation. The method will be invoked
140      * <strong>only</strong> if both local and intermediary results are available
141      * (not null).
142      * <p/>
143      * Overriding methods must call <code>super(localResult, intermediaryResult)</code>,
144      * unless they are modifying the correlation workflow (if you know what that means,
145      * you know what you are doing and when to do it).
146      * <p/>
147      * Default implementation propagates
148      * the following properties:
149      * <ul>
150      * <li>correlationId
151      * <li>correlationSequence
152      * <li>correlationGroupSize
153      * <li>replyTo
154      * </ul>
155      * @param localResult result of the last endpoint invocation
156      * @param intermediaryResult the message travelling across the endpoints
157      */
158     protected void processIntermediaryResult(UMOMessage localResult, UMOMessage intermediaryResult)
159     {
160         localResult.setCorrelationId(intermediaryResult.getCorrelationId());
161         localResult.setCorrelationSequence(intermediaryResult.getCorrelationSequence());
162         localResult.setCorrelationGroupSize(intermediaryResult.getCorrelationGroupSize());
163         localResult.setReplyTo(intermediaryResult.getReplyTo());
164     }
165 }