View Javadoc

1   /*
2    * $Id: SimpleRetryConnectionStrategy.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.config.ExceptionHelper;
14  import org.mule.config.i18n.CoreMessages;
15  import org.mule.umo.provider.UMOConnectable;
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          public AtomicInteger current()
45          {
46              return (AtomicInteger) get();
47          }
48  
49          // @Override
50          protected Object initialValue()
51          {
52              return new AtomicInteger(0);
53          }
54      }
55  
56      protected static final RetryCounter retryCounter = new RetryCounter();
57  
58      protected static final ThreadLocal called = new ThreadLocal();
59  
60      private volatile int retryCount = DEFAULT_RETRY_COUNT;
61      private volatile long frequency = DEFAULT_FREQUENCY;
62  
63      protected void doConnect(UMOConnectable connectable) throws FatalConnectException
64      {
65          while (true)
66          {
67              final Boolean recursiveCallDetected = (Boolean) ObjectUtils.defaultIfNull(called.get(), Boolean.FALSE);
68              if (!recursiveCallDetected.booleanValue())
69              {
70                  retryCounter.countRetry();
71              }
72              called.set(Boolean.TRUE);
73  
74              try
75              {
76                  connectable.connect();
77                  if (logger.isDebugEnabled())
78                  {
79                      logger.debug("Successfully connected to " + getDescription(connectable));
80                  }
81                  break;
82              }
83              catch (InterruptedException ie)
84              {
85                  // If we were interrupted it's probably because the server is
86                  // shutting down
87                  throw new FatalConnectException(
88                      // TODO it's not only endpoint that is reconnected, connectors too
89                      CoreMessages.reconnectStrategyFailed(this.getClass(), 
90                          this.getDescription(connectable)), ie, connectable);
91              }
92              catch (Exception e)
93              {
94                  if (e instanceof FatalConnectException)
95                  {
96                      // rethrow
97                      throw (FatalConnectException) e;
98                  }
99                  if (retryCount != RETRY_COUNT_FOREVER && retryCounter.current().get() >= retryCount)
100                 {
101                     throw new FatalConnectException(
102                         // TODO it's not only endpoint that is reconnected, connectors too
103                         CoreMessages.reconnectStrategyFailed(this.getClass(),
104                             this.getDescription(connectable)), e, connectable);
105                 }
106 
107                 if (logger.isErrorEnabled())
108                 {
109                     StringBuffer msg = new StringBuffer(512);
110                     msg.append("Failed to connect/reconnect: ").append(
111                             getDescription(connectable));
112                     Throwable t = ExceptionHelper.getRootException(e);
113                     msg.append(". Root Exception was: ").append(ExceptionHelper.writeException(t));
114                     logger.error(msg.toString(), e);
115                 }
116 
117                 if (logger.isInfoEnabled())
118                 {
119                     logger.info("Waiting for " + frequency + "ms before reconnecting. Failed attempt "
120                                 + retryCounter.current().get() + " of " +
121                                 (retryCount != RETRY_COUNT_FOREVER ? String.valueOf(retryCount) : "unlimited"));
122                 }
123 
124                 try
125                 {
126                     Thread.sleep(frequency);
127                 }
128                 catch (InterruptedException e1)
129                 {
130                     throw new FatalConnectException(
131                         // TODO it's not only endpoint that is reconnected, connectors too
132                         CoreMessages.reconnectStrategyFailed(this.getClass(), 
133                             this.getDescription(connectable)), e, connectable);
134                 }
135             }
136             finally
137             {
138                 called.set(Boolean.FALSE);
139             }
140         }
141     }
142 
143     /**
144      * Resets any state stored in the retry strategy
145      */
146     public synchronized void resetState()
147     {
148         retryCounter.reset();
149     }
150 
151     public int getRetryCount()
152     {
153         return retryCount;
154     }
155 
156     /**
157      * How many times to retry. Set to -1 to retry forever.
158      * @param retryCount number of retries
159      */
160     public void setRetryCount(int retryCount)
161     {
162         this.retryCount = retryCount;
163     }
164 
165     public long getFrequency()
166     {
167         return frequency;
168     }
169 
170     public void setFrequency(long frequency)
171     {
172         this.frequency = frequency;
173     }
174 }