Made possible for cache entries to depend on system resources that may require deallocation

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@980767 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-07-30 12:44:44 +00:00
parent b8c1bb05cc
commit 4ae188acbb
13 changed files with 232 additions and 61 deletions

View File

@ -44,12 +44,15 @@ import org.apache.http.annotation.Immutable;
import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeader;
/** /**
* Structure used to store an {@link HttpResponse} in a cache * Structure used to store an {@link HttpResponse} in a cache. Some entries can optionally depend
* on system resources that may require explicit deallocation. In such a case {@link #getResource()}
* should return a non-null instance of {@link Resource} that must be deallocated by calling
* {@link Resource#dispose()} method when no longer used.
* *
* @since 4.1 * @since 4.1
*/ */
@Immutable @Immutable
public class HttpCacheEntry implements Serializable { public abstract class HttpCacheEntry implements Serializable {
private static final long serialVersionUID = -6300496422359477413L; private static final long serialVersionUID = -6300496422359477413L;
@ -198,21 +201,12 @@ public class HttpCacheEntry implements Serializable {
this.responseHeaders.setHeaders(headers); this.responseHeaders.setHeaders(headers);
} }
public abstract Resource getResource();
@Override @Override
public String toString() { public String toString() {
return "[request date=" + this.requestDate + "; response date=" + this.responseDate return "[request date=" + this.requestDate + "; response date=" + this.responseDate
+ "; statusLine=" + this.statusLine + "]"; + "; statusLine=" + this.statusLine + "]";
} }
public static HttpCacheEntry copyWithVariant(final HttpCacheEntry entry, final String variantURI){
Set<String> variants = new HashSet<String>(entry.getVariantURIs());
variants.add(variantURI);
return new HttpCacheEntry(
entry.getRequestDate(),
entry.getResponseDate(),
entry.getStatusLine(),
entry.getAllHeaders(),
entry.getBody(), variants);
}
} }

View File

@ -0,0 +1,38 @@
/*
* ====================================================================
* 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;
/**
* Represents a disposable system resource.
*
* @since 4.1
*/
public interface Resource {
void dispose();
}

View File

@ -28,7 +28,6 @@ package org.apache.http.impl.client.cache;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.http.annotation.ThreadSafe; import org.apache.http.annotation.ThreadSafe;
import org.apache.http.client.cache.HttpCache; import org.apache.http.client.cache.HttpCache;
@ -36,29 +35,19 @@ import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheUpdateCallback; import org.apache.http.client.cache.HttpCacheUpdateCallback;
/** /**
* Implements {@link HttpCache} using LinkedHashMap for backing store * Basic {@link HttpCache} implementation backed by an instance of {@link LinkedHashMap}.
* This cache does NOT deallocate resources associated with the cache entries. It is intended
* for use with {@link BasicHttpCacheEntry} and similar.
* *
* @since 4.1 * @since 4.1
*/ */
@ThreadSafe @ThreadSafe
public class BasicHttpCache implements HttpCache { public class BasicHttpCache implements HttpCache {
private final LinkedHashMap<String, HttpCacheEntry> baseMap = new LinkedHashMap<String, HttpCacheEntry>( private final CacheMap entries;
20, 0.75f, true) {
private static final long serialVersionUID = -7750025207539768511L;
@Override
protected boolean removeEldestEntry(Map.Entry<String, HttpCacheEntry> eldest) {
return size() > maxEntries;
}
};
private final int maxEntries;
public BasicHttpCache(int maxEntries) { public BasicHttpCache(int maxEntries) {
this.maxEntries = maxEntries; this.entries = new CacheMap(maxEntries);
} }
/** /**
@ -70,7 +59,7 @@ public class BasicHttpCache implements HttpCache {
* HttpCacheEntry to place in the cache * HttpCacheEntry to place in the cache
*/ */
public synchronized void putEntry(String url, HttpCacheEntry entry) throws IOException { public synchronized void putEntry(String url, HttpCacheEntry entry) throws IOException {
baseMap.put(url, entry); entries.put(url, entry);
} }
/** /**
@ -81,7 +70,7 @@ public class BasicHttpCache implements HttpCache {
* @return HttpCacheEntry if one exists, or null for cache miss * @return HttpCacheEntry if one exists, or null for cache miss
*/ */
public synchronized HttpCacheEntry getEntry(String url) throws IOException { public synchronized HttpCacheEntry getEntry(String url) throws IOException {
return baseMap.get(url); return entries.get(url);
} }
/** /**
@ -91,14 +80,14 @@ public class BasicHttpCache implements HttpCache {
* Url that is the cache key * Url that is the cache key
*/ */
public synchronized void removeEntry(String url) throws IOException { public synchronized void removeEntry(String url) throws IOException {
baseMap.remove(url); entries.remove(url);
} }
public synchronized void updateEntry( public synchronized void updateEntry(
String url, String url,
HttpCacheUpdateCallback callback) throws IOException { HttpCacheUpdateCallback callback) throws IOException {
HttpCacheEntry existingEntry = baseMap.get(url); HttpCacheEntry existingEntry = entries.get(url);
baseMap.put(url, callback.update(existingEntry)); entries.put(url, callback.update(existingEntry));
} }
} }

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.util.Date;
import java.util.Set;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
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;
/**
* Basic {@link HttpCacheEntry} that does not depend on any system resources that may require
* explicit deallocation.
*/
@Immutable
public class BasicHttpCacheEntry extends HttpCacheEntry {
private static final long serialVersionUID = -8464486112875881235L;
public BasicHttpCacheEntry(
final Date requestDate,
final Date responseDate,
final StatusLine statusLine,
final Header[] responseHeaders,
final HttpEntity body,
final Set<String> variants) {
super(requestDate, responseDate, statusLine, responseHeaders, body, variants);
}
@Override
public Resource getResource() {
return null;
}
}

View File

@ -1,3 +1,29 @@
/*
* ====================================================================
* 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; package org.apache.http.impl.client.cache;
/** /**

View File

@ -27,6 +27,8 @@
package org.apache.http.impl.client.cache; package org.apache.http.impl.client.cache;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
@ -53,7 +55,7 @@ class CacheEntryGenerator {
body, body,
ct != null ? ct.getValue() : null, ct != null ? ct.getValue() : null,
ce != null ? ce.getValue() : null); ce != null ? ce.getValue() : null);
return new HttpCacheEntry(requestDate, return new BasicHttpCacheEntry(requestDate,
responseDate, responseDate,
response.getStatusLine(), response.getStatusLine(),
response.getAllHeaders(), response.getAllHeaders(),
@ -61,4 +63,15 @@ class CacheEntryGenerator {
null); null);
} }
public HttpCacheEntry copyWithVariant(final HttpCacheEntry entry, final String variantURI){
Set<String> variants = new HashSet<String>(entry.getVariantURIs());
variants.add(variantURI);
return new BasicHttpCacheEntry(
entry.getRequestDate(),
entry.getResponseDate(),
entry.getStatusLine(),
entry.getAllHeaders(),
entry.getBody(), variants);
}
} }

View File

@ -69,7 +69,7 @@ class CacheEntryUpdater {
HttpResponse response) throws IOException { HttpResponse response) throws IOException {
Header[] mergedHeaders = mergeHeaders(entry, response); Header[] mergedHeaders = mergeHeaders(entry, response);
HttpCacheEntry updated = new HttpCacheEntry(requestDate, responseDate, HttpCacheEntry updated = new BasicHttpCacheEntry(requestDate, responseDate,
entry.getStatusLine(), entry.getStatusLine(),
mergedHeaders, mergedHeaders,
entry.getBody(), entry.getBody(),

View File

@ -0,0 +1,50 @@
/*
* ====================================================================
* 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.util.LinkedHashMap;
import java.util.Map;
import org.apache.http.client.cache.HttpCacheEntry;
final class CacheMap extends LinkedHashMap<String, HttpCacheEntry> {
private static final long serialVersionUID = -7750025207539768511L;
private final int maxEntries;
CacheMap(int maxEntries) {
super(20, 0.75f, true);
this.maxEntries = maxEntries;
}
@Override
protected boolean removeEldestEntry(final Map.Entry<String, HttpCacheEntry> eldest) {
return size() > this.maxEntries;
}
}

View File

@ -532,9 +532,9 @@ public class CachingHttpClient implements HttpClient {
HttpCacheEntry existing, HttpCacheEntry existing,
HttpCacheEntry entry, String variantURI) { HttpCacheEntry entry, String variantURI) {
if (existing != null) { if (existing != null) {
return HttpCacheEntry.copyWithVariant(existing, variantURI); return cacheEntryGenerator.copyWithVariant(existing, variantURI);
} else { } else {
return HttpCacheEntry.copyWithVariant(entry, variantURI); return cacheEntryGenerator.copyWithVariant(entry, variantURI);
} }
} }

View File

@ -31,9 +31,8 @@ import java.util.Date;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.StatusLine; import org.apache.http.StatusLine;
import org.apache.http.client.cache.HttpCacheEntry;
public class CacheEntry extends HttpCacheEntry { public class CacheEntry extends BasicHttpCacheEntry {
private static final long serialVersionUID = 7964121802841871079L; private static final long serialVersionUID = 7964121802841871079L;

View File

@ -108,8 +108,10 @@ public class TestCacheEntry {
Header[] headers = new Header[]{}; Header[] headers = new Header[]{};
CacheEntry entry = new CacheEntry(headers); CacheEntry entry = new CacheEntry(headers);
HttpCacheEntry addedOne = HttpCacheEntry.copyWithVariant(entry, "foo"); CacheEntryGenerator entryGenerator = new CacheEntryGenerator();
HttpCacheEntry addedTwo = HttpCacheEntry.copyWithVariant(addedOne, "bar");
HttpCacheEntry addedOne = entryGenerator.copyWithVariant(entry, "foo");
HttpCacheEntry addedTwo = entryGenerator.copyWithVariant(addedOne, "bar");
Set<String> variants = addedTwo.getVariantURIs(); Set<String> variants = addedTwo.getVariantURIs();

View File

@ -26,8 +26,6 @@
*/ */
package org.apache.http.impl.client.cache; package org.apache.http.impl.client.cache;
import static junit.framework.Assert.assertTrue;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -319,18 +317,12 @@ public class TestCachingHttpClient {
@Test @Test
public void testCacheUpdateAddsVariantURIToParentEntry() throws Exception { public void testCacheUpdateAddsVariantURIToParentEntry() throws Exception {
final String variantURI = "variantURI"; final String variantURI = "variantURI";
final CacheEntry entry = new CacheEntry(); final CacheEntry entry = new CacheEntry();
copyCacheEntry(entry, variantURI);
replayMocks(); replayMocks();
impl.doGetUpdatedParentEntry(null, entry, variantURI);
HttpCacheEntry updatedEntry = impl.doGetUpdatedParentEntry(null, entry, variantURI);
verifyMocks(); verifyMocks();
assertTrue(updatedEntry.getVariantURIs().contains(variantURI));
} }
@ -1228,6 +1220,11 @@ public class TestCachingHttpClient {
bytes)).andReturn(mockCacheEntry); bytes)).andReturn(mockCacheEntry);
} }
private void copyCacheEntry(CacheEntry entry, String variantURI) {
EasyMock.expect(
mockEntryGenerator.copyWithVariant(entry, variantURI)).andReturn(entry);
}
private void handleBackendResponseReturnsResponse(HttpRequest request, HttpResponse response) private void handleBackendResponseReturnsResponse(HttpRequest request, HttpResponse response)
throws IOException { throws IOException {
EasyMock.expect( EasyMock.expect(

View File

@ -143,7 +143,7 @@ public class TestResponseCache {
cache.updateEntry("foo", new HttpCacheUpdateCallback() { cache.updateEntry("foo", new HttpCacheUpdateCallback() {
public HttpCacheEntry update(HttpCacheEntry existing) { public HttpCacheEntry update(HttpCacheEntry existing) {
HttpCacheEntry updated = new HttpCacheEntry( HttpCacheEntry updated = new BasicHttpCacheEntry(
existing.getRequestDate(), existing.getRequestDate(),
existing.getRequestDate(), existing.getRequestDate(),
existing.getStatusLine(), existing.getStatusLine(),