Added HttpCache implementations that can be used to store cache entries requiring explict deallocation of system resources; cache entries can be generated using HttpCacheEntryFactory interface

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@981667 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-08-02 19:48:53 +00:00
parent 6602f53a8b
commit 911350a0c2
16 changed files with 535 additions and 119 deletions

View File

@ -149,7 +149,7 @@ public abstract class HttpCacheEntry implements Serializable {
public abstract Resource getResource();
public abstract InputStream getBody();
public abstract InputStream getBody() throws IOException;
public abstract long getBodyLength();

View File

@ -0,0 +1,53 @@
/*
* ====================================================================
* 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.http.client.cache;
import java.io.IOException;
import java.util.Date;
import org.apache.http.Header;
import org.apache.http.StatusLine;
/**
* Generates {@link HttpCacheEntry} instances.
*
* @since 4.1
*/
public interface HttpCacheEntryFactory {
HttpCacheEntry generate(
Date requestDate,
Date responseDate,
StatusLine statusLine,
Header[] headers,
byte[] body) throws IOException;
HttpCacheEntry copyVariant(
HttpCacheEntry entry,
String variantURI) throws IOException;
}

View File

@ -69,7 +69,7 @@ class CacheEntity implements HttpEntity, Serializable {
return this.cacheEntry.getBodyLength();
}
public InputStream getContent() {
public InputStream getContent() throws IOException {
return this.cacheEntry.getBody();
}

View File

@ -33,17 +33,20 @@ import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheEntryFactory;
/**
* Generates a {@link CacheEntry} from a {@link HttpResponse}
* Generates {@link MemCacheEntry}s.
*
* @since 4.1
*/
@Immutable
class CacheEntryGenerator {
class CacheEntryGenerator implements HttpCacheEntryFactory {
public HttpCacheEntry generateEntry(
Date requestDate,
@ -58,7 +61,21 @@ class CacheEntryGenerator {
null);
}
public HttpCacheEntry copyWithVariant(
public HttpCacheEntry generate(
final Date requestDate,
final Date responseDate,
final StatusLine statusLine,
final Header[] headers,
byte[] body) throws IOException {
return new MemCacheEntry(requestDate,
responseDate,
statusLine,
headers,
body,
null);
}
public HttpCacheEntry copyVariant(
final HttpCacheEntry entry, final String variantURI) throws IOException {
Set<String> variants = new HashSet<String>(entry.getVariantURIs());
variants.add(variantURI);

View File

@ -40,6 +40,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheEntryFactory;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP;
@ -54,6 +55,17 @@ import org.apache.http.protocol.HTTP;
@Immutable
class CacheEntryUpdater {
private final HttpCacheEntryFactory cacheEntryFactory;
CacheEntryUpdater() {
this(new CacheEntryGenerator());
}
CacheEntryUpdater(final HttpCacheEntryFactory cacheEntryFactory) {
super();
this.cacheEntryFactory = cacheEntryFactory;
}
/**
* Update the entry with the new information from the response.
*
@ -78,11 +90,12 @@ class CacheEntryUpdater {
while ((len = instream.read(buf)) != -1) {
outstream.write(buf, 0, len);
}
HttpCacheEntry updated = new MemCacheEntry(requestDate, responseDate,
HttpCacheEntry updated = cacheEntryFactory.generate(
requestDate,
responseDate,
entry.getStatusLine(),
mergedHeaders,
outstream.toByteArray(),
null);
outstream.toByteArray());
return updated;
}

View File

@ -50,6 +50,7 @@ import org.apache.http.client.ResponseHandler;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCache;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheEntryFactory;
import org.apache.http.client.cache.HttpCacheUpdateCallback;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
@ -75,9 +76,9 @@ public class CachingHttpClient implements HttpClient {
private final HttpClient backend;
private final HttpCache responseCache;
private final HttpCacheEntryFactory cacheEntryFactory;
private final CacheValidityPolicy validityPolicy;
private final ResponseCachingPolicy responseCachingPolicy;
private final CacheEntryGenerator cacheEntryGenerator;
private final URIExtractor uriExtractor;
private final CachedHttpResponseGenerator responseGenerator;
private final CacheInvalidator cacheInvalidator;
@ -96,7 +97,11 @@ public class CachingHttpClient implements HttpClient {
private final Log log = LogFactory.getLog(getClass());
public CachingHttpClient(HttpClient client, HttpCache cache, CacheConfig config) {
public CachingHttpClient(
HttpClient client,
HttpCache cache,
HttpCacheEntryFactory cacheEntryFactory,
CacheConfig config) {
super();
if (client == null) {
throw new IllegalArgumentException("HttpClient may not be null");
@ -104,6 +109,9 @@ public class CachingHttpClient implements HttpClient {
if (cache == null) {
throw new IllegalArgumentException("HttpCache may not be null");
}
if (cacheEntryFactory == null) {
throw new IllegalArgumentException("HttpCacheEntryFactory may not be null");
}
if (config == null) {
throw new IllegalArgumentException("CacheConfig may not be null");
}
@ -111,51 +119,81 @@ public class CachingHttpClient implements HttpClient {
this.sharedCache = config.isSharedCache();
this.backend = client;
this.responseCache = cache;
this.cacheEntryFactory = cacheEntryFactory;
this.validityPolicy = new CacheValidityPolicy();
this.responseCachingPolicy = new ResponseCachingPolicy(maxObjectSizeBytes, sharedCache);
this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy);
this.cacheEntryGenerator = new CacheEntryGenerator();
this.uriExtractor = new URIExtractor();
this.cacheInvalidator = new CacheInvalidator(this.uriExtractor, this.responseCache);
this.cacheableRequestPolicy = new CacheableRequestPolicy();
this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy);
this.conditionalRequestBuilder = new ConditionalRequestBuilder();
this.cacheEntryUpdater = new CacheEntryUpdater();
this.cacheEntryUpdater = new CacheEntryUpdater(this.cacheEntryFactory);
this.responseCompliance = new ResponseProtocolCompliance();
this.requestCompliance = new RequestProtocolCompliance();
}
public CachingHttpClient() {
this(new DefaultHttpClient(), new BasicHttpCache(MAX_CACHE_ENTRIES), new CacheConfig());
this(new DefaultHttpClient(),
new BasicHttpCache(MAX_CACHE_ENTRIES),
new CacheEntryGenerator(),
new CacheConfig());
}
public CachingHttpClient(CacheConfig config) {
this(new DefaultHttpClient(), new BasicHttpCache(MAX_CACHE_ENTRIES), config);
this(new DefaultHttpClient(),
new BasicHttpCache(MAX_CACHE_ENTRIES),
new CacheEntryGenerator(),
config);
}
public CachingHttpClient(HttpClient client) {
this(client, new BasicHttpCache(MAX_CACHE_ENTRIES), new CacheConfig());
this(client,
new BasicHttpCache(MAX_CACHE_ENTRIES),
new CacheEntryGenerator(),
new CacheConfig());
}
public CachingHttpClient(HttpClient client, CacheConfig config) {
this(client, new BasicHttpCache(MAX_CACHE_ENTRIES), config);
this(client,
new BasicHttpCache(MAX_CACHE_ENTRIES),
new CacheEntryGenerator(),
config);
}
public CachingHttpClient(HttpCache cache) {
this(new DefaultHttpClient(), cache, new CacheConfig());
public CachingHttpClient(
HttpCache cache,
HttpCacheEntryFactory cacheEntryFactory) {
this(new DefaultHttpClient(),
cache,
cacheEntryFactory,
new CacheConfig());
}
public CachingHttpClient(HttpCache cache, CacheConfig config) {
this(new DefaultHttpClient(), cache, config);
public CachingHttpClient(
HttpCache cache,
HttpCacheEntryFactory cacheEntryFactory,
CacheConfig config) {
this(new DefaultHttpClient(),
cache,
cacheEntryFactory,
config);
}
public CachingHttpClient(HttpClient client, HttpCache cache) {
this(client, cache, new CacheConfig());
public CachingHttpClient(
HttpClient client,
HttpCache cache,
HttpCacheEntryFactory cacheEntryFactory) {
this(client,
cache,
cacheEntryFactory,
new CacheConfig());
}
CachingHttpClient(HttpClient backend, CacheValidityPolicy validityPolicy, ResponseCachingPolicy responseCachingPolicy,
CacheEntryGenerator cacheEntryGenerator, URIExtractor uriExtractor,
HttpCacheEntryFactory cacheEntryFactory, URIExtractor uriExtractor,
HttpCache responseCache, CachedHttpResponseGenerator responseGenerator,
CacheInvalidator cacheInvalidator, CacheableRequestPolicy cacheableRequestPolicy,
CachedResponseSuitabilityChecker suitabilityChecker,
@ -166,9 +204,9 @@ public class CachingHttpClient implements HttpClient {
this.maxObjectSizeBytes = config.getMaxObjectSizeBytes();
this.sharedCache = config.isSharedCache();
this.backend = backend;
this.cacheEntryFactory = cacheEntryFactory;
this.validityPolicy = validityPolicy;
this.responseCachingPolicy = responseCachingPolicy;
this.cacheEntryGenerator = cacheEntryGenerator;
this.uriExtractor = uriExtractor;
this.responseCache = responseCache;
this.responseGenerator = responseGenerator;
@ -532,9 +570,9 @@ public class CachingHttpClient implements HttpClient {
HttpCacheEntry existing,
HttpCacheEntry entry, String variantURI) throws IOException {
if (existing != null) {
return cacheEntryGenerator.copyWithVariant(existing, variantURI);
return cacheEntryFactory.copyVariant(existing, variantURI);
} else {
return cacheEntryGenerator.copyWithVariant(entry, variantURI);
return cacheEntryFactory.copyVariant(entry, variantURI);
}
}
@ -590,8 +628,11 @@ public class CachingHttpClient implements HttpClient {
responseBytes);
int correctedStatus = corrected.getStatusLine().getStatusCode();
if (HttpStatus.SC_BAD_GATEWAY != correctedStatus) {
HttpCacheEntry entry = cacheEntryGenerator
.generateEntry(requestDate, responseDate, corrected,
HttpCacheEntry entry = cacheEntryFactory.generate(
requestDate,
responseDate,
corrected.getStatusLine(),
corrected.getAllHeaders(),
responseBytes);
storeInCache(target, request, entry);
return responseGenerator.generateResponse(entry);

View File

@ -0,0 +1,98 @@
/*
* ====================================================================
* 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.http.impl.client.cache;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Set;
import org.apache.http.Header;
import org.apache.http.StatusLine;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.Resource;
/**
* {@link File} backed {@link HttpCacheEntry} that requires explicit deallocation.
*/
@Immutable
class FileCacheEntry extends HttpCacheEntry {
private static final long serialVersionUID = -8396589100351931966L;
private final File file;
private final FileResource resource;
public FileCacheEntry(
final Date requestDate,
final Date responseDate,
final StatusLine statusLine,
final Header[] responseHeaders,
final File file,
final Set<String> variants) {
super(requestDate, responseDate, statusLine, responseHeaders, variants);
this.file = file;
this.resource = new FileResource(file);
}
@Override
public long getBodyLength() {
return this.file.length();
}
@Override
public InputStream getBody() throws IOException {
return new FileInputStream(this.file);
}
@Override
public Resource getResource() {
return this.resource;
}
class FileResource implements Resource {
private File file;
FileResource(final File file) {
super();
this.file = file;
}
public synchronized void dispose() {
if (this.file != null) {
this.file.delete();
this.file = null;
}
}
}
}

View File

@ -0,0 +1,171 @@
/*
* ====================================================================
* 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.http.impl.client.cache;
import java.io.IOException;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.HashSet;
import java.util.Set;
import org.apache.http.annotation.ThreadSafe;
import org.apache.http.client.cache.HttpCache;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheUpdateCallback;
import org.apache.http.client.cache.Resource;
/**
* {@link HttpCache} implementation capable of deallocating resources associated with
* the cache entries. This cache keeps track of cache entries using {@link PhantomReference}
* and maintains a collection of all resources that are no longer in use. The cache, however,
* does not automatically deallocates associated resources by invoking {@link Resource#dispose()}
* method. The consumer MUST periodically call {@link #cleanResources()} method to trigger
* resource deallocation. The cache can be permanently shut down using {@link #shutdown()}
* method. All resources associated with the entries used by the cache will be deallocated.
*
* This {@link HttpCache} implementation is intended for use with {@link FileCacheEntry}
* and similar.
*
* @since 4.1
*/
@ThreadSafe
public class ManagedHttpCache implements HttpCache {
private final CacheMap entries;
private final ReferenceQueue<HttpCacheEntry> morque;
private final Set<ResourceReference> resources;
private volatile boolean shutdown;
public ManagedHttpCache(int maxEntries) {
super();
this.entries = new CacheMap(maxEntries);
this.morque = new ReferenceQueue<HttpCacheEntry>();
this.resources = new HashSet<ResourceReference>();
}
private void ensureValidState() throws IllegalStateException {
if (this.shutdown) {
throw new IllegalStateException("Cache has been shut down");
}
}
private void keepResourceReference(final HttpCacheEntry entry) {
Resource resource = entry.getResource();
if (resource != null) {
// Must deallocate the resource when the entry is no longer in used
ResourceReference ref = new ResourceReference(entry, this.morque);
this.resources.add(ref);
}
}
public void putEntry(final String url, final HttpCacheEntry entry) throws IOException {
if (url == null) {
throw new IllegalArgumentException("URL may not be null");
}
if (entry == null) {
throw new IllegalArgumentException("Cache entry may not be null");
}
ensureValidState();
synchronized (this) {
this.entries.put(url, entry);
keepResourceReference(entry);
}
}
public HttpCacheEntry getEntry(final String url) throws IOException {
if (url == null) {
throw new IllegalArgumentException("URL may not be null");
}
ensureValidState();
synchronized (this) {
return this.entries.get(url);
}
}
public void removeEntry(String url) throws IOException {
if (url == null) {
throw new IllegalArgumentException("URL may not be null");
}
ensureValidState();
synchronized (this) {
// Cannot deallocate the associated resources immediately as the
// cache entry may still be in use
this.entries.remove(url);
}
}
public void updateEntry(
final String url,
final HttpCacheUpdateCallback callback) throws IOException {
if (url == null) {
throw new IllegalArgumentException("URL may not be null");
}
if (callback == null) {
throw new IllegalArgumentException("Callback may not be null");
}
ensureValidState();
synchronized (this) {
HttpCacheEntry existing = this.entries.get(url);
HttpCacheEntry updated = callback.update(existing);
this.entries.put(url, updated);
if (existing != updated) {
keepResourceReference(updated);
}
}
}
public void cleanResources() {
if (this.shutdown) {
return;
}
ResourceReference ref;
while ((ref = (ResourceReference) this.morque.poll()) != null) {
synchronized (this) {
this.resources.remove(ref);
}
ref.getResource().dispose();
}
}
public void shutdown() {
if (this.shutdown) {
return;
}
this.shutdown = true;
synchronized (this) {
this.entries.clear();
for (ResourceReference ref: this.resources) {
ref.getResource().dispose();
}
this.resources.clear();
while (this.morque.poll() != null) {
}
}
}
}

View File

@ -42,7 +42,7 @@ import org.apache.http.client.cache.Resource;
* explicit deallocation.
*/
@Immutable
public class MemCacheEntry extends HttpCacheEntry {
class MemCacheEntry extends HttpCacheEntry {
private static final long serialVersionUID = -8464486112875881235L;

View File

@ -0,0 +1,63 @@
/*
* ====================================================================
* 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.http.impl.client.cache;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.Resource;
@Immutable
class ResourceReference extends PhantomReference<HttpCacheEntry> {
private final Resource resource;
public ResourceReference(final HttpCacheEntry entry, final ReferenceQueue<HttpCacheEntry> q) {
super(entry, q);
if (entry.getResource() == null) {
throw new IllegalArgumentException("Resource may not be null");
}
this.resource = entry.getResource();
}
public Resource getResource() {
return this.resource;
}
@Override
public int hashCode() {
return this.resource.hashCode();
}
@Override
public boolean equals(final Object obj) {
return this.resource.equals(obj);
}
}

View File

@ -10,6 +10,7 @@ import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.cache.HttpCache;
import org.apache.http.client.cache.HttpCacheEntryFactory;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.BasicHttpResponse;
@ -32,7 +33,8 @@ public abstract class AbstractProtocolTest {
protected HttpResponse originResponse;
protected CacheConfig params;
protected CachingHttpClient impl;
private HttpCache cache;
protected HttpCache cache;
protected HttpCacheEntryFactory cacheEntryFactory;
public static HttpRequest eqRequest(HttpRequest in) {
EasyMock.reportMatcher(new RequestEquivalent(in));
@ -50,12 +52,13 @@ public abstract class AbstractProtocolTest {
originResponse = make200Response();
cache = new BasicHttpCache(MAX_ENTRIES);
cacheEntryFactory = new CacheEntryGenerator();
mockBackend = EasyMock.createMock(HttpClient.class);
mockEntity = EasyMock.createMock(HttpEntity.class);
mockCache = EasyMock.createMock(HttpCache.class);
params = new CacheConfig();
params.setMaxObjectSizeBytes(MAX_BYTES);
impl = new CachingHttpClient(mockBackend, cache, params);
impl = new CachingHttpClient(mockBackend, cache, cacheEntryFactory, params);
}
protected void replayMocks() {
@ -94,7 +97,7 @@ public abstract class AbstractProtocolTest {
mockCache = EasyMock.createMock(HttpCache.class);
mockEntity = EasyMock.createMock(HttpEntity.class);
impl = new CachingHttpClient(mockBackend, mockCache, params);
impl = new CachingHttpClient(mockBackend, mockCache, cacheEntryFactory, params);
EasyMock.expect(mockCache.getEntry((String) EasyMock.anyObject())).andReturn(null)
.anyTimes();
@ -105,7 +108,7 @@ public abstract class AbstractProtocolTest {
protected void behaveAsNonSharedCache() {
params.setSharedCache(false);
impl = new CachingHttpClient(mockBackend, cache, params);
impl = new CachingHttpClient(mockBackend, cache, cacheEntryFactory, params);
}
public AbstractProtocolTest() {

View File

@ -82,7 +82,7 @@ public class DoNotTestProtocolRequirements {
mockCache = EasyMock.createMock(HttpCache.class);
CacheConfig params = new CacheConfig();
params.setMaxObjectSizeBytes(MAX_BYTES);
impl = new CachingHttpClient(mockBackend, cache, params);
impl = new CachingHttpClient(mockBackend, cache, new CacheEntryGenerator(), params);
}
private HttpResponse make200Response() {

View File

@ -110,8 +110,8 @@ public class TestCacheEntry {
CacheEntryGenerator entryGenerator = new CacheEntryGenerator();
HttpCacheEntry addedOne = entryGenerator.copyWithVariant(entry, "foo");
HttpCacheEntry addedTwo = entryGenerator.copyWithVariant(addedOne, "bar");
HttpCacheEntry addedOne = entryGenerator.copyVariant(entry, "foo");
HttpCacheEntry addedTwo = entryGenerator.copyVariant(addedOne, "bar");
Set<String> variants = addedTwo.getVariantURIs();

View File

@ -26,7 +26,6 @@
*/
package org.apache.http.impl.client.cache;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
@ -40,8 +39,8 @@ import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.ProtocolException;
import org.apache.http.ProtocolVersion;
import org.apache.http.RequestLine;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
@ -49,16 +48,10 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.cache.HttpCache;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.cache.HttpCacheEntryFactory;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import org.apache.http.params.HttpParams;
@ -66,13 +59,10 @@ import org.apache.http.protocol.HttpContext;
import org.easymock.classextension.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class TestCachingHttpClient {
private static final ProtocolVersion HTTP_1_1 = new ProtocolVersion("HTTP",1,1);
private static final String GET_CURRENT_DATE = "getCurrentDate";
private static final String HANDLE_BACKEND_RESPONSE = "handleBackendResponse";
@ -87,6 +77,9 @@ public class TestCachingHttpClient {
private static final String GET_RESPONSE_READER = "getResponseReader";
private static final StatusLine SC_OK = new BasicStatusLine(HttpVersion.HTTP_1_1, 200, "OK");
private static final Header[] HEADERS = new Header[] {};
private CachingHttpClient impl;
private boolean mockedImpl;
@ -103,7 +96,7 @@ public class TestCachingHttpClient {
private CacheEntry mockVariantCacheEntry;
private CacheEntry mockUpdatedCacheEntry;
private URIExtractor mockExtractor;
private CacheEntryGenerator mockEntryGenerator;
private HttpCacheEntryFactory mockEntryGenerator;
private CachedHttpResponseGenerator mockResponseGenerator;
private SizeLimitedResponseReader mockResponseReader;
@ -151,7 +144,7 @@ public class TestCachingHttpClient {
mockUpdatedCacheEntry = EasyMock.createMock(CacheEntry.class);
mockVariantCacheEntry = EasyMock.createMock(CacheEntry.class);
mockExtractor = EasyMock.createMock(URIExtractor.class);
mockEntryGenerator = EasyMock.createMock(CacheEntryGenerator.class);
mockEntryGenerator = EasyMock.createMock(HttpCacheEntryFactory.class);
mockResponseGenerator = EasyMock.createMock(CachedHttpResponseGenerator.class);
mockCachedResponse = EasyMock.createMock(HttpResponse.class);
mockConditionalRequestBuilder = EasyMock.createMock(ConditionalRequestBuilder.class);
@ -259,6 +252,7 @@ public class TestCachingHttpClient {
storeInCacheWasCalled();
responseIsGeneratedFromCache();
responseStatusLineIsInspectable();
responseGetHeaders();
responseDoesNotHaveExplicitContentLength();
replayMocks();
@ -602,6 +596,7 @@ public class TestCachingHttpClient {
storeInCacheWasCalled();
responseIsGeneratedFromCache();
responseStatusLineIsInspectable();
responseGetHeaders();
responseDoesNotHaveExplicitContentLength();
replayMocks();
@ -863,34 +858,6 @@ public class TestCachingHttpClient {
Assert.assertSame(mockParams, result);
}
@Test
@Ignore
public void testRealResultsMatch() throws IOException {
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
ClientConnectionManager cm = new ThreadSafeClientConnManager(schemeRegistry);
HttpClient httpClient = new DefaultHttpClient(cm);
HttpCache cacheImpl = new BasicHttpCache(100);
CachingHttpClient cachingClient = new CachingHttpClient(httpClient, cacheImpl);
HttpUriRequest request = new HttpGet("http://www.fancast.com/static-28262/styles/base.css");
HttpClient baseClient = new DefaultHttpClient();
HttpResponse cachedResponse = cachingClient.execute(request);
HttpResponse realResponse = baseClient.execute(request);
byte[] cached = readResponse(cachedResponse);
byte[] real = readResponse(realResponse);
Assert.assertArrayEquals(cached, real);
}
@Test
public void testResponseIsGeneratedWhenCacheEntryIsUsable() throws Exception {
@ -936,7 +903,7 @@ public class TestCachingHttpClient {
@Test
public void testCorrectIncompleteResponseDoesNotCorrectComplete200Response()
throws Exception {
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_OK, "OK");
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","128");
@ -948,7 +915,7 @@ public class TestCachingHttpClient {
@Test
public void testCorrectIncompleteResponseDoesNotCorrectComplete206Response()
throws Exception {
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","128");
@ -961,7 +928,7 @@ public class TestCachingHttpClient {
@Test
public void testCorrectIncompleteResponseGenerates502ForIncomplete200Response()
throws Exception {
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_OK, "OK");
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","256");
@ -973,7 +940,7 @@ public class TestCachingHttpClient {
@Test
public void testCorrectIncompleteResponseDoesNotCorrectIncompleteNon200Or206Responses()
throws Exception {
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_FORBIDDEN, "Forbidden");
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_FORBIDDEN, "Forbidden");
byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","256");
@ -985,7 +952,7 @@ public class TestCachingHttpClient {
@Test
public void testCorrectIncompleteResponseDoesNotCorrectResponsesWithoutExplicitContentLength()
throws Exception {
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_OK, "OK");
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setEntity(new ByteArrayEntity(bytes));
@ -996,7 +963,7 @@ public class TestCachingHttpClient {
@Test
public void testCorrectIncompleteResponseDoesNotCorrectResponsesWithUnparseableContentLengthHeader()
throws Exception {
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_OK, "OK");
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setHeader("Content-Length","foo");
resp.setEntity(new ByteArrayEntity(bytes));
@ -1008,7 +975,7 @@ public class TestCachingHttpClient {
@Test
public void testCorrectIncompleteResponseProvidesPlainTextErrorMessage()
throws Exception {
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_OK, "OK");
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","256");
@ -1021,7 +988,7 @@ public class TestCachingHttpClient {
@Test
public void testCorrectIncompleteResponseProvidesNonEmptyErrorMessage()
throws Exception {
HttpResponse resp = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_OK, "OK");
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
byte[] bytes = HttpTestUtils.getRandomBytes(128);
resp.setEntity(new ByteArrayEntity(bytes));
resp.setHeader("Content-Length","256");
@ -1048,18 +1015,6 @@ public class TestCachingHttpClient {
Assert.assertTrue(impl.isSharedCache());
}
private byte[] readResponse(HttpResponse response) {
try {
ByteArrayOutputStream s1 = new ByteArrayOutputStream();
response.getEntity().writeTo(s1);
return s1.toByteArray();
} catch (Exception ex) {
return new byte[]{};
}
}
private void cacheInvalidatorWasCalled() throws IOException {
mockInvalidator.flushInvalidatedCacheEntries(host, mockRequest);
}
@ -1182,9 +1137,11 @@ public class TestCachingHttpClient {
}
private void responseStatusLineIsInspectable() {
StatusLine statusLine = new BasicStatusLine(HTTP_1_1, HttpStatus.SC_OK, "OK");
EasyMock.expect(mockBackendResponse.getStatusLine())
.andReturn(statusLine).anyTimes();
EasyMock.expect(mockBackendResponse.getStatusLine()).andReturn(SC_OK).anyTimes();
}
private void responseGetHeaders() {
EasyMock.expect(mockBackendResponse.getAllHeaders()).andReturn(HEADERS).anyTimes();
}
private void responseIsGeneratedFromCache(CacheEntry entry) {
@ -1214,15 +1171,15 @@ public class TestCachingHttpClient {
mockCache.putEntry(theURI, entry);
}
private void generateCacheEntry(Date requestDate, Date responseDate, byte[] bytes) {
private void generateCacheEntry(Date requestDate, Date responseDate, byte[] bytes) throws IOException {
EasyMock.expect(
mockEntryGenerator.generateEntry(requestDate, responseDate, mockBackendResponse,
mockEntryGenerator.generate(requestDate, responseDate, SC_OK, HEADERS,
bytes)).andReturn(mockCacheEntry);
}
private void copyCacheEntry(CacheEntry entry, String variantURI) throws IOException {
EasyMock.expect(
mockEntryGenerator.copyWithVariant(entry, variantURI)).andReturn(entry);
mockEntryGenerator.copyVariant(entry, variantURI)).andReturn(entry);
}
private void handleBackendResponseReturnsResponse(HttpRequest request, HttpResponse response)

View File

@ -100,7 +100,7 @@ public class TestProtocolDeviations {
CacheConfig params = new CacheConfig();
params.setMaxObjectSizeBytes(MAX_BYTES);
impl = new CachingHttpClient(mockBackend, cache, params);
impl = new CachingHttpClient(mockBackend, cache, new CacheEntryGenerator(), params);
}
private HttpResponse make200Response() {

View File

@ -2221,7 +2221,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
mockCache.putEntry(EasyMock.eq("http://foo.example.com/thing"), EasyMock.isA(HttpCacheEntry.class));
impl = new CachingHttpClient(mockBackend, mockCache, params);
impl = new CachingHttpClient(mockBackend, mockCache, cacheEntryFactory, params);
HttpRequest validate = new BasicHttpRequest("GET", "/thing", HttpVersion.HTTP_1_1);
validate.setHeader("If-None-Match", "\"etag\"");
@ -2263,7 +2263,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
CacheEntry entry = new CacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
impl = new CachingHttpClient(mockBackend, mockCache, params);
impl = new CachingHttpClient(mockBackend, mockCache, cacheEntryFactory, params);
EasyMock.expect(mockCache.getEntry("http://foo.example.com/thing")).andReturn(entry);
@ -2304,7 +2304,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
CacheEntry entry = new CacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
impl = new CachingHttpClient(mockBackend, mockCache, params);
impl = new CachingHttpClient(mockBackend, mockCache, cacheEntryFactory, params);
EasyMock.expect(mockCache.getEntry("http://foo.example.com/thing")).andReturn(entry);
EasyMock.expect(
@ -2505,7 +2505,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
CacheEntry entry = new CacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
impl = new CachingHttpClient(mockBackend, mockCache, params);
impl = new CachingHttpClient(mockBackend, mockCache, cacheEntryFactory, params);
EasyMock.expect(mockCache.getEntry("http://foo.example.com/thing")).andReturn(entry);
@ -2549,7 +2549,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
CacheEntry entry = new CacheEntry(requestTime, responseTime, hdrs, bytes);
impl = new CachingHttpClient(mockBackend, mockCache, params);
impl = new CachingHttpClient(mockBackend, mockCache, cacheEntryFactory, params);
HttpResponse validated = make200Response();
validated.setHeader("Cache-Control", "public");