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 java.io.Closeable;
30  import java.io.IOException;
31  import java.util.Collections;
32  import java.util.HashSet;
33  import java.util.Set;
34  import java.util.concurrent.RejectedExecutionException;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.http.client.cache.HttpCacheEntry;
39  import org.apache.http.client.methods.HttpExecutionAware;
40  import org.apache.http.client.methods.HttpRequestWrapper;
41  import org.apache.http.client.protocol.HttpClientContext;
42  import org.apache.http.conn.routing.HttpRoute;
43  
44  /**
45   * Class used for asynchronous revalidations to be used when the "stale-
46   * while-revalidate" directive is present
47   */
48  class AsynchronousValidator implements Closeable {
49      private final SchedulingStrategy schedulingStrategy;
50      private final Set<String> queued;
51      private final CacheKeyGenerator cacheKeyGenerator;
52      private final FailureCache failureCache;
53  
54      private final Log log = LogFactory.getLog(getClass());
55  
56      /**
57       * Create AsynchronousValidator which will make revalidation requests
58       * using an {@link ImmediateSchedulingStrategy}. Its thread
59       * pool will be configured according to the given {@link CacheConfig}.
60       * @param config specifies thread pool settings. See
61       * {@link CacheConfig#getAsynchronousWorkersMax()},
62       * {@link CacheConfig#getAsynchronousWorkersCore()},
63       * {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
64       * and {@link CacheConfig#getRevalidationQueueSize()}.
65       */
66      public AsynchronousValidator(final CacheConfig config) {
67          this(new ImmediateSchedulingStrategy(config));
68      }
69  
70      /**
71       * Create AsynchronousValidator which will make revalidation requests
72       * using the supplied {@link SchedulingStrategy}. Closing the validator
73       * will also close the given schedulingStrategy.
74       * @param schedulingStrategy used to maintain a pool of worker threads and
75       *                           schedules when requests are executed
76       */
77      AsynchronousValidator(final SchedulingStrategy schedulingStrategy) {
78          this.schedulingStrategy = schedulingStrategy;
79          this.queued = new HashSet<String>();
80          this.cacheKeyGenerator = new CacheKeyGenerator();
81          this.failureCache = new DefaultFailureCache();
82      }
83  
84      @Override
85      public void close() throws IOException {
86          schedulingStrategy.close();
87      }
88  
89      /**
90       * Schedules an asynchronous revalidation
91       */
92      public synchronized void revalidateCacheEntry(
93              final CachingExec cachingExec,
94              final HttpRoute route,
95              final HttpRequestWrapper request,
96              final HttpClientContext context,
97              final HttpExecutionAware execAware,
98              final HttpCacheEntry entry) {
99          // getVariantURI will fall back on getURI if no variants exist
100         final String uri = cacheKeyGenerator.getVariantURI(context.getTargetHost(), request, entry);
101 
102         if (!queued.contains(uri)) {
103             final int consecutiveFailedAttempts = failureCache.getErrorCount(uri);
104             final AsynchronousValidationRequest revalidationRequest =
105                 new AsynchronousValidationRequest(
106                         this, cachingExec, route, request, context, execAware, entry, uri, consecutiveFailedAttempts);
107 
108             try {
109                 schedulingStrategy.schedule(revalidationRequest);
110                 queued.add(uri);
111             } catch (final RejectedExecutionException ree) {
112                 log.debug("Revalidation for [" + uri + "] not scheduled: " + ree);
113             }
114         }
115     }
116 
117     /**
118      * Removes an identifier from the internal list of revalidation jobs in
119      * progress.  This is meant to be called by
120      * {@link AsynchronousValidationRequest#run()} once the revalidation is
121      * complete, using the identifier passed in during constructions.
122      * @param identifier
123      */
124     synchronized void markComplete(final String identifier) {
125         queued.remove(identifier);
126     }
127 
128     /**
129      * The revalidation job was successful thus the number of consecutive
130      * failed attempts will be reset to zero. Should be called by
131      * {@link AsynchronousValidationRequest#run()}.
132      * @param identifier the revalidation job's unique identifier
133      */
134     void jobSuccessful(final String identifier) {
135         failureCache.resetErrorCount(identifier);
136     }
137 
138     /**
139      * The revalidation job did fail and thus the number of consecutive failed
140      * attempts will be increased. Should be called by
141      * {@link AsynchronousValidationRequest#run()}.
142      * @param identifier the revalidation job's unique identifier
143      */
144     void jobFailed(final String identifier) {
145         failureCache.increaseErrorCount(identifier);
146     }
147 
148     Set<String> getScheduledIdentifiers() {
149         return Collections.unmodifiableSet(queued);
150     }
151 }