From d47c30baaeaf7b3a76def41ef0f93f9c29fc4dde Mon Sep 17 00:00:00 2001
From: Jonathan Moore
Date: Tue, 17 Jan 2012 17:12:14 +0000
Subject: [PATCH] HTTPCLIENT-1153: Added a hashing scheme to map the
higher-level logical storage keys the CachingHttpClient wants to store cache
entries under onto a keyspace suitable for use with memcached (which has a
max key length smaller than the logical keys we use). A default hashing
scheme based on SHA-256 is also provided. Finally, since hashing now
introduces the possibility of collisions, we have to store the logical
storage key along with the cache entry itself so that it can be compared on
retrieval. Implemented a new serialization scheme to accommodate this (with
associated interfaces so this could be overridden if desired). Unfortunately,
this meant that one of the existing constructors that accepted an old-style
custom serializer had to be deprecated (default implementations of the new
serializers are used instead).
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1232489 13f79535-47bb-0310-9956-ffa450edef68
---
.../cache/memcached/KeyHashingScheme.java | 45 ++
.../cache/memcached/MemcachedCacheEntry.java | 75 ++
.../memcached/MemcachedCacheEntryFactory.java | 62 ++
.../MemcachedCacheEntryFactoryImpl.java | 45 ++
.../memcached/MemcachedCacheEntryImpl.java | 109 +++
.../memcached/MemcachedHttpCacheStorage.java | 141 +++-
.../MemcachedKeyHashingException.java | 41 ++
.../MemcachedOperationTimeoutException.java | 3 +
.../MemcachedSerializationException.java | 41 ++
.../memcached/SHA256KeyHashingScheme.java | 60 ++
.../TestMemcachedCacheEntryFactoryImpl.java | 23 +
.../TestMemcachedCacheEntryImpl.java | 91 +++
.../TestMemcachedHttpCacheStorage.java | 677 ++++++++++++------
.../memcached/TestSHA256HashingScheme.java | 16 +
14 files changed, 1169 insertions(+), 260 deletions(-)
create mode 100644 httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/KeyHashingScheme.java
create mode 100644 httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/MemcachedCacheEntry.java
create mode 100644 httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/MemcachedCacheEntryFactory.java
create mode 100644 httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactoryImpl.java
create mode 100644 httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryImpl.java
create mode 100644 httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedKeyHashingException.java
create mode 100644 httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedSerializationException.java
create mode 100644 httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/SHA256KeyHashingScheme.java
create mode 100644 httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedCacheEntryFactoryImpl.java
create mode 100644 httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedCacheEntryImpl.java
create mode 100644 httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestSHA256HashingScheme.java
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/KeyHashingScheme.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/KeyHashingScheme.java
new file mode 100644
index 000000000..de7a2a4e0
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/KeyHashingScheme.java
@@ -0,0 +1,45 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */package org.apache.http.client.cache.memcached;
+
+/**
+ * Since the {@link HttpCacheStorage} interface expects to use variant-annotated
+ * URLs for its storage keys, but Memcached has a maximum key size, we need to
+ * support mapping storage keys to cache keys. Clients can implement this
+ * interface to change the way the mapping is done (for example, to add a prefix
+ * to all cache keys to provide a form of memcached namespacing).
+ */
+public interface KeyHashingScheme {
+
+ /** Maps a storage key to a cache key. The storage key is what
+ * the higher-level HTTP cache uses as a key; the cache key is what
+ * we use as a key for talking to memcached.
+ * @param storageKey what the higher-level HTTP cache wants to use
+ * as its key for looking up cache entries
+ * @return a cache key suitable for use with memcached
+ */
+ public String hash(String storageKey);
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/MemcachedCacheEntry.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/MemcachedCacheEntry.java
new file mode 100644
index 000000000..4ba345a2b
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/MemcachedCacheEntry.java
@@ -0,0 +1,75 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+package org.apache.http.client.cache.memcached;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+
+/**
+ * Provides for serialization and deserialization of higher-level
+ * {@link HttpCacheEntry} objects into byte arrays suitable for
+ * storage in memcached. Clients wishing to change the serialization
+ * mechanism from the provided defaults should implement this
+ * interface as well as {@link MemcachedCacheEntryFactory}.
+ */
+public interface MemcachedCacheEntry {
+
+ /**
+ * Returns a serialized representation of the current cache entry.
+ */
+ byte[] toByteArray();
+
+ /**
+ * Returns the storage key associated with this entry. May return
+ * null if this is an "unset" instance waiting to be
+ * {@link #set(byte[])} with a serialized representation.
+ */
+ String getStorageKey();
+
+ /**
+ * Returns the {@link HttpCacheEntry} associated with this entry.
+ * May return null if this is an "unset" instance
+ * waiting to be {@link #set(byte[])} with a serialized
+ * representation.
+ */
+ HttpCacheEntry getHttpCacheEntry();
+
+ /**
+ * Given a serialized representation of a {@link MemcachedCacheEntry},
+ * attempt to reconstitute the storage key and {@link HttpCacheEntry}
+ * represented therein. After a successful call to this method, this
+ * object should return updated (as appropriate) values for
+ * {@link #getStorageKey()} and {@link #getHttpCacheEntry()}. This
+ * should be viewed as an atomic operation on the
+ * MemcachedCacheEntry.
+ * @param bytes serialized representation
+ * @throws {@link MemcachedSerializationException} if deserialization
+ * fails. In this case, the prior values for {{@link #getStorageKey()}
+ * and {@link #getHttpCacheEntry()} should remain unchanged.
+ */
+ void set(byte[] bytes);
+
+}
\ No newline at end of file
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/MemcachedCacheEntryFactory.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/MemcachedCacheEntryFactory.java
new file mode 100644
index 000000000..853874e04
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/memcached/MemcachedCacheEntryFactory.java
@@ -0,0 +1,62 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+package org.apache.http.client.cache.memcached;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+
+/**
+ * Creates {@link MemcachedCacheEntry} instances that can be used for
+ * serializing and deserializing {@link HttpCacheEntry} instances for
+ * storage in memcached.
+ */
+public interface MemcachedCacheEntryFactory {
+
+ /**
+ * Creates a new {@link MemcachedCacheEntry} for storing the
+ * given {@link HttpCacheEntry} under the given storage key. Since
+ * we are hashing storage keys into cache keys to accommodate
+ * limitations in memcached's key space, it is possible to have
+ * cache collisions. Therefore, we store the storage key along
+ * with the HttpCacheEntry so it can be compared
+ * on retrieval and thus detect collisions.
+ * @param storageKey storage key under which the entry will
+ * be logically stored
+ * @param entry the cache entry to store
+ * @return a {@link MemcachedCacheEntry} ready to provide
+ * a serialized representation
+ */
+ MemcachedCacheEntry getMemcachedCacheEntry(String storageKey, HttpCacheEntry entry);
+
+ /**
+ * Creates an "unset" {@link MemcachedCacheEntry} ready to accept
+ * a serialized representation via {@link MemcachedCacheEntry#set(byte[])}
+ * and deserialize it into a storage key and a {@link HttpCacheEntry}.
+ * @return MemcachedCacheEntry
+ */
+ MemcachedCacheEntry getUnsetCacheEntry();
+
+}
\ No newline at end of file
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactoryImpl.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactoryImpl.java
new file mode 100644
index 000000000..9d0306237
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactoryImpl.java
@@ -0,0 +1,45 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.cache.memcached.MemcachedCacheEntry;
+import org.apache.http.client.cache.memcached.MemcachedCacheEntryFactory;
+
+/**
+ * Default implementation of {@link MemcachedCacheEntryFactory}.
+ */
+public class MemcachedCacheEntryFactoryImpl implements MemcachedCacheEntryFactory {
+
+ public MemcachedCacheEntry getMemcachedCacheEntry(String key, HttpCacheEntry entry) {
+ return new MemcachedCacheEntryImpl(key, entry);
+ }
+
+ public MemcachedCacheEntry getUnsetCacheEntry() {
+ return new MemcachedCacheEntryImpl(null, null);
+ }
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryImpl.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryImpl.java
new file mode 100644
index 000000000..782511385
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryImpl.java
@@ -0,0 +1,109 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.cache.memcached.MemcachedCacheEntry;
+
+/**
+ * Default implementation of {@link MemcachedCacheEntry}. This implementation
+ * simply uses Java serialization to serialize the storage key followed by
+ * the {@link HttpCacheEntry} into a byte array.
+ */
+public class MemcachedCacheEntryImpl implements MemcachedCacheEntry {
+
+ private String key;
+ private HttpCacheEntry httpCacheEntry;
+
+ public MemcachedCacheEntryImpl(String key, HttpCacheEntry httpCacheEntry) {
+ this.key = key;
+ this.httpCacheEntry = httpCacheEntry;
+ }
+
+ public MemcachedCacheEntryImpl() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#toByteArray()
+ */
+ synchronized public byte[] toByteArray() {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos;
+ try {
+ oos = new ObjectOutputStream(bos);
+ oos.writeObject(this.key);
+ oos.writeObject(this.httpCacheEntry);
+ oos.close();
+ } catch (IOException ioe) {
+ throw new MemcachedSerializationException(ioe);
+ }
+ return bos.toByteArray();
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#getKey()
+ */
+ public synchronized String getStorageKey() {
+ return key;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#getHttpCacheEntry()
+ */
+ public synchronized HttpCacheEntry getHttpCacheEntry() {
+ return httpCacheEntry;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#set(byte[])
+ */
+ synchronized public void set(byte[] bytes) {
+ ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois;
+ String s;
+ HttpCacheEntry entry;
+ try {
+ ois = new ObjectInputStream(bis);
+ s = (String)ois.readObject();
+ entry = (HttpCacheEntry)ois.readObject();
+ ois.close();
+ bis.close();
+ } catch (IOException ioe) {
+ throw new MemcachedSerializationException(ioe);
+ } catch (ClassNotFoundException cnfe) {
+ throw new MemcachedSerializationException(cnfe);
+ }
+ this.key = s;
+ this.httpCacheEntry = entry;
+ }
+
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
index e85f98b8a..f9e895208 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
@@ -26,10 +26,7 @@
*/
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;
@@ -45,6 +42,9 @@ import org.apache.http.client.cache.HttpCacheEntrySerializer;
import org.apache.http.client.cache.HttpCacheUpdateException;
import org.apache.http.client.cache.HttpCacheStorage;
import org.apache.http.client.cache.HttpCacheUpdateCallback;
+import org.apache.http.client.cache.memcached.KeyHashingScheme;
+import org.apache.http.client.cache.memcached.MemcachedCacheEntry;
+import org.apache.http.client.cache.memcached.MemcachedCacheEntryFactory;
import org.apache.http.impl.client.cache.CacheConfig;
import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer;
@@ -57,8 +57,7 @@ import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer;
*
in-memory cached objects can survive an application restart since
* they are held in a separate process
*
it becomes possible for several cooperating applications to share
- * a large memcached farm together, effectively providing cache
- * peering of a sort
+ * a large memcached farm together
*
* Note that in a shared memcached pool setting you may wish to make use
* of the Ketama consistent hashing algorithm to reduce the number of
@@ -66,6 +65,19 @@ import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer;
* fails (see the
* KetamaConnectionFactory).
*
+ *
+ *
Because memcached places limits on the size of its keys, we need to
+ * introduce a key hashing scheme to map the annotated URLs the higher-level
+ * {@link CachingHttpClient} wants to use as keys onto ones that are suitable
+ * for use with memcached. Please see {@link KeyHashingScheme} if you would
+ * like to use something other than the provided {@link SHA256KeyHashingScheme}.
+ *
+ *
Because this hashing scheme can potentially result in key collisions (though
+ * highly unlikely), we need to store the higher-level logical storage key along
+ * with the {@link HttpCacheEntry} so that we can re-check it on retrieval. There
+ * is a default serialization scheme provided for this, although you can provide
+ * your own implementations of {@link MemcachedCacheEntry} and
+ * {@link MemcachedCacheEntryFactory} to customize this serialization.
*
*
Please refer to the
* memcached documentation and in particular to the documentation for
@@ -80,7 +92,8 @@ public class MemcachedHttpCacheStorage implements HttpCacheStorage {
private static final Log log = LogFactory.getLog(MemcachedHttpCacheStorage.class);
private final MemcachedClientIF client;
- private final HttpCacheEntrySerializer serializer;
+ private final KeyHashingScheme keyHashingScheme;
+ private final MemcachedCacheEntryFactory memcachedCacheEntryFactory;
private final int maxUpdateRetries;
/**
@@ -101,37 +114,82 @@ public class MemcachedHttpCacheStorage implements HttpCacheStorage {
* @param cache client to use for communicating with memcached
*/
public MemcachedHttpCacheStorage(MemcachedClientIF cache) {
- this(cache, new CacheConfig(), new DefaultHttpCacheEntrySerializer());
+ this(cache, new CacheConfig(), new MemcachedCacheEntryFactoryImpl(),
+ new SHA256KeyHashingScheme());
}
/**
* Create a storage backend using the given memcached client and
* applying the given cache configuration and cache entry serialization
- * mechanism.
+ * mechanism. Deprecation note: In the process of fixing a bug
+ * based on the need to hash logical storage keys onto memcached cache
+ * keys, the serialization process was revamped. This constructor still
+ * works, but the serializer argument will be ignored and default
+ * implementations of the new framework will be used. You can still
+ * provide custom serialization by using the
+ * {@link #MemcachedHttpCacheStorage(MemcachedClientIF, CacheConfig,
+ * MemcachedCacheEntryFactory, KeyHashingScheme)} constructor.
* @param client how to talk to memcached
* @param config apply HTTP cache-related options
- * @param serializer how to serialize the cache entries before writing
- * them out to memcached. The provided {@link
- * DefaultHttpCacheEntrySerializer} is a fine serialization mechanism
- * to use here.
+ * @param serializer ignored
*/
+ @Deprecated
public MemcachedHttpCacheStorage(MemcachedClientIF client, CacheConfig config,
HttpCacheEntrySerializer serializer) {
- this.client = client;
- this.maxUpdateRetries = config.getMaxUpdateRetries();
- this.serializer = serializer;
+ this(client, config, new MemcachedCacheEntryFactoryImpl(),
+ new SHA256KeyHashingScheme());
}
+ /**
+ * Create a storage backend using the given memcached client and
+ * applying the given cache configuration, serialization, and hashing
+ * mechanisms.
+ * @param client how to talk to memcached
+ * @param config apply HTTP cache-related options
+ * @param memcachedCacheEntryFactory Factory pattern used for obtaining
+ * instances of alternative cache entry serialization mechanisms
+ * @param keyHashingScheme how to map higher-level logical "storage keys"
+ * onto "cache keys" suitable for use with memcached
+ */
+ public MemcachedHttpCacheStorage(MemcachedClientIF client, CacheConfig config,
+ MemcachedCacheEntryFactory memcachedCacheEntryFactory,
+ KeyHashingScheme keyHashingScheme) {
+ this.client = client;
+ this.maxUpdateRetries = config.getMaxUpdateRetries();
+ this.memcachedCacheEntryFactory = memcachedCacheEntryFactory;
+ this.keyHashingScheme = keyHashingScheme;
+ }
+
public void putEntry(String url, HttpCacheEntry entry) throws IOException {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- serializer.writeTo(entry, bos);
+ byte[] bytes = serializeEntry(url, entry);
+ String key = getCacheKey(url);
+ if (key == null) return;
try {
- client.set(url, 0, bos.toByteArray());
+ client.set(key, 0, bytes);
} catch (OperationTimeoutException ex) {
throw new MemcachedOperationTimeoutException(ex);
}
}
+ private String getCacheKey(String url) {
+ try {
+ return keyHashingScheme.hash(url);
+ } catch (MemcachedKeyHashingException mkhe) {
+ return null;
+ }
+ }
+
+ private byte[] serializeEntry(String url, HttpCacheEntry hce) throws IOException {
+ MemcachedCacheEntry mce = memcachedCacheEntryFactory.getMemcachedCacheEntry(url, hce);
+ try {
+ return mce.toByteArray();
+ } catch (MemcachedSerializationException mse) {
+ IOException ioe = new IOException();
+ ioe.initCause(mse);
+ throw ioe;
+ }
+ }
+
private byte[] convertToByteArray(Object o) {
if (o == null) return null;
if (!(o instanceof byte[])) {
@@ -141,24 +199,35 @@ public class MemcachedHttpCacheStorage implements HttpCacheStorage {
return (byte[])o;
}
- private HttpCacheEntry reconstituteEntry(Object o) throws IOException {
- byte[] out = convertToByteArray(o);
- if (out == null) return null;
- InputStream bis = new ByteArrayInputStream(out);
- return serializer.readFrom(bis);
+ private MemcachedCacheEntry reconstituteEntry(Object o) throws IOException {
+ byte[] bytes = convertToByteArray(o);
+ if (bytes == null) return null;
+ MemcachedCacheEntry mce = memcachedCacheEntryFactory.getUnsetCacheEntry();
+ try {
+ mce.set(bytes);
+ } catch (MemcachedSerializationException mse) {
+ return null;
+ }
+ return mce;
}
public HttpCacheEntry getEntry(String url) throws IOException {
+ String key = getCacheKey(url);
+ if (key == null) return null;
try {
- return reconstituteEntry(client.get(url));
+ MemcachedCacheEntry mce = reconstituteEntry(client.get(key));
+ if (mce == null || !url.equals(mce.getStorageKey())) return null;
+ return mce.getHttpCacheEntry();
} catch (OperationTimeoutException ex) {
throw new MemcachedOperationTimeoutException(ex);
}
}
public void removeEntry(String url) throws IOException {
+ String key = getCacheKey(url);
+ if (key == null) return;
try {
- client.delete(url);
+ client.delete(key);
} catch (OperationTimeoutException ex) {
throw new MemcachedOperationTimeoutException(ex);
}
@@ -167,22 +236,30 @@ public class MemcachedHttpCacheStorage implements HttpCacheStorage {
public void updateEntry(String url, HttpCacheUpdateCallback callback)
throws HttpCacheUpdateException, IOException {
int numRetries = 0;
+ String key = getCacheKey(url);
+ if (key == null) {
+ throw new HttpCacheUpdateException("couldn't generate cache key");
+ }
do {
try {
- CASValue