View Javadoc

1   /*
2    * $Id: AbstractTransportMessageHandler.java 21939 2011-05-18 13:32:09Z aperepel $
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.config.MuleConfiguration;
16  import org.mule.api.context.WorkManager;
17  import org.mule.api.endpoint.ImmutableEndpoint;
18  import org.mule.api.lifecycle.CreateException;
19  import org.mule.api.lifecycle.InitialisationException;
20  import org.mule.api.lifecycle.LifecycleCallback;
21  import org.mule.api.lifecycle.LifecycleState;
22  import org.mule.api.lifecycle.LifecycleStateEnabled;
23  import org.mule.api.retry.RetryContext;
24  import org.mule.api.retry.RetryPolicyTemplate;
25  import org.mule.api.transport.Connectable;
26  import org.mule.api.transport.Connector;
27  import org.mule.api.transport.MuleMessageFactory;
28  import org.mule.config.i18n.CoreMessages;
29  import org.mule.config.i18n.Message;
30  import org.mule.config.i18n.MessageFactory;
31  import org.mule.context.notification.ConnectionNotification;
32  import org.mule.util.ClassUtils;
33  
34  import java.util.concurrent.atomic.AtomicBoolean;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  /**
40   * Provide a default dispatch (client) support for handling threads lifecycle and validation.
41   */
42  public abstract class AbstractTransportMessageHandler<O> implements Connectable, LifecycleStateEnabled
43  {
44      protected transient Log logger = LogFactory.getLog(getClass());
45  
46      protected ImmutableEndpoint endpoint;
47      protected final AbstractConnector connector;
48      protected RetryPolicyTemplate retryTemplate;
49      protected MuleMessageFactory muleMessageFactory = null;
50  
51      protected ConnectableLifecycleManager<O> lifecycleManager;
52      // TODO This state info. needs to be incorporated into the ConnectableLifecycleManager
53      protected final AtomicBoolean connected = new AtomicBoolean(false);
54      
55      public AbstractTransportMessageHandler(ImmutableEndpoint endpoint)
56      {
57          this.endpoint = endpoint;
58          this.connector = (AbstractConnector) endpoint.getConnector();
59          this.lifecycleManager = createLifecycleManager();
60      }
61  
62      protected abstract ConnectableLifecycleManager<O> createLifecycleManager();
63  
64      public LifecycleState getLifecycleState()
65      {
66          return lifecycleManager.getState();
67      }
68  
69      protected void disposeAndLogException()
70      {
71          try
72          {
73              dispose();
74          }
75          catch (Throwable t)
76          {
77              logger.error("Could not dispose of the message dispatcher!", t);
78          }
79      }
80  
81      public boolean validate()
82      {
83          // by default a dispatcher/requester can be used unless disposed
84          return !getLifecycleState().isDisposed();
85      }
86  
87      public void activate()
88      {
89          // nothing to do by default
90      }
91  
92      public void passivate()
93      {
94          // nothing to do by default
95      }
96  
97      public void initialise() throws InitialisationException
98      {
99          try
100         {
101             lifecycleManager.fireInitialisePhase(new LifecycleCallback<O>()
102             {
103                 public void onTransition(String phaseName, O object) throws MuleException
104                 {
105                     initializeRetryPolicy();
106                     initializeMessageFactory();
107                     doInitialise();
108                 }
109             });
110         }
111         catch (InitialisationException e)
112         {
113             throw e;
114         }
115         catch (MuleException e)
116         {
117             throw new InitialisationException(e, this);
118         }
119 
120     }
121 
122     protected void initializeRetryPolicy()
123     {
124         if (endpoint.getRetryPolicyTemplate() != null)
125         {
126             retryTemplate = endpoint.getRetryPolicyTemplate();
127         }
128         else
129         {
130             retryTemplate = connector.getRetryPolicyTemplate();
131         }
132     }
133 
134     /**
135      * Subclasses can override this method to create a custom {@link MuleMessageFactory} instead
136      * of re-using the instance from the connector.
137      */
138     protected void initializeMessageFactory() throws InitialisationException
139     {
140         try
141         {
142             muleMessageFactory = connector.getMuleMessageFactory();
143         }
144         catch (CreateException ce)
145         {
146             Message message = MessageFactory.createStaticMessage(ce.getMessage());
147             throw new InitialisationException(message, ce, this);
148         }
149     }
150 
151     /**
152      * Template method to destroy any resources held by the Message Dispatcher
153      */
154     public synchronized void dispose()
155     {
156         try
157         {
158             if (isStarted())
159             {
160                 stop();
161             }
162             if (isConnected())
163             {
164                 disconnect();
165             }
166         }
167         catch (Exception e)
168         {
169             logger.warn(e.getMessage(), e);
170         }
171 
172         try
173         {
174             lifecycleManager.fireDisposePhase(new LifecycleCallback<O>() 
175             {
176                 public void onTransition(String phaseName, O object) throws MuleException
177                 {
178                     doDispose();
179                 }
180             });
181         }
182         catch (MuleException e)
183         {
184             logger.warn(e.getMessage(), e);
185         }
186     }
187 
188     public Connector getConnector()
189     {
190         return connector;
191     }
192 
193     public ImmutableEndpoint getEndpoint()
194     {
195         return endpoint;
196     }
197 
198     public final synchronized void connect() throws Exception
199     {
200         // This method may be called to ensure transport is connected, if it is
201         // already connected then just return.
202         if (connected.get())
203         {
204             return;
205         }
206 
207         if (getLifecycleState().isDisposed())
208         {
209             throw new IllegalStateException(
210                     "Requester/dispatcher has been disposed; cannot connect to resource:" + this);
211         }
212 
213         if (logger.isDebugEnabled())
214         {
215             logger.debug("Connecting: " + this);
216         }
217 
218         doConnect();
219         connected.set(true);
220 
221         if (logger.isDebugEnabled())
222         {
223             logger.debug("Connected: " + getConnectionDescription());
224         }
225     }
226 
227     public RetryContext validateConnection(RetryContext retryContext)
228     {
229         retryContext.setOk();
230         return retryContext;
231     }
232 
233     public final synchronized void disconnect() throws Exception
234     {
235         if (isStarted())
236         {
237             stop();
238         }
239         
240         if (logger.isDebugEnabled())
241         {
242             logger.debug("Disconnecting: " + this);
243         }
244 
245         doDisconnect();
246         connected.set(false);
247 
248         if (logger.isDebugEnabled())
249         {
250             logger.debug("Disconnected: " + this);
251         }
252         connector.fireNotification(new ConnectionNotification(this, getConnectEventId(endpoint),
253                 ConnectionNotification.CONNECTION_DISCONNECTED));
254     }
255 
256     protected String getConnectEventId(ImmutableEndpoint endpoint)
257     {
258         return connector.getName() + ".dispatcher(" + endpoint.getEndpointURI().getUri() + ")";
259     }
260 
261     public final boolean isConnected()
262     {
263         return connected.get();
264     }
265 
266     protected boolean isDoThreading()
267     {
268         return connector.getDispatcherThreadingProfile().isDoThreading();
269     }
270 
271     /**
272      * Returns a string identifying the underlying resource
273      */
274     public String getConnectionDescription()
275     {
276         return "endpoint.outbound." + endpoint.getEndpointURI().toString();
277     }
278 
279     /**
280      * This method will start the connectable, calling {@link #connect()} if it is
281      * needed.
282      * <p/>
283      * This method is synchronous or not depending on how the {@link #retryTemplate}
284      * behaves.
285      * <p/>
286      * This method ensures that {@link #doStart()} will be called at most one time
287      * and will return without error if the component is already started.
288      */
289     public final void start() throws MuleException
290     {
291         if (isStarted() || isStarting())
292         {
293             return;
294         }
295 
296         if (!isConnected())
297         {
298             try
299             {
300                 connect();
301             }
302             catch (MuleException me)
303             {
304                 throw me;
305             }
306             catch (Exception e)
307             {
308                 throw new ConnectException(e, this);
309             }
310         }
311 
312         lifecycleManager.fireStartPhase(new LifecycleCallback<O>()
313         {
314             public void onTransition(String phaseName, O object) throws MuleException
315             {
316                 doStart();
317             }
318         });
319     }
320 
321     public final void stop() throws MuleException
322     {
323         lifecycleManager.fireStopPhase(new LifecycleCallback<O>()
324         {
325             public void onTransition(String phaseName, O object) throws MuleException
326             {
327                 try
328                 {
329                     doStop();
330                 }
331                 catch (MuleException e)
332                 {
333                     logger.error(e.getMessage(), e);
334                 }
335             }
336         });
337 
338     }
339 
340     protected void doInitialise() throws InitialisationException
341     {
342         // nothing to do by default
343     }
344 
345     protected void doDispose()
346     {
347         // nothing to do by default
348     }
349 
350     protected void doConnect() throws Exception
351     {
352         // nothing to do by default
353     }
354 
355     protected void doDisconnect() throws Exception
356     {
357         // nothing to do by default
358     }
359 
360     protected void doStart() throws MuleException
361     {
362         // nothing to do by default
363     }
364 
365     protected void doStop() throws MuleException
366     {
367         // nothing to do by default
368     }
369 
370     @Override
371     public String toString()
372     {
373         final StringBuffer sb = new StringBuffer(80);
374         sb.append(ClassUtils.getSimpleName(this.getClass()));
375         sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
376         sb.append(", endpoint=").append(endpoint.getEndpointURI());
377         sb.append(", disposed=").append(getLifecycleState().isDisposed());
378         sb.append('}');
379         return sb.toString();
380     }
381 
382     // TODO MULE-4871 Endpoint should not be mutable
383 
384     public void setEndpoint(ImmutableEndpoint endpoint)
385     {
386         if (endpoint == null)
387         {
388             throw new IllegalArgumentException("Endpoint cannot be null");
389         }
390         this.endpoint = endpoint;
391     }
392 
393     abstract protected WorkManager getWorkManager() throws MuleException;
394 
395     public boolean isStarted()
396     {
397         return getLifecycleState().isStarted();
398     }
399 
400     public boolean isStarting()
401     {
402         return getLifecycleState().isStarting();
403     }
404 
405     public boolean isStopping()
406     {
407         return getLifecycleState().isStopping();
408     }
409 
410     /**
411      * This method uses the connector's <code>createMuleMessageFactory</code> method to create
412      * a new {@link MuleMessageFactory}. Subclasses may need to override this method in order to
413      * perform additional initialization on the message factory before it's actually used.
414      */
415     protected MuleMessageFactory createMuleMessageFactory() throws CreateException
416     {
417         return connector.createMuleMessageFactory();
418     }
419 
420     /**
421      * Uses this object's {@link MuleMessageFactory} to create a new {@link MuleMessage} instance.
422      * The payload of the new message will be taken from <code>transportMessage</code>, all
423      * message properties will be copied from <code>previousMessage</code>.
424      */
425     public MuleMessage createMuleMessage(Object transportMessage, MuleMessage previousMessage,
426                                          String encoding) throws MuleException
427     {
428         try
429         {
430             return muleMessageFactory.create(transportMessage, previousMessage, encoding);
431         }
432         catch (Exception e)
433         {
434             throw new CreateException(CoreMessages.failedToCreate("MuleMessage"), e);
435         }
436     }
437 
438     /**
439      * Uses this object's {@link MuleMessageFactory} to create a new {@link MuleMessage} instance.
440      * This is the designated way to build {@link MuleMessage}s from the transport specific message.
441      */
442     public MuleMessage createMuleMessage(Object transportMessage, String encoding) throws MuleException
443     {
444         try
445         {
446             return muleMessageFactory.create(transportMessage, encoding);
447         }
448         catch (Exception e)
449         {
450             throw new CreateException(CoreMessages.failedToCreate("MuleMessage"), e, this);
451         }
452     }
453 
454     /**
455      * Uses this object's {@link MuleMessageFactory} to create a new {@link MuleMessage} instance.
456      * Uses the default encoding.
457      *
458      * @see MuleConfiguration#getDefaultEncoding()
459      */
460     public MuleMessage createMuleMessage(Object transportMessage) throws MuleException
461     {
462         String encoding = endpoint.getMuleContext().getConfiguration().getDefaultEncoding();
463         return createMuleMessage(transportMessage, encoding);
464     }
465 
466     /**
467      * Uses this object's {@link MuleMessageFactory} to create a new {@link MuleMessage} instance.
468      * Rather than passing in a transport message instance, {@link NullPayload} is used instead.
469      * Uses the default encoding.
470      */
471     protected MuleMessage createNullMuleMessage() throws MuleException
472     {
473         return createMuleMessage(null);
474     }
475 }