Completed rewrite of re-validation code in the classic caching exec interceptor; added re-validation to the async caching exec interceptor

This commit is contained in:
Oleg Kalnichevski 2018-01-07 14:51:02 +01:00
parent f16ac3ec3b
commit 16147b1852
28 changed files with 487 additions and 374 deletions

View File

@ -32,6 +32,7 @@ import java.nio.ByteBuffer;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -47,8 +48,10 @@ import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage;
import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.client5.http.impl.ExecSupport;
import org.apache.hc.client5.http.impl.RequestCopier; import org.apache.hc.client5.http.impl.RequestCopier;
import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.annotation.ThreadingBehavior;
@ -87,21 +90,16 @@ import org.apache.hc.core5.util.ByteArrayBuffer;
public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler { public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler {
private final HttpAsyncCache responseCache; private final HttpAsyncCache responseCache;
private final DefaultAsyncCacheRevalidator cacheRevalidator;
private final ConditionalRequestBuilder<HttpRequest> conditionalRequestBuilder; private final ConditionalRequestBuilder<HttpRequest> conditionalRequestBuilder;
public AsyncCachingExec(final HttpAsyncCache cache, final CacheConfig config) { AsyncCachingExec(final HttpAsyncCache cache, final DefaultAsyncCacheRevalidator cacheRevalidator, final CacheConfig config) {
super(config); super(config);
this.responseCache = Args.notNull(cache, "Response cache"); this.responseCache = Args.notNull(cache, "Response cache");
this.cacheRevalidator = cacheRevalidator;
this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(RequestCopier.INSTANCE); this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(RequestCopier.INSTANCE);
} }
public AsyncCachingExec(
final ResourceFactory resourceFactory,
final HttpAsyncCacheStorage storage,
final CacheConfig config) {
this(new BasicHttpAsyncCache(resourceFactory, storage), config);
}
AsyncCachingExec( AsyncCachingExec(
final HttpAsyncCache responseCache, final HttpAsyncCache responseCache,
final CacheValidityPolicy validityPolicy, final CacheValidityPolicy validityPolicy,
@ -109,16 +107,37 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
final CachedHttpResponseGenerator responseGenerator, final CachedHttpResponseGenerator responseGenerator,
final CacheableRequestPolicy cacheableRequestPolicy, final CacheableRequestPolicy cacheableRequestPolicy,
final CachedResponseSuitabilityChecker suitabilityChecker, final CachedResponseSuitabilityChecker suitabilityChecker,
final ConditionalRequestBuilder<HttpRequest> conditionalRequestBuilder,
final ResponseProtocolCompliance responseCompliance, final ResponseProtocolCompliance responseCompliance,
final RequestProtocolCompliance requestCompliance, final RequestProtocolCompliance requestCompliance,
final DefaultAsyncCacheRevalidator cacheRevalidator,
final ConditionalRequestBuilder<HttpRequest> conditionalRequestBuilder,
final CacheConfig config) { final CacheConfig config) {
super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy, super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy,
suitabilityChecker, responseCompliance, requestCompliance, config); suitabilityChecker, responseCompliance, requestCompliance, config);
this.responseCache = responseCache; this.responseCache = responseCache;
this.cacheRevalidator = cacheRevalidator;
this.conditionalRequestBuilder = conditionalRequestBuilder; this.conditionalRequestBuilder = conditionalRequestBuilder;
} }
public AsyncCachingExec(
final HttpAsyncCache cache,
final ScheduledExecutorService executorService,
final SchedulingStrategy schedulingStrategy,
final CacheConfig config) {
this(cache,
executorService != null ? new DefaultAsyncCacheRevalidator(executorService, schedulingStrategy) : null,
config);
}
public AsyncCachingExec(
final ResourceFactory resourceFactory,
final HttpAsyncCacheStorage storage,
final ScheduledExecutorService executorService,
final SchedulingStrategy schedulingStrategy,
final CacheConfig config) {
this(new BasicHttpAsyncCache(resourceFactory, storage), executorService, schedulingStrategy, config);
}
private void triggerResponse( private void triggerResponse(
final SimpleHttpResponse cacheResponse, final SimpleHttpResponse cacheResponse,
final AsyncExecChain.Scope scope, final AsyncExecChain.Scope scope,
@ -609,7 +628,38 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
triggerResponse(cacheResponse, scope, asyncExecCallback); triggerResponse(cacheResponse, scope, asyncExecCallback);
} else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) { } else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) {
log.debug("Revalidating cache entry"); log.debug("Revalidating cache entry");
revalidateCacheEntry(target, request, entityProducer, scope, chain, asyncExecCallback, entry); if (cacheRevalidator != null
&& !staleResponseNotAllowed(request, entry, now)
&& validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
log.debug("Serving stale with asynchronous revalidation");
try {
final SimpleHttpResponse cacheResponse = generateCachedResponse(request, context, entry, now);
final String exchangeId = ExecSupport.getNextExchangeId();
final AsyncExecChain.Scope fork = new AsyncExecChain.Scope(
exchangeId,
scope.route,
scope.originalRequest,
new ComplexFuture<>(null),
HttpClientContext.create(),
scope.execRuntime.fork());
cacheRevalidator.revalidateCacheEntry(
responseCache.generateKey(target, request, entry),
asyncExecCallback,
new DefaultAsyncCacheRevalidator.RevalidationCall() {
@Override
public void execute(final AsyncExecCallback asyncExecCallback) {
revalidateCacheEntry(target, request, entityProducer, fork, chain, asyncExecCallback, entry);
}
});
triggerResponse(cacheResponse, scope, asyncExecCallback);
} catch (final ResourceIOException ex) {
asyncExecCallback.failed(ex);
}
} else {
revalidateCacheEntry(target, request, entityProducer, scope, chain, asyncExecCallback, entry);
}
} else { } else {
log.debug("Cache entry not usable; calling backend"); log.debug("Cache entry not usable; calling backend");
callBackend(target, request, entityProducer, scope, chain, asyncExecCallback); callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);

View File

@ -84,6 +84,15 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
this( resourceFactory, storage, CacheKeyGenerator.INSTANCE); this( resourceFactory, storage, CacheKeyGenerator.INSTANCE);
} }
@Override
public String generateKey(final HttpHost host, final HttpRequest request, final HttpCacheEntry cacheEntry) {
if (cacheEntry == null) {
return cacheKeyGenerator.generateKey(host, request);
} else {
return cacheKeyGenerator.generateKey(host, request, cacheEntry);
}
}
@Override @Override
public Cancellable flushCacheEntriesFor( public Cancellable flushCacheEntriesFor(
final HttpHost host, final HttpRequest request, final FutureCallback<Boolean> callback) { final HttpHost host, final HttpRequest request, final FutureCallback<Boolean> callback) {

View File

@ -88,6 +88,15 @@ class BasicHttpCache implements HttpCache {
this(CacheConfig.DEFAULT); this(CacheConfig.DEFAULT);
} }
@Override
public String generateKey(final HttpHost host, final HttpRequest request, final HttpCacheEntry cacheEntry) {
if (cacheEntry == null) {
return cacheKeyGenerator.generateKey(host, request);
} else {
return cacheKeyGenerator.generateKey(host, request, cacheEntry);
}
}
@Override @Override
public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request) { public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {

View File

@ -90,15 +90,10 @@ import org.apache.hc.core5.util.Args;
* <p><b>Background validation</b>. The cache module supports the * <p><b>Background validation</b>. The cache module supports the
* {@code stale-while-revalidate} directive of * {@code stale-while-revalidate} directive of
* <a href="http://tools.ietf.org/html/rfc5861">RFC5861</a>, which allows * <a href="http://tools.ietf.org/html/rfc5861">RFC5861</a>, which allows
* certain cache entry revalidations to happen in the background. You may * certain cache entry revalidations to happen in the background. Asynchronous
* want to tweak the settings for the {@link * validation is enabled by default but it could be disabled by setting the number
* CacheConfig#getAsynchronousWorkersCore() minimum} and {@link * of re-validation workers to {@code 0} with {@link CacheConfig#getAsynchronousWorkers()}
* CacheConfig#getAsynchronousWorkersMax() maximum} number of background * parameter</p>
* worker threads, as well as the {@link
* CacheConfig#getAsynchronousWorkerIdleLifetimeSecs() maximum time they
* can be idle before being reclaimed}. You can also control the {@link
* CacheConfig#getRevalidationQueueSize() size of the queue} used for
* revalidations when there aren't enough workers to keep up with demand.</p>
*/ */
public class CacheConfig implements Cloneable { public class CacheConfig implements Cloneable {
@ -142,21 +137,7 @@ public class CacheConfig implements Cloneable {
/** Default number of worker threads to allow for background revalidations /** Default number of worker threads to allow for background revalidations
* resulting from the stale-while-revalidate directive. * resulting from the stale-while-revalidate directive.
*/ */
public static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1; public static final int DEFAULT_ASYNCHRONOUS_WORKERS = 1;
/** Default minimum number of worker threads to allow for background
* revalidations resulting from the stale-while-revalidate directive.
*/
public static final int DEFAULT_ASYNCHRONOUS_WORKERS_CORE = 1;
/** Default maximum idle lifetime for a background revalidation thread
* before it gets reclaimed.
*/
public static final int DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS = 60;
/** Default maximum queue length for background revalidation requests.
*/
public static final int DEFAULT_REVALIDATION_QUEUE_SIZE = 100;
public static final CacheConfig DEFAULT = new Builder().build(); public static final CacheConfig DEFAULT = new Builder().build();
@ -170,10 +151,7 @@ public class CacheConfig implements Cloneable {
private final long heuristicDefaultLifetime; private final long heuristicDefaultLifetime;
private final boolean sharedCache; private final boolean sharedCache;
private final boolean freshnessCheckEnabled; private final boolean freshnessCheckEnabled;
private final int asynchronousWorkersMax; private final int asynchronousWorkers;
private final int asynchronousWorkersCore;
private final int asynchronousWorkerIdleLifetimeSecs;
private final int revalidationQueueSize;
private final boolean neverCacheHTTP10ResponsesWithQuery; private final boolean neverCacheHTTP10ResponsesWithQuery;
CacheConfig( CacheConfig(
@ -187,10 +165,7 @@ public class CacheConfig implements Cloneable {
final long heuristicDefaultLifetime, final long heuristicDefaultLifetime,
final boolean sharedCache, final boolean sharedCache,
final boolean freshnessCheckEnabled, final boolean freshnessCheckEnabled,
final int asynchronousWorkersMax, final int asynchronousWorkers,
final int asynchronousWorkersCore,
final int asynchronousWorkerIdleLifetimeSecs,
final int revalidationQueueSize,
final boolean neverCacheHTTP10ResponsesWithQuery) { final boolean neverCacheHTTP10ResponsesWithQuery) {
super(); super();
this.maxObjectSize = maxObjectSize; this.maxObjectSize = maxObjectSize;
@ -203,10 +178,7 @@ public class CacheConfig implements Cloneable {
this.heuristicDefaultLifetime = heuristicDefaultLifetime; this.heuristicDefaultLifetime = heuristicDefaultLifetime;
this.sharedCache = sharedCache; this.sharedCache = sharedCache;
this.freshnessCheckEnabled = freshnessCheckEnabled; this.freshnessCheckEnabled = freshnessCheckEnabled;
this.asynchronousWorkersMax = asynchronousWorkersMax; this.asynchronousWorkers = asynchronousWorkers;
this.asynchronousWorkersCore = asynchronousWorkersCore;
this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
this.revalidationQueueSize = revalidationQueueSize;
this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery; this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery;
} }
@ -306,33 +278,8 @@ public class CacheConfig implements Cloneable {
* revalidations due to the {@code stale-while-revalidate} directive. A * revalidations due to the {@code stale-while-revalidate} directive. A
* value of 0 means background revalidations are disabled. * value of 0 means background revalidations are disabled.
*/ */
public int getAsynchronousWorkersMax() { public int getAsynchronousWorkers() {
return asynchronousWorkersMax; return asynchronousWorkers;
}
/**
* Returns the minimum number of threads to keep alive for background
* revalidations due to the {@code stale-while-revalidate} directive.
*/
public int getAsynchronousWorkersCore() {
return asynchronousWorkersCore;
}
/**
* Returns the current maximum idle lifetime in seconds for a
* background revalidation worker thread. If a worker thread is idle
* for this long, and there are more than the core number of worker
* threads alive, the worker will be reclaimed.
*/
public int getAsynchronousWorkerIdleLifetimeSecs() {
return asynchronousWorkerIdleLifetimeSecs;
}
/**
* Returns the current maximum queue size for background revalidations.
*/
public int getRevalidationQueueSize() {
return revalidationQueueSize;
} }
@Override @Override
@ -354,10 +301,7 @@ public class CacheConfig implements Cloneable {
.setHeuristicCoefficient(config.getHeuristicCoefficient()) .setHeuristicCoefficient(config.getHeuristicCoefficient())
.setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime()) .setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime())
.setSharedCache(config.isSharedCache()) .setSharedCache(config.isSharedCache())
.setAsynchronousWorkersMax(config.getAsynchronousWorkersMax()) .setAsynchronousWorkers(config.getAsynchronousWorkers())
.setAsynchronousWorkersCore(config.getAsynchronousWorkersCore())
.setAsynchronousWorkerIdleLifetimeSecs(config.getAsynchronousWorkerIdleLifetimeSecs())
.setRevalidationQueueSize(config.getRevalidationQueueSize())
.setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery()); .setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery());
} }
@ -374,10 +318,7 @@ public class CacheConfig implements Cloneable {
private long heuristicDefaultLifetime; private long heuristicDefaultLifetime;
private boolean sharedCache; private boolean sharedCache;
private boolean freshnessCheckEnabled; private boolean freshnessCheckEnabled;
private int asynchronousWorkersMax; private int asynchronousWorkers;
private int asynchronousWorkersCore;
private int asynchronousWorkerIdleLifetimeSecs;
private int revalidationQueueSize;
private boolean neverCacheHTTP10ResponsesWithQuery; private boolean neverCacheHTTP10ResponsesWithQuery;
Builder() { Builder() {
@ -391,10 +332,7 @@ public class CacheConfig implements Cloneable {
this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME; this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
this.sharedCache = true; this.sharedCache = true;
this.freshnessCheckEnabled = true; this.freshnessCheckEnabled = true;
this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX; this.asynchronousWorkers = DEFAULT_ASYNCHRONOUS_WORKERS;
this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
this.revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE;
} }
/** /**
@ -495,42 +433,11 @@ public class CacheConfig implements Cloneable {
/** /**
* Sets the maximum number of threads to allow for background * Sets the maximum number of threads to allow for background
* revalidations due to the {@code stale-while-revalidate} directive. * revalidations due to the {@code stale-while-revalidate} directive.
* @param asynchronousWorkersMax number of threads; a value of 0 disables background * @param asynchronousWorkers number of threads; a value of 0 disables background
* revalidations. * revalidations.
*/ */
public Builder setAsynchronousWorkersMax(final int asynchronousWorkersMax) { public Builder setAsynchronousWorkers(final int asynchronousWorkers) {
this.asynchronousWorkersMax = asynchronousWorkersMax; this.asynchronousWorkers = asynchronousWorkers;
return this;
}
/**
* Sets the minimum number of threads to keep alive for background
* revalidations due to the {@code stale-while-revalidate} directive.
* @param asynchronousWorkersCore should be greater than zero and less than or equal
* to {@code getAsynchronousWorkersMax()}
*/
public Builder setAsynchronousWorkersCore(final int asynchronousWorkersCore) {
this.asynchronousWorkersCore = asynchronousWorkersCore;
return this;
}
/**
* Sets the current maximum idle lifetime in seconds for a
* background revalidation worker thread. If a worker thread is idle
* for this long, and there are more than the core number of worker
* threads alive, the worker will be reclaimed.
* @param asynchronousWorkerIdleLifetimeSecs idle lifetime in seconds
*/
public Builder setAsynchronousWorkerIdleLifetimeSecs(final int asynchronousWorkerIdleLifetimeSecs) {
this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
return this;
}
/**
* Sets the current maximum queue size for background revalidations.
*/
public Builder setRevalidationQueueSize(final int revalidationQueueSize) {
this.revalidationQueueSize = revalidationQueueSize;
return this; return this;
} }
@ -547,6 +454,11 @@ public class CacheConfig implements Cloneable {
return this; return this;
} }
public Builder setFreshnessCheckEnabled(final boolean freshnessCheckEnabled) {
this.freshnessCheckEnabled = freshnessCheckEnabled;
return this;
}
public CacheConfig build() { public CacheConfig build() {
return new CacheConfig( return new CacheConfig(
maxObjectSize, maxObjectSize,
@ -559,10 +471,7 @@ public class CacheConfig implements Cloneable {
heuristicDefaultLifetime, heuristicDefaultLifetime,
sharedCache, sharedCache,
freshnessCheckEnabled, freshnessCheckEnabled,
asynchronousWorkersMax, asynchronousWorkers,
asynchronousWorkersCore,
asynchronousWorkerIdleLifetimeSecs,
revalidationQueueSize,
neverCacheHTTP10ResponsesWithQuery); neverCacheHTTP10ResponsesWithQuery);
} }
@ -581,10 +490,7 @@ public class CacheConfig implements Cloneable {
.append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime) .append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)
.append(", sharedCache=").append(this.sharedCache) .append(", sharedCache=").append(this.sharedCache)
.append(", freshnessCheckEnabled=").append(this.freshnessCheckEnabled) .append(", freshnessCheckEnabled=").append(this.freshnessCheckEnabled)
.append(", asynchronousWorkersMax=").append(this.asynchronousWorkersMax) .append(", asynchronousWorkers=").append(this.asynchronousWorkers)
.append(", asynchronousWorkersCore=").append(this.asynchronousWorkersCore)
.append(", asynchronousWorkerIdleLifetimeSecs=").append(this.asynchronousWorkerIdleLifetimeSecs)
.append(", revalidationQueueSize=").append(this.revalidationQueueSize)
.append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery) .append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery)
.append("]"); .append("]");
return builder.toString(); return builder.toString();

View File

@ -33,6 +33,7 @@ import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
@ -62,7 +63,7 @@ class CacheRevalidatorBase implements Closeable {
} }
public static ScheduledExecutor wrap(final ScheduledThreadPoolExecutor threadPoolExecutor) { public static ScheduledExecutor wrap(final ScheduledExecutorService executorService) {
return new ScheduledExecutor() { return new ScheduledExecutor() {
@ -70,18 +71,18 @@ class CacheRevalidatorBase implements Closeable {
public ScheduledFuture<?> schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException { public ScheduledFuture<?> schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException {
Args.notNull(command, "Runnable"); Args.notNull(command, "Runnable");
Args.notNull(timeValue, "Time value"); Args.notNull(timeValue, "Time value");
return threadPoolExecutor.schedule(command, timeValue.getDuration(), timeValue.getTimeUnit()); return executorService.schedule(command, timeValue.getDuration(), timeValue.getTimeUnit());
} }
@Override @Override
public void shutdown() { public void shutdown() {
threadPoolExecutor.shutdown(); executorService.shutdown();
} }
@Override @Override
public void awaitTermination(final Timeout timeout) throws InterruptedException { public void awaitTermination(final Timeout timeout) throws InterruptedException {
Args.notNull(timeout, "Timeout"); Args.notNull(timeout, "Timeout");
threadPoolExecutor.awaitTermination(timeout.getDuration(), timeout.getTimeUnit()); executorService.awaitTermination(timeout.getDuration(), timeout.getTimeUnit());
} }
}; };

View File

@ -31,6 +31,7 @@ import java.io.InputStream;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.async.methods.SimpleBody; import org.apache.hc.client5.http.async.methods.SimpleBody;
@ -43,8 +44,10 @@ import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChain;
import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.ExecChainHandler;
import org.apache.hc.client5.http.impl.ExecSupport;
import org.apache.hc.client5.http.impl.classic.ClassicRequestCopier; import org.apache.hc.client5.http.impl.classic.ClassicRequestCopier;
import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.annotation.ThreadingBehavior;
@ -101,27 +104,18 @@ import org.apache.logging.log4j.Logger;
public class CachingExec extends CachingExecBase implements ExecChainHandler { public class CachingExec extends CachingExecBase implements ExecChainHandler {
private final HttpCache responseCache; private final HttpCache responseCache;
private final DefaultCacheRevalidator cacheRevalidator;
private final ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder; private final ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder;
private final Logger log = LogManager.getLogger(getClass()); private final Logger log = LogManager.getLogger(getClass());
public CachingExec(final HttpCache cache, final CacheConfig config) { CachingExec(final HttpCache cache, final DefaultCacheRevalidator cacheRevalidator, final CacheConfig config) {
super(config); super(config);
this.responseCache = Args.notNull(cache, "Response cache"); this.responseCache = Args.notNull(cache, "Response cache");
this.cacheRevalidator = cacheRevalidator;
this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(ClassicRequestCopier.INSTANCE); this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(ClassicRequestCopier.INSTANCE);
} }
public CachingExec(
final ResourceFactory resourceFactory,
final HttpCacheStorage storage,
final CacheConfig config) {
this(new BasicHttpCache(resourceFactory, storage), config);
}
public CachingExec() {
this(new BasicHttpCache(), CacheConfig.DEFAULT);
}
CachingExec( CachingExec(
final HttpCache responseCache, final HttpCache responseCache,
final CacheValidityPolicy validityPolicy, final CacheValidityPolicy validityPolicy,
@ -129,16 +123,37 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
final CachedHttpResponseGenerator responseGenerator, final CachedHttpResponseGenerator responseGenerator,
final CacheableRequestPolicy cacheableRequestPolicy, final CacheableRequestPolicy cacheableRequestPolicy,
final CachedResponseSuitabilityChecker suitabilityChecker, final CachedResponseSuitabilityChecker suitabilityChecker,
final ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
final ResponseProtocolCompliance responseCompliance, final ResponseProtocolCompliance responseCompliance,
final RequestProtocolCompliance requestCompliance, final RequestProtocolCompliance requestCompliance,
final DefaultCacheRevalidator cacheRevalidator,
final ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
final CacheConfig config) { final CacheConfig config) {
super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy, super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy,
suitabilityChecker, responseCompliance, requestCompliance, config); suitabilityChecker, responseCompliance, requestCompliance, config);
this.responseCache = responseCache; this.responseCache = responseCache;
this.cacheRevalidator = cacheRevalidator;
this.conditionalRequestBuilder = conditionalRequestBuilder; this.conditionalRequestBuilder = conditionalRequestBuilder;
} }
public CachingExec(
final HttpCache cache,
final ScheduledExecutorService executorService,
final SchedulingStrategy schedulingStrategy,
final CacheConfig config) {
this(cache,
executorService != null ? new DefaultCacheRevalidator(executorService, schedulingStrategy) : null,
config);
}
public CachingExec(
final ResourceFactory resourceFactory,
final HttpCacheStorage storage,
final ScheduledExecutorService executorService,
final SchedulingStrategy schedulingStrategy,
final CacheConfig config) {
this(new BasicHttpCache(resourceFactory, storage), executorService, schedulingStrategy, config);
}
@Override @Override
public ClassicHttpResponse execute( public ClassicHttpResponse execute(
final ClassicHttpRequest request, final ClassicHttpRequest request,
@ -167,7 +182,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
final SimpleHttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context); final SimpleHttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context);
if (fatalErrorResponse != null) { if (fatalErrorResponse != null) {
return convert(fatalErrorResponse); return convert(fatalErrorResponse, scope);
} }
requestCompliance.makeRequestCompliant(request); requestCompliance.makeRequestCompliant(request);
@ -188,7 +203,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
} }
} }
private static ClassicHttpResponse convert(final SimpleHttpResponse cacheResponse) { private static ClassicHttpResponse convert(final SimpleHttpResponse cacheResponse, final ExecChain.Scope scope) {
if (cacheResponse == null) { if (cacheResponse == null) {
return null; return null;
} }
@ -205,6 +220,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
response.setEntity(new ByteArrayEntity(body.getBodyBytes(), body.getContentType())); response.setEntity(new ByteArrayEntity(body.getBodyBytes(), body.getContentType()));
} }
} }
scope.clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
return response; return response;
} }
@ -240,15 +256,11 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) { if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) {
log.debug("Cache hit"); log.debug("Cache hit");
try { try {
final ClassicHttpResponse response = convert(generateCachedResponse(request, context, entry, now)); return convert(generateCachedResponse(request, context, entry, now), scope);
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
return response;
} catch (final ResourceIOException ex) { } catch (final ResourceIOException ex) {
recordCacheFailure(target, request); recordCacheFailure(target, request);
if (!mayCallBackend(request)) { if (!mayCallBackend(request)) {
final ClassicHttpResponse response = convert(generateGatewayTimeout(context)); return convert(generateGatewayTimeout(context), scope);
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
return response;
} else { } else {
setResponseStatus(scope.clientContext, CacheResponseStatus.FAILURE); setResponseStatus(scope.clientContext, CacheResponseStatus.FAILURE);
return chain.proceed(request, scope); return chain.proceed(request, scope);
@ -256,15 +268,38 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
} }
} else if (!mayCallBackend(request)) { } else if (!mayCallBackend(request)) {
log.debug("Cache entry not suitable but only-if-cached requested"); log.debug("Cache entry not suitable but only-if-cached requested");
final ClassicHttpResponse response = convert(generateGatewayTimeout(context)); return convert(generateGatewayTimeout(context), scope);
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
return response;
} else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) { } else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) {
log.debug("Revalidating cache entry"); log.debug("Revalidating cache entry");
try { try {
return revalidateCacheEntry(target, request, scope, chain, entry); if (cacheRevalidator != null
&& !staleResponseNotAllowed(request, entry, now)
&& validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
log.debug("Serving stale with asynchronous revalidation");
final String exchangeId = ExecSupport.getNextExchangeId();
final ExecChain.Scope fork = new ExecChain.Scope(
exchangeId,
scope.route,
scope.originalRequest,
scope.execRuntime.fork(null),
HttpClientContext.create());
final SimpleHttpResponse response = generateCachedResponse(request, context, entry, now);
cacheRevalidator.revalidateCacheEntry(
responseCache.generateKey(target, request, entry),
new DefaultCacheRevalidator.RevalidationCall() {
@Override
public ClassicHttpResponse execute() throws HttpException, IOException {
return revalidateCacheEntry(target, request, fork, chain, entry);
}
});
return convert(response, scope);
} else {
return revalidateCacheEntry(target, request, scope, chain, entry);
}
} catch (final IOException ioex) { } catch (final IOException ioex) {
return convert(handleRevalidationFailure(request, context, entry, now)); return convert(handleRevalidationFailure(request, context, entry, now), scope);
} }
} else { } else {
log.debug("Cache entry not usable; calling backend"); log.debug("Cache entry not usable; calling backend");
@ -307,9 +342,9 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
target, request, cacheEntry, backendResponse, requestDate, responseDate); target, request, cacheEntry, backendResponse, requestDate, responseDate);
if (suitabilityChecker.isConditional(request) if (suitabilityChecker.isConditional(request)
&& suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) { && suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
return convert(responseGenerator.generateNotModifiedResponse(updatedEntry)); return convert(responseGenerator.generateNotModifiedResponse(updatedEntry), scope);
} }
return convert(responseGenerator.generateResponse(request, updatedEntry)); return convert(responseGenerator.generateResponse(request, updatedEntry), scope);
} }
if (staleIfErrorAppliesTo(statusCode) if (staleIfErrorAppliesTo(statusCode)
@ -318,7 +353,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
try { try {
final SimpleHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry); final SimpleHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry);
cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\""); cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
return convert(cachedResponse); return convert(cachedResponse, scope);
} finally { } finally {
backendResponse.close(); backendResponse.close();
} }
@ -344,7 +379,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse); final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse);
if (cacheable) { if (cacheable) {
storeRequestIfModifiedSinceFor304Response(request, backendResponse); storeRequestIfModifiedSinceFor304Response(request, backendResponse);
return cacheAndReturnResponse(target, request, backendResponse, requestDate, responseDate); return cacheAndReturnResponse(target, request, backendResponse, scope, requestDate, responseDate);
} else { } else {
log.debug("Backend response is not cacheable"); log.debug("Backend response is not cacheable");
responseCache.flushCacheEntriesFor(target, request); responseCache.flushCacheEntriesFor(target, request);
@ -356,6 +391,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
final HttpHost target, final HttpHost target,
final HttpRequest request, final HttpRequest request,
final ClassicHttpResponse backendResponse, final ClassicHttpResponse backendResponse,
final ExecChain.Scope scope,
final Date requestSent, final Date requestSent,
final Date responseReceived) throws IOException { final Date responseReceived) throws IOException {
log.debug("Caching backend response"); log.debug("Caching backend response");
@ -395,7 +431,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
cacheEntry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived); cacheEntry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived);
log.debug("Backend response successfully cached (freshness check skipped)"); log.debug("Backend response successfully cached (freshness check skipped)");
} }
return convert(responseGenerator.generateResponse(request, cacheEntry)); return convert(responseGenerator.generateResponse(request, cacheEntry), scope);
} }
private ClassicHttpResponse handleCacheMiss( private ClassicHttpResponse handleCacheMiss(
@ -423,8 +459,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
final ExecChain.Scope scope, final ExecChain.Scope scope,
final ExecChain chain, final ExecChain chain,
final Map<String, Variant> variants) throws IOException, HttpException { final Map<String, Variant> variants) throws IOException, HttpException {
final ClassicHttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants( final ClassicHttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants(request, variants);
request, variants);
final Date requestDate = getCurrentDate(); final Date requestDate = getCurrentDate();
final ClassicHttpResponse backendResponse = chain.proceed(conditionalRequest, scope); final ClassicHttpResponse backendResponse = chain.proceed(conditionalRequest, scope);
@ -468,11 +503,11 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
target, conditionalRequest, backendResponse, matchingVariant, requestDate, responseDate); target, conditionalRequest, backendResponse, matchingVariant, requestDate, responseDate);
backendResponse.close(); backendResponse.close();
if (shouldSendNotModifiedResponse(request, responseEntry)) { if (shouldSendNotModifiedResponse(request, responseEntry)) {
return convert(responseGenerator.generateNotModifiedResponse(responseEntry)); return convert(responseGenerator.generateNotModifiedResponse(responseEntry), scope);
} }
final SimpleHttpResponse resp = responseGenerator.generateResponse(request, responseEntry); final SimpleHttpResponse response = responseGenerator.generateResponse(request, responseEntry);
responseCache.reuseVariantEntryFor(target, request, matchingVariant); responseCache.reuseVariantEntryFor(target, request, matchingVariant);
return convert(resp); return convert(response, scope);
} catch (final IOException | RuntimeException ex) { } catch (final IOException | RuntimeException ex) {
backendResponse.close(); backendResponse.close();
throw ex; throw ex;

View File

@ -29,6 +29,8 @@ package org.apache.hc.client5.http.impl.cache;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.async.AsyncExecChainHandler;
import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator; import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator;
@ -38,6 +40,8 @@ import org.apache.hc.client5.http.cache.HttpCacheStorage;
import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.impl.ChainElements; import org.apache.hc.client5.http.impl.ChainElements;
import org.apache.hc.client5.http.impl.async.Http2AsyncClientBuilder; import org.apache.hc.client5.http.impl.async.Http2AsyncClientBuilder;
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.config.NamedElementChain;
/** /**
@ -51,6 +55,7 @@ public class CachingHttp2AsyncClientBuilder extends Http2AsyncClientBuilder {
private ResourceFactory resourceFactory; private ResourceFactory resourceFactory;
private HttpAsyncCacheStorage storage; private HttpAsyncCacheStorage storage;
private File cacheDir; private File cacheDir;
private SchedulingStrategy schedulingStrategy;
private CacheConfig cacheConfig; private CacheConfig cacheConfig;
private HttpAsyncCacheInvalidator httpCacheInvalidator; private HttpAsyncCacheInvalidator httpCacheInvalidator;
private boolean deleteCache; private boolean deleteCache;
@ -84,6 +89,11 @@ public class CachingHttp2AsyncClientBuilder extends Http2AsyncClientBuilder {
return this; return this;
} }
public final CachingHttp2AsyncClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) {
this.schedulingStrategy = schedulingStrategy;
return this;
}
public final CachingHttp2AsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) { public final CachingHttp2AsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) {
this.cacheConfig = cacheConfig; this.cacheConfig = cacheConfig;
return this; return this;
@ -138,7 +148,26 @@ public class CachingHttp2AsyncClientBuilder extends Http2AsyncClientBuilder {
CacheKeyGenerator.INSTANCE, CacheKeyGenerator.INSTANCE,
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator()); this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator());
final AsyncCachingExec cachingExec = new AsyncCachingExec(httpCache, config); DefaultAsyncCacheRevalidator cacheRevalidator = null;
if (config.getAsynchronousWorkers() > 0) {
final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers());
addCloseable(new Closeable() {
@Override
public void close() throws IOException {
executorService.shutdownNow();
}
});
cacheRevalidator = new DefaultAsyncCacheRevalidator(
executorService,
this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE);
}
final AsyncCachingExec cachingExec = new AsyncCachingExec(
httpCache,
cacheRevalidator,
config);
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name()); execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
} }

View File

@ -29,6 +29,8 @@ package org.apache.hc.client5.http.impl.cache;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.async.AsyncExecChainHandler;
import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator; import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator;
@ -38,6 +40,8 @@ import org.apache.hc.client5.http.cache.HttpCacheStorage;
import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.impl.ChainElements; import org.apache.hc.client5.http.impl.ChainElements;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder; import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.config.NamedElementChain;
/** /**
@ -51,6 +55,7 @@ public class CachingHttpAsyncClientBuilder extends HttpAsyncClientBuilder {
private ResourceFactory resourceFactory; private ResourceFactory resourceFactory;
private HttpAsyncCacheStorage storage; private HttpAsyncCacheStorage storage;
private File cacheDir; private File cacheDir;
private SchedulingStrategy schedulingStrategy;
private CacheConfig cacheConfig; private CacheConfig cacheConfig;
private HttpAsyncCacheInvalidator httpCacheInvalidator; private HttpAsyncCacheInvalidator httpCacheInvalidator;
private boolean deleteCache; private boolean deleteCache;
@ -84,6 +89,11 @@ public class CachingHttpAsyncClientBuilder extends HttpAsyncClientBuilder {
return this; return this;
} }
public final CachingHttpAsyncClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) {
this.schedulingStrategy = schedulingStrategy;
return this;
}
public final CachingHttpAsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) { public final CachingHttpAsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) {
this.cacheConfig = cacheConfig; this.cacheConfig = cacheConfig;
return this; return this;
@ -138,7 +148,26 @@ public class CachingHttpAsyncClientBuilder extends HttpAsyncClientBuilder {
CacheKeyGenerator.INSTANCE, CacheKeyGenerator.INSTANCE,
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator()); this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator());
final AsyncCachingExec cachingExec = new AsyncCachingExec(httpCache, config); DefaultAsyncCacheRevalidator cacheRevalidator = null;
if (config.getAsynchronousWorkers() > 0) {
final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers());
addCloseable(new Closeable() {
@Override
public void close() throws IOException {
executorService.shutdownNow();
}
});
cacheRevalidator = new DefaultAsyncCacheRevalidator(
executorService,
this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE);
}
final AsyncCachingExec cachingExec = new AsyncCachingExec(
httpCache,
cacheRevalidator,
config);
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name()); execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
} }

View File

@ -29,6 +29,8 @@ package org.apache.hc.client5.http.impl.cache;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.apache.hc.client5.http.cache.HttpCacheInvalidator; import org.apache.hc.client5.http.cache.HttpCacheInvalidator;
import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.HttpCacheStorage;
@ -36,6 +38,8 @@ import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.ExecChainHandler;
import org.apache.hc.client5.http.impl.ChainElements; import org.apache.hc.client5.http.impl.ChainElements;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.config.NamedElementChain;
/** /**
@ -49,6 +53,7 @@ public class CachingHttpClientBuilder extends HttpClientBuilder {
private ResourceFactory resourceFactory; private ResourceFactory resourceFactory;
private HttpCacheStorage storage; private HttpCacheStorage storage;
private File cacheDir; private File cacheDir;
private SchedulingStrategy schedulingStrategy;
private CacheConfig cacheConfig; private CacheConfig cacheConfig;
private HttpCacheInvalidator httpCacheInvalidator; private HttpCacheInvalidator httpCacheInvalidator;
private boolean deleteCache; private boolean deleteCache;
@ -68,31 +73,32 @@ public class CachingHttpClientBuilder extends HttpClientBuilder {
return this; return this;
} }
public final CachingHttpClientBuilder setHttpCacheStorage( public final CachingHttpClientBuilder setHttpCacheStorage(final HttpCacheStorage storage) {
final HttpCacheStorage storage) {
this.storage = storage; this.storage = storage;
return this; return this;
} }
public final CachingHttpClientBuilder setCacheDir( public final CachingHttpClientBuilder setCacheDir(final File cacheDir) {
final File cacheDir) {
this.cacheDir = cacheDir; this.cacheDir = cacheDir;
return this; return this;
} }
public final CachingHttpClientBuilder setCacheConfig( public final CachingHttpClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) {
final CacheConfig cacheConfig) { this.schedulingStrategy = schedulingStrategy;
return this;
}
public final CachingHttpClientBuilder setCacheConfig(final CacheConfig cacheConfig) {
this.cacheConfig = cacheConfig; this.cacheConfig = cacheConfig;
return this; return this;
} }
public final CachingHttpClientBuilder setHttpCacheInvalidator( public final CachingHttpClientBuilder setHttpCacheInvalidator(final HttpCacheInvalidator cacheInvalidator) {
final HttpCacheInvalidator cacheInvalidator) {
this.httpCacheInvalidator = cacheInvalidator; this.httpCacheInvalidator = cacheInvalidator;
return this; return this;
} }
public CachingHttpClientBuilder setDeleteCache(final boolean deleteCache) { public final CachingHttpClientBuilder setDeleteCache(final boolean deleteCache) {
this.deleteCache = deleteCache; this.deleteCache = deleteCache;
return this; return this;
} }
@ -136,7 +142,25 @@ public class CachingHttpClientBuilder extends HttpClientBuilder {
CacheKeyGenerator.INSTANCE, CacheKeyGenerator.INSTANCE,
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultCacheInvalidator()); this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultCacheInvalidator());
final CachingExec cachingExec = new CachingExec(httpCache, config); DefaultCacheRevalidator cacheRevalidator = null;
if (config.getAsynchronousWorkers() > 0) {
final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers());
addCloseable(new Closeable() {
@Override
public void close() throws IOException {
executorService.shutdownNow();
}
});
cacheRevalidator = new DefaultCacheRevalidator(
executorService,
this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE);
}
final CachingExec cachingExec = new CachingExec(
httpCache,
cacheRevalidator,
config);
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name()); execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
} }

View File

@ -26,20 +26,20 @@
*/ */
package org.apache.hc.client5.http.impl.cache; package org.apache.hc.client5.http.impl.cache;
import java.util.concurrent.ExecutionException; import java.io.IOException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.TimeoutException;
import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecCallback;
import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.impl.Operations;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.nio.AsyncDataConsumer;
import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout; import org.apache.hc.core5.util.Timeout;
@ -49,33 +49,10 @@ import org.apache.hc.core5.util.Timeout;
*/ */
class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase { class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
private static final Future<Void> NOOP_FUTURE = new Future<Void>() { interface RevalidationCall {
@Override void execute(AsyncExecCallback asyncExecCallback);;
public Void get() throws InterruptedException, ExecutionException { }
return null;
}
@Override
public Void get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return null;
}
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
};
static class InternalScheduledExecutor implements ScheduledExecutor { static class InternalScheduledExecutor implements ScheduledExecutor {
@ -89,7 +66,7 @@ class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
public Future<?> schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException { public Future<?> schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException {
if (timeValue.toMillis() <= 0) { if (timeValue.toMillis() <= 0) {
command.run(); command.run();
return NOOP_FUTURE; return new Operations.CompletedFuture<Void>(null);
} else { } else {
return executor.schedule(command, timeValue); return executor.schedule(command, timeValue);
} }
@ -107,7 +84,6 @@ class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
} }
private final AsyncCachingExec cachingExec;
private final CacheKeyGenerator cacheKeyGenerator; private final CacheKeyGenerator cacheKeyGenerator;
/** /**
@ -116,42 +92,72 @@ class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
*/ */
public DefaultAsyncCacheRevalidator( public DefaultAsyncCacheRevalidator(
final ScheduledExecutor scheduledExecutor, final ScheduledExecutor scheduledExecutor,
final SchedulingStrategy schedulingStrategy, final SchedulingStrategy schedulingStrategy) {
final AsyncCachingExec cachingExec) {
super(new InternalScheduledExecutor(scheduledExecutor), schedulingStrategy); super(new InternalScheduledExecutor(scheduledExecutor), schedulingStrategy);
this.cachingExec = cachingExec;
this.cacheKeyGenerator = CacheKeyGenerator.INSTANCE; this.cacheKeyGenerator = CacheKeyGenerator.INSTANCE;
} }
/** /**
* Create CacheValidator which will make ache revalidation requests * Create CacheValidator which will make ache revalidation requests
* using the supplied {@link SchedulingStrategy} and {@link ScheduledThreadPoolExecutor}. * using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutorService}.
*/ */
public DefaultAsyncCacheRevalidator( public DefaultAsyncCacheRevalidator(
final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor, final ScheduledExecutorService executorService,
final SchedulingStrategy schedulingStrategy, final SchedulingStrategy schedulingStrategy) {
final AsyncCachingExec cachingExec) { this(wrap(executorService), schedulingStrategy);
this(wrap(scheduledThreadPoolExecutor), schedulingStrategy, cachingExec);
} }
/** /**
* Schedules an asynchronous re-validation * Schedules an asynchronous re-validation
*/ */
public void revalidateCacheEntry( public void revalidateCacheEntry(
final HttpHost target, final String cacheKey ,
final HttpRequest request,
final AsyncEntityProducer entityProducer,
final AsyncExecChain.Scope scope,
final AsyncExecChain chain,
final AsyncExecCallback asyncExecCallback, final AsyncExecCallback asyncExecCallback,
final HttpCacheEntry entry) { final RevalidationCall call) {
final String cacheKey = cacheKeyGenerator.generateKey(target, request, entry);
scheduleRevalidation(cacheKey, new Runnable() { scheduleRevalidation(cacheKey, new Runnable() {
@Override @Override
public void run() { public void run() {
cachingExec.revalidateCacheEntry(target, request, entityProducer, scope, chain, asyncExecCallback, entry); call.execute(new AsyncExecCallback() {
private final AtomicReference<HttpResponse> responseRef = new AtomicReference<>(null);
@Override
public AsyncDataConsumer handleResponse(
final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
responseRef.set(response);
return asyncExecCallback.handleResponse(response, entityDetails);
}
@Override
public void completed() {
final HttpResponse httpResponse = responseRef.getAndSet(null);
if (httpResponse != null && httpResponse.getCode() < HttpStatus.SC_SERVER_ERROR && !isStale(httpResponse)) {
jobSuccessful(cacheKey);
} else {
jobFailed(cacheKey);
}
asyncExecCallback.completed();
}
@Override
public void failed(final Exception cause) {
if (cause instanceof IOException) {
log.debug("Asynchronous revalidation failed due to I/O error", cause);
} else if (cause instanceof HttpException) {
log.error("HTTP protocol exception during asynchronous revalidation", cause);
} else {
log.error("Unexpected runtime exception thrown during asynchronous revalidation", cause);
}
try {
jobFailed(cacheKey);
} finally {
asyncExecCallback.failed(cause);
}
}
});
} }
}); });

View File

@ -27,15 +27,11 @@
package org.apache.hc.client5.http.impl.cache; package org.apache.hc.client5.http.impl.cache;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledExecutorService;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.classic.ExecChain;
import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpStatus;
/** /**
@ -44,8 +40,10 @@ import org.apache.hc.core5.http.HttpStatus;
*/ */
class DefaultCacheRevalidator extends CacheRevalidatorBase { class DefaultCacheRevalidator extends CacheRevalidatorBase {
private final CachingExec cachingExec; interface RevalidationCall {
private final CacheKeyGenerator cacheKeyGenerator;
ClassicHttpResponse execute() throws IOException, HttpException;
}
/** /**
* Create DefaultCacheRevalidator which will make ache revalidation requests * Create DefaultCacheRevalidator which will make ache revalidation requests
@ -53,56 +51,45 @@ class DefaultCacheRevalidator extends CacheRevalidatorBase {
*/ */
public DefaultCacheRevalidator( public DefaultCacheRevalidator(
final CacheRevalidatorBase.ScheduledExecutor scheduledExecutor, final CacheRevalidatorBase.ScheduledExecutor scheduledExecutor,
final SchedulingStrategy schedulingStrategy, final SchedulingStrategy schedulingStrategy) {
final CachingExec cachingExec) {
super(scheduledExecutor, schedulingStrategy); super(scheduledExecutor, schedulingStrategy);
this.cachingExec = cachingExec;
this.cacheKeyGenerator = CacheKeyGenerator.INSTANCE;
} }
/** /**
* Create CacheValidator which will make ache revalidation requests * Create CacheValidator which will make ache revalidation requests
* using the supplied {@link SchedulingStrategy} and {@link ScheduledThreadPoolExecutor}. * using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutorService}.
*/ */
public DefaultCacheRevalidator( public DefaultCacheRevalidator(
final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor, final ScheduledExecutorService scheduledThreadPoolExecutor,
final SchedulingStrategy schedulingStrategy, final SchedulingStrategy schedulingStrategy) {
final CachingExec cachingExec) { this(wrap(scheduledThreadPoolExecutor), schedulingStrategy);
this(wrap(scheduledThreadPoolExecutor), schedulingStrategy, cachingExec);
} }
/** /**
* Schedules an asynchronous re-validation * Schedules an asynchronous re-validation
*/ */
public void revalidateCacheEntry( public void revalidateCacheEntry(
final HttpHost target, final String cacheKey,
final ClassicHttpRequest request, final RevalidationCall call) {
final ExecChain.Scope scope,
final ExecChain chain,
final HttpCacheEntry entry) {
final String cacheKey = cacheKeyGenerator.generateKey(target, request, entry);
scheduleRevalidation(cacheKey, new Runnable() { scheduleRevalidation(cacheKey, new Runnable() {
@Override @Override
public void run() { public void run() {
try { try (ClassicHttpResponse httpResponse = call.execute()) {
try (ClassicHttpResponse httpResponse = cachingExec.revalidateCacheEntry(target, request, scope, chain, entry)) { if (httpResponse.getCode() < HttpStatus.SC_SERVER_ERROR && !isStale(httpResponse)) {
if (httpResponse.getCode() < HttpStatus.SC_SERVER_ERROR && !isStale(httpResponse)) { jobSuccessful(cacheKey);
jobSuccessful(cacheKey); } else {
} else { jobFailed(cacheKey);
jobFailed(cacheKey);
}
} }
} catch (final IOException ioe) { } catch (final IOException ex) {
jobFailed(cacheKey); jobFailed(cacheKey);
log.debug("Asynchronous revalidation failed due to I/O error", ioe); log.debug("Asynchronous revalidation failed due to I/O error", ex);
} catch (final HttpException pe) { } catch (final HttpException ex) {
jobFailed(cacheKey); jobFailed(cacheKey);
log.error("HTTP protocol exception during asynchronous revalidation", pe); log.error("HTTP protocol exception during asynchronous revalidation", ex);
} catch (final RuntimeException re) { } catch (final RuntimeException ex) {
jobFailed(cacheKey); jobFailed(cacheKey);
log.error("Unexpected runtime exception thrown during asynchronous revalidation" + re); log.error("Unexpected runtime exception thrown during asynchronous revalidation", ex);
} }
} }

View File

@ -44,6 +44,8 @@ import org.apache.hc.core5.util.ByteArrayBuffer;
@Internal @Internal
interface HttpAsyncCache { interface HttpAsyncCache {
String generateKey (HttpHost host, HttpRequest request, HttpCacheEntry cacheEntry);
/** /**
* Clear all matching {@link HttpCacheEntry}s. * Clear all matching {@link HttpCacheEntry}s.
*/ */

View File

@ -40,6 +40,8 @@ import org.apache.hc.core5.util.ByteArrayBuffer;
*/ */
interface HttpCache { interface HttpCache {
String generateKey (HttpHost host, HttpRequest request, HttpCacheEntry cacheEntry);
/** /**
* Clear all matching {@link HttpCacheEntry}s. * Clear all matching {@link HttpCacheEntry}s.
*/ */

View File

@ -39,7 +39,7 @@ import org.apache.hc.core5.util.TimeValue;
@Contract(threading = ThreadingBehavior.STATELESS) @Contract(threading = ThreadingBehavior.STATELESS)
public class ImmediateSchedulingStrategy implements SchedulingStrategy { public class ImmediateSchedulingStrategy implements SchedulingStrategy {
private final static ImmediateSchedulingStrategy INSTANCE = new ImmediateSchedulingStrategy(); public final static ImmediateSchedulingStrategy INSTANCE = new ImmediateSchedulingStrategy();
@Override @Override
public TimeValue schedule(final int attemptNumber) { public TimeValue schedule(final int attemptNumber) {

View File

@ -57,7 +57,7 @@ public abstract class AbstractProtocolTest {
protected HttpEntity body; protected HttpEntity body;
protected HttpClientContext context; protected HttpClientContext context;
protected ExecChain mockExecChain; protected ExecChain mockExecChain;
protected ExecRuntime mockEndpoint; protected ExecRuntime mockExecRuntime;
protected HttpCache mockCache; protected HttpCache mockCache;
protected ClassicHttpRequest request; protected ClassicHttpRequest request;
protected ClassicHttpResponse originResponse; protected ClassicHttpResponse originResponse;
@ -101,18 +101,18 @@ public abstract class AbstractProtocolTest {
cache = new BasicHttpCache(config); cache = new BasicHttpCache(config);
mockExecChain = EasyMock.createNiceMock(ExecChain.class); mockExecChain = EasyMock.createNiceMock(ExecChain.class);
mockEndpoint = EasyMock.createNiceMock(ExecRuntime.class); mockExecRuntime = EasyMock.createNiceMock(ExecRuntime.class);
mockCache = EasyMock.createNiceMock(HttpCache.class); mockCache = EasyMock.createNiceMock(HttpCache.class);
impl = createCachingExecChain(cache, config); impl = createCachingExecChain(cache, config);
} }
public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException { public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
return impl.execute(ClassicRequestCopier.INSTANCE.copy(request), new ExecChain.Scope( return impl.execute(ClassicRequestCopier.INSTANCE.copy(request), new ExecChain.Scope(
"test", route, request, mockEndpoint, context), mockExecChain); "test", route, request, mockExecRuntime, context), mockExecChain);
} }
protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) { protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) {
return new CachingExec(cache, config); return new CachingExec(cache, null, config);
} }
protected boolean supportsRangeAndContentRangeHeaders(final ExecChainHandler impl) { protected boolean supportsRangeAndContentRangeHeaders(final ExecChainHandler impl) {
@ -148,7 +148,7 @@ public abstract class AbstractProtocolTest {
mockExecChain = EasyMock.createNiceMock(ExecChain.class); mockExecChain = EasyMock.createNiceMock(ExecChain.class);
mockCache = EasyMock.createNiceMock(HttpCache.class); mockCache = EasyMock.createNiceMock(HttpCache.class);
impl = new CachingExec(mockCache, config); impl = new CachingExec(mockCache, null, config);
EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class))) EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
.andReturn(null).anyTimes(); .andReturn(null).anyTimes();
@ -174,7 +174,7 @@ public abstract class AbstractProtocolTest {
.setMaxObjectSize(MAX_BYTES) .setMaxObjectSize(MAX_BYTES)
.setSharedCache(false) .setSharedCache(false)
.build(); .build();
impl = new CachingExec(cache, config); impl = new CachingExec(cache, null, config);
} }
public AbstractProtocolTest() { public AbstractProtocolTest() {

View File

@ -65,7 +65,6 @@ import org.easymock.EasyMock;
import org.easymock.IExpectationSetters; import org.easymock.IExpectationSetters;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@SuppressWarnings("boxing") // test code @SuppressWarnings("boxing") // test code
@ -107,9 +106,10 @@ public class TestCachingExec extends TestCachingExecChain {
final CachedHttpResponseGenerator mockResponseGenerator, final CachedHttpResponseGenerator mockResponseGenerator,
final CacheableRequestPolicy mockRequestPolicy, final CacheableRequestPolicy mockRequestPolicy,
final CachedResponseSuitabilityChecker mockSuitabilityChecker, final CachedResponseSuitabilityChecker mockSuitabilityChecker,
final ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder,
final ResponseProtocolCompliance mockResponseProtocolCompliance, final ResponseProtocolCompliance mockResponseProtocolCompliance,
final RequestProtocolCompliance mockRequestProtocolCompliance, final RequestProtocolCompliance mockRequestProtocolCompliance,
final DefaultCacheRevalidator mockCacheRevalidator,
final ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder,
final CacheConfig config) { final CacheConfig config) {
return impl = new CachingExec( return impl = new CachingExec(
mockCache, mockCache,
@ -118,15 +118,16 @@ public class TestCachingExec extends TestCachingExecChain {
mockResponseGenerator, mockResponseGenerator,
mockRequestPolicy, mockRequestPolicy,
mockSuitabilityChecker, mockSuitabilityChecker,
mockConditionalRequestBuilder,
mockResponseProtocolCompliance, mockResponseProtocolCompliance,
mockRequestProtocolCompliance, mockRequestProtocolCompliance,
mockCacheRevalidator,
mockConditionalRequestBuilder,
config); config);
} }
@Override @Override
public CachingExec createCachingExecChain(final HttpCache cache, final CacheConfig config) { public CachingExec createCachingExecChain(final HttpCache cache, final CacheConfig config) {
return impl = new CachingExec(cache, config); return impl = new CachingExec(cache, null, config);
} }
@Override @Override
@ -210,7 +211,7 @@ public class TestCachingExec extends TestCachingExecChain {
Assert.assertEquals(1, impl.getCacheUpdates()); Assert.assertEquals(1, impl.getCacheUpdates());
} }
@Test @Ignore @Test
public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception { public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception {
mockImplMethods(REVALIDATE_CACHE_ENTRY); mockImplMethods(REVALIDATE_CACHE_ENTRY);
requestPolicyAllowsCaching(true); requestPolicyAllowsCaching(true);
@ -447,9 +448,10 @@ public class TestCachingExec extends TestCachingExecChain {
mockResponseGenerator, mockResponseGenerator,
mockRequestPolicy, mockRequestPolicy,
mockSuitabilityChecker, mockSuitabilityChecker,
mockConditionalRequestBuilder,
mockResponseProtocolCompliance, mockResponseProtocolCompliance,
mockRequestProtocolCompliance, mockRequestProtocolCompliance,
mockCacheRevalidator,
mockConditionalRequestBuilder,
config).addMockedMethods(methods).createNiceMock(); config).addMockedMethods(methods).createNiceMock();
} }

View File

@ -104,10 +104,11 @@ public abstract class TestCachingExecChain {
protected CachedHttpResponseGenerator mockResponseGenerator; protected CachedHttpResponseGenerator mockResponseGenerator;
private HttpClientResponseHandler<Object> mockHandler; private HttpClientResponseHandler<Object> mockHandler;
private ClassicHttpRequest mockUriRequest; private ClassicHttpRequest mockUriRequest;
protected ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder;
private HttpRequest mockConditionalRequest; private HttpRequest mockConditionalRequest;
protected ResponseProtocolCompliance mockResponseProtocolCompliance; protected ResponseProtocolCompliance mockResponseProtocolCompliance;
protected RequestProtocolCompliance mockRequestProtocolCompliance; protected RequestProtocolCompliance mockRequestProtocolCompliance;
protected DefaultCacheRevalidator mockCacheRevalidator;
protected ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder;
protected CacheConfig config; protected CacheConfig config;
protected HttpRoute route; protected HttpRoute route;
@ -130,10 +131,11 @@ public abstract class TestCachingExecChain {
mockUriRequest = createNiceMock(ClassicHttpRequest.class); mockUriRequest = createNiceMock(ClassicHttpRequest.class);
mockCacheEntry = createNiceMock(HttpCacheEntry.class); mockCacheEntry = createNiceMock(HttpCacheEntry.class);
mockResponseGenerator = createNiceMock(CachedHttpResponseGenerator.class); mockResponseGenerator = createNiceMock(CachedHttpResponseGenerator.class);
mockConditionalRequestBuilder = createNiceMock(ConditionalRequestBuilder.class);
mockConditionalRequest = createNiceMock(HttpRequest.class); mockConditionalRequest = createNiceMock(HttpRequest.class);
mockResponseProtocolCompliance = createNiceMock(ResponseProtocolCompliance.class); mockResponseProtocolCompliance = createNiceMock(ResponseProtocolCompliance.class);
mockRequestProtocolCompliance = createNiceMock(RequestProtocolCompliance.class); mockRequestProtocolCompliance = createNiceMock(RequestProtocolCompliance.class);
mockCacheRevalidator = createNiceMock(DefaultCacheRevalidator.class);
mockConditionalRequestBuilder = createNiceMock(ConditionalRequestBuilder.class);
mockStorage = createNiceMock(HttpCacheStorage.class); mockStorage = createNiceMock(HttpCacheStorage.class);
config = CacheConfig.DEFAULT; config = CacheConfig.DEFAULT;
@ -143,9 +145,9 @@ public abstract class TestCachingExecChain {
context = HttpCacheContext.create(); context = HttpCacheContext.create();
entry = HttpTestUtils.makeCacheEntry(); entry = HttpTestUtils.makeCacheEntry();
impl = createCachingExecChain(mockCache, mockValidityPolicy, impl = createCachingExecChain(mockCache, mockValidityPolicy,
mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker, mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
mockConditionalRequestBuilder, mockResponseProtocolCompliance, mockResponseProtocolCompliance,mockRequestProtocolCompliance,
mockRequestProtocolCompliance, config); mockCacheRevalidator, mockConditionalRequestBuilder, config);
} }
public abstract CachingExec createCachingExecChain( public abstract CachingExec createCachingExecChain(
@ -153,8 +155,9 @@ public abstract class TestCachingExecChain {
ResponseCachingPolicy responseCachingPolicy, CachedHttpResponseGenerator responseGenerator, ResponseCachingPolicy responseCachingPolicy, CachedHttpResponseGenerator responseGenerator,
CacheableRequestPolicy cacheableRequestPolicy, CacheableRequestPolicy cacheableRequestPolicy,
CachedResponseSuitabilityChecker suitabilityChecker, CachedResponseSuitabilityChecker suitabilityChecker,
ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
ResponseProtocolCompliance responseCompliance, RequestProtocolCompliance requestCompliance, ResponseProtocolCompliance responseCompliance, RequestProtocolCompliance requestCompliance,
DefaultCacheRevalidator cacheRevalidator,
ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
CacheConfig config); CacheConfig config);
public abstract CachingExec createCachingExecChain(HttpCache cache, CacheConfig config); public abstract CachingExec createCachingExecChain(HttpCache cache, CacheConfig config);
@ -1242,11 +1245,11 @@ public abstract class TestCachingExecChain {
mockCache = EasyMock.createStrictMock(HttpCache.class); mockCache = EasyMock.createStrictMock(HttpCache.class);
impl = createCachingExecChain(mockCache, mockValidityPolicy, impl = createCachingExecChain(mockCache, mockValidityPolicy,
mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker, mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
mockConditionalRequestBuilder, mockResponseProtocolCompliance, mockResponseProtocolCompliance, mockRequestProtocolCompliance,
mockRequestProtocolCompliance, config); mockCacheRevalidator, mockConditionalRequestBuilder, config);
final HttpHost host = new HttpHost("foo.example.com"); final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest request = new HttpGet("http://foo.example.com/bar"); final ClassicHttpRequest request = new HttpGet("http://foo.example.com/bar");
final Date now = new Date(); final Date now = new Date();
final Date requestSent = new Date(now.getTime() - 3 * 1000L); final Date requestSent = new Date(now.getTime() - 3 * 1000L);
@ -1260,8 +1263,8 @@ public abstract class TestCachingExecChain {
originResponse.setHeader("ETag", "\"etag\""); originResponse.setHeader("ETag", "\"etag\"");
replayMocks(); replayMocks();
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockEndpoint, context);
impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived); impl.cacheAndReturnResponse(host, request, originResponse, scope, requestSent, responseReceived);
verifyMocks(); verifyMocks();
} }
@ -1269,7 +1272,7 @@ public abstract class TestCachingExecChain {
@Test @Test
public void testSmallEnoughResponsesAreCached() throws Exception { public void testSmallEnoughResponsesAreCached() throws Exception {
final HttpHost host = new HttpHost("foo.example.com"); final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest request = new HttpGet("http://foo.example.com/bar"); final ClassicHttpRequest request = new HttpGet("http://foo.example.com/bar");
final Date now = new Date(); final Date now = new Date();
final Date requestSent = new Date(now.getTime() - 3 * 1000L); final Date requestSent = new Date(now.getTime() - 3 * 1000L);
@ -1297,7 +1300,8 @@ public abstract class TestCachingExecChain {
same(httpCacheEntry))).andReturn(response).once(); same(httpCacheEntry))).andReturn(response).once();
replayMocks(); replayMocks();
impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockEndpoint, context);
impl.cacheAndReturnResponse(host, request, originResponse, scope, requestSent, responseReceived);
verifyMocks(); verifyMocks();
} }

View File

@ -1,48 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.impl.cache;
import org.junit.Test;
public class TestCachingHttpClientBuilder {
@Test
public void testAsynchronousWorkersMax0() throws Exception {
final CacheConfig cacheConfig = CacheConfig.custom()
.setAsynchronousWorkersMax(0)
.build();
// Asynchronous validation should be disabled but we should not get an
// Exception
CachingHttpClientBuilder.create().setCacheConfig(cacheConfig).build();
}
@Test
public void testNullCacheConfig() throws Exception {
CachingHttpClientBuilder.create().setCacheConfig(null).build();
}
}

View File

@ -142,7 +142,7 @@ public class TestHttpCacheJiraNumber1147 {
} }
protected ExecChainHandler createCachingExecChain(final BasicHttpCache cache, final CacheConfig config) { protected ExecChainHandler createCachingExecChain(final BasicHttpCache cache, final CacheConfig config) {
return new CachingExec(cache, config); return new CachingExec(cache, null, config);
} }
} }

View File

@ -127,7 +127,7 @@ public class TestProtocolDeviations {
} }
protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) { protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) {
return new CachingExec(cache, config); return new CachingExec(cache, null, config);
} }
private ClassicHttpResponse make200Response() { private ClassicHttpResponse make200Response() {

View File

@ -2166,7 +2166,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
impl = new CachingExec(mockCache, config); impl = new CachingExec(mockCache, null, config);
request = new BasicClassicHttpRequest("GET", "/thing"); request = new BasicClassicHttpRequest("GET", "/thing");
@ -2217,7 +2217,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
impl = new CachingExec(mockCache, config); impl = new CachingExec(mockCache, null, config);
request = new BasicClassicHttpRequest("GET", "/thing"); request = new BasicClassicHttpRequest("GET", "/thing");
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry); EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
@ -2263,7 +2263,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
impl = new CachingExec(mockCache, config); impl = new CachingExec(mockCache, null, config);
request = new BasicClassicHttpRequest("GET", "/thing"); request = new BasicClassicHttpRequest("GET", "/thing");
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry); EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
@ -2471,7 +2471,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
impl = new CachingExec(mockCache, config); impl = new CachingExec(mockCache, null, config);
request = new BasicClassicHttpRequest("GET", "/thing"); request = new BasicClassicHttpRequest("GET", "/thing");
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry); EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
@ -2521,7 +2521,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(requestTime, responseTime, hdrs, bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(requestTime, responseTime, hdrs, bytes);
impl = new CachingExec(mockCache, config); impl = new CachingExec(mockCache, null, config);
request = new BasicClassicHttpRequest("GET", "/thing"); request = new BasicClassicHttpRequest("GET", "/thing");

View File

@ -32,7 +32,10 @@ import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.Date; import java.util.Date;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ClassicHttpResponse;
@ -41,7 +44,9 @@ import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.InputStreamEntity;
import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
import org.junit.Ignore; import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
/** /**
@ -51,6 +56,31 @@ import org.junit.Test;
*/ */
public class TestRFC5861Compliance extends AbstractProtocolTest { public class TestRFC5861Compliance extends AbstractProtocolTest {
private ScheduledExecutorService executorService;
@Before
public void setup() {
executorService = new ScheduledThreadPoolExecutor(1);
EasyMock.expect(mockExecRuntime.fork(null)).andReturn(mockExecRuntime).anyTimes();
}
@After
public void cleanup() {
executorService.shutdownNow();
}
@Override
protected void replayMocks() {
super.replayMocks();
EasyMock.replay(mockExecRuntime);
}
@Override
protected void verifyMocks() {
super.verifyMocks();
EasyMock.verify(mockExecRuntime);
}
/* /*
* "The stale-if-error Cache-Control extension indicates that when an * "The stale-if-error Cache-Control extension indicates that when an
* error is encountered, a cached stale response MAY be used to satisfy * error is encountered, a cached stale response MAY be used to satisfy
@ -169,7 +199,7 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
throws Exception{ throws Exception{
final CacheConfig configUnshared = CacheConfig.custom() final CacheConfig configUnshared = CacheConfig.custom()
.setSharedCache(false).build(); .setSharedCache(false).build();
impl = new CachingExec(new BasicHttpCache(configUnshared), configUnshared); impl = new CachingExec(new BasicHttpCache(configUnshared), null, configUnshared);
final Date tenSecondsAgo = new Date(new Date().getTime() - 10 * 1000L); final Date tenSecondsAgo = new Date(new Date().getTime() - 10 * 1000L);
final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
@ -323,16 +353,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
* *
* http://tools.ietf.org/html/rfc5861 * http://tools.ietf.org/html/rfc5861
*/ */
@Test @Ignore @Test
public void testStaleWhileRevalidateReturnsStaleEntryWithWarning() public void testStaleWhileRevalidateReturnsStaleEntryWithWarning()
throws Exception { throws Exception {
config = CacheConfig.custom() config = CacheConfig.custom()
.setMaxCacheEntries(MAX_ENTRIES) .setMaxCacheEntries(MAX_ENTRIES)
.setMaxObjectSize(MAX_BYTES) .setMaxObjectSize(MAX_BYTES)
.setAsynchronousWorkersMax(1) .setAsynchronousWorkers(1)
.build(); .build();
impl = new CachingExec(cache, config); impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config);
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
@ -365,17 +395,12 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
} }
@Test @Test
public void testHTTPCLIENT1470() {
impl = new CachingExec(cache, null);
}
@Test @Ignore
public void testStaleWhileRevalidateReturnsStaleNonRevalidatableEntryWithWarning() public void testStaleWhileRevalidateReturnsStaleNonRevalidatableEntryWithWarning()
throws Exception { throws Exception {
config = CacheConfig.custom().setMaxCacheEntries(MAX_ENTRIES).setMaxObjectSize(MAX_BYTES) config = CacheConfig.custom().setMaxCacheEntries(MAX_ENTRIES).setMaxObjectSize(MAX_BYTES)
.setAsynchronousWorkersMax(1).build(); .setAsynchronousWorkers(1).build();
impl = new CachingExec(cache, config); impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config);
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
@ -406,17 +431,17 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
assertTrue(warning110Found); assertTrue(warning110Found);
} }
@Test @Ignore @Test
public void testCanAlsoServeStale304sWhileRevalidating() public void testCanAlsoServeStale304sWhileRevalidating()
throws Exception { throws Exception {
config = CacheConfig.custom() config = CacheConfig.custom()
.setMaxCacheEntries(MAX_ENTRIES) .setMaxCacheEntries(MAX_ENTRIES)
.setMaxObjectSize(MAX_BYTES) .setMaxObjectSize(MAX_BYTES)
.setAsynchronousWorkersMax(1) .setAsynchronousWorkers(1)
.setSharedCache(false) .setSharedCache(false)
.build(); .build();
impl = new CachingExec(cache, config); impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config);
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
@ -460,9 +485,9 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
config = CacheConfig.custom() config = CacheConfig.custom()
.setMaxCacheEntries(MAX_ENTRIES) .setMaxCacheEntries(MAX_ENTRIES)
.setMaxObjectSize(MAX_BYTES) .setMaxObjectSize(MAX_BYTES)
.setAsynchronousWorkersMax(1) .setAsynchronousWorkers(1)
.build(); .build();
impl = new CachingExec(cache, config); impl = new CachingExec(cache, null, config);
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
@ -508,10 +533,10 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
config = CacheConfig.custom() config = CacheConfig.custom()
.setMaxCacheEntries(MAX_ENTRIES) .setMaxCacheEntries(MAX_ENTRIES)
.setMaxObjectSize(MAX_BYTES) .setMaxObjectSize(MAX_BYTES)
.setAsynchronousWorkersMax(1) .setAsynchronousWorkers(1)
.setSharedCache(true) .setSharedCache(true)
.build(); .build();
impl = new CachingExec(cache, config); impl = new CachingExec(cache, null, config);
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
@ -557,10 +582,10 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
config = CacheConfig.custom() config = CacheConfig.custom()
.setMaxCacheEntries(MAX_ENTRIES) .setMaxCacheEntries(MAX_ENTRIES)
.setMaxObjectSize(MAX_BYTES) .setMaxObjectSize(MAX_BYTES)
.setAsynchronousWorkersMax(1) .setAsynchronousWorkers(1)
.setSharedCache(true) .setSharedCache(true)
.build(); .build();
impl = new CachingExec(cache, config); impl = new CachingExec(cache, null, config);
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();

View File

@ -36,4 +36,8 @@ public final class ExecSupport {
return COUNT.incrementAndGet(); return COUNT.incrementAndGet();
} }
public static String getNextExchangeId() {
return String.format("ex-%08X", COUNT.incrementAndGet());
}
} }

View File

@ -27,7 +27,10 @@
package org.apache.hc.client5.http.impl; package org.apache.hc.client5.http.impl;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.Cancellable;
@ -42,6 +45,40 @@ public final class Operations {
}; };
public static class CompletedFuture<T> implements Future<T> {
private final T result;
public CompletedFuture(final T result) {
this.result = result;
}
@Override
public T get() throws InterruptedException, ExecutionException {
return result;
}
@Override
public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return result;
}
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
};
public static Cancellable nonCancellable() { public static Cancellable nonCancellable() {
return NOOP_CANCELLABLE; return NOOP_CANCELLABLE;
} }

View File

@ -158,7 +158,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
clientContext.setRequestConfig(requestConfig); clientContext.setRequestConfig(requestConfig);
} }
final HttpRoute route = determineRoute(request, clientContext); final HttpRoute route = determineRoute(request, clientContext);
final String exchangeId = String.format("ex-%08X", ExecSupport.getNextExecNumber()); final String exchangeId = ExecSupport.getNextExchangeId();
final AsyncExecRuntime execRuntime = crerateAsyncExecRuntime(); final AsyncExecRuntime execRuntime = crerateAsyncExecRuntime();
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug(exchangeId + ": preparing request execution"); log.debug(exchangeId + ": preparing request execution");

View File

@ -210,7 +210,7 @@ public final class MinimalHttp2AsyncClient extends AbstractMinimalHttpAsyncClien
}; };
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
final String exchangeId = String.format("ex-%08X", ExecSupport.getNextExecNumber()); final String exchangeId = ExecSupport.getNextExchangeId();
log.debug(ConnPoolSupport.getId(session) + ": executing message exchange " + exchangeId); log.debug(ConnPoolSupport.getId(session) + ": executing message exchange " + exchangeId);
session.addLast(new ExecutionCommand( session.addLast(new ExecutionCommand(
new LoggingAsyncClientExchangeHandler(log, exchangeId, internalExchangeHandler), new LoggingAsyncClientExchangeHandler(log, exchangeId, internalExchangeHandler),

View File

@ -396,7 +396,7 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient
Asserts.check(!released.get(), "Endpoint has already been released"); Asserts.check(!released.get(), "Endpoint has already been released");
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
final String exchangeId = String.format("ex-%08X", ExecSupport.getNextExecNumber()); final String exchangeId = ExecSupport.getNextExchangeId();
log.debug(ConnPoolSupport.getId(connectionEndpoint) + ": executing message exchange " + exchangeId); log.debug(ConnPoolSupport.getId(connectionEndpoint) + ": executing message exchange " + exchangeId);
connectionEndpoint.execute( connectionEndpoint.execute(
new LoggingAsyncClientExchangeHandler(log, exchangeId, exchangeHandler), new LoggingAsyncClientExchangeHandler(log, exchangeId, exchangeHandler),

View File

@ -158,7 +158,7 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable {
} }
setupContext(localcontext); setupContext(localcontext);
final HttpRoute route = determineRoute(target, request, localcontext); final HttpRoute route = determineRoute(target, request, localcontext);
final String exchangeId = String.format("ex-%08X", ExecSupport.getNextExecNumber()); final String exchangeId = ExecSupport.getNextExchangeId();
final ExecRuntime execRuntime = new InternalExecRuntime(log, connManager, requestExecutor, final ExecRuntime execRuntime = new InternalExecRuntime(log, connManager, requestExecutor,
request instanceof CancellableAware ? (CancellableAware) request : null); request instanceof CancellableAware ? (CancellableAware) request : null);
final ExecChain.Scope scope = new ExecChain.Scope(exchangeId, route, request, execRuntime, localcontext); final ExecChain.Scope scope = new ExecChain.Scope(exchangeId, route, request, execRuntime, localcontext);