Coverage Report - org.mule.transport.AbstractTransportMessageHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractTransportMessageHandler
0%
0/139
0%
0/36
0
AbstractTransportMessageHandler$1
0%
0/5
N/A
0
AbstractTransportMessageHandler$2
0%
0/3
N/A
0
AbstractTransportMessageHandler$3
0%
0/15
0%
0/6
0
AbstractTransportMessageHandler$4
0%
0/3
N/A
0
AbstractTransportMessageHandler$5
0%
0/6
N/A
0
AbstractTransportMessageHandler$5$1
0%
0/3
N/A
0
AbstractTransportMessageHandler$6
0%
0/6
N/A
0
 
 1  
 /*
 2  
  * $Id: AbstractTransportMessageHandler.java 20358 2010-11-26 20:15:18Z tcarlson $
 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.transport;
 12  
 
 13  
 import org.mule.api.MuleException;
 14  
 import org.mule.api.MuleMessage;
 15  
 import org.mule.api.MuleRuntimeException;
 16  
 import org.mule.api.config.MuleConfiguration;
 17  
 import org.mule.api.context.WorkManager;
 18  
 import org.mule.api.endpoint.ImmutableEndpoint;
 19  
 import org.mule.api.lifecycle.CreateException;
 20  
 import org.mule.api.lifecycle.InitialisationException;
 21  
 import org.mule.api.lifecycle.LifecycleCallback;
 22  
 import org.mule.api.lifecycle.LifecycleException;
 23  
 import org.mule.api.lifecycle.LifecycleState;
 24  
 import org.mule.api.lifecycle.LifecycleStateEnabled;
 25  
 import org.mule.api.lifecycle.StartException;
 26  
 import org.mule.api.retry.RetryCallback;
 27  
 import org.mule.api.retry.RetryContext;
 28  
 import org.mule.api.retry.RetryPolicyTemplate;
 29  
 import org.mule.api.transport.Connectable;
 30  
 import org.mule.api.transport.Connector;
 31  
 import org.mule.api.transport.MuleMessageFactory;
 32  
 import org.mule.config.i18n.CoreMessages;
 33  
 import org.mule.config.i18n.Message;
 34  
 import org.mule.config.i18n.MessageFactory;
 35  
 import org.mule.context.notification.ConnectionNotification;
 36  
 import org.mule.util.ClassUtils;
 37  
 import org.mule.util.concurrent.WaitableBoolean;
 38  
 
 39  
 import org.apache.commons.logging.Log;
 40  
 import org.apache.commons.logging.LogFactory;
 41  
 
 42  
 /**
 43  
  * Provide a default dispatch (client) support for handling threads lifecycle and validation.
 44  
  */
 45  
 public abstract class AbstractTransportMessageHandler<O> implements Connectable, LifecycleStateEnabled
 46  
 {
 47  0
     protected transient Log logger = LogFactory.getLog(getClass());
 48  
 
 49  
     protected ImmutableEndpoint endpoint;
 50  
     protected final AbstractConnector connector;
 51  
     protected RetryPolicyTemplate retryTemplate;
 52  0
     protected MuleMessageFactory muleMessageFactory = null;
 53  
 
 54  0
     protected final WaitableBoolean connected = new WaitableBoolean(false);
 55  0
     protected final WaitableBoolean connecting = new WaitableBoolean(false);
 56  
 
 57  
     /**
 58  
      * Indicates whether the receiver/dispatcher/requester should start upon connecting.
 59  
      * This is necessary to support asynchronous retry policies, otherwise the start()
 60  
      * method would block until connection is successful.
 61  
      */
 62  0
     protected volatile boolean startOnConnect = false;
 63  
 
 64  
     protected ConnectableLifecycleManager<O> lifecycleManager;
 65  
 
 66  
     public AbstractTransportMessageHandler(ImmutableEndpoint endpoint)
 67  0
     {
 68  0
         this.endpoint = endpoint;
 69  0
         this.connector = (AbstractConnector) endpoint.getConnector();
 70  0
         this.lifecycleManager = createLifecycleManager();
 71  0
     }
 72  
 
 73  
     protected abstract ConnectableLifecycleManager<O> createLifecycleManager();
 74  
 
 75  
     public LifecycleState getLifecycleState()
 76  
     {
 77  0
         return lifecycleManager.getState();
 78  
     }
 79  
 
 80  
     protected void disposeAndLogException()
 81  
     {
 82  
         try
 83  
         {
 84  0
             dispose();
 85  
         }
 86  0
         catch (Throwable t)
 87  
         {
 88  0
             logger.error("Could not dispose of the message dispatcher!", t);
 89  0
         }
 90  0
     }
 91  
 
 92  
     public boolean validate()
 93  
     {
 94  
         // by default a dispatcher/requester can be used unless disposed
 95  0
         return !getLifecycleState().isDisposed();
 96  
     }
 97  
 
 98  
     public void activate()
 99  
     {
 100  
         // nothing to do by default
 101  0
     }
 102  
 
 103  
     public void passivate()
 104  
     {
 105  
         // nothing to do by default
 106  0
     }
 107  
 
 108  
     public void initialise() throws InitialisationException
 109  
     {
 110  
         try
 111  
         {
 112  0
             lifecycleManager.fireInitialisePhase(new LifecycleCallback<O>()
 113  0
             {
 114  
                 public void onTransition(String phaseName, O object) throws MuleException
 115  
                 {
 116  0
                     initializeRetryPolicy();
 117  0
                     initializeMessageFactory();
 118  0
                     doInitialise();
 119  0
                 }
 120  
             });
 121  
         }
 122  0
         catch (InitialisationException e)
 123  
         {
 124  0
             throw e;
 125  
         }
 126  0
         catch (MuleException e)
 127  
         {
 128  0
             throw new InitialisationException(e, this);
 129  0
         }
 130  
 
 131  0
     }
 132  
 
 133  
     protected void initializeRetryPolicy()
 134  
     {
 135  0
         if (endpoint.getRetryPolicyTemplate() != null)
 136  
         {
 137  0
             retryTemplate = endpoint.getRetryPolicyTemplate();
 138  
         }
 139  
         else
 140  
         {
 141  0
             retryTemplate = connector.getRetryPolicyTemplate();
 142  
         }
 143  0
     }
 144  
 
 145  
     /**
 146  
      * Subclasses can override this method to create a custom {@link MuleMessageFactory} instead
 147  
      * of re-using the instance from the connector.
 148  
      */
 149  
     protected void initializeMessageFactory() throws InitialisationException
 150  
     {
 151  
         try
 152  
         {
 153  0
             muleMessageFactory = connector.getMuleMessageFactory();
 154  
         }
 155  0
         catch (CreateException ce)
 156  
         {
 157  0
             Message message = MessageFactory.createStaticMessage(ce.getMessage());
 158  0
             throw new InitialisationException(message, ce, this);
 159  0
         }
 160  0
     }
 161  
 
 162  
     /**
 163  
      * Template method to destroy any resources held by the Message Dispatcher
 164  
      */
 165  
     public synchronized void dispose()
 166  
     {
 167  
         try
 168  
         {
 169  
             try
 170  
             {
 171  0
                 disconnect();
 172  
             }
 173  0
             catch (Exception e)
 174  
             {
 175  0
                 logger.warn(e.getMessage(), e);
 176  0
             }
 177  0
             if (isStarted())
 178  
             {
 179  0
                 stop();
 180  
             }
 181  
             
 182  
             //Nothing to do when disposing, just transition
 183  0
             lifecycleManager.fireDisposePhase(new LifecycleCallback<O>() {
 184  
                 public void onTransition(String phaseName, O object) throws MuleException
 185  
                 {
 186  0
                     doDispose();
 187  0
                 }
 188  
             });
 189  
         }
 190  0
         catch (Exception e)
 191  
         {
 192  0
             logger.warn(e.getMessage(), e);
 193  0
         }
 194  0
     }
 195  
 
 196  
     public Connector getConnector()
 197  
     {
 198  0
         return connector;
 199  
     }
 200  
 
 201  
     public ImmutableEndpoint getEndpoint()
 202  
     {
 203  0
         return endpoint;
 204  
     }
 205  
 
 206  
     public final synchronized void connect() throws Exception
 207  
     {
 208  
         // This method may be called to ensure transport is connected, if it is
 209  
         // already connected then just return.
 210  0
         if (connected.get() || connecting.get())
 211  
         {
 212  0
             return;
 213  
         }
 214  
 
 215  0
         if (getLifecycleState().isDisposed())
 216  
         {
 217  0
             throw new IllegalStateException(
 218  
                     "Requester/dispatcher has been disposed; cannot connect to resource:" + this);
 219  
         }
 220  
 
 221  0
         if (!connecting.compareAndSet(false, true))
 222  
         {
 223  0
             return;
 224  
         }
 225  
 
 226  0
         if (logger.isDebugEnabled())
 227  
         {
 228  0
             logger.debug("Connecting: " + this);
 229  
         }
 230  
 
 231  0
         retryTemplate.execute(
 232  
                 new RetryCallback()
 233  0
                 {
 234  
                     public void doWork(RetryContext context) throws Exception
 235  
                     {
 236  
                         try
 237  
                         {
 238  0
                             doConnect();
 239  0
                             connected.set(true);
 240  0
                             connecting.set(false);
 241  
 
 242  0
                             if (logger.isDebugEnabled())
 243  
                             {
 244  0
                                 logger.debug("Connected: " + getWorkDescription());
 245  
                             }
 246  
                             // TODO Make this work somehow inside the RetryTemplate
 247  
                             //connector.fireNotification(new ConnectionNotification(this, getConnectEventId(endpoint),
 248  
                             //    ConnectionNotification.CONNECTION_CONNECTED));
 249  
 
 250  0
                             if (startOnConnect)
 251  
                             {
 252  0
                                 start();
 253  
                             }
 254  
                         }
 255  0
                         catch (Exception e)
 256  
                         {
 257  0
                             if (logger.isDebugEnabled())
 258  
                             {
 259  0
                                 logger.debug("exception in doWork", e);
 260  
                             }
 261  0
                             throw e;
 262  0
                         }
 263  0
                     }
 264  
 
 265  
                     public String getWorkDescription()
 266  
                     {
 267  0
                         return getConnectionDescription();
 268  
                     }
 269  
                 },
 270  
                 getWorkManager()
 271  
         );
 272  0
     }
 273  
 
 274  
     public RetryContext validateConnection(RetryContext retryContext)
 275  
     {
 276  0
         retryContext.setOk();
 277  0
         return retryContext;
 278  
     }
 279  
 
 280  
     public final synchronized void disconnect() throws Exception
 281  
     {
 282  0
         if (!connected.get())
 283  
         {
 284  0
             return;
 285  
         }
 286  
 
 287  0
         if (logger.isDebugEnabled())
 288  
         {
 289  0
             logger.debug("Disconnecting: " + this);
 290  
         }
 291  
 
 292  0
         this.doDisconnect();
 293  0
         connected.set(false);
 294  
 
 295  0
         if (logger.isDebugEnabled())
 296  
         {
 297  0
             logger.debug("Disconnected: " + this);
 298  
         }
 299  0
         connector.fireNotification(new ConnectionNotification(this, getConnectEventId(endpoint),
 300  
                 ConnectionNotification.CONNECTION_DISCONNECTED));
 301  0
     }
 302  
 
 303  
     protected String getConnectEventId(ImmutableEndpoint endpoint)
 304  
     {
 305  0
         return connector.getName() + ".dispatcher(" + endpoint.getEndpointURI().getUri() + ")";
 306  
     }
 307  
 
 308  
     public final boolean isConnected()
 309  
     {
 310  0
         return connected.get();
 311  
     }
 312  
 
 313  
     public final boolean isConnecting()
 314  
     {
 315  0
         return connecting.get();
 316  
     }
 317  
 
 318  
     protected boolean isDoThreading()
 319  
     {
 320  0
         return connector.getDispatcherThreadingProfile().isDoThreading();
 321  
     }
 322  
 
 323  
     /**
 324  
      * Returns a string identifying the underlying resource
 325  
      */
 326  
     public String getConnectionDescription()
 327  
     {
 328  0
         return "endpoint.outbound." + endpoint.getEndpointURI().toString();
 329  
     }
 330  
 
 331  
     /**
 332  
      * This method will start the connectable, calling {@link #connect()} if it is
 333  
      * needed.
 334  
      * <p/>
 335  
      * This method is synchronous or not depending on how the {@link #retryTemplate}
 336  
      * behaves.
 337  
      * <p/>
 338  
      * This method ensures that {@link #doStart()} will be called at most one time
 339  
      * and will return without error if the component is already {@link #started}.
 340  
      */
 341  
     public final void start() throws MuleException
 342  
     {
 343  
         //We only fire start lifecycle once we are connected, see {@link #callDoStartWhenItIsConnected}
 344  0
         if (!connected.get() && !connecting.get())
 345  
         {
 346  0
             connectAndThenStart();
 347  
         }
 348  0
         else if (connecting.get() && isDoStartMustFollowDoConnect())
 349  
         {
 350  
             try
 351  
             {
 352  0
                 callDoStartWhenItIsConnected();
 353  
             }
 354  0
             catch (InterruptedException e)
 355  
             {
 356  0
                 throw new StartException(CoreMessages.failedToStart("Connectable: " + this), e, this);
 357  0
             }
 358  
         }
 359  
         else
 360  
         {
 361  
             try
 362  
             {
 363  0
                 lifecycleManager.fireStartPhase(new LifecycleCallback<O>()
 364  0
                 {
 365  
                     public void onTransition(String phaseName, O object) throws MuleException
 366  
                     {
 367  0
                         doStart();
 368  0
                     }
 369  
                 });
 370  
             }
 371  0
             catch (MuleException e)
 372  
             {
 373  0
                 throw e;
 374  
             }
 375  0
             catch (Exception e)
 376  
             {
 377  0
                 throw new StartException(CoreMessages.failedToStart("Connectable: " + this), e, this);
 378  0
             }
 379  
         }
 380  0
     }
 381  
 
 382  
     /**
 383  
      * This method will call {@link #connect()} after setting {@link #startOnConnect}
 384  
      * in true. This will make the {@link #connect()} method call {@link #start()}
 385  
      * after finishing establishing connection.
 386  
      *
 387  
      * @throws LifecycleException
 388  
      */
 389  
     protected void connectAndThenStart() throws LifecycleException
 390  
     {
 391  0
         startOnConnect = true;
 392  
 
 393  
         // Make sure we are connected
 394  
         try
 395  
         {
 396  0
             connect();
 397  
         }
 398  0
         catch (Exception e)
 399  
         {
 400  0
             throw new LifecycleException(e, this);
 401  0
         }
 402  0
     }
 403  
 
 404  
     /**
 405  
      * This method will block until {@link #connected} is true and then will call
 406  
      * {@link #doStart()}.
 407  
      *
 408  
      * @throws InterruptedException if the thread is interrupted while waiting for
 409  
      *                              {@link #connected} to be true.
 410  
      * @throws MuleException        this is just a propagation of any {@link MuleException}
 411  
      *                              that {@link #doStart()} may throw.
 412  
      */
 413  
     protected void callDoStartWhenItIsConnected() throws InterruptedException, MuleException
 414  
     {
 415  
         try
 416  
         {
 417  0
             connected.whenTrue(new Runnable()
 418  0
             {
 419  
                 public void run()
 420  
                 {
 421  
                     try
 422  
                     {
 423  0
                         lifecycleManager.fireStartPhase(new LifecycleCallback<O>()
 424  0
                         {
 425  
                             public void onTransition(String phaseName, O object) throws MuleException
 426  
                             {
 427  0
                                 doStart();
 428  0
                             }
 429  
                         });
 430  
                     }
 431  0
                     catch (MuleException e)
 432  
                     {
 433  0
                         throw new MuleRuntimeException(
 434  
                                 CoreMessages.createStaticMessage("wrapper exception for a MuleException"), e);
 435  0
                     }
 436  0
                 }
 437  
             });
 438  
         }
 439  0
         catch (MuleRuntimeException e)
 440  
         {
 441  0
             if (e.getCause() instanceof MuleException)
 442  
             {
 443  0
                 throw (MuleException) e.getCause();
 444  
             }
 445  
             else
 446  
             {
 447  0
                 throw e;
 448  
             }
 449  0
         }
 450  0
     }
 451  
 
 452  
     public final void stop() throws MuleException
 453  
     {
 454  
         try
 455  
         {
 456  0
             if (connected.get())
 457  
             {
 458  0
                 disconnect();
 459  
             }
 460  
         }
 461  0
         catch (Exception e)
 462  
         {
 463  0
             logger.error(e.getMessage(), e);
 464  0
         }
 465  
 
 466  0
         lifecycleManager.fireStopPhase(new LifecycleCallback<O>()
 467  0
         {
 468  
             public void onTransition(String phaseName, O object) throws MuleException
 469  
             {
 470  
                 try
 471  
                 {
 472  0
                     doStop();
 473  
                 }
 474  0
                 catch (MuleException e)
 475  
                 {
 476  0
                     logger.error(e.getMessage(), e);
 477  0
                 }
 478  0
             }
 479  
         });
 480  
 
 481  0
     }
 482  
 
 483  
     protected void doInitialise() throws InitialisationException
 484  
     {
 485  
         // nothing to do by default
 486  0
     }
 487  
 
 488  
     protected void doDispose()
 489  
     {
 490  
         // nothing to do by default
 491  0
     }
 492  
 
 493  
     protected void doConnect() throws Exception
 494  
     {
 495  
         // nothing to do by default
 496  0
     }
 497  
 
 498  
     protected void doDisconnect() throws Exception
 499  
     {
 500  
         // nothing to do by default
 501  0
     }
 502  
 
 503  
     protected void doStart() throws MuleException
 504  
     {
 505  
         // nothing to do by default
 506  0
     }
 507  
 
 508  
     protected void doStop() throws MuleException
 509  
     {
 510  
         // nothing to do by default
 511  0
     }
 512  
 
 513  
     @Override
 514  
     public String toString()
 515  
     {
 516  0
         final StringBuffer sb = new StringBuffer(80);
 517  0
         sb.append(ClassUtils.getSimpleName(this.getClass()));
 518  0
         sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
 519  0
         sb.append(", endpoint=").append(endpoint.getEndpointURI());
 520  0
         sb.append(", disposed=").append(getLifecycleState().isDisposed());
 521  0
         sb.append('}');
 522  0
         return sb.toString();
 523  
     }
 524  
 
 525  
     // TODO MULE-4871 Endpoint should not be mutable
 526  
 
 527  
     public void setEndpoint(ImmutableEndpoint endpoint)
 528  
     {
 529  0
         if (endpoint == null)
 530  
         {
 531  0
             throw new IllegalArgumentException("Endpoint cannot be null");
 532  
         }
 533  0
         this.endpoint = endpoint;
 534  0
     }
 535  
 
 536  
     abstract protected WorkManager getWorkManager() throws MuleException;
 537  
 
 538  
     public boolean isStarted()
 539  
     {
 540  0
         return getLifecycleState().isStarted();
 541  
     }
 542  
 
 543  
     /**
 544  
      * This method uses the connector's <code>createMuleMessageFactory</code> method to create
 545  
      * a new {@link MuleMessageFactory}. Subclasses may need to override this method in order to
 546  
      * perform additional initialization on the message factory before it's actually used.
 547  
      */
 548  
     protected MuleMessageFactory createMuleMessageFactory() throws CreateException
 549  
     {
 550  0
         return connector.createMuleMessageFactory();
 551  
     }
 552  
 
 553  
     /**
 554  
      * Uses this object's {@link MuleMessageFactory} to create a new {@link MuleMessage} instance.
 555  
      * The payload of the new message will be taken from <code>transportMessage</code>, all
 556  
      * message properties will be copied from <code>previousMessage</code>.
 557  
      */
 558  
     public MuleMessage createMuleMessage(Object transportMessage, MuleMessage previousMessage,
 559  
                                          String encoding) throws MuleException
 560  
     {
 561  
         try
 562  
         {
 563  0
             return muleMessageFactory.create(transportMessage, previousMessage, encoding);
 564  
         }
 565  0
         catch (Exception e)
 566  
         {
 567  0
             throw new CreateException(CoreMessages.failedToCreate("MuleMessage"), e);
 568  
         }
 569  
     }
 570  
 
 571  
     /**
 572  
      * Uses this object's {@link MuleMessageFactory} to create a new {@link MuleMessage} instance.
 573  
      * This is the designated way to build {@link MuleMessage}s from the transport specific message.
 574  
      */
 575  
     public MuleMessage createMuleMessage(Object transportMessage, String encoding) throws MuleException
 576  
     {
 577  
         try
 578  
         {
 579  0
             return muleMessageFactory.create(transportMessage, encoding);
 580  
         }
 581  0
         catch (Exception e)
 582  
         {
 583  0
             throw new CreateException(CoreMessages.failedToCreate("MuleMessage"), e, this);
 584  
         }
 585  
     }
 586  
 
 587  
     /**
 588  
      * Uses this object's {@link MuleMessageFactory} to create a new {@link MuleMessage} instance.
 589  
      * Uses the default encoding.
 590  
      *
 591  
      * @see MuleConfiguration#getDefaultEncoding()
 592  
      */
 593  
     public MuleMessage createMuleMessage(Object transportMessage) throws MuleException
 594  
     {
 595  0
         String encoding = endpoint.getMuleContext().getConfiguration().getDefaultEncoding();
 596  0
         return createMuleMessage(transportMessage, encoding);
 597  
     }
 598  
 
 599  
     /**
 600  
      * Uses this object's {@link MuleMessageFactory} to create a new {@link MuleMessage} instance.
 601  
      * Rather than passing in a transport message instance, {@link NullPayload} is used instead.
 602  
      * Uses the default encoding.
 603  
      */
 604  
     protected MuleMessage createNullMuleMessage() throws MuleException
 605  
     {
 606  0
         return createMuleMessage(null);
 607  
     }
 608  
 
 609  
     /**
 610  
      * @return true if doStart() must come strictly after the completion of doConnect()
 611  
      */
 612  
     protected boolean isDoStartMustFollowDoConnect()
 613  
     {
 614  0
         return false;
 615  
     }
 616  
 }