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
|
||||
-------------------
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
*/
|
||||
package org.apache.http.impl.client.cache;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
@ -47,8 +49,7 @@ import org.apache.http.conn.routing.HttpRoute;
|
|||
* Class used for asynchronous revalidations to be used when the "stale-
|
||||
* while-revalidate" directive is present
|
||||
*/
|
||||
class AsynchronousValidator {
|
||||
private final CachingExec cachingExec;
|
||||
class AsynchronousValidator implements Closeable {
|
||||
private final ExecutorService executor;
|
||||
private final Set<String> queued;
|
||||
private final CacheKeyGenerator cacheKeyGenerator;
|
||||
|
@ -57,20 +58,16 @@ class AsynchronousValidator {
|
|||
|
||||
/**
|
||||
* Create AsynchronousValidator which will make revalidation requests
|
||||
* using the supplied {@link CachingHttpClient}, and
|
||||
* a {@link ThreadPoolExecutor} generated according to the thread
|
||||
* using a {@link ThreadPoolExecutor} generated according to the thread
|
||||
* pool settings provided in the given {@link CacheConfig}.
|
||||
* @param cachingExect used to execute asynchronous requests
|
||||
* @param config specifies thread pool settings. See
|
||||
* {@link CacheConfig#getAsynchronousWorkersMax()},
|
||||
* {@link CacheConfig#getAsynchronousWorkersCore()},
|
||||
* {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
|
||||
* and {@link CacheConfig#getRevalidationQueueSize()}.
|
||||
*/
|
||||
public AsynchronousValidator(final CachingExec cachingExect,
|
||||
final CacheConfig config) {
|
||||
this(cachingExect,
|
||||
new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
|
||||
public AsynchronousValidator(final CacheConfig config) {
|
||||
this(new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
|
||||
config.getAsynchronousWorkersMax(),
|
||||
config.getAsynchronousWorkerIdleLifetimeSecs(),
|
||||
TimeUnit.SECONDS,
|
||||
|
@ -82,20 +79,24 @@ class AsynchronousValidator {
|
|||
* Create AsynchronousValidator which will make revalidation requests
|
||||
* using the supplied {@link CachingHttpClient} and
|
||||
* {@link ExecutorService}.
|
||||
* @param cachingExect used to execute asynchronous requests
|
||||
* @param executor used to manage a thread pool of revalidation workers
|
||||
*/
|
||||
AsynchronousValidator(final CachingExec cachingExec, final ExecutorService executor) {
|
||||
this.cachingExec = cachingExec;
|
||||
AsynchronousValidator(final ExecutorService executor) {
|
||||
this.executor = executor;
|
||||
this.queued = new HashSet<String>();
|
||||
this.cacheKeyGenerator = new CacheKeyGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules an asynchronous revalidation
|
||||
*/
|
||||
public synchronized void revalidateCacheEntry(
|
||||
final CachingExec cachingExec,
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpClientContext context,
|
||||
|
|
|
@ -119,6 +119,14 @@ public class CachingExec implements ClientExecChain {
|
|||
final ClientExecChain backend,
|
||||
final HttpCache cache,
|
||||
final CacheConfig config) {
|
||||
this(backend, cache, config, null);
|
||||
}
|
||||
|
||||
public CachingExec(
|
||||
final ClientExecChain backend,
|
||||
final HttpCache cache,
|
||||
final CacheConfig config,
|
||||
final AsynchronousValidator asynchRevalidator) {
|
||||
super();
|
||||
Args.notNull(backend, "HTTP backend");
|
||||
Args.notNull(cache, "HttpCache");
|
||||
|
@ -135,7 +143,7 @@ public class CachingExec implements ClientExecChain {
|
|||
this.responseCachingPolicy = new ResponseCachingPolicy(
|
||||
this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache(),
|
||||
this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery());
|
||||
this.asynchRevalidator = makeAsynchronousValidator(config);
|
||||
this.asynchRevalidator = asynchRevalidator != null ? asynchRevalidator : makeAsynchronousValidator(config);
|
||||
}
|
||||
|
||||
public CachingExec(
|
||||
|
@ -179,7 +187,7 @@ public class CachingExec implements ClientExecChain {
|
|||
private AsynchronousValidator makeAsynchronousValidator(
|
||||
final CacheConfig config) {
|
||||
if (config.getAsynchronousWorkersMax() > 0) {
|
||||
return new AsynchronousValidator(this, config);
|
||||
return new AsynchronousValidator(config);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -312,7 +320,7 @@ public class CachingExec implements ClientExecChain {
|
|||
&& validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
|
||||
log.trace("Serving stale with asynchronous revalidation");
|
||||
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 revalidateCacheEntry(route, request, context, execAware, entry);
|
||||
|
|
|
@ -97,8 +97,15 @@ public class CachingHttpClientBuilder extends HttpClientBuilder {
|
|||
}
|
||||
storage = new BasicHttpCacheStorage(cacheConfig);
|
||||
}
|
||||
final AsynchronousValidator revalidator = createAsynchronousRevalidator(config);
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -74,13 +75,13 @@ public class TestAsynchronousValidator {
|
|||
|
||||
@Test
|
||||
public void testRevalidateCacheEntrySchedulesExecutionAndPopulatesIdentifier() {
|
||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
||||
impl = new AsynchronousValidator(mockExecutor);
|
||||
|
||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
||||
|
||||
replayMocks();
|
||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
||||
|
@ -88,14 +89,14 @@ public class TestAsynchronousValidator {
|
|||
|
||||
@Test
|
||||
public void testMarkCompleteRemovesIdentifier() {
|
||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
||||
impl = new AsynchronousValidator(mockExecutor);
|
||||
|
||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||
final Capture<AsynchronousValidationRequest> cap = new Capture<AsynchronousValidationRequest>();
|
||||
mockExecutor.execute(EasyMock.capture(cap));
|
||||
|
||||
replayMocks();
|
||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
||||
|
@ -107,14 +108,14 @@ public class TestAsynchronousValidator {
|
|||
|
||||
@Test
|
||||
public void testRevalidateCacheEntryDoesNotPopulateIdentifierOnRejectedExecutionException() {
|
||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
||||
impl = new AsynchronousValidator(mockExecutor);
|
||||
|
||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
||||
EasyMock.expectLastCall().andThrow(new RejectedExecutionException());
|
||||
|
||||
replayMocks();
|
||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertEquals(0, impl.getScheduledIdentifiers().size());
|
||||
|
@ -122,7 +123,7 @@ public class TestAsynchronousValidator {
|
|||
|
||||
@Test
|
||||
public void testRevalidateCacheEntryProperlyCollapsesRequest() {
|
||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
||||
impl = new AsynchronousValidator(mockExecutor);
|
||||
|
||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||
mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
|
||||
|
@ -130,8 +131,8 @@ public class TestAsynchronousValidator {
|
|||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||
|
||||
replayMocks();
|
||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
|
||||
|
@ -139,7 +140,7 @@ public class TestAsynchronousValidator {
|
|||
|
||||
@Test
|
||||
public void testVariantsBothRevalidated() {
|
||||
impl = new AsynchronousValidator(mockClient, mockExecutor);
|
||||
impl = new AsynchronousValidator(mockExecutor);
|
||||
|
||||
final HttpRequest req1 = new HttpGet("/");
|
||||
req1.addHeader(new BasicHeader("Accept-Encoding", "identity"));
|
||||
|
@ -157,8 +158,8 @@ public class TestAsynchronousValidator {
|
|||
EasyMock.expectLastCall().times(2);
|
||||
|
||||
replayMocks();
|
||||
impl.revalidateCacheEntry(route, HttpRequestWrapper.wrap(req1), context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(route, HttpRequestWrapper.wrap(req2), context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(mockClient, route, HttpRequestWrapper.wrap(req1), context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(mockClient, route, HttpRequestWrapper.wrap(req2), context, mockExecAware, mockCacheEntry);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertEquals(2, impl.getScheduledIdentifiers().size());
|
||||
|
@ -171,14 +172,14 @@ public class TestAsynchronousValidator {
|
|||
.setAsynchronousWorkersMax(1)
|
||||
.setAsynchronousWorkersCore(1)
|
||||
.build();
|
||||
impl = new AsynchronousValidator(mockClient, config);
|
||||
impl = new AsynchronousValidator(config);
|
||||
|
||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||
EasyMock.expect(mockClient.revalidateCacheEntry(
|
||||
route, request, context, mockExecAware, mockCacheEntry)).andReturn(null);
|
||||
|
||||
replayMocks();
|
||||
impl.revalidateCacheEntry(route, request, context, mockExecAware, mockCacheEntry);
|
||||
impl.revalidateCacheEntry(mockClient, route, request, context, mockExecAware, mockCacheEntry);
|
||||
|
||||
try {
|
||||
// shut down backend executor and make sure all finishes properly, 1 second should be sufficient
|
||||
|
@ -194,6 +195,17 @@ public class TestAsynchronousValidator {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecutorShutdownOnClose() throws IOException {
|
||||
impl = new AsynchronousValidator(mockExecutor);
|
||||
|
||||
mockExecutor.shutdown();
|
||||
|
||||
replayMocks();
|
||||
impl.close();
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
public void replayMocks() {
|
||||
EasyMock.replay(mockExecutor);
|
||||
EasyMock.replay(mockClient);
|
||||
|
|
Loading…
Reference in New Issue