You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2017/10/19 14:04:55 UTC
[2/4] httpcomponents-client git commit: Factored out logic shared by
Memcached and Ehcache implementations into an abstract base class
Factored out logic shared by Memcached and Ehcache implementations into an abstract base class
Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/f215fdcd
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/f215fdcd
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/f215fdcd
Branch: refs/heads/master
Commit: f215fdcd32f714c2ec9b390300debac7fa8e9ec7
Parents: f70c974
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Tue Oct 17 16:23:51 2017 +0200
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Thu Oct 19 15:36:11 2017 +0200
----------------------------------------------------------------------
.../http/cache/HttpCacheEntrySerializer.java | 26 +-
.../http/cache/HttpCacheStorageEntry.java | 56 ++
.../http/cache/HttpCacheUpdateException.java | 4 +-
.../impl/cache/AbstractBinaryCacheStorage.java | 46 ++
.../cache/AbstractSerializingCacheStorage.java | 127 ++++
.../cache/ByteArrayCacheEntrySerializer.java | 80 +++
.../cache/DefaultHttpCacheEntrySerializer.java | 70 ---
.../cache/ehcache/EhcacheHttpCacheStorage.java | 114 ++--
.../cache/memcached/MemcachedCacheEntry.java | 77 ---
.../memcached/MemcachedCacheEntryFactory.java | 62 --
.../MemcachedCacheEntryFactoryImpl.java | 45 --
.../memcached/MemcachedCacheEntryImpl.java | 111 ----
.../memcached/MemcachedHttpCacheStorage.java | 169 ++----
.../MemcachedSerializationException.java | 41 --
.../cache/memcached/PrefixKeyHashingScheme.java | 2 +-
.../cache/memcached/SHA256KeyHashingScheme.java | 4 +-
.../http/impl/cache/HttpCacheEntryMatcher.java | 100 +++
.../TestAbstractSerializingCacheStorage.java | 256 ++++++++
.../TestByteArrayCacheEntrySerializer.java | 89 +++
.../cache/TestHttpCacheEntrySerializers.java | 149 -----
.../ehcache/TestEhcacheHttpCacheStorage.java | 247 --------
.../TestEhcacheProtocolRequirements.java | 88 ---
.../TestMemcachedCacheEntryFactoryImpl.java | 48 --
.../memcached/TestMemcachedCacheEntryImpl.java | 119 ----
.../TestMemcachedHttpCacheStorage.java | 603 -------------------
25 files changed, 865 insertions(+), 1868 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntrySerializer.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntrySerializer.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntrySerializer.java
index dcf9fb2..70f57c6 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntrySerializer.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheEntrySerializer.java
@@ -26,28 +26,28 @@
*/
package org.apache.hc.client5.http.cache;
-import java.io.InputStream;
-import java.io.OutputStream;
-
/**
- * Used by some {@link HttpCacheStorage} implementations to serialize
- * {@link HttpCacheEntry} instances to a byte representation before
- * storage.
+ * Serializer / deserializer for {@link HttpCacheStorageEntry} entries.
+ *
+ * @since 5.0
*/
-public interface HttpCacheEntrySerializer {
+public interface HttpCacheEntrySerializer<T> {
/**
- * Serializes the given entry to a byte representation on the
- * given {@link OutputStream}.
+ * Serializes the given entry.
+ *
+ * @param entry cache entry
+ * @return serialized representation of the cache entry
* @throws ResourceIOException
*/
- void writeTo(HttpCacheEntry entry, OutputStream os) throws ResourceIOException;
+ T serialize(HttpCacheStorageEntry entry) throws ResourceIOException;
/**
- * Deserializes a byte representation of a cache entry by reading
- * from the given {@link InputStream}.
+ * Deserializes a cache entry from its serialized representation.
+ * @param serializedObject serialized representation of the cache entry
+ * @return cache entry
* @throws ResourceIOException
*/
- HttpCacheEntry readFrom(InputStream is) throws ResourceIOException;
+ HttpCacheStorageEntry deserialize(T serializedObject) throws ResourceIOException;
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheStorageEntry.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheStorageEntry.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheStorageEntry.java
new file mode 100644
index 0000000..d806069
--- /dev/null
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheStorageEntry.java
@@ -0,0 +1,56 @@
+/*
+ * ====================================================================
+ * 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.hc.client5.http.cache;
+
+import java.io.Serializable;
+
+import org.apache.hc.core5.util.Args;
+
+public final class HttpCacheStorageEntry implements Serializable {
+
+ private final String key;
+ private final HttpCacheEntry content;
+
+ public HttpCacheStorageEntry(final String key, final HttpCacheEntry content) {
+ this.key = key;
+ this.content = Args.notNull(content, "Cache entry");
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public HttpCacheEntry getContent() {
+ return content;
+ }
+
+ @Override
+ public String toString() {
+ return "[key=" + key + "; content=" + content + "]";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheUpdateException.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheUpdateException.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheUpdateException.java
index 317593a..2e7617d 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheUpdateException.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheUpdateException.java
@@ -27,8 +27,8 @@
package org.apache.hc.client5.http.cache;
/**
- * Signals that {@link HttpCacheStorage} encountered an error performing an
- * processChallenge operation.
+ * Signals that {@link HttpCacheStorage} encountered an error performing
+ * an update operation.
*
* @since 4.1
*/
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AbstractBinaryCacheStorage.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AbstractBinaryCacheStorage.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AbstractBinaryCacheStorage.java
new file mode 100644
index 0000000..e959216
--- /dev/null
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AbstractBinaryCacheStorage.java
@@ -0,0 +1,46 @@
+/*
+ * ====================================================================
+ * 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.hc.client5.http.impl.cache;
+
+import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
+
+/**
+ * Abstract cache backend for serialized binary objects capable of CAS (compare-and-swap) updates.
+ *
+ * @since 5.0
+ */
+public abstract class AbstractBinaryCacheStorage<CAS> extends AbstractSerializingCacheStorage<byte[], CAS> {
+
+ public AbstractBinaryCacheStorage(final int maxUpdateRetries, final HttpCacheEntrySerializer<byte[]> serializer) {
+ super(maxUpdateRetries, serializer);
+ }
+
+ public AbstractBinaryCacheStorage(final int maxUpdateRetries) {
+ super(maxUpdateRetries, ByteArrayCacheEntrySerializer.INSTANCE);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AbstractSerializingCacheStorage.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AbstractSerializingCacheStorage.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AbstractSerializingCacheStorage.java
new file mode 100644
index 0000000..084cbd7
--- /dev/null
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AbstractSerializingCacheStorage.java
@@ -0,0 +1,127 @@
+/*
+ * ====================================================================
+ * 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.hc.client5.http.impl.cache;
+
+import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
+import org.apache.hc.client5.http.cache.HttpCacheStorage;
+import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
+import org.apache.hc.client5.http.cache.HttpCacheUpdateCallback;
+import org.apache.hc.client5.http.cache.HttpCacheUpdateException;
+import org.apache.hc.client5.http.cache.ResourceIOException;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * Abstract cache backend for serialized objects capable of CAS (compare-and-swap) updates.
+ *
+ * @since 5.0
+ */
+public abstract class AbstractSerializingCacheStorage<T, CAS> implements HttpCacheStorage {
+
+ private final int maxUpdateRetries;
+ private final HttpCacheEntrySerializer<T> serializer;
+
+ public AbstractSerializingCacheStorage(final int maxUpdateRetries, final HttpCacheEntrySerializer<T> serializer) {
+ this.maxUpdateRetries = Args.notNegative(maxUpdateRetries, "Max retries");
+ this.serializer = Args.notNull(serializer, "Cache entry serializer");
+ }
+
+ protected abstract String digestToStorageKey(String key);
+
+ protected abstract void store(String storageKey, T storageObject) throws ResourceIOException;
+
+ protected abstract T restore(String storageKey) throws ResourceIOException;
+
+ protected abstract CAS getForUpdateCAS(String storageKey) throws ResourceIOException;
+
+ protected abstract T getStorageObject(CAS cas) throws ResourceIOException;
+
+ protected abstract boolean updateCAS(String storageKey, CAS cas, T storageObject) throws ResourceIOException;
+
+ protected abstract void delete(String storageKey) throws ResourceIOException;
+
+ @Override
+ public final void putEntry(final String key, final HttpCacheEntry entry) throws ResourceIOException {
+ final String storageKey = digestToStorageKey(key);
+ final T storageObject = serializer.serialize(new HttpCacheStorageEntry(key, entry));
+ store(storageKey, storageObject);
+ }
+
+ @Override
+ public final HttpCacheEntry getEntry(final String key) throws ResourceIOException {
+ final String storageKey = digestToStorageKey(key);
+ final T storageObject = restore(storageKey);
+ if (storageObject == null) {
+ return null;
+ }
+ final HttpCacheStorageEntry entry = serializer.deserialize(storageObject);
+ if (key.equals(entry.getKey())) {
+ return entry.getContent();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public final void removeEntry(final String key) throws ResourceIOException {
+ final String storageKey = digestToStorageKey(key);
+ delete(storageKey);
+ }
+
+ @Override
+ public final void updateEntry(
+ final String key,
+ final HttpCacheUpdateCallback callback) throws HttpCacheUpdateException, ResourceIOException {
+ int numRetries = 0;
+ final String storageKey = digestToStorageKey(key);
+ for (;;) {
+ final CAS cas = getForUpdateCAS(storageKey);
+ HttpCacheStorageEntry storageEntry = cas != null ? serializer.deserialize(getStorageObject(cas)) : null;
+ if (storageEntry != null && !key.equals(storageEntry.getKey())) {
+ storageEntry = null;
+ }
+ final HttpCacheEntry existingEntry = storageEntry != null ? storageEntry.getContent() : null;
+ final HttpCacheEntry updatedEntry = callback.update(existingEntry);
+
+ if (existingEntry == null) {
+ putEntry(key, updatedEntry);
+ return;
+
+ }
+ final T storageObject = serializer.serialize(new HttpCacheStorageEntry(key, updatedEntry));
+ if (!updateCAS(storageKey, cas, storageObject)) {
+ numRetries++;
+ if (numRetries >= maxUpdateRetries) {
+ throw new HttpCacheUpdateException("Cache update failed after " + numRetries + " retries");
+ }
+ } else {
+ return;
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ByteArrayCacheEntrySerializer.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ByteArrayCacheEntrySerializer.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ByteArrayCacheEntrySerializer.java
new file mode 100644
index 0000000..e5f653c
--- /dev/null
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ByteArrayCacheEntrySerializer.java
@@ -0,0 +1,80 @@
+/*
+ * ====================================================================
+ * 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.hc.client5.http.impl.cache;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
+import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
+import org.apache.hc.client5.http.cache.ResourceIOException;
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+
+/**
+ * {@link HttpCacheEntrySerializer} implementation that uses the default (native)
+ * serialization.
+ *
+ * @see java.io.Serializable
+ *
+ * @since 4.1
+ */
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
+public final class ByteArrayCacheEntrySerializer implements HttpCacheEntrySerializer<byte[]> {
+
+ public static final ByteArrayCacheEntrySerializer INSTANCE = new ByteArrayCacheEntrySerializer();
+
+ @Override
+ public byte[] serialize(final HttpCacheStorageEntry cacheEntry) throws ResourceIOException {
+ if (cacheEntry == null) {
+ return null;
+ }
+ final ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ try (final ObjectOutputStream oos = new ObjectOutputStream(buf)) {
+ oos.writeObject(cacheEntry);
+ } catch (final IOException ex) {
+ throw new ResourceIOException(ex.getMessage(), ex);
+ }
+ return buf.toByteArray();
+ }
+
+ @Override
+ public HttpCacheStorageEntry deserialize(final byte[] serializedObject) throws ResourceIOException {
+ if (serializedObject == null) {
+ return null;
+ }
+ try (final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serializedObject))) {
+ return (HttpCacheStorageEntry) ois.readObject();
+ } catch (final IOException | ClassNotFoundException ex) {
+ throw new ResourceIOException(ex.getMessage(), ex);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/DefaultHttpCacheEntrySerializer.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/DefaultHttpCacheEntrySerializer.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/DefaultHttpCacheEntrySerializer.java
deleted file mode 100644
index ca39e46..0000000
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/DefaultHttpCacheEntrySerializer.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * ====================================================================
- * 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.hc.client5.http.impl.cache;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-
-import org.apache.hc.client5.http.cache.HttpCacheEntry;
-import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
-import org.apache.hc.client5.http.cache.ResourceIOException;
-import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.ThreadingBehavior;
-
-/**
- * {@link HttpCacheEntrySerializer} implementation that uses the default (native)
- * serialization.
- *
- * @see java.io.Serializable
- *
- * @since 4.1
- */
-@Contract(threading = ThreadingBehavior.IMMUTABLE)
-public class DefaultHttpCacheEntrySerializer implements HttpCacheEntrySerializer {
-
- @Override
- public void writeTo(final HttpCacheEntry cacheEntry, final OutputStream os) throws ResourceIOException {
- try (final ObjectOutputStream oos = new ObjectOutputStream(os)) {
- oos.writeObject(cacheEntry);
- } catch (final IOException ex) {
- throw new ResourceIOException(ex.getMessage(), ex);
- }
- }
-
- @Override
- public HttpCacheEntry readFrom(final InputStream is) throws ResourceIOException {
- try (final ObjectInputStream ois = new ObjectInputStream(is)) {
- return (HttpCacheEntry) ois.readObject();
- } catch (final IOException | ClassNotFoundException ex) {
- throw new ResourceIOException(ex.getMessage(), ex);
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ehcache/EhcacheHttpCacheStorage.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ehcache/EhcacheHttpCacheStorage.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ehcache/EhcacheHttpCacheStorage.java
index ad31ace..36cc4c2 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ehcache/EhcacheHttpCacheStorage.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ehcache/EhcacheHttpCacheStorage.java
@@ -26,17 +26,12 @@
*/
package org.apache.hc.client5.http.impl.cache.ehcache;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
-import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
-import org.apache.hc.client5.http.cache.HttpCacheStorage;
-import org.apache.hc.client5.http.cache.HttpCacheUpdateCallback;
-import org.apache.hc.client5.http.cache.HttpCacheUpdateException;
import org.apache.hc.client5.http.cache.ResourceIOException;
+import org.apache.hc.client5.http.impl.cache.AbstractBinaryCacheStorage;
import org.apache.hc.client5.http.impl.cache.CacheConfig;
-import org.apache.hc.client5.http.impl.cache.DefaultHttpCacheEntrySerializer;
+import org.apache.hc.client5.http.impl.cache.ByteArrayCacheEntrySerializer;
+import org.apache.hc.core5.util.Args;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
@@ -58,31 +53,17 @@ import net.sf.ehcache.Element;
* itself.</p>
* @since 4.1
*/
-public class EhcacheHttpCacheStorage implements HttpCacheStorage {
+public class EhcacheHttpCacheStorage extends AbstractBinaryCacheStorage<Element> {
private final Ehcache cache;
- private final HttpCacheEntrySerializer serializer;
- private final int maxUpdateRetries;
/**
* Constructs a storage backend using the provided Ehcache
* with default configuration options.
* @param cache where to store cached origin responses
*/
- public EhcacheHttpCacheStorage(final Ehcache cache) {
- this(cache, CacheConfig.DEFAULT, new DefaultHttpCacheEntrySerializer());
- }
-
- /**
- * Constructs a storage backend using the provided Ehcache
- * with the given configuration options.
- * @param cache where to store cached origin responses
- * @param config cache storage configuration options - note that
- * the setting for max object size <b>will be ignored</b> and
- * should be configured in the Ehcache instead.
- */
- public EhcacheHttpCacheStorage(final Ehcache cache, final CacheConfig config){
- this(cache, config, new DefaultHttpCacheEntrySerializer());
+ public EhcacheHttpCacheStorage(final Ehcache cache){
+ this(cache, CacheConfig.DEFAULT, ByteArrayCacheEntrySerializer.INSTANCE);
}
/**
@@ -95,67 +76,60 @@ public class EhcacheHttpCacheStorage implements HttpCacheStorage {
* should be configured in the Ehcache instead.
* @param serializer alternative serialization mechanism
*/
- public EhcacheHttpCacheStorage(final Ehcache cache, final CacheConfig config, final HttpCacheEntrySerializer serializer){
- this.cache = cache;
- this.maxUpdateRetries = config.getMaxUpdateRetries();
- this.serializer = serializer;
+ public EhcacheHttpCacheStorage(
+ final Ehcache cache,
+ final CacheConfig config,
+ final HttpCacheEntrySerializer<byte[]> serializer) {
+ super((config != null ? config : CacheConfig.DEFAULT).getMaxUpdateRetries(), serializer);
+ this.cache = Args.notNull(cache, "Ehcache");
}
@Override
- public synchronized void putEntry(final String key, final HttpCacheEntry entry) throws ResourceIOException {
- final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- serializer.writeTo(entry, bos);
- cache.put(new Element(key, bos.toByteArray()));
+ protected String digestToStorageKey(final String key) {
+ return key;
}
@Override
- public synchronized HttpCacheEntry getEntry(final String key) throws ResourceIOException {
- final Element e = cache.get(key);
- if(e == null){
+ protected void store(final String storageKey, final byte[] storageObject) throws ResourceIOException {
+ cache.put(new Element(storageKey, storageKey));
+ }
+
+ private byte[] castAsByteArray(final Object storageObject) throws ResourceIOException {
+ if (storageObject == null) {
return null;
}
-
- final byte[] data = (byte[])e.getObjectValue();
- return serializer.readFrom(new ByteArrayInputStream(data));
+ if (storageObject instanceof byte[]) {
+ return (byte[]) storageObject;
+ } else {
+ throw new ResourceIOException("Unexpected cache content: " + storageObject.getClass());
+ }
}
@Override
- public synchronized void removeEntry(final String key) {
- cache.remove(key);
+ protected byte[] restore(final String storageKey) throws ResourceIOException {
+ final Element element = cache.get(storageKey);
+ return element != null ? castAsByteArray(element.getObjectValue()) : null;
}
@Override
- public synchronized void updateEntry(
- final String key, final HttpCacheUpdateCallback callback) throws ResourceIOException, HttpCacheUpdateException {
- int numRetries = 0;
- do{
- final Element oldElement = cache.get(key);
+ protected Element getForUpdateCAS(final String storageKey) throws ResourceIOException {
+ return cache.get(storageKey);
+ }
- HttpCacheEntry existingEntry = null;
- if(oldElement != null){
- final byte[] data = (byte[])oldElement.getObjectValue();
- existingEntry = serializer.readFrom(new ByteArrayInputStream(data));
- }
+ @Override
+ protected byte[] getStorageObject(final Element element) throws ResourceIOException {
+ return castAsByteArray(element.getObjectValue());
+ }
- final HttpCacheEntry updatedEntry = callback.update(existingEntry);
+ @Override
+ protected boolean updateCAS(final String storageKey, final Element element, final byte[] storageObject) throws ResourceIOException {
+ final Element newElement = new Element(storageKey, storageObject);
+ return cache.replace(element, newElement);
+ }
- if (existingEntry == null) {
- putEntry(key, updatedEntry);
- return;
- } else {
- // Attempt to do a CAS replace, if we fail then retry
- // While this operation should work fine within this instance, multiple instances
- // could trample each others' data
- final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- serializer.writeTo(updatedEntry, bos);
- final Element newElement = new Element(key, bos.toByteArray());
- if (cache.replace(oldElement, newElement)) {
- return;
- }else{
- numRetries++;
- }
- }
- }while(numRetries <= maxUpdateRetries);
- throw new HttpCacheUpdateException("Failed to processChallenge");
+ @Override
+ protected void delete(final String storageKey) throws ResourceIOException {
+ cache.remove(storageKey);
}
+
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntry.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntry.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntry.java
deleted file mode 100644
index 769841d..0000000
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntry.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * ====================================================================
- * 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.hc.client5.http.impl.cache.memcached;
-
-import org.apache.hc.client5.http.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.
- * @throws MemcachedSerializationException if serialization fails.
- * */
- byte[] toByteArray() throws MemcachedSerializationException;
-
- /**
- * Returns the storage key associated with this entry. May return
- * {@code 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 {@code 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
- * {@code MemcachedCacheEntry}.
- *
- * @param bytes serialized representation
- * @throws MemcachedSerializationException if deserialization
- * fails. In this case, the prior values for {{@link #getStorageKey()}
- * and {@link #getHttpCacheEntry()} should remain unchanged.
- */
- void set(byte[] bytes) throws MemcachedSerializationException ;
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryFactory.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryFactory.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryFactory.java
deleted file mode 100644
index 0da3d65..0000000
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryFactory.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * ====================================================================
- * 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.hc.client5.http.impl.cache.memcached;
-
-import org.apache.hc.client5.http.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 {@code 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 {@code MemcachedCacheEntry}
- */
- MemcachedCacheEntry getUnsetCacheEntry();
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryFactoryImpl.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryFactoryImpl.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryFactoryImpl.java
deleted file mode 100644
index 872cb2e..0000000
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryFactoryImpl.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * ====================================================================
- * 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.hc.client5.http.impl.cache.memcached;
-
-import org.apache.hc.client5.http.cache.HttpCacheEntry;
-
-/**
- * Default implementation of {@link MemcachedCacheEntryFactory}.
- */
-public class MemcachedCacheEntryFactoryImpl implements MemcachedCacheEntryFactory {
-
- @Override
- public MemcachedCacheEntry getMemcachedCacheEntry(final String key, final HttpCacheEntry entry) {
- return new MemcachedCacheEntryImpl(key, entry);
- }
-
- @Override
- public MemcachedCacheEntry getUnsetCacheEntry() {
- return new MemcachedCacheEntryImpl(null, null);
- }
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryImpl.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryImpl.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryImpl.java
deleted file mode 100644
index 566e882..0000000
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedCacheEntryImpl.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * ====================================================================
- * 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.hc.client5.http.impl.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.hc.client5.http.cache.HttpCacheEntry;
-
-/**
- * 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(final String key, final HttpCacheEntry httpCacheEntry) {
- this.key = key;
- this.httpCacheEntry = httpCacheEntry;
- }
-
- public MemcachedCacheEntryImpl() {
- }
-
- /* (non-Javadoc)
- * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#toByteArray()
- */
- @Override
- synchronized public byte[] toByteArray() throws MemcachedSerializationException {
- final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- final ObjectOutputStream oos;
- try {
- oos = new ObjectOutputStream(bos);
- oos.writeObject(this.key);
- oos.writeObject(this.httpCacheEntry);
- oos.close();
- } catch (final IOException ioe) {
- throw new MemcachedSerializationException(ioe);
- }
- return bos.toByteArray();
- }
-
- /* (non-Javadoc)
- * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#getKey()
- */
- @Override
- public synchronized String getStorageKey() {
- return key;
- }
-
- /* (non-Javadoc)
- * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#getHttpCacheEntry()
- */
- @Override
- public synchronized HttpCacheEntry getHttpCacheEntry() {
- return httpCacheEntry;
- }
-
- /* (non-Javadoc)
- * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#set(byte[])
- */
- @Override
- synchronized public void set(final byte[] bytes) throws MemcachedSerializationException {
- final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
- final ObjectInputStream ois;
- final String s;
- final HttpCacheEntry entry;
- try {
- ois = new ObjectInputStream(bis);
- s = (String)ois.readObject();
- entry = (HttpCacheEntry)ois.readObject();
- ois.close();
- bis.close();
- } catch (final IOException | ClassNotFoundException ioe) {
- throw new MemcachedSerializationException(ioe);
- }
- this.key = s;
- this.httpCacheEntry = entry;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedHttpCacheStorage.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedHttpCacheStorage.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedHttpCacheStorage.java
index f132b5f..39d209e 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedHttpCacheStorage.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedHttpCacheStorage.java
@@ -29,14 +29,12 @@ package org.apache.hc.client5.http.impl.cache.memcached;
import java.io.IOException;
import java.net.InetSocketAddress;
-import org.apache.hc.client5.http.cache.HttpCacheEntry;
-import org.apache.hc.client5.http.cache.HttpCacheStorage;
-import org.apache.hc.client5.http.cache.HttpCacheUpdateCallback;
-import org.apache.hc.client5.http.cache.HttpCacheUpdateException;
+import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
import org.apache.hc.client5.http.cache.ResourceIOException;
+import org.apache.hc.client5.http.impl.cache.AbstractBinaryCacheStorage;
import org.apache.hc.client5.http.impl.cache.CacheConfig;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
+import org.apache.hc.client5.http.impl.cache.ByteArrayCacheEntrySerializer;
+import org.apache.hc.core5.util.Args;
import net.spy.memcached.CASResponse;
import net.spy.memcached.CASValue;
@@ -73,15 +71,6 @@ import net.spy.memcached.OperationTimeoutException;
* </p>
*
* <p>
- * 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.
- * </p>
- *
- * <p>
* Please refer to the <a href="http://code.google.com/p/memcached/wiki/NewStart">
* memcached documentation</a> and in particular to the documentation for
* the <a href="http://code.google.com/p/spymemcached/">spymemcached
@@ -91,14 +80,10 @@ import net.spy.memcached.OperationTimeoutException;
*
* @since 4.1
*/
-public class MemcachedHttpCacheStorage implements HttpCacheStorage {
-
- private final Logger log = LogManager.getLogger(getClass());
+public class MemcachedHttpCacheStorage extends AbstractBinaryCacheStorage<CASValue<Object>> {
private final MemcachedClientIF client;
private final KeyHashingScheme keyHashingScheme;
- private final MemcachedCacheEntryFactory memcachedCacheEntryFactory;
- private final int maxUpdateRetries;
/**
* Create a storage backend talking to a <i>memcached</i> instance
@@ -118,8 +103,7 @@ public class MemcachedHttpCacheStorage implements HttpCacheStorage {
* @param cache client to use for communicating with <i>memcached</i>
*/
public MemcachedHttpCacheStorage(final MemcachedClientIF cache) {
- this(cache, CacheConfig.DEFAULT, new MemcachedCacheEntryFactoryImpl(),
- new SHA256KeyHashingScheme());
+ this(cache, CacheConfig.DEFAULT, ByteArrayCacheEntrySerializer.INSTANCE, SHA256KeyHashingScheme.INSTANCE);
}
/**
@@ -128,140 +112,83 @@ public class MemcachedHttpCacheStorage implements HttpCacheStorage {
* mechanisms.
* @param client how to talk to <i>memcached</i>
* @param config apply HTTP cache-related options
- * @param memcachedCacheEntryFactory Factory pattern used for obtaining
- * instances of alternative cache entry serialization mechanisms
+ * @param serializer alternative serialization mechanism
* @param keyHashingScheme how to map higher-level logical "storage keys"
* onto "cache keys" suitable for use with memcached
*/
- public MemcachedHttpCacheStorage(final MemcachedClientIF client, final CacheConfig config,
- final MemcachedCacheEntryFactory memcachedCacheEntryFactory,
+ public MemcachedHttpCacheStorage(
+ final MemcachedClientIF client,
+ final CacheConfig config,
+ final HttpCacheEntrySerializer<byte[]> serializer,
final KeyHashingScheme keyHashingScheme) {
- this.client = client;
- this.maxUpdateRetries = config.getMaxUpdateRetries();
- this.memcachedCacheEntryFactory = memcachedCacheEntryFactory;
+ super((config != null ? config : CacheConfig.DEFAULT).getMaxUpdateRetries(),
+ serializer != null ? serializer : ByteArrayCacheEntrySerializer.INSTANCE);
+ this.client = Args.notNull(client, "Memcached client");
this.keyHashingScheme = keyHashingScheme;
}
@Override
- public void putEntry(final String url, final HttpCacheEntry entry) throws ResourceIOException {
- final byte[] bytes = serializeEntry(url, entry);
- final String key = getCacheKey(url);
- if (key == null) {
- return;
- }
- try {
- client.set(key, 0, bytes);
- } catch (final OperationTimeoutException ex) {
- throw new MemcachedOperationTimeoutException(ex);
- }
+ protected String digestToStorageKey(final String key) {
+ return keyHashingScheme.hash(key);
}
- private String getCacheKey(final String url) {
+ @Override
+ protected void store(final String storageKey, final byte[] storageObject) throws ResourceIOException {
try {
- return keyHashingScheme.hash(url);
- } catch (final MemcachedKeyHashingException mkhe) {
- return null;
- }
- }
-
- private byte[] serializeEntry(final String url, final HttpCacheEntry hce) throws ResourceIOException {
- final MemcachedCacheEntry mce = memcachedCacheEntryFactory.getMemcachedCacheEntry(url, hce);
- return mce.toByteArray();
- }
-
- private byte[] convertToByteArray(final Object o) {
- if (o == null) {
- return null;
- }
- if (!(o instanceof byte[])) {
- log.warn("got a non-bytearray back from memcached: " + o);
- return null;
+ client.set(storageKey, 0, storageObject);
+ } catch (final OperationTimeoutException ex) {
+ throw new MemcachedOperationTimeoutException(ex);
}
- return (byte[])o;
}
- private MemcachedCacheEntry reconstituteEntry(final Object o) {
- final byte[] bytes = convertToByteArray(o);
- if (bytes == null) {
+ private byte[] castAsByteArray(final Object storageObject) throws ResourceIOException {
+ if (storageObject == null) {
return null;
}
- final MemcachedCacheEntry mce = memcachedCacheEntryFactory.getUnsetCacheEntry();
- try {
- mce.set(bytes);
- } catch (final MemcachedSerializationException mse) {
- return null;
+ if (storageObject instanceof byte[]) {
+ return (byte[]) storageObject;
+ } else {
+ throw new ResourceIOException("Unexpected cache content: " + storageObject.getClass());
}
- return mce;
}
@Override
- public HttpCacheEntry getEntry(final String url) throws ResourceIOException {
- final String key = getCacheKey(url);
- if (key == null) {
- return null;
- }
+ protected byte[] restore(final String storageKey) throws ResourceIOException {
try {
- final MemcachedCacheEntry mce = reconstituteEntry(client.get(key));
- if (mce == null || !url.equals(mce.getStorageKey())) {
- return null;
- }
- return mce.getHttpCacheEntry();
+ return castAsByteArray(client.get(storageKey));
} catch (final OperationTimeoutException ex) {
throw new MemcachedOperationTimeoutException(ex);
}
}
@Override
- public void removeEntry(final String url) throws ResourceIOException {
- final String key = getCacheKey(url);
- if (key == null) {
- return;
- }
+ protected CASValue<Object> getForUpdateCAS(final String storageKey) throws ResourceIOException {
try {
- client.delete(key);
+ return client.gets(storageKey);
} catch (final OperationTimeoutException ex) {
throw new MemcachedOperationTimeoutException(ex);
}
}
@Override
- public void updateEntry(final String url, final HttpCacheUpdateCallback callback)
- throws HttpCacheUpdateException, ResourceIOException {
- int numRetries = 0;
- final String key = getCacheKey(url);
- if (key == null) {
- throw new HttpCacheUpdateException("couldn't generate cache key");
- }
- do {
- try {
- final CASValue<Object> v = client.gets(key);
- MemcachedCacheEntry mce = (v == null) ? null
- : reconstituteEntry(v.getValue());
- if (mce != null && (!url.equals(mce.getStorageKey()))) {
- mce = null;
- }
- final HttpCacheEntry existingEntry = (mce == null) ? null
- : mce.getHttpCacheEntry();
- final HttpCacheEntry updatedEntry = callback.update(existingEntry);
-
- if (existingEntry == null) {
- putEntry(url, updatedEntry);
- return;
+ protected byte[] getStorageObject(final CASValue<Object> casValue) throws ResourceIOException {
+ return castAsByteArray(casValue.getValue());
+ }
- }
- final byte[] updatedBytes = serializeEntry(url, updatedEntry);
- final CASResponse casResult = client.cas(key, v.getCas(),
- updatedBytes);
- if (casResult != CASResponse.OK) {
- numRetries++;
- } else {
- return;
- }
- } catch (final OperationTimeoutException ex) {
- throw new MemcachedOperationTimeoutException(ex);
- }
- } while (numRetries <= maxUpdateRetries);
+ @Override
+ protected boolean updateCAS(
+ final String storageKey, final CASValue<Object> casValue, final byte[] storageObject) throws ResourceIOException {
+ final CASResponse casResult = client.cas(storageKey, casValue.getCas(), storageObject);
+ return casResult == CASResponse.OK;
+ }
- throw new HttpCacheUpdateException("Failed to processChallenge");
+ @Override
+ protected void delete(final String storageKey) throws ResourceIOException {
+ try {
+ client.delete(storageKey);
+ } catch (final OperationTimeoutException ex) {
+ throw new MemcachedOperationTimeoutException(ex);
+ }
}
+
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedSerializationException.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedSerializationException.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedSerializationException.java
deleted file mode 100644
index 69cea8a..0000000
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/MemcachedSerializationException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * ====================================================================
- * 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.hc.client5.http.impl.cache.memcached;
-
-import org.apache.hc.client5.http.cache.ResourceIOException;
-
-/**
- * Raised when there is a problem serializing or deserializing cache
- * entries into a byte representation suitable for memcached storage.
- */
-public class MemcachedSerializationException extends ResourceIOException {
-
- public MemcachedSerializationException(final Throwable cause) {
- super(cause != null ? cause.getMessage() : null, cause);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/PrefixKeyHashingScheme.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/PrefixKeyHashingScheme.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/PrefixKeyHashingScheme.java
index f6e2231..b7a1fd4 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/PrefixKeyHashingScheme.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/PrefixKeyHashingScheme.java
@@ -33,7 +33,7 @@ package org.apache.hc.client5.http.impl.cache.memcached;
* Primarily useful for namespacing a shared memcached cluster, for
* example.
*/
-public class PrefixKeyHashingScheme implements KeyHashingScheme {
+public final class PrefixKeyHashingScheme implements KeyHashingScheme {
private final String prefix;
private final KeyHashingScheme backingScheme;
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/SHA256KeyHashingScheme.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/SHA256KeyHashingScheme.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/SHA256KeyHashingScheme.java
index 0ebf07e..9b1c13f 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/SHA256KeyHashingScheme.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/SHA256KeyHashingScheme.java
@@ -40,7 +40,9 @@ import org.apache.logging.log4j.Logger;
* digests and hence are always 64-character hexadecimal
* strings.
*/
-public class SHA256KeyHashingScheme implements KeyHashingScheme {
+public final class SHA256KeyHashingScheme implements KeyHashingScheme {
+
+ public static final SHA256KeyHashingScheme INSTANCE = new SHA256KeyHashingScheme();
private final Logger log = LogManager.getLogger(getClass());
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/HttpCacheEntryMatcher.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/HttpCacheEntryMatcher.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/HttpCacheEntryMatcher.java
new file mode 100644
index 0000000..7721d89
--- /dev/null
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/HttpCacheEntryMatcher.java
@@ -0,0 +1,100 @@
+/*
+ * ====================================================================
+ * 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.hc.client5.http.impl.cache;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Objects;
+
+import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.Resource;
+import org.apache.hc.client5.http.cache.ResourceIOException;
+import org.apache.hc.core5.http.Header;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+
+public class HttpCacheEntryMatcher extends BaseMatcher<HttpCacheEntry> {
+
+ private final HttpCacheEntry expectedValue;
+
+ public HttpCacheEntryMatcher(final HttpCacheEntry expectedValue) {
+ this.expectedValue = expectedValue;
+ }
+
+ @Override
+ public boolean matches(final Object item) {
+ if (item instanceof HttpCacheEntry) {
+ try {
+ final HttpCacheEntry otherValue = (HttpCacheEntry) item;
+
+ final int expectedStatus = expectedValue.getStatus();
+ final int otherStatus = otherValue.getStatus();
+ if (expectedStatus != otherStatus) {
+ return false;
+ }
+ final Date expectedRequestDate = expectedValue.getRequestDate();
+ final Date otherRequestDate = otherValue.getRequestDate();
+ if (!Objects.equals(expectedRequestDate, otherRequestDate)) {
+ return false;
+ }
+ final Date expectedResponseDate = expectedValue.getResponseDate();
+ final Date otherResponseDate = otherValue.getResponseDate();
+ if (!Objects.equals(expectedResponseDate, otherResponseDate)) {
+ return false;
+ }
+ final Header[] expectedHeaders = expectedValue.getAllHeaders();
+ final Header[] otherHeaders = otherValue.getAllHeaders();
+ if (!Arrays.deepEquals(expectedHeaders, otherHeaders)) {
+ return false;
+ }
+ final Resource expectedResource = expectedValue.getResource();
+ final byte[] expectedContent = expectedResource != null ? expectedResource.get() : null;
+ final Resource otherResource = otherValue.getResource();
+ final byte[] otherContent = otherResource != null ? otherResource.get() : null;
+ if (!Arrays.equals(expectedContent, otherContent)) {
+ return false;
+ }
+ } catch (final ResourceIOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendValue(expectedValue);
+ }
+
+ @Factory
+ public static Matcher<HttpCacheEntry> equivalent(final HttpCacheEntry target) {
+ return new HttpCacheEntryMatcher(target);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestAbstractSerializingCacheStorage.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestAbstractSerializingCacheStorage.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestAbstractSerializingCacheStorage.java
new file mode 100644
index 0000000..c52c469
--- /dev/null
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestAbstractSerializingCacheStorage.java
@@ -0,0 +1,256 @@
+/*
+ * ====================================================================
+ * 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.hc.client5.http.impl.cache;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
+import org.apache.hc.client5.http.cache.HttpCacheUpdateCallback;
+import org.apache.hc.client5.http.cache.HttpCacheUpdateException;
+import org.apache.hc.client5.http.cache.ResourceIOException;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+@SuppressWarnings("boxing") // test code
+public class TestAbstractSerializingCacheStorage {
+
+ public static byte[] serialize(final String key, final HttpCacheEntry value) throws ResourceIOException {
+ return ByteArrayCacheEntrySerializer.INSTANCE.serialize(new HttpCacheStorageEntry(key, value));
+ }
+
+ private AbstractBinaryCacheStorage<String> impl;
+
+ @Before
+ @SuppressWarnings("unchecked")
+ public void setUp() {
+ impl = Mockito.mock(AbstractBinaryCacheStorage.class,
+ Mockito.withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS).useConstructor(3));
+ }
+
+ @Test
+ public void testCachePut() throws Exception {
+ final String key = "foo";
+ final HttpCacheEntry value = HttpTestUtils.makeCacheEntry();
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+
+ impl.putEntry(key, value);
+
+ final ArgumentCaptor<byte[]> argumentCaptor = ArgumentCaptor.forClass(byte[].class);
+ verify(impl).store(eq("bar"), argumentCaptor.capture());
+ Assert.assertArrayEquals(serialize(key, value), argumentCaptor.getValue());
+ }
+
+ @Test
+ public void testCacheGetNullEntry() throws Exception {
+ final String key = "foo";
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ when(impl.restore("bar")).thenReturn(null);
+
+ final HttpCacheEntry resultingEntry = impl.getEntry(key);
+
+ verify(impl).restore("bar");
+
+ Assert.assertThat(resultingEntry, CoreMatchers.nullValue());
+ }
+
+ @Test
+ public void testCacheGet() throws Exception {
+ final String key = "foo";
+ final HttpCacheEntry value = HttpTestUtils.makeCacheEntry();
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ when(impl.restore("bar")).thenReturn(serialize(key, value));
+
+ final HttpCacheEntry resultingEntry = impl.getEntry(key);
+
+ verify(impl).restore("bar");
+
+ Assert.assertThat(resultingEntry, HttpCacheEntryMatcher.equivalent(value));
+ }
+
+ @Test
+ public void testCacheGetKeyMismatch() throws Exception {
+ final String key = "foo";
+ final HttpCacheEntry value = HttpTestUtils.makeCacheEntry();
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ when(impl.restore("bar")).thenReturn(serialize("not-foo", value));
+
+ final HttpCacheEntry resultingEntry = impl.getEntry(key);
+
+ verify(impl).restore("bar");
+
+ Assert.assertThat(resultingEntry, CoreMatchers.nullValue());
+ }
+
+ @Test
+ public void testCacheRemove() throws Exception{
+ final String key = "foo";
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ impl.removeEntry(key);
+
+ verify(impl).delete("bar");
+ }
+
+ @Test
+ public void testCacheUpdateNullEntry() throws Exception {
+ final String key = "foo";
+ final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ when(impl.getForUpdateCAS("bar")).thenReturn(null);
+
+ impl.updateEntry(key, new HttpCacheUpdateCallback() {
+
+ @Override
+ public HttpCacheEntry update(final HttpCacheEntry existing) throws ResourceIOException {
+ Assert.assertThat(existing, CoreMatchers.nullValue());
+ return updatedValue;
+ }
+
+ });
+
+ verify(impl).getForUpdateCAS("bar");
+ verify(impl).store(Mockito.eq("bar"), Mockito.<byte[]>any());
+ }
+
+ @Test
+ public void testCacheCASUpdate() throws Exception {
+ final String key = "foo";
+ final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry();
+ final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ when(impl.getForUpdateCAS("bar")).thenReturn("stuff");
+ when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue));
+ when(impl.updateCAS(Mockito.eq("bar"), Mockito.eq("stuff"), Mockito.<byte[]>any())).thenReturn(true);
+
+ impl.updateEntry(key, new HttpCacheUpdateCallback() {
+
+ @Override
+ public HttpCacheEntry update(final HttpCacheEntry existing) throws ResourceIOException {
+ return updatedValue;
+ }
+
+ });
+
+ verify(impl).getForUpdateCAS("bar");
+ verify(impl).getStorageObject("stuff");
+ verify(impl).updateCAS(Mockito.eq("bar"), Mockito.eq("stuff"), Mockito.<byte[]>any());
+ }
+
+ @Test
+ public void testCacheCASUpdateKeyMismatch() throws Exception {
+ final String key = "foo";
+ final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry();
+ final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ when(impl.getForUpdateCAS("bar")).thenReturn("stuff");
+ when(impl.getStorageObject("stuff")).thenReturn(serialize("not-foo", existingValue));
+ when(impl.updateCAS(Mockito.eq("bar"), Mockito.eq("stuff"), Mockito.<byte[]>any())).thenReturn(true);
+
+ impl.updateEntry(key, new HttpCacheUpdateCallback() {
+
+ @Override
+ public HttpCacheEntry update(final HttpCacheEntry existing) throws ResourceIOException {
+ Assert.assertThat(existing, CoreMatchers.nullValue());
+ return updatedValue;
+ }
+
+ });
+
+ verify(impl).getForUpdateCAS("bar");
+ verify(impl).getStorageObject("stuff");
+ verify(impl).store(Mockito.eq("bar"), Mockito.<byte[]>any());
+ }
+
+ @Test
+ public void testSingleCacheUpdateRetry() throws Exception {
+ final String key = "foo";
+ final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry();
+ final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ when(impl.getForUpdateCAS("bar")).thenReturn("stuff");
+ when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue));
+ when(impl.updateCAS(Mockito.eq("bar"), Mockito.eq("stuff"), Mockito.<byte[]>any())).thenReturn(false, true);
+
+ impl.updateEntry(key, new HttpCacheUpdateCallback() {
+
+ @Override
+ public HttpCacheEntry update(final HttpCacheEntry existing) throws ResourceIOException {
+ return updatedValue;
+ }
+
+ });
+
+ verify(impl, Mockito.times(2)).getForUpdateCAS("bar");
+ verify(impl, Mockito.times(2)).getStorageObject("stuff");
+ verify(impl, Mockito.times(2)).updateCAS(Mockito.eq("bar"), Mockito.eq("stuff"), Mockito.<byte[]>any());
+ }
+
+ @Test
+ public void testCacheUpdateFail() throws Exception {
+ final String key = "foo";
+ final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry();
+ final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
+
+ when(impl.digestToStorageKey(key)).thenReturn("bar");
+ when(impl.getForUpdateCAS("bar")).thenReturn("stuff");
+ when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue));
+ when(impl.updateCAS(Mockito.eq("bar"), Mockito.eq("stuff"), Mockito.<byte[]>any())).thenReturn(false, false, false, true);
+
+ try {
+ impl.updateEntry(key, new HttpCacheUpdateCallback() {
+
+ @Override
+ public HttpCacheEntry update(final HttpCacheEntry existing) throws ResourceIOException {
+ return updatedValue;
+ }
+
+ });
+ Assert.fail("HttpCacheUpdateException expected");
+ } catch (final HttpCacheUpdateException ignore) {
+ }
+
+ verify(impl, Mockito.times(3)).getForUpdateCAS("bar");
+ verify(impl, Mockito.times(3)).getStorageObject("stuff");
+ verify(impl, Mockito.times(3)).updateCAS(Mockito.eq("bar"), Mockito.eq("stuff"), Mockito.<byte[]>any());
+ }
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestByteArrayCacheEntrySerializer.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestByteArrayCacheEntrySerializer.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestByteArrayCacheEntrySerializer.java
new file mode 100644
index 0000000..b391495
--- /dev/null
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestByteArrayCacheEntrySerializer.java
@@ -0,0 +1,89 @@
+/*
+ * ====================================================================
+ * 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.hc.client5.http.impl.cache;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.nio.charset.Charset;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestByteArrayCacheEntrySerializer {
+
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+
+ private ByteArrayCacheEntrySerializer impl;
+
+ @Before
+ public void setUp() {
+ impl = new ByteArrayCacheEntrySerializer();
+ }
+
+ @Test
+ public void canSerializeEntriesWithVariantMaps() throws Exception {
+ readWriteVerify(makeCacheEntryWithVariantMap("key"));
+ }
+
+ public void readWriteVerify(final HttpCacheStorageEntry writeEntry) throws Exception {
+ // write the entry
+ final byte[] bytes = impl.serialize(writeEntry);
+ // read the entry
+ final HttpCacheStorageEntry readEntry = impl.deserialize(bytes);
+ // compare
+ assertEquals(readEntry.getKey(), writeEntry.getKey());
+ assertThat(readEntry.getContent(), HttpCacheEntryMatcher.equivalent(writeEntry.getContent()));
+ }
+
+ private HttpCacheStorageEntry makeCacheEntryWithVariantMap(final String key) {
+ final Header[] headers = new Header[5];
+ for (int i = 0; i < headers.length; i++) {
+ headers[i] = new BasicHeader("header" + i, "value" + i);
+ }
+ final String body = "Lorem ipsum dolor sit amet";
+
+ final Map<String,String> variantMap = new HashMap<>();
+ variantMap.put("test variant 1","true");
+ variantMap.put("test variant 2","true");
+ final HttpCacheEntry cacheEntry = new HttpCacheEntry(new Date(), new Date(),
+ HttpStatus.SC_OK, headers,
+ new HeapResource(Base64.decodeBase64(body.getBytes(UTF8))), variantMap);
+
+ return new HttpCacheStorageEntry(key, cacheEntry);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/f215fdcd/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestHttpCacheEntrySerializers.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestHttpCacheEntrySerializers.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestHttpCacheEntrySerializers.java
deleted file mode 100644
index e3238ae..0000000
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestHttpCacheEntrySerializers.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * ====================================================================
- * 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.hc.client5.http.impl.cache;
-
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.hc.client5.http.cache.HttpCacheEntry;
-import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
-import org.apache.hc.client5.http.cache.Resource;
-import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.message.BasicHeader;
-import org.junit.Before;
-import org.junit.Test;
-
-public class TestHttpCacheEntrySerializers {
-
- private static final Charset UTF8 = Charset.forName("UTF-8");
-
- private HttpCacheEntrySerializer impl;
-
- @Before
- public void setUp() {
- impl = new DefaultHttpCacheEntrySerializer();
- }
-
- @Test
- public void canSerializeEntriesWithVariantMaps() throws Exception {
- readWriteVerify(makeCacheEntryWithVariantMap());
- }
-
- public void readWriteVerify(final HttpCacheEntry writeEntry) throws IOException {
- // write the entry
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- impl.writeTo(writeEntry, out);
-
- // read the entry
- final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
- final HttpCacheEntry readEntry = impl.readFrom(in);
-
- // compare
- assertTrue(areEqual(readEntry, writeEntry));
- }
-
- private HttpCacheEntry makeCacheEntryWithVariantMap() {
- final Header[] headers = new Header[5];
- for (int i = 0; i < headers.length; i++) {
- headers[i] = new BasicHeader("header" + i, "value" + i);
- }
- final String body = "Lorem ipsum dolor sit amet";
-
- final Map<String,String> variantMap = new HashMap<>();
- variantMap.put("test variant 1","true");
- variantMap.put("test variant 2","true");
- final HttpCacheEntry cacheEntry = new HttpCacheEntry(new Date(), new Date(),
- HttpStatus.SC_OK, headers,
- new HeapResource(Base64.decodeBase64(body.getBytes(UTF8))), variantMap);
-
- return cacheEntry;
- }
-
- private boolean areEqual(final HttpCacheEntry one, final 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;
- }
-
- final byte[] onesByteArray = resourceToBytes(one.getResource());
- final byte[] twosByteArray = resourceToBytes(two.getResource());
-
- if (!Arrays.equals(onesByteArray,twosByteArray)) {
- return false;
- }
-
- final Header[] oneHeaders = one.getAllHeaders();
- final 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(final Resource res) throws IOException {
- final InputStream inputStream = res.getInputStream();
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-
- int readBytes;
- final byte[] bytes = new byte[8096];
- while ((readBytes = inputStream.read(bytes)) > 0) {
- outputStream.write(bytes, 0, readBytes);
- }
-
- final byte[] byteData = outputStream.toByteArray();
-
- inputStream.close();
- outputStream.close();
-
- return byteData;
- }
-}