Coverage Report - org.mule.providers.AbstractMessageDispatcher
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractMessageDispatcher
0%
0/183
0%
0/31
3.2
AbstractMessageDispatcher$Worker
0%
0/16
0%
0/2
3.2
 
 1  
 /*
 2  
  * $Id: AbstractMessageDispatcher.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.providers;
 12  
 
 13  
 import org.mule.MuleRuntimeException;
 14  
 import org.mule.config.MuleProperties;
 15  
 import org.mule.config.i18n.CoreMessages;
 16  
 import org.mule.impl.OptimizedRequestContext;
 17  
 import org.mule.impl.RequestContext;
 18  
 import org.mule.impl.internal.notifications.ConnectionNotification;
 19  
 import org.mule.impl.internal.notifications.MessageNotification;
 20  
 import org.mule.impl.internal.notifications.SecurityNotification;
 21  
 import org.mule.transaction.TransactionCoordination;
 22  
 import org.mule.umo.TransactionException;
 23  
 import org.mule.umo.UMOEvent;
 24  
 import org.mule.umo.UMOException;
 25  
 import org.mule.umo.UMOMessage;
 26  
 import org.mule.umo.UMOTransaction;
 27  
 import org.mule.umo.endpoint.UMOImmutableEndpoint;
 28  
 import org.mule.umo.manager.UMOWorkManager;
 29  
 import org.mule.umo.provider.DispatchException;
 30  
 import org.mule.umo.provider.ReceiveException;
 31  
 import org.mule.umo.provider.UMOConnector;
 32  
 import org.mule.umo.provider.UMOMessageDispatcher;
 33  
 import org.mule.util.ClassUtils;
 34  
 
 35  
 import java.beans.ExceptionListener;
 36  
 
 37  
 import javax.resource.spi.work.Work;
 38  
 import javax.resource.spi.work.WorkManager;
 39  
 
 40  
 import org.apache.commons.logging.Log;
 41  
 import org.apache.commons.logging.LogFactory;
 42  
 
 43  
 /**
 44  
  * <p/> <code>AbstractMessageDispatcher</code> provides a default dispatch (client)
 45  
  * support for handling threads lifecycle and validation.
 46  
  */
 47  
 public abstract class AbstractMessageDispatcher implements UMOMessageDispatcher, ExceptionListener
 48  
 {
 49  
     /**
 50  
      * logger used by this class
 51  
      */
 52  0
     protected transient Log logger = LogFactory.getLog(getClass());
 53  
 
 54  
     /**
 55  
      * Thread pool of Connector sessions
 56  
      */
 57  0
     protected UMOWorkManager workManager = null;
 58  
 
 59  
     protected final UMOImmutableEndpoint endpoint;
 60  
     protected final AbstractConnector connector;
 61  
 
 62  0
     protected boolean disposed = false;
 63  
 
 64  
     protected ConnectionStrategy connectionStrategy;
 65  
 
 66  0
     protected volatile boolean connecting = false;
 67  0
     protected volatile boolean connected = false;
 68  
 
 69  
     public AbstractMessageDispatcher(UMOImmutableEndpoint endpoint)
 70  0
     {
 71  0
         this.endpoint = endpoint;
 72  0
         this.connector = (AbstractConnector) endpoint.getConnector();
 73  
 
 74  0
         connectionStrategy = connector.getConnectionStrategy();
 75  0
         if (connectionStrategy instanceof AbstractConnectionStrategy)
 76  
         {
 77  
             // We don't want to do threading in the dispatcher because we're either
 78  
             // already running in a worker thread (asynchronous) or we need to
 79  
             // complete the operation in a single thread
 80  0
             final AbstractConnectionStrategy connStrategy = (AbstractConnectionStrategy) connectionStrategy;
 81  0
             if (connStrategy.isDoThreading())
 82  
             {
 83  0
                 if (logger.isDebugEnabled())
 84  
                 {
 85  0
                     logger.debug("Overriding doThreading to false on " + connStrategy);
 86  
                 }
 87  0
                 connStrategy.setDoThreading(false);
 88  
             }
 89  
         }
 90  
 
 91  0
         if (isDoThreading())
 92  
         {
 93  
             try
 94  
             {
 95  0
                 workManager = connector.getDispatcherWorkManager();
 96  
             }
 97  0
             catch (UMOException e)
 98  
             {
 99  0
                 dispose();
 100  0
                 throw new MuleRuntimeException(CoreMessages.failedToStart("WorkManager"), e);
 101  0
             }
 102  
         }
 103  0
     }
 104  
 
 105  
     /*
 106  
      * (non-Javadoc)
 107  
      * 
 108  
      * @see org.mule.umo.provider.UMOMessageDispatcher#dispatch(org.mule.umo.UMOEvent)
 109  
      */
 110  
     public final void dispatch(UMOEvent event) throws DispatchException
 111  
     {
 112  0
         event.setSynchronous(false);
 113  0
         event.getMessage().setProperty(MuleProperties.MULE_ENDPOINT_PROPERTY,
 114  
             event.getEndpoint().getEndpointURI().toString());
 115  0
         event = OptimizedRequestContext.criticalSetEvent(event); // MULE-2112
 116  
 
 117  
         // Apply Security filter if one is set
 118  0
         UMOImmutableEndpoint endpoint = event.getEndpoint();
 119  0
         if (endpoint.getSecurityFilter() != null)
 120  
         {
 121  
             try
 122  
             {
 123  0
                 endpoint.getSecurityFilter().authenticate(event);
 124  
             }
 125  0
             catch (org.mule.umo.security.SecurityException e)
 126  
             {
 127  
                 // TODO MULE-863: Do we need this warning?
 128  0
                 logger.warn("Outbound Request was made but was not authenticated: " + e.getMessage(), e);
 129  0
                 connector.fireNotification(new SecurityNotification(e,
 130  
                     SecurityNotification.ADMIN_EVENT_ACTION_START_RANGE));
 131  0
                 connector.handleException(e);
 132  0
                 return;
 133  
             }
 134  0
             catch (UMOException e)
 135  
             {
 136  0
                 dispose();
 137  0
                 throw new DispatchException(event.getMessage(), event.getEndpoint(), e);
 138  0
             }
 139  
         }
 140  
         // the security filter may update the payload so we need to get the
 141  
         // latest event again
 142  0
         event = RequestContext.getEvent();
 143  
 
 144  
         try
 145  
         {
 146  0
             UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
 147  0
             if (isDoThreading() && !event.isSynchronous() && tx == null)
 148  
             {
 149  0
                 workManager.scheduleWork(new Worker(event), WorkManager.INDEFINITE, null, connector);
 150  
             }
 151  
             else
 152  
             {
 153  
                 // Make sure we are connected
 154  0
                 connectionStrategy.connect(this);
 155  0
                 doDispatch(event);
 156  0
                 if (connector.isEnableMessageEvents())
 157  
                 {
 158  0
                     String component = null;
 159  0
                     if (event.getComponent() != null)
 160  
                     {
 161  0
                         component = event.getComponent().getDescriptor().getName();
 162  
                     }
 163  0
                     connector.fireNotification(new MessageNotification(event.getMessage(), event
 164  
                         .getEndpoint(), component, MessageNotification.MESSAGE_DISPATCHED));
 165  
                 }
 166  
             }
 167  
         }
 168  0
         catch (DispatchException e)
 169  
         {
 170  0
             dispose();
 171  0
             throw e;
 172  
         }
 173  0
         catch (Exception e)
 174  
         {
 175  0
             dispose();
 176  0
             throw new DispatchException(event.getMessage(), event.getEndpoint(), e);
 177  0
         }
 178  0
     }
 179  
 
 180  
     public final UMOMessage send(UMOEvent event) throws DispatchException
 181  
     {
 182  
         // No point continuing if the component has rolledback the transaction
 183  0
         if (isTransactionRollback())
 184  
         {
 185  0
             return event.getMessage();
 186  
         }
 187  
 
 188  0
         event.setSynchronous(true);
 189  0
         event.getMessage().setProperty(MuleProperties.MULE_ENDPOINT_PROPERTY,
 190  
             event.getEndpoint().getEndpointURI().toString());
 191  0
         event = OptimizedRequestContext.unsafeSetEvent(event);
 192  
 
 193  
         // Apply Security filter if one is set
 194  0
         UMOImmutableEndpoint endpoint = event.getEndpoint();
 195  0
         if (endpoint.getSecurityFilter() != null)
 196  
         {
 197  
             try
 198  
             {
 199  0
                 endpoint.getSecurityFilter().authenticate(event);
 200  
             }
 201  0
             catch (org.mule.umo.security.SecurityException e)
 202  
             {
 203  0
                 logger.warn("Outbound Request was made but was not authenticated: " + e.getMessage(), e);
 204  0
                 connector.fireNotification(new SecurityNotification(e,
 205  
                     SecurityNotification.SECURITY_AUTHENTICATION_FAILED));
 206  0
                 connector.handleException(e);
 207  0
                 return event.getMessage();
 208  
             }
 209  0
             catch (UMOException e)
 210  
             {
 211  0
                 dispose();
 212  0
                 throw new DispatchException(event.getMessage(), event.getEndpoint(), e);
 213  0
             }
 214  
         }
 215  
         // the security filter may update the payload so we need to get the
 216  
         // latest event again
 217  0
         event = RequestContext.getEvent();
 218  
 
 219  
         try
 220  
         {
 221  
             // Make sure we are connected
 222  0
             connectionStrategy.connect(this);
 223  
 
 224  0
             UMOMessage result = doSend(event);
 225  0
             if (connector.isEnableMessageEvents())
 226  
             {
 227  0
                 String component = null;
 228  0
                 if (event.getComponent() != null)
 229  
                 {
 230  0
                     component = event.getComponent().getDescriptor().getName();
 231  
                 }
 232  0
                 connector.fireNotification(new MessageNotification(event.getMessage(), event.getEndpoint(),
 233  
                     component, MessageNotification.MESSAGE_SENT));
 234  
             }
 235  
 
 236  
             // Once a dispatcher has done its work we need to remove this property
 237  
             // so that it is not propagated to the next request
 238  0
             if (result != null)
 239  
             {
 240  0
                 result.removeProperty(MuleProperties.MULE_REMOTE_SYNC_PROPERTY);
 241  
             }
 242  0
             return result;
 243  
         }
 244  0
         catch (DispatchException e)
 245  
         {
 246  0
             dispose();
 247  0
             throw e;
 248  
         }
 249  0
         catch (Exception e)
 250  
         {
 251  0
             dispose();
 252  0
             throw new DispatchException(event.getMessage(), event.getEndpoint(), e);
 253  
         }
 254  
     }
 255  
 
 256  
     /**
 257  
      * Make a specific request to the underlying transport
 258  
      * 
 259  
      * @param timeout the maximum time the operation should block before returning.
 260  
      *            The call should return immediately if there is data available. If
 261  
      *            no data becomes available before the timeout elapses, null will be
 262  
      *            returned
 263  
      * @return the result of the request wrapped in a UMOMessage object. Null will be
 264  
      *         returned if no data was avaialable
 265  
      * @throws Exception if the call to the underlying protocal cuases an exception
 266  
      */
 267  
     public final UMOMessage receive(long timeout) throws Exception
 268  
     {
 269  
         try
 270  
         {
 271  
             // Make sure we are connected
 272  0
             connectionStrategy.connect(this);
 273  0
             UMOMessage result = doReceive(timeout);
 274  0
             if (result != null && connector.isEnableMessageEvents())
 275  
             {
 276  0
                 connector.fireNotification(new MessageNotification(result, endpoint, null,
 277  
                     MessageNotification.MESSAGE_RECEIVED));
 278  
             }
 279  0
             return result;
 280  
         }
 281  0
         catch (DispatchException e)
 282  
         {
 283  0
             dispose();
 284  0
             throw e;
 285  
         }
 286  0
         catch (Exception e)
 287  
         {
 288  0
             dispose();
 289  0
             throw new ReceiveException(endpoint, timeout, e);
 290  
         }
 291  
     }
 292  
 
 293  
     /*
 294  
      * (non-Javadoc)
 295  
      * 
 296  
      * @see org.mule.util.ExceptionListener#onException(java.lang.Throwable)
 297  
      */
 298  
     public void exceptionThrown(Exception e)
 299  
     {
 300  
         try
 301  
         {
 302  0
             getConnector().handleException(e);
 303  
         }
 304  
         finally
 305  
         {
 306  0
             dispose();
 307  0
         }
 308  0
     }
 309  
 
 310  
     public boolean validate()
 311  
     {
 312  
         // by default a dispatcher can be used unless disposed
 313  0
         return !disposed;
 314  
     }
 315  
 
 316  
     public void activate()
 317  
     {
 318  
         // nothing to do by default
 319  0
     }
 320  
 
 321  
     public void passivate()
 322  
     {
 323  
         // nothing to do by default
 324  0
     }
 325  
 
 326  
     /**
 327  
      * Template method to destroy any resources held by the Message Dispatcher
 328  
      */
 329  
     public final synchronized void dispose()
 330  
     {
 331  0
         if (!disposed)
 332  
         {
 333  
             try
 334  
             {
 335  
                 try
 336  
                 {
 337  0
                     this.disconnect();
 338  
                 }
 339  0
                 catch (Exception e)
 340  
                 {
 341  
                     // TODO MULE-863: What should we really do?
 342  0
                     logger.warn(e.getMessage(), e);
 343  0
                 }
 344  
 
 345  0
                 this.doDispose();
 346  
 
 347  0
                 if (workManager != null)
 348  
                 {
 349  0
                     workManager.dispose();
 350  
                 }
 351  
             }
 352  
             finally
 353  
             {
 354  0
                 disposed = true;
 355  0
             }
 356  
         }
 357  0
     }
 358  
 
 359  
     public UMOConnector getConnector()
 360  
     {
 361  0
         return connector;
 362  
     }
 363  
 
 364  
     public UMOImmutableEndpoint getEndpoint()
 365  
     {
 366  0
         return endpoint;
 367  
     }
 368  
 
 369  
     /**
 370  
      * RemoteSync causes the message dispatch to wait for a response to an event on a
 371  
      * response channel after it sends the event. The following rules apply to
 372  
      * RemoteSync 1. The connector has to support remoteSync. Some transports do not
 373  
      * have the notion of a response channel 2. Check if the endpoint has been
 374  
      * configured for remoteSync 3. Check if the REMOTE_SYNC message header has been
 375  
      * set 4. Finally, if the current component has a response router configured,
 376  
      * that the router will handle the response channel event and we should not try
 377  
      * and receive a response in the Message dispatcher If remotesync should not be
 378  
      * used we must remove the REMOTE_SYNC header Note the MuleClient will
 379  
      * automatically set the REMOTE_SYNC header when client.send(..) is called so
 380  
      * that results are returned from remote invocations too.
 381  
      * 
 382  
      * @param event the current event
 383  
      * @return true if a response channel should be used to get a resposne from the
 384  
      *         event dispatch.
 385  
      */
 386  
     protected boolean useRemoteSync(UMOEvent event)
 387  
     {
 388  0
         boolean remoteSync = false;
 389  0
         if (event.getEndpoint().getConnector().isRemoteSyncEnabled())
 390  
         {
 391  0
             remoteSync = event.getEndpoint().isRemoteSync()
 392  
                             || event.getMessage().getBooleanProperty(
 393  
                                 MuleProperties.MULE_REMOTE_SYNC_PROPERTY, false);
 394  0
             if (remoteSync)
 395  
             {
 396  
                 // component will be null for client calls
 397  0
                 if (event.getComponent() != null)
 398  
                 {
 399  0
                     remoteSync = event.getComponent().getDescriptor().getResponseRouter() == null;
 400  
                 }
 401  
             }
 402  
         }
 403  0
         if (!remoteSync)
 404  
         {
 405  0
             event.getMessage().removeProperty(MuleProperties.MULE_REMOTE_SYNC_PROPERTY);
 406  
         }
 407  0
         return remoteSync;
 408  
     }
 409  
 
 410  
     public synchronized void connect() throws Exception
 411  
     {
 412  0
         if (disposed)
 413  
         {
 414  0
             throw new IllegalStateException("Dispatcher has been disposed; cannot connect to resource");
 415  
         }
 416  
 
 417  0
         if (connected)
 418  
         {
 419  0
             return;
 420  
         }
 421  
 
 422  0
         if (!connecting)
 423  
         {
 424  0
             connecting = true;
 425  
 
 426  0
             if (logger.isDebugEnabled())
 427  
             {
 428  0
                 logger.debug("Connecting: " + this);
 429  
             }
 430  
 
 431  0
             connectionStrategy.connect(this);
 432  
 
 433  0
             logger.info("Connected: " + this);
 434  0
             return;
 435  
         }
 436  
 
 437  
         try
 438  
         {
 439  0
             this.doConnect();
 440  0
             connected = true;
 441  0
             connecting = false;
 442  
 
 443  0
             connector.fireNotification(new ConnectionNotification(this, getConnectEventId(endpoint),
 444  
                 ConnectionNotification.CONNECTION_CONNECTED));
 445  
         }
 446  0
         catch (Exception e)
 447  
         {
 448  0
             connected = false;
 449  0
             connecting = false;
 450  
 
 451  0
             connector.fireNotification(new ConnectionNotification(this, getConnectEventId(endpoint),
 452  
                 ConnectionNotification.CONNECTION_FAILED));
 453  
 
 454  0
             if (e instanceof ConnectException)
 455  
             {
 456  0
                 throw (ConnectException) e;
 457  
             }
 458  
             else
 459  
             {
 460  0
                 throw new ConnectException(e, this);
 461  
             }
 462  0
         }
 463  0
     }
 464  
 
 465  
     public synchronized void disconnect() throws Exception
 466  
     {
 467  0
         if (!connected)
 468  
         {
 469  0
             return;
 470  
         }
 471  
 
 472  0
         if (logger.isDebugEnabled())
 473  
         {
 474  0
             logger.debug("Disconnecting: " + this);
 475  
         }
 476  
 
 477  0
         this.doDisconnect();
 478  0
         connected = false;
 479  
 
 480  0
         logger.info("Disconnected: " + this);
 481  
 
 482  0
         connector.fireNotification(new ConnectionNotification(this, getConnectEventId(endpoint),
 483  
             ConnectionNotification.CONNECTION_DISCONNECTED));
 484  0
     }
 485  
 
 486  
     protected String getConnectEventId(UMOImmutableEndpoint endpoint)
 487  
     {
 488  0
         return connector.getName() + ".dispatcher(" + endpoint.getEndpointURI() + ")";
 489  
     }
 490  
 
 491  
     public final boolean isConnected()
 492  
     {
 493  0
         return connected;
 494  
     }
 495  
 
 496  
     protected boolean isDoThreading ()
 497  
     {
 498  0
         return connector.getDispatcherThreadingProfile().isDoThreading();
 499  
     }
 500  
 
 501  
     /**
 502  
      * Returns a string identifying the underlying resource
 503  
      * 
 504  
      * @return
 505  
      */
 506  
     public String getConnectionDescription()
 507  
     {
 508  0
         return endpoint.getEndpointURI().toString();
 509  
     }
 510  
 
 511  
     public synchronized void reconnect() throws Exception
 512  
     {
 513  0
         disconnect();
 514  0
         connect();
 515  0
     }
 516  
 
 517  
     protected abstract void doDispose();
 518  
 
 519  
     protected abstract void doDispatch(UMOEvent event) throws Exception;
 520  
 
 521  
     protected abstract UMOMessage doSend(UMOEvent event) throws Exception;
 522  
 
 523  
     protected abstract void doConnect() throws Exception;
 524  
 
 525  
     protected abstract void doDisconnect() throws Exception;
 526  
 
 527  
     /**
 528  
      * Make a specific request to the underlying transport
 529  
      * 
 530  
      * @param timeout the maximum time the operation should block before returning.
 531  
      *            The call should return immediately if there is data available. If
 532  
      *            no data becomes available before the timeout elapses, null will be
 533  
      *            returned
 534  
      * @return the result of the request wrapped in a UMOMessage object. Null will be
 535  
      *         returned if no data was avaialable
 536  
      * @throws Exception if the call to the underlying protocal cuases an exception
 537  
      */
 538  
     protected abstract UMOMessage doReceive(long timeout) throws Exception;
 539  
 
 540  
     private class Worker implements Work
 541  
     {
 542  
         private UMOEvent event;
 543  
 
 544  
         public Worker(UMOEvent event)
 545  0
         {
 546  0
             this.event = event;
 547  0
         }
 548  
 
 549  
         /*
 550  
          * (non-Javadoc)
 551  
          * 
 552  
          * @see java.lang.Runnable#run()
 553  
          */
 554  
         public void run()
 555  
         {
 556  
             try
 557  
             {
 558  0
                 event = OptimizedRequestContext.criticalSetEvent(event);
 559  
                 // Make sure we are connected
 560  0
                 connectionStrategy.connect(AbstractMessageDispatcher.this);
 561  0
                 AbstractMessageDispatcher.this.doDispatch(event);
 562  
 
 563  0
                 if (connector.isEnableMessageEvents())
 564  
                 {
 565  0
                     String component = null;
 566  0
                     if (event.getComponent() != null)
 567  
                     {
 568  0
                         component = event.getComponent().getDescriptor().getName();
 569  
                     }
 570  
 
 571  0
                     connector.fireNotification(new MessageNotification(event.getMessage(), event
 572  
                         .getEndpoint(), component, MessageNotification.MESSAGE_DISPATCHED));
 573  
                 }
 574  
             }
 575  0
             catch (Exception e)
 576  
             {
 577  0
                 AbstractMessageDispatcher.this.getConnector().handleException(e);
 578  0
             }
 579  0
         }
 580  
 
 581  
         public void release()
 582  
         {
 583  
             // nothing to do
 584  0
         }
 585  
     }
 586  
 
 587  
     /**
 588  
      * Checks to see if the current transaction has been rolled back
 589  
      * 
 590  
      * @return
 591  
      */
 592  
     protected boolean isTransactionRollback()
 593  
     {
 594  
         try
 595  
         {
 596  0
             UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
 597  0
             if (tx != null && tx.isRollbackOnly())
 598  
             {
 599  0
                 return true;
 600  
             }
 601  
         }
 602  0
         catch (TransactionException e)
 603  
         {
 604  
             // TODO MULE-863: What should we really do?
 605  0
             logger.warn(e.getMessage());
 606  0
         }
 607  0
         return false;
 608  
     }
 609  
 
 610  
     //  @Override
 611  
     public String toString()
 612  
     {
 613  0
         final StringBuffer sb = new StringBuffer(80);
 614  0
         sb.append(ClassUtils.getSimpleName(this.getClass()));
 615  0
         sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
 616  0
         sb.append(", endpoint=").append(endpoint.getEndpointURI());
 617  0
         sb.append('}');
 618  0
         return sb.toString();
 619  
     }
 620  
 }