View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.client.cache;
28  
29  import org.apache.http.annotation.ThreadSafe;
30  import org.apache.http.util.Args;
31  
32  import java.util.concurrent.ScheduledExecutorService;
33  import java.util.concurrent.ScheduledThreadPoolExecutor;
34  import java.util.concurrent.TimeUnit;
35  
36  /**
37   * An implementation that backs off exponentially based on the number of
38   * consecutive failed attempts stored in the
39   * {@link AsynchronousValidationRequest}. It uses the following defaults:
40   * <pre>
41   *         no delay in case it was never tried or didn't fail so far
42   *     6 secs delay for one failed attempt (= {@link #getInitialExpiryInMillis()})
43   *    60 secs delay for two failed attempts
44   *    10 mins delay for three failed attempts
45   *   100 mins delay for four failed attempts
46   *  ~16 hours delay for five failed attempts
47   *   24 hours delay for six or more failed attempts (= {@link #getMaxExpiryInMillis()})
48   * </pre>
49   *
50   * The following equation is used to calculate the delay for a specific revalidation request:
51   * <pre>
52   *     delay = {@link #getInitialExpiryInMillis()} * Math.pow({@link #getBackOffRate()},
53   *     {@link AsynchronousValidationRequest#getConsecutiveFailedAttempts()} - 1))
54   * </pre>
55   * The resulting delay won't exceed {@link #getMaxExpiryInMillis()}.
56   *
57   * @since 4.3
58   */
59  @ThreadSafe
60  public class ExponentialBackOffSchedulingStrategy implements SchedulingStrategy {
61  
62      public static final long DEFAULT_BACK_OFF_RATE = 10;
63      public static final long DEFAULT_INITIAL_EXPIRY_IN_MILLIS = TimeUnit.SECONDS.toMillis(6);
64      public static final long DEFAULT_MAX_EXPIRY_IN_MILLIS = TimeUnit.SECONDS.toMillis(86400);
65  
66      private final long backOffRate;
67      private final long initialExpiryInMillis;
68      private final long maxExpiryInMillis;
69  
70      private final ScheduledExecutorService executor;
71  
72      /**
73       * Create a new scheduling strategy using a fixed pool of worker threads.
74       * @param cacheConfig the thread pool configuration to be used; not <code>null</code>
75       * @see org.apache.http.impl.client.cache.CacheConfig#getAsynchronousWorkersMax()
76       * @see #DEFAULT_BACK_OFF_RATE
77       * @see #DEFAULT_INITIAL_EXPIRY_IN_MILLIS
78       * @see #DEFAULT_MAX_EXPIRY_IN_MILLIS
79       */
80      public ExponentialBackOffSchedulingStrategy(final CacheConfig cacheConfig) {
81          this(cacheConfig,
82                  DEFAULT_BACK_OFF_RATE,
83                  DEFAULT_INITIAL_EXPIRY_IN_MILLIS,
84                  DEFAULT_MAX_EXPIRY_IN_MILLIS);
85      }
86  
87      /**
88       * Create a new scheduling strategy by using a fixed pool of worker threads and the
89       * given parameters to calculated the delay.
90       *
91       * @param cacheConfig the thread pool configuration to be used; not <code>null</code>
92       * @param backOffRate the back off rate to be used; not negative
93       * @param initialExpiryInMillis the initial expiry in milli seconds; not negative
94       * @param maxExpiryInMillis the upper limit of the delay in milli seconds; not negative
95       * @see org.apache.http.impl.client.cache.CacheConfig#getAsynchronousWorkersMax()
96       * @see ExponentialBackOffSchedulingStrategy
97       */
98      public ExponentialBackOffSchedulingStrategy(
99              final CacheConfig cacheConfig,
100             final long backOffRate,
101             final long initialExpiryInMillis,
102             final long maxExpiryInMillis) {
103         this(createThreadPoolFromCacheConfig(cacheConfig),
104                 backOffRate,
105                 initialExpiryInMillis,
106                 maxExpiryInMillis);
107     }
108 
109     private static ScheduledThreadPoolExecutor createThreadPoolFromCacheConfig(
110             final CacheConfig cacheConfig) {
111         final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(
112                 cacheConfig.getAsynchronousWorkersMax());
113         scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
114         return scheduledThreadPoolExecutor;
115     }
116 
117     ExponentialBackOffSchedulingStrategy(
118             final ScheduledExecutorService executor,
119             final long backOffRate,
120             final long initialExpiryInMillis,
121             final long maxExpiryInMillis) {
122         this.executor = Args.notNull(executor, "Executor");
123         this.backOffRate = Args.notNegative(backOffRate, "BackOffRate");
124         this.initialExpiryInMillis = Args.notNegative(initialExpiryInMillis, "InitialExpiryInMillis");
125         this.maxExpiryInMillis = Args.notNegative(maxExpiryInMillis, "MaxExpiryInMillis");
126     }
127 
128     @Override
129     public void schedule(
130             final AsynchronousValidationRequest revalidationRequest) {
131         Args.notNull(revalidationRequest, "RevalidationRequest");
132         final int consecutiveFailedAttempts = revalidationRequest.getConsecutiveFailedAttempts();
133         final long delayInMillis = calculateDelayInMillis(consecutiveFailedAttempts);
134         executor.schedule(revalidationRequest, delayInMillis, TimeUnit.MILLISECONDS);
135     }
136 
137     @Override
138     public void close() {
139         executor.shutdown();
140     }
141 
142     public long getBackOffRate() {
143         return backOffRate;
144     }
145 
146     public long getInitialExpiryInMillis() {
147         return initialExpiryInMillis;
148     }
149 
150     public long getMaxExpiryInMillis() {
151         return maxExpiryInMillis;
152     }
153 
154     protected long calculateDelayInMillis(final int consecutiveFailedAttempts) {
155         if (consecutiveFailedAttempts > 0) {
156             final long delayInSeconds = (long) (initialExpiryInMillis *
157                     Math.pow(backOffRate, consecutiveFailedAttempts - 1));
158             return Math.min(delayInSeconds, maxExpiryInMillis);
159         }
160         else {
161             return 0;
162         }
163     }
164 
165     /**
166      * @deprecated Use {@link org.apache.http.util.Args#notNull(Object, String)}
167      */
168     @Deprecated
169     protected static <T> T checkNotNull(final String parameterName, final T value) {
170         if (value == null) {
171             throw new IllegalArgumentException(parameterName + " may not be null");
172         }
173         return value;
174     }
175 
176     /**
177      * @deprecated Use {@link org.apache.http.util.Args#notNegative(long, String)}
178      */
179     @Deprecated
180     protected static long checkNotNegative(final String parameterName, final long value) {
181         if (value < 0) {
182             throw new IllegalArgumentException(parameterName + " may not be negative");
183         }
184         return value;
185     }
186 }