mirror of
https://github.com/apache/httpcomponents-client.git
synced 2025-02-09 03:25:28 +00:00
HTTPCLIENT-1298: Add AsynchronousValidator in HttpClientBuilder's list of closeable objects
Contributed by Martin Meinhold <mmeinhold at atlassian.com> * Resolve constructor dependency between CachingExec and AsynchronousValidator * AsynchronousValidator implements Closeable interface git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1437952 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7f6198fe46
commit
508497a8be
@ -1,3 +1,11 @@
|
|||||||
|
Changes since 4.3 ALPHA1
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* [HTTPCLIENT-1298] Add AsynchronousValidator in HttpClientBuilder's list of closeable objects.
|
||||||
|
Contributed by Martin Meinhold <mmeinhold at atlassian.com>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Release 4.3 ALPHA1
|
Release 4.3 ALPHA1
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.http.impl.client.cache;
|
package org.apache.http.impl.client.cache;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -47,8 +49,7 @@
|
|||||||
* Class used for asynchronous revalidations to be used when the "stale-
|
* Class used for asynchronous revalidations to be used when the "stale-
|
||||||
* while-revalidate" directive is present
|
* while-revalidate" directive is present
|
||||||
*/
|
*/
|
||||||
class AsynchronousValidator {
|
class AsynchronousValidator implements Closeable {
|
||||||
private final CachingExec cachingExec;
|
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
private final Set<String> queued;
|
private final Set<String> queued;
|
||||||
private final CacheKeyGenerator cacheKeyGenerator;
|
private final CacheKeyGenerator cacheKeyGenerator;
|
||||||
@ -57,20 +58,16 @@ class AsynchronousValidator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create AsynchronousValidator which will make revalidation requests
|
* Create AsynchronousValidator which will make revalidation requests
|
||||||
* using the supplied {@link CachingHttpClient}, and
|
* using a {@link ThreadPoolExecutor} generated according to the thread
|
||||||
* a {@link ThreadPoolExecutor} generated according to the thread
|
|
||||||
* pool settings provided in the given {@link CacheConfig}.
|
* pool settings provided in the given {@link CacheConfig}.
|
||||||
* @param cachingExect used to execute asynchronous requests
|
|
||||||
* @param config specifies thread pool settings. See
|
* @param config specifies thread pool settings. See
|
||||||
* {@link CacheConfig#getAsynchronousWorkersMax()},
|
* {@link CacheConfig#getAsynchronousWorkersMax()},
|
||||||
* {@link CacheConfig#getAsynchronousWorkersCore()},
|
* {@link CacheConfig#getAsynchronousWorkersCore()},
|
||||||
* {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
|
* {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
|
||||||
* and {@link CacheConfig#getRevalidationQueueSize()}.
|
* and {@link CacheConfig#getRevalidationQueueSize()}.
|
||||||
*/
|
*/
|
||||||
public AsynchronousValidator(final CachingExec cachingExect,
|
public AsynchronousValidator(final CacheConfig config) {
|
||||||
final CacheConfig config) {
|
this(new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
|
||||||
this(cachingExect,
|
|
||||||
new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
|
|
||||||
config.getAsynchronousWorkersMax(),
|
config.getAsynchronousWorkersMax(),
|
||||||
config.getAsynchronousWorkerIdleLifetimeSecs(),
|
config.getAsynchronousWorkerIdleLifetimeSecs(),
|
||||||
TimeUnit.SECONDS,
|
TimeUnit.SECONDS,
|
||||||
@ -82,20 +79,24 @@ public AsynchronousValidator(final CachingExec cachingExect,
|
|||||||
* Create AsynchronousValidator which will make revalidation requests
|
* Create AsynchronousValidator which will make revalidation requests
|
||||||
* using the supplied {@link CachingHttpClient} and
|
* using the supplied {@link CachingHttpClient} and
|
||||||
* {@link ExecutorService}.
|
* {@link ExecutorService}.
|
||||||
* @param cachingExect used to execute asynchronous requests
|
|
||||||
* @param executor used to manage a thread pool of revalidation workers
|
* @param executor used to manage a thread pool of revalidation workers
|
||||||
*/
|
*/
|
||||||
AsynchronousValidator(final CachingExec cachingExec, final ExecutorService executor) {
|
AsynchronousValidator(final ExecutorService executor) {
|
||||||
this.cachingExec = cachingExec;
|
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.queued = new HashSet<String>();
|
this.queued = new HashSet<String>();
|
||||||
this.cacheKeyGenerator = new CacheKeyGenerator();
|
this.cacheKeyGenerator = new CacheKeyGenerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules an asynchronous revalidation
|
* Schedules an asynchronous revalidation
|
||||||
*/
|
*/
|
||||||
public synchronized void revalidateCacheEntry(
|
public synchronized void revalidateCacheEntry(
|
||||||
|
final CachingExec cachingExec,
|
||||||
final HttpRoute route,
|
final HttpRoute route,
|
||||||
final HttpRequestWrapper request,
|
final HttpRequestWrapper request,
|
||||||
final HttpClientContext context,
|
final HttpClientContext context,
|
||||||
|
@ -119,6 +119,14 @@ public CachingExec(
|
|||||||
final ClientExecChain backend,
|
final ClientExecChain backend,
|
||||||
final HttpCache cache,
|
final HttpCache cache,
|
||||||
final CacheConfig config) {
|
final CacheConfig config) {
|
||||||
|
this(backend, cache, config, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CachingExec(
|
||||||
|
final ClientExecChain backend,
|
||||||
|
final HttpCache cache,
|
||||||
|
final CacheConfig config,
|
||||||
|
final AsynchronousValidator asynchRevalidator) {
|
||||||
super();
|
super();
|
||||||
Args.notNull(backend, "HTTP backend");
|
Args.notNull(backend, "HTTP backend");
|
||||||
Args.notNull(cache, "HttpCache");
|
Args.notNull(cache, "HttpCache");
|
||||||
@ -135,7 +143,7 @@ public CachingExec(
|
|||||||
this.responseCachingPolicy = new ResponseCachingPolicy(
|
this.responseCachingPolicy = new ResponseCachingPolicy(
|
||||||
this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache(),
|
this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache(),
|
||||||
this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery());
|
this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery());
|
||||||
this.asynchRevalidator = makeAsynchronousValidator(config);
|
this.asynchRevalidator = asynchRevalidator != null ? asynchRevalidator : makeAsynchronousValidator(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachingExec(
|
public CachingExec(
|
||||||
@ -179,7 +187,7 @@ public CachingExec(final ClientExecChain backend) {
|
|||||||
private AsynchronousValidator makeAsynchronousValidator(
|
private AsynchronousValidator makeAsynchronousValidator(
|
||||||
final CacheConfig config) {
|
final CacheConfig config) {
|
||||||
if (config.getAsynchronousWorkersMax() > 0) {
|
if (config.getAsynchronousWorkersMax() > 0) {
|
||||||
return new AsynchronousValidator(this, config);
|
return new AsynchronousValidator(config);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -312,7 +320,7 @@ private CloseableHttpResponse revalidateCacheEntry(
|
|||||||
&& validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
|
&& validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
|
||||||
log.trace("Serving stale with asynchronous revalidation");
|
log.trace("Serving stale with asynchronous revalidation");
|
||||||
final HttpResponse resp = generateCachedResponse(request, context, entry, now);
|
final HttpResponse resp = generateCachedResponse(request, context, entry, now);
|
||||||
asynchRevalidator.revalidateCacheEntry(route, request, context, execAware, entry);
|
asynchRevalidator.revalidateCacheEntry(this, route, request, context, execAware, entry);
|
||||||
return Proxies.enhanceResponse(resp);
|
return Proxies.enhanceResponse(resp);
|
||||||
}
|
}
|
||||||
return revalidateCacheEntry(route, request, context, execAware, entry);
|
return revalidateCacheEntry(route, request, context, execAware, entry);
|
||||||
|
@ -97,8 +97,15 @@ protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) {
|
|||||||
}
|
}
|
||||||
storage = new BasicHttpCacheStorage(cacheConfig);
|
storage = new BasicHttpCacheStorage(cacheConfig);
|
||||||
}
|
}
|
||||||
|
final AsynchronousValidator revalidator = createAsynchronousRevalidator(config);
|
||||||
return new CachingExec(mainExec,
|
return new CachingExec(mainExec,
|
||||||
new BasicHttpCache(resourceFactory, storage, config), config);
|
new BasicHttpCache(resourceFactory, storage, config), config, revalidator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AsynchronousValidator createAsynchronousRevalidator(final CacheConfig config) {
|
||||||
|
final AsynchronousValidator revalidator = new AsynchronousValidator(config);
|
||||||
|
addCloseable(revalidator);
|
||||||
|
return revalidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.http.impl.client.cache;
|
package org.apache.http.impl.client.cache;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -74,13 +75,13 @@ public void setUp() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRevalidateCacheEntrySchedulesExecutionAndPopulatesIdentifier() {
|
public void testRevalidateCacheEntrySchedulesExecutionAndPopulatesIdentifier() {
|
||||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
impl = new AsynchronousValidator(mockExecutor);
|
||||||
|
|
||||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||||
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
||||||
|
|
||||||
replayMocks();
|
replayMocks();
|
||||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||||
verifyMocks();
|
verifyMocks();
|
||||||
|
|
||||||
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
||||||
@ -88,14 +89,14 @@ public void testRevalidateCacheEntrySchedulesExecutionAndPopulatesIdentifier() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMarkCompleteRemovesIdentifier() {
|
public void testMarkCompleteRemovesIdentifier() {
|
||||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
impl = new AsynchronousValidator(mockExecutor);
|
||||||
|
|
||||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||||
final Capture<AsynchronousValidationRequest> cap = new Capture<AsynchronousValidationRequest>();
|
final Capture<AsynchronousValidationRequest> cap = new Capture<AsynchronousValidationRequest>();
|
||||||
mockExecutor.execute(EasyMock.capture(cap));
|
mockExecutor.execute(EasyMock.capture(cap));
|
||||||
|
|
||||||
replayMocks();
|
replayMocks();
|
||||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||||
verifyMocks();
|
verifyMocks();
|
||||||
|
|
||||||
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
||||||
@ -107,14 +108,14 @@ public void testMarkCompleteRemovesIdentifier() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRevalidateCacheEntryDoesNotPopulateIdentifierOnRejectedExecutionException() {
|
public void testRevalidateCacheEntryDoesNotPopulateIdentifierOnRejectedExecutionException() {
|
||||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
impl = new AsynchronousValidator(mockExecutor);
|
||||||
|
|
||||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||||
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
||||||
EasyMock.expectLastCall().andThrow(new RejectedExecutionException());
|
EasyMock.expectLastCall().andThrow(new RejectedExecutionException());
|
||||||
|
|
||||||
replayMocks();
|
replayMocks();
|
||||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||||
verifyMocks();
|
verifyMocks();
|
||||||
|
|
||||||
Assert.assertEquals(0, impl.getScheduledIdentifiers().size());
|
Assert.assertEquals(0, impl.getScheduledIdentifiers().size());
|
||||||
@ -122,7 +123,7 @@ public void testRevalidateCacheEntryDoesNotPopulateIdentifierOnRejectedExecution
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRevalidateCacheEntryProperlyCollapsesRequest() {
|
public void testRevalidateCacheEntryProperlyCollapsesRequest() {
|
||||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
impl = new AsynchronousValidator(mockExecutor);
|
||||||
|
|
||||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||||
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
||||||
@ -130,8 +131,8 @@ public void testRevalidateCacheEntryProperlyCollapsesRequest() {
|
|||||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||||
|
|
||||||
replayMocks();
|
replayMocks();
|
||||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||||
verifyMocks();
|
verifyMocks();
|
||||||
|
|
||||||
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
||||||
@ -139,7 +140,7 @@ public void testRevalidateCacheEntryProperlyCollapsesRequest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVariantsBothRevalidated() {
|
public void testVariantsBothRevalidated() {
|
||||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
impl = new AsynchronousValidator(mockExecutor);
|
||||||
|
|
||||||
final HttpRequest req1 = new HttpGet("/");
|
final HttpRequest req1 = new HttpGet("/");
|
||||||
req1.addHeader(new BasicHeader("Accept-Encoding", "identity"));
|
req1.addHeader(new BasicHeader("Accept-Encoding", "identity"));
|
||||||
@ -157,8 +158,8 @@ public void testVariantsBothRevalidated() {
|
|||||||
EasyMock.expectLastCall().times(2);
|
EasyMock.expectLastCall().times(2);
|
||||||
|
|
||||||
replayMocks();
|
replayMocks();
|
||||||
impl.revalidateCacheEntry(route, HttpRequestWrapper.wrap(req1), context, mockExecAware, mockCacheEntry);
|
impl.revalidateCacheEntry(mockClient, route, HttpRequestWrapper.wrap(req1), context, mockExecAware, mockCacheEntry);
|
||||||
impl.revalidateCacheEntry(route, HttpRequestWrapper.wrap(req2), context, mockExecAware, mockCacheEntry);
|
impl.revalidateCacheEntry(mockClient, route, HttpRequestWrapper.wrap(req2), context, mockExecAware, mockCacheEntry);
|
||||||
verifyMocks();
|
verifyMocks();
|
||||||
|
|
||||||
Assert.assertEquals(2, impl.getScheduledIdentifiers().size());
|
Assert.assertEquals(2, impl.getScheduledIdentifiers().size());
|
||||||
@ -171,14 +172,14 @@ public void testRevalidateCacheEntryEndToEnd() throws Exception {
|
|||||||
.setAsynchronousWorkersMax(1)
|
.setAsynchronousWorkersMax(1)
|
||||||
.setAsynchronousWorkersCore(1)
|
.setAsynchronousWorkersCore(1)
|
||||||
.build();
|
.build();
|
||||||
impl = new AsynchronousValidator(mockClient, config);
|
impl = new AsynchronousValidator(config);
|
||||||
|
|
||||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||||
EasyMock.expect(mockClient.revalidateCacheEntry(
|
EasyMock.expect(mockClient.revalidateCacheEntry(
|
||||||
route, request, context, mockExecAware, mockCacheEntry)).andReturn(null);
|
route, request, context, mockExecAware, mockCacheEntry)).andReturn(null);
|
||||||
|
|
||||||
replayMocks();
|
replayMocks();
|
||||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// shut down backend executor and make sure all finishes properly, 1 second should be sufficient
|
// shut down backend executor and make sure all finishes properly, 1 second should be sufficient
|
||||||
@ -194,6 +195,17 @@ public void testRevalidateCacheEntryEndToEnd() throws Exception {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecutorShutdownOnClose() throws IOException {
|
||||||
|
impl = new AsynchronousValidator(mockExecutor);
|
||||||
|
|
||||||
|
mockExecutor.shutdown();
|
||||||
|
|
||||||
|
replayMocks();
|
||||||
|
impl.close();
|
||||||
|
verifyMocks();
|
||||||
|
}
|
||||||
|
|
||||||
public void replayMocks() {
|
public void replayMocks() {
|
||||||
EasyMock.replay(mockExecutor);
|
EasyMock.replay(mockExecutor);
|
||||||
EasyMock.replay(mockClient);
|
EasyMock.replay(mockClient);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user