Index: pom.xml =================================================================== --- pom.xml (revision 987312) +++ pom.xml (working copy) @@ -47,7 +47,17 @@ repo - + + + spy + Spy Repository + default + http://bleu.west.spy.net/~dustin/m2repo/ + + false + + + org.apache.httpcomponents @@ -62,6 +72,11 @@ compile + spy + memcached + 2.5 + + junit junit ${junit.version} @@ -93,6 +108,11 @@ compile true + + org.codehaus.jettison + jettison + 1.2 + Index: src/main/java/org/apache/http/client/cache/HttpCache.java =================================================================== --- src/main/java/org/apache/http/client/cache/HttpCache.java (revision 987312) +++ src/main/java/org/apache/http/client/cache/HttpCache.java (working copy) @@ -39,22 +39,22 @@ public interface HttpCache { void flushCacheEntriesFor(HttpHost host, HttpRequest request) - throws IOException; + throws IOException, HttpCacheOperationException; void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request) - throws IOException; + throws IOException, HttpCacheOperationException; HttpCacheEntry getCacheEntry(HttpHost host, HttpRequest request) - throws IOException; + throws IOException, HttpCacheOperationException; HttpResponse cacheAndReturnResponse( HttpHost host, HttpRequest request, HttpResponse originResponse, Date requestSent, Date responseReceived) - throws IOException; + throws IOException, HttpCacheOperationException; HttpResponse updateCacheEntry( HttpHost target, HttpRequest request, HttpCacheEntry stale, HttpResponse originResponse, Date requestSent, Date responseReceived) - throws IOException; + throws IOException, HttpCacheOperationException; } Index: src/main/java/org/apache/http/client/cache/HttpCacheEntrySerializer.java =================================================================== --- src/main/java/org/apache/http/client/cache/HttpCacheEntrySerializer.java (revision 0) +++ src/main/java/org/apache/http/client/cache/HttpCacheEntrySerializer.java (revision 0) @@ -0,0 +1,11 @@ +package org.apache.http.client.cache; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface HttpCacheEntrySerializer { + public void writeTo(HttpCacheEntry entry, OutputStream os) throws IOException; + + public HttpCacheEntry readFrom(InputStream is) throws IOException; +} Index: src/main/java/org/apache/http/client/cache/HttpCacheOperationException.java =================================================================== --- src/main/java/org/apache/http/client/cache/HttpCacheOperationException.java (revision 0) +++ src/main/java/org/apache/http/client/cache/HttpCacheOperationException.java (revision 0) @@ -0,0 +1,30 @@ +package org.apache.http.client.cache; + + +/** + * Exception to be thrown when an {@link HttpCacheStorage} encounters an error performing + * an caching operation. + * + * @since 4.1 + */ +public class HttpCacheOperationException extends Exception { + + private static final long serialVersionUID = 823573584868632876L; + + public HttpCacheOperationException() { + super(); + } + + public HttpCacheOperationException(String message) { + super(message); + } + + public HttpCacheOperationException(String message, Throwable cause) { + super(message, cause); + } + + public HttpCacheOperationException(Throwable cause) { + super(cause); + } + +} \ No newline at end of file Index: src/main/java/org/apache/http/client/cache/HttpCacheStorage.java =================================================================== --- src/main/java/org/apache/http/client/cache/HttpCacheStorage.java (revision 987312) +++ src/main/java/org/apache/http/client/cache/HttpCacheStorage.java (working copy) @@ -33,13 +33,13 @@ */ public interface HttpCacheStorage { - void putEntry(String key, HttpCacheEntry entry) throws IOException; + void putEntry(String key, HttpCacheEntry entry) throws IOException, HttpCacheOperationException; - HttpCacheEntry getEntry(String key) throws IOException; + HttpCacheEntry getEntry(String key) throws IOException, HttpCacheOperationException; - void removeEntry(String key) throws IOException; + void removeEntry(String key) throws IOException, HttpCacheOperationException; void updateEntry( - String key, HttpCacheUpdateCallback callback) throws IOException; + String key, HttpCacheUpdateCallback callback) throws IOException, HttpCacheOperationException; } Index: src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java (revision 987312) +++ src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java (working copy) @@ -13,6 +13,7 @@ import org.apache.http.HttpVersion; import org.apache.http.client.cache.HttpCache; import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.HttpCacheOperationException; import org.apache.http.client.cache.HttpCacheStorage; import org.apache.http.client.cache.HttpCacheUpdateCallback; import org.apache.http.client.cache.Resource; @@ -49,13 +50,13 @@ } public void flushCacheEntriesFor(HttpHost host, HttpRequest request) - throws IOException { + throws IOException, HttpCacheOperationException { String uri = uriExtractor.getURI(host, request); storage.removeEntry(uri); } void storeInCache( - HttpHost target, HttpRequest request, HttpCacheEntry entry) throws IOException { + HttpHost target, HttpRequest request, HttpCacheEntry entry) throws IOException, HttpCacheOperationException { if (entry.hasVariants()) { storeVariantEntry(target, request, entry); } else { @@ -64,7 +65,7 @@ } void storeNonVariantEntry( - HttpHost target, HttpRequest req, HttpCacheEntry entry) throws IOException { + HttpHost target, HttpRequest req, HttpCacheEntry entry) throws IOException, HttpCacheOperationException { String uri = uriExtractor.getURI(target, req); storage.putEntry(uri, entry); } @@ -72,7 +73,7 @@ void storeVariantEntry( final HttpHost target, final HttpRequest req, - final HttpCacheEntry entry) throws IOException { + final HttpCacheEntry entry) throws IOException, HttpCacheOperationException { final String parentURI = uriExtractor.getURI(target, req); final String variantURI = uriExtractor.getVariantURI(target, req, entry); storage.putEntry(variantURI, entry); @@ -144,7 +145,7 @@ public HttpResponse updateCacheEntry(HttpHost target, HttpRequest request, HttpCacheEntry stale, HttpResponse originResponse, - Date requestSent, Date responseReceived) throws IOException { + Date requestSent, Date responseReceived) throws IOException, HttpCacheOperationException { HttpCacheEntry updatedEntry = cacheEntryUpdater.updateCacheEntry( request.getRequestLine().getUri(), stale, @@ -157,7 +158,7 @@ public HttpResponse cacheAndReturnResponse(HttpHost host, HttpRequest request, HttpResponse originResponse, Date requestSent, Date responseReceived) - throws IOException { + throws IOException, HttpCacheOperationException { SizeLimitedResponseReader responseReader = getResponseReader(request, originResponse); responseReader.readResponse(); @@ -187,7 +188,7 @@ resourceFactory, maxObjectSizeBytes, request, backEndResponse); } - public HttpCacheEntry getCacheEntry(HttpHost host, HttpRequest request) throws IOException { + public HttpCacheEntry getCacheEntry(HttpHost host, HttpRequest request) throws IOException, HttpCacheOperationException { HttpCacheEntry root = storage.getEntry(uriExtractor.getURI(host, request)); if (root == null) return null; if (!root.hasVariants()) return root; @@ -196,7 +197,7 @@ } public void flushInvalidatedCacheEntriesFor(HttpHost host, - HttpRequest request) throws IOException { + HttpRequest request) throws IOException, HttpCacheOperationException { cacheInvalidator.flushInvalidatedCacheEntries(host, request); } Index: src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java (revision 987312) +++ src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java (working copy) @@ -40,6 +40,7 @@ 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.HttpCacheOperationException; import org.apache.http.client.cache.HttpCacheStorage; /** @@ -76,8 +77,9 @@ * * @param host The backend host we are talking to * @param req The HttpRequest to that host + * @throws HttpCacheOperationException */ - public void flushInvalidatedCacheEntries(HttpHost host, HttpRequest req) throws IOException { + public void flushInvalidatedCacheEntries(HttpHost host, HttpRequest req) throws IOException, HttpCacheOperationException { if (requestShouldNotBeCached(req)) { log.debug("Request should not be cached"); @@ -114,13 +116,13 @@ } } - protected void flushUriIfSameHost(URL requestURL, URL targetURL) throws IOException { + protected void flushUriIfSameHost(URL requestURL, URL targetURL) throws IOException, HttpCacheOperationException { if (targetURL.getAuthority().equalsIgnoreCase(requestURL.getAuthority())) { storage.removeEntry(targetURL.toString()); } } - protected void flushRelativeUriFromSameHost(URL reqURL, String relUri) throws IOException { + protected void flushRelativeUriFromSameHost(URL reqURL, String relUri) throws IOException, HttpCacheOperationException { URL relURL; try { relURL = new URL(reqURL,relUri); @@ -131,7 +133,7 @@ flushUriIfSameHost(reqURL, relURL); } - protected boolean flushAbsoluteUriFromSameHost(URL reqURL, String uri) throws IOException { + protected boolean flushAbsoluteUriFromSameHost(URL reqURL, String uri) throws IOException, HttpCacheOperationException { URL absURL; try { absURL = new URL(uri); Index: src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java (revision 987312) +++ src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java (working copy) @@ -48,6 +48,7 @@ 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.HttpCacheOperationException; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.impl.client.DefaultHttpClient; @@ -368,13 +369,29 @@ throw new ClientProtocolException(e); } - responseCache.flushInvalidatedCacheEntriesFor(target, request); + try { + responseCache.flushInvalidatedCacheEntriesFor(target, request); + } catch (HttpCacheOperationException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } if (!cacheableRequestPolicy.isServableFromCache(request)) { - return callBackend(target, request, context); + try { + return callBackend(target, request, context); + } catch (HttpCacheOperationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } - HttpCacheEntry entry = responseCache.getCacheEntry(target, request); + HttpCacheEntry entry = null; + try { + entry = responseCache.getCacheEntry(target, request); + } catch (HttpCacheOperationException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } if (entry == null) { cacheMisses.getAndIncrement(); if (log.isDebugEnabled()) { @@ -382,7 +399,12 @@ log.debug("Cache miss [host: " + target + "; uri: " + rl.getUri() + "]"); } - return callBackend(target, request, context); + try { + return callBackend(target, request, context); + } catch (HttpCacheOperationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } if (log.isDebugEnabled()) { @@ -413,9 +435,18 @@ } } catch (ProtocolException e) { throw new ClientProtocolException(e); - } + } catch (HttpCacheOperationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } - return callBackend(target, request, context); + try { + return callBackend(target, request, context); + } catch (HttpCacheOperationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; } public boolean supportsRangeAndContentRangeHeaders() { @@ -446,7 +477,7 @@ } HttpResponse callBackend(HttpHost target, HttpRequest request, HttpContext context) - throws IOException { + throws IOException, HttpCacheOperationException { Date requestDate = getCurrentDate(); @@ -461,7 +492,7 @@ HttpHost target, HttpRequest request, HttpContext context, - HttpCacheEntry cacheEntry) throws IOException, ProtocolException { + HttpCacheEntry cacheEntry) throws IOException, ProtocolException, HttpCacheOperationException { HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequest(request, cacheEntry); Date requestDate = getCurrentDate(); @@ -485,7 +516,7 @@ HttpRequest request, Date requestDate, Date responseDate, - HttpResponse backendResponse) throws IOException { + HttpResponse backendResponse) throws IOException, HttpCacheOperationException { log.debug("Handling Backend response"); responseCompliance.ensureProtocolCompliance(request, backendResponse); Index: src/main/java/org/apache/http/impl/client/cache/DefaultHttpCacheEntrySerializer.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/DefaultHttpCacheEntrySerializer.java (revision 0) +++ src/main/java/org/apache/http/impl/client/cache/DefaultHttpCacheEntrySerializer.java (revision 0) @@ -0,0 +1,119 @@ +package org.apache.http.impl.client.cache; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.util.Date; +import java.util.Set; + +import org.apache.http.Header; +import org.apache.http.NameValuePair; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; +import org.apache.http.annotation.Immutable; +import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.HttpCacheEntrySerializer; +import org.apache.http.client.cache.Resource; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.message.BasicStatusLine; + +/** + * {@link HttpCacheEntrySerializer} implementation that uses the default (native) + * serialization. + * + * @see java.io.Serializable + * + * @since 4.1 + */ +@Immutable +public class DefaultHttpCacheEntrySerializer implements HttpCacheEntrySerializer { + + /** + * + * @param cacheEntry + * @param os + * @throws IOException + */ + public void writeTo(HttpCacheEntry cacheEntry, OutputStream os) throws IOException { + + ObjectOutputStream oos = null; + try { + oos = new ObjectOutputStream(os); + + oos.writeObject(cacheEntry.getRequestDate()); + oos.writeObject(cacheEntry.getResponseDate()); + + // workaround to nonserializable BasicStatusLine object + // TODO: can change to directly serialize once new httpcore is released + oos.writeObject(cacheEntry.getStatusLine().getProtocolVersion()); + oos.writeObject(cacheEntry.getStatusLine().getStatusCode()); + oos.writeObject(cacheEntry.getStatusLine().getReasonPhrase()); + + // workaround to nonserializable BasicHeader object + // TODO: can change to directly serialize once new httpcore is released + Header[] headers = cacheEntry.getAllHeaders(); + NameValuePair[] headerNvps = new NameValuePair[headers.length]; + for(int i=0; i variants = (Set)ois.readObject(); + + return new HttpCacheEntry(requestDate, responseDate, statusLine, headers, resource, variants); + } catch (ClassNotFoundException cnfe) { + // CacheEntry should be known, it not we have a runtime issue + throw new RuntimeException(cnfe); + } finally { + if (ois != null) { + ois.close(); + } + is.close(); + } + + } + +} Index: src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java (revision 0) +++ src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java (revision 0) @@ -0,0 +1,84 @@ +package org.apache.http.impl.client.cache.memcached; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; + +import net.spy.memcached.CASResponse; +import net.spy.memcached.CASValue; +import net.spy.memcached.MemcachedClient; +import net.spy.memcached.MemcachedClientIF; + +import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.HttpCacheEntrySerializer; +import org.apache.http.client.cache.HttpCacheOperationException; +import org.apache.http.client.cache.HttpCacheStorage; +import org.apache.http.client.cache.HttpCacheUpdateCallback; +import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer; + +public class MemcachedHttpCacheStorage implements HttpCacheStorage { + + private MemcachedClientIF client; + private HttpCacheEntrySerializer serializer; + + public MemcachedHttpCacheStorage(InetSocketAddress address) throws IOException { + this(new MemcachedClient(address)); + } + + public MemcachedHttpCacheStorage(MemcachedClientIF client) { + this.client = client; + this.serializer = new DefaultHttpCacheEntrySerializer(); + } + + public MemcachedHttpCacheStorage(MemcachedClientIF client, + HttpCacheEntrySerializer serializer) { + this.client = client; + this.serializer = serializer; + } + + public void putEntry(String url, HttpCacheEntry entry) throws HttpCacheOperationException, IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + serializer.writeTo(entry, bos); + client.set(url, 0, bos.toByteArray()); + } + + public HttpCacheEntry getEntry(String url) throws HttpCacheOperationException,IOException { + byte[] data = (byte[]) client.get(url); + if (null == data) + return null; + InputStream bis = new ByteArrayInputStream(data); + return (HttpCacheEntry) serializer.readFrom(bis); + } + + public void removeEntry(String url) throws HttpCacheOperationException,IOException { + client.delete(url); + } + + public void updateEntry(String key, HttpCacheUpdateCallback callback) + throws HttpCacheOperationException, IOException { + CASValue v = client.gets(key); + byte[] oldBytes = (v != null) ? (byte[]) v.getValue() : null; + HttpCacheEntry existingEntry = null; + if (oldBytes != null) { + ByteArrayInputStream bis = new ByteArrayInputStream(oldBytes); + existingEntry = serializer.readFrom(bis); + } + HttpCacheEntry updatedEntry = callback.update(existingEntry); + + if (v == null) { + putEntry(key, updatedEntry); + } else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + serializer.writeTo(updatedEntry, bos); + CASResponse casResult = client.cas(key, v.getCas(), bos + .toByteArray()); + if (casResult != CASResponse.OK) { + throw new HttpCacheOperationException(); + } + } + + } + +} \ No newline at end of file Index: src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java =================================================================== --- src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java (revision 987312) +++ src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java (working copy) @@ -34,6 +34,7 @@ import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.ProtocolVersion; +import org.apache.http.client.cache.HttpCacheOperationException; import org.apache.http.client.cache.HttpCacheStorage; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.apache.http.message.BasicHttpRequest; @@ -282,16 +283,16 @@ org.easymock.EasyMock.expect(mockEntry.getVariantURIs()).andReturn(variantURIs); } - private void cacheReturnsEntryForUri(String theUri) throws IOException { + private void cacheReturnsEntryForUri(String theUri) throws IOException, HttpCacheOperationException { org.easymock.EasyMock.expect(mockStorage.getEntry(theUri)).andReturn(mockEntry); } - private void cacheReturnsExceptionForUri(String theUri) throws IOException { + private void cacheReturnsExceptionForUri(String theUri) throws IOException, HttpCacheOperationException { org.easymock.EasyMock.expect(mockStorage.getEntry(theUri)).andThrow( new IOException("TOTAL FAIL")); } - private void entryIsRemoved(String theUri) throws IOException { + private void entryIsRemoved(String theUri) throws IOException, HttpCacheOperationException { mockStorage.removeEntry(theUri); } Index: src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java =================================================================== --- src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java (revision 987312) +++ src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java (working copy) @@ -44,6 +44,7 @@ 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.cache.HttpCacheOperationException; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.message.BasicHttpRequest; @@ -731,17 +732,17 @@ Assert.assertTrue(impl.isSharedCache()); } - private void getCacheEntryReturns(HttpCacheEntry result) throws IOException { + private void getCacheEntryReturns(HttpCacheEntry result) throws IOException, HttpCacheOperationException { EasyMock.expect(mockCache.getCacheEntry(host, request)).andReturn(result); } - private void cacheInvalidatorWasCalled() throws IOException { + private void cacheInvalidatorWasCalled() throws IOException, HttpCacheOperationException { mockCache.flushInvalidatedCacheEntriesFor( EasyMock.anyObject(), EasyMock.anyObject()); } - private void callBackendReturnsResponse(HttpResponse response) throws IOException { + private void callBackendReturnsResponse(HttpResponse response) throws IOException, HttpCacheOperationException { EasyMock.expect(impl.callBackend( EasyMock.anyObject(), EasyMock.anyObject(), @@ -749,7 +750,7 @@ } private void revalidateCacheEntryReturns(HttpResponse response) throws IOException, - ProtocolException { + ProtocolException, HttpCacheOperationException { EasyMock.expect( impl.revalidateCacheEntry( EasyMock.anyObject(), @@ -811,12 +812,12 @@ EasyMock.anyObject())).andReturn(mockCachedResponse); } - private void flushCache() throws IOException { + private void flushCache() throws IOException, HttpCacheOperationException { mockCache.flushCacheEntriesFor(host, request); } private void handleBackendResponseReturnsResponse(HttpRequest request, HttpResponse response) - throws IOException { + throws IOException, HttpCacheOperationException { EasyMock.expect( impl.handleBackendResponse( EasyMock.anyObject(), Index: src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java =================================================================== --- src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java (revision 0) +++ src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java (revision 0) @@ -0,0 +1,116 @@ +package org.apache.http.impl.client.cache; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestCase; + +import org.apache.commons.codec.binary.Base64; +import org.apache.http.Header; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; +import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.HttpCacheEntrySerializer; +import org.apache.http.client.cache.Resource; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicStatusLine; + +public class TestHttpCacheEntrySerializers extends TestCase { + + private static final Charset UTF8 = Charset.forName("UTF-8"); + + public void testDefaultSerializer() throws Exception { + readWriteVerify(new DefaultHttpCacheEntrySerializer()); + } + + public void readWriteVerify(HttpCacheEntrySerializer serializer) throws IOException { + // write the entry + HttpCacheEntry writeEntry = newCacheEntry(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializer.writeTo(writeEntry, out); + + // read the entry + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + HttpCacheEntry readEntry = serializer.readFrom(in); + + // compare + assertTrue(areEqual(readEntry, writeEntry)); + } + + private HttpCacheEntry newCacheEntry() throws UnsupportedEncodingException { + Header[] headers = new Header[5]; + for (int i = 0; i < headers.length; i++) { + headers[i] = new BasicHeader("header" + i, "value" + i); + } + String body = "Lorem ipsum dolor sit amet"; + + ProtocolVersion pvObj = new ProtocolVersion("HTTP", 1, 1); + StatusLine slObj = new BasicStatusLine(pvObj, 200, "ok"); + Set variants = new HashSet(); + variants.add("test variant 1"); + variants.add("test variant 2"); + + HttpCacheEntry cacheEntry = new HttpCacheEntry(new Date(), new Date(), + slObj, headers, new HeapResource(Base64.decodeBase64(body + .getBytes(UTF8.name()))), variants); + + return cacheEntry; + } + + private boolean areEqual(HttpCacheEntry one, HttpCacheEntry two) throws IOException { + // dates are only stored with second precision, so scrub milliseconds + if (!((one.getRequestDate().getTime() / 1000) == (two.getRequestDate() + .getTime() / 1000))) + return false; + if (!((one.getResponseDate().getTime() / 1000) == (two + .getResponseDate().getTime() / 1000))) + return false; + if (!one.getProtocolVersion().equals(two.getProtocolVersion())) + return false; + + byte[] onesByteArray = resourceToBytes(one.getResource()); + byte[] twosByteArray = resourceToBytes(two.getResource()); + + if (!Arrays.equals(onesByteArray,twosByteArray)) + return false; + + Header[] oneHeaders = one.getAllHeaders(); + Header[] twoHeaders = two.getAllHeaders(); + if (!(oneHeaders.length == twoHeaders.length)) + return false; + for (int i = 0; i < oneHeaders.length; i++) { + if (!oneHeaders[i].getName().equals(twoHeaders[i].getName())) + return false; + if (!oneHeaders[i].getValue().equals(twoHeaders[i].getValue())) + return false; + } + + return true; + } + + private byte[] resourceToBytes(Resource res) throws IOException { + InputStream inputStream = res.getInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + int readBytes; + byte[] bytes = new byte[8096]; + while ((readBytes = inputStream.read(bytes)) > 0) { + outputStream.write(bytes, 0, readBytes); + } + + byte[] byteData = outputStream.toByteArray(); + + inputStream.close(); + outputStream.close(); + + return byteData; + } +} Index: src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCache.java =================================================================== --- src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCache.java (revision 0) +++ src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCache.java (revision 0) @@ -0,0 +1,78 @@ +package org.apache.http.impl.client.cache.memcached; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import junit.framework.TestCase; +import net.spy.memcached.MemcachedClientIF; + +import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.HttpCacheEntrySerializer; +import org.apache.http.client.cache.HttpCacheOperationException; +import org.apache.http.impl.client.cache.CacheEntry; +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; + +public class TestMemcachedHttpCache extends TestCase { + private MemcachedHttpCacheStorage impl; + private MemcachedClientIF mockMemcachedClient; + private HttpCacheEntrySerializer mockSerializer; + + @Before + public void setUp() throws Exception { + mockMemcachedClient = EasyMock.createMock(MemcachedClientIF.class); + mockSerializer = EasyMock.createMock(HttpCacheEntrySerializer.class); + impl = new MemcachedHttpCacheStorage(mockMemcachedClient, mockSerializer); + } + + @Test + public void testCachePut() throws IOException, HttpCacheOperationException { + final String url = "foo"; + final HttpCacheEntry value = new CacheEntry(); + mockSerializer.writeTo(EasyMock.isA(HttpCacheEntry.class), EasyMock + .isA(OutputStream.class)); + EasyMock.expect( + mockMemcachedClient.set(EasyMock.eq(url), EasyMock.eq(0), + EasyMock.aryEq(new byte[0]))).andReturn(null); + replayMocks(); + impl.putEntry(url, value); + verifyMocks(); + } + + @Test + public void testCacheGet() throws UnsupportedEncodingException, IOException, HttpCacheOperationException { + final String url = "foo"; + final HttpCacheEntry cacheEntry = new CacheEntry(); + EasyMock.expect(mockMemcachedClient.get(url)).andReturn(new byte[] {}); + EasyMock.expect( + mockSerializer.readFrom(EasyMock.isA(InputStream.class))) + .andReturn(cacheEntry); + replayMocks(); + HttpCacheEntry resultingEntry = impl.getEntry(url); + verifyMocks(); + assertSame(cacheEntry, resultingEntry); + } + + @Test + public void testCacheRemove() throws IOException, HttpCacheOperationException { + final String url = "foo"; + EasyMock.expect(mockMemcachedClient.delete(url)).andReturn(null); + replayMocks(); + impl.removeEntry(url); + verifyMocks(); + } + + private void replayMocks() { + EasyMock.replay(mockMemcachedClient); + EasyMock.replay(mockSerializer); + } + + private void verifyMocks() { + EasyMock.verify(mockMemcachedClient); + EasyMock.verify(mockSerializer); + } + +}