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