View Javadoc

1   /*
2    * $Id: SimpleRetryConnectionStrategy.java 10489 2008-01-23 17:53:38Z dfeist $
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.transport;
12  
13  import org.mule.api.transport.Connectable;
14  import org.mule.config.ExceptionHelper;
15  import org.mule.config.i18n.CoreMessages;
16  import org.mule.util.ObjectUtils;
17  
18  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
19  
20  /**
21   * A simple connection retry strategy where the a connection will be attempted X
22   * number of retryCount every Y milliseconds. The <i>retryCount</i> and <i>frequency</i>
23   * properties can be set to customise the behaviour.
24   */
25  
26  public class SimpleRetryConnectionStrategy extends AbstractConnectionStrategy
27  {
28      public static final int DEFAULT_FREQUENCY = 2000;
29      public static final int DEFAULT_RETRY_COUNT = 2;
30      public static final int RETRY_COUNT_FOREVER = -1;
31  
32      protected static class RetryCounter extends ThreadLocal
33      {
34          public int countRetry()
35          {
36              return ((AtomicInteger) get()).incrementAndGet();
37          }
38          
39          public void reset()
40          {
41              ((AtomicInteger) get()).set(0);
42          }
43  
44          //@Override
45          public AtomicInteger current()
46          {
47              return (AtomicInteger) get();
48          }
49  
50          // @Override
51          protected Object initialValue()
52          {
53              return new AtomicInteger(0);
54          }
55      }
56  
57      protected static final RetryCounter retryCounter = new RetryCounter();
58  
59      protected static final ThreadLocal called = new ThreadLocal();
60  
61      private volatile int retryCount = DEFAULT_RETRY_COUNT;
62      private volatile long retryFrequency = DEFAULT_FREQUENCY;
63  
64      protected void doConnect(Connectable connectable) throws FatalConnectException
65      {
66          while (true)
67          {
68              final Boolean recursiveCallDetected = (Boolean) ObjectUtils.defaultIfNull(called.get(), Boolean.FALSE);
69              if (!recursiveCallDetected.booleanValue())
70              {
71                  retryCounter.countRetry();
72              }
73              called.set(Boolean.TRUE);
74  
75              try
76              {
77                  connectable.connect();
78                  if (logger.isDebugEnabled())
79                  {
80                      logger.debug("Successfully connected to " + getDescription(connectable));
81                  }
82                  break;
83              }
84              catch (InterruptedException ie)
85              {
86                  // If we were interrupted it's probably because the server is
87                  // shutting down
88                  throw new FatalConnectException(
89                      // TODO it's not only endpoint that is reconnected, connectors too
90                      CoreMessages.reconnectStrategyFailed(this.getClass(), 
91                          this.getDescription(connectable)), ie, connectable);
92              }
93              catch (Exception e)
94              {
95                  if (e instanceof FatalConnectException)
96                  {
97                      // rethrow
98                      throw (FatalConnectException) e;
99                  }
100                 if (retryCount != RETRY_COUNT_FOREVER && retryCounter.current().get() >= retryCount)
101                 {
102                     throw new FatalConnectException(
103                         // TODO it's not only endpoint that is reconnected, connectors too
104                         CoreMessages.reconnectStrategyFailed(this.getClass(),
105                             this.getDescription(connectable)), e, connectable);
106                 }
107 
108                 if (logger.isErrorEnabled())
109                 {
110                     StringBuffer msg = new StringBuffer(512);
111                     msg.append("Failed to connect/reconnect: ").append(
112                             getDescription(connectable));
113                     Throwable t = ExceptionHelper.getRootException(e);
114                     msg.append(". Root Exception was: ").append(ExceptionHelper.writeException(t));
115                     logger.error(msg.toString(), e);
116                 }
117 
118                 if (logger.isInfoEnabled())
119                 {
120                     logger.info("Waiting for " + retryFrequency + "ms before reconnecting. Failed attempt "
121                                 + retryCounter.current().get() + " of " +
122                                 (retryCount != RETRY_COUNT_FOREVER ? String.valueOf(retryCount) : "unlimited"));
123                 }
124 
125                 try
126                 {
127                     Thread.sleep(retryFrequency);
128                 }
129                 catch (InterruptedException e1)
130                 {
131                     throw new FatalConnectException(
132                         // TODO it's not only endpoint that is reconnected, connectors too
133                         CoreMessages.reconnectStrategyFailed(this.getClass(), 
134                             this.getDescription(connectable)), e, connectable);
135                 }
136             }
137             finally
138             {
139                 called.set(Boolean.FALSE);
140             }
141         }
142     }
143 
144     /**
145      * Resets any state stored in the retry strategy
146      */
147     public synchronized void resetState()
148     {
149         retryCounter.reset();
150     }
151 
152     public int getRetryCount()
153     {
154         return retryCount;
155     }
156 
157     /**
158      * How many times to retry. Set to -1 to retry forever.
159      * @param retryCount number of retries
160      */
161     public void setRetryCount(int retryCount)
162     {
163         this.retryCount = retryCount;
164     }
165 
166     public long getRetryFrequency()
167     {
168         return retryFrequency;
169     }
170 
171     public void setRetryFrequency(long retryFrequency)
172     {
173         this.retryFrequency = retryFrequency;
174     }
175 }