hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject svn commit: r995257 - in /httpcomponents/httpclient/trunk/httpclient-cache: ./ src/main/java/org/apache/http/impl/client/cache/memcached/ src/test/java/org/apache/http/impl/client/cache/memcached/
Date Wed, 08 Sep 2010 21:17:06 GMT
Author: olegk
Date: Wed Sep  8 21:17:05 2010
New Revision: 995257

URL: http://svn.apache.org/viewvc?rev=995257&view=rev
Log:
HTTPCLIENT-977: memcached implementation for HttpCache
Contributed by Mohammed Azeem Uddin <mohammedazeem_uddin at comcast.com>

Added:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
  (with props)
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
  (with props)
Modified:
    httpcomponents/httpclient/trunk/httpclient-cache/pom.xml

Modified: httpcomponents/httpclient/trunk/httpclient-cache/pom.xml
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/pom.xml?rev=995257&r1=995256&r2=995257&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/pom.xml (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/pom.xml Wed Sep  8 21:17:05 2010
@@ -47,7 +47,17 @@
       <distribution>repo</distribution>
     </license>
   </licenses>
-
+  <repositories>
+    <repository>
+      <id>spy</id>
+      <name>Spy Repository</name>
+      <layout>default</layout>
+      <url>http://bleu.west.spy.net/~dustin/m2repo/</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+  </repositories>
   <dependencies>
     <dependency>
       <groupId>org.apache.httpcomponents</groupId>
@@ -93,6 +103,12 @@
       <scope>compile</scope>
       <optional>true</optional>
     </dependency>
+    <dependency>
+      <groupId>spy</groupId>
+      <artifactId>memcached</artifactId>
+      <version>2.5</version>
+      <optional>true</optional>
+    </dependency>
   </dependencies>
 
   <properties>

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java?rev=995257&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
(added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
Wed Sep  8 21:17:05 2010
@@ -0,0 +1,118 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+
+import net.spy.memcached.CASResponse;
+import net.spy.memcached.CASValue;
+import net.spy.memcached.MemcachedClient;
+import net.spy.memcached.MemcachedClientIF;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.cache.HttpCacheEntrySerializer;
+import org.apache.http.client.cache.HttpCacheUpdateException;
+import org.apache.http.client.cache.HttpCacheStorage;
+import org.apache.http.client.cache.HttpCacheUpdateCallback;
+import org.apache.http.impl.client.cache.CacheConfig;
+import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer;
+
+public class MemcachedHttpCacheStorage implements HttpCacheStorage {
+
+    private MemcachedClientIF client;
+    private HttpCacheEntrySerializer serializer;
+    private final int maxUpdateRetries;
+
+    public MemcachedHttpCacheStorage(InetSocketAddress address) throws IOException {
+        this(new MemcachedClient(address));
+    }
+
+    public MemcachedHttpCacheStorage(MemcachedClientIF cache) {
+        this(cache, new CacheConfig(), new DefaultHttpCacheEntrySerializer());
+    }
+
+    public MemcachedHttpCacheStorage(MemcachedClientIF client, CacheConfig config,
+            HttpCacheEntrySerializer serializer) {
+        this.client = client;
+        this.maxUpdateRetries = config.getMaxUpdateRetries();
+        this.serializer = serializer;
+    }
+
+    public void putEntry(String url, HttpCacheEntry entry) throws IOException  {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        serializer.writeTo(entry, bos);
+        client.set(url, 0, bos.toByteArray());
+    }
+
+    public HttpCacheEntry getEntry(String url) throws IOException {
+        byte[] data = (byte[]) client.get(url);
+        if (null == data)
+            return null;
+        InputStream bis = new ByteArrayInputStream(data);
+        return (HttpCacheEntry) serializer.readFrom(bis);
+    }
+
+    public void removeEntry(String url) throws IOException {
+        client.delete(url);
+    }
+
+    public void updateEntry(String url, HttpCacheUpdateCallback callback)
+            throws HttpCacheUpdateException, IOException {
+        int numRetries = 0;
+        do{
+
+        CASValue<Object> v = client.gets(url);
+        byte[] oldBytes = (v != null) ? (byte[]) v.getValue() : null;
+        HttpCacheEntry existingEntry = null;
+        if (oldBytes != null) {
+            ByteArrayInputStream bis = new ByteArrayInputStream(oldBytes);
+            existingEntry = serializer.readFrom(bis);
+        }
+        HttpCacheEntry updatedEntry = callback.update(existingEntry);
+
+        if (v == null) {
+            putEntry(url, updatedEntry);
+            return;
+
+        } else {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            serializer.writeTo(updatedEntry, bos);
+            CASResponse casResult = client.cas(url, v.getCas(), bos.toByteArray());
+            if (casResult != CASResponse.OK) {
+                 numRetries++;
+            }
+            else return;
+        }
+
+    } while(numRetries <= maxUpdateRetries);
+    throw new HttpCacheUpdateException("Failed to update");
+    }
+}
\ No newline at end of file

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java?rev=995257&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
(added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
Wed Sep  8 21:17:05 2010
@@ -0,0 +1,267 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import junit.framework.TestCase;
+import net.spy.memcached.CASResponse;
+import net.spy.memcached.CASValue;
+import net.spy.memcached.MemcachedClientIF;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.cache.HttpCacheEntrySerializer;
+import org.apache.http.client.cache.HttpCacheUpdateCallback;
+import org.apache.http.client.cache.HttpCacheUpdateException;
+import org.apache.http.impl.client.cache.CacheConfig;
+import org.apache.http.impl.client.cache.CacheEntry;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestMemcachedHttpCacheStorage extends TestCase {
+    private MemcachedHttpCacheStorage impl;
+    private MemcachedClientIF mockMemcachedClient;
+    private HttpCacheEntrySerializer mockSerializer;
+
+    @Before
+    public void setUp() throws Exception {
+        mockMemcachedClient = EasyMock.createMock(MemcachedClientIF.class);
+        mockSerializer = EasyMock.createMock(HttpCacheEntrySerializer.class);
+        CacheConfig config = new CacheConfig();
+        config.setMaxUpdateRetries(1);
+        impl = new MemcachedHttpCacheStorage(mockMemcachedClient, config,
+                mockSerializer);
+    }
+
+    private void replayMocks() {
+        EasyMock.replay(mockMemcachedClient);
+        EasyMock.replay(mockSerializer);
+    }
+
+    private void verifyMocks() {
+        EasyMock.verify(mockMemcachedClient);
+        EasyMock.verify(mockSerializer);
+    }
+
+    @Test
+    public void testCachePut() throws IOException, HttpCacheUpdateException {
+        final String url = "foo";
+        final HttpCacheEntry value = new CacheEntry();
+        mockSerializer.writeTo(EasyMock.isA(HttpCacheEntry.class), EasyMock
+                .isA(OutputStream.class));
+        EasyMock.expect(
+                mockMemcachedClient.set(EasyMock.eq(url), EasyMock.eq(0),
+                        EasyMock.aryEq(new byte[0]))).andReturn(null);
+        replayMocks();
+        impl.putEntry(url, value);
+        verifyMocks();
+    }
+
+    @Test
+    public void testCacheGet() throws UnsupportedEncodingException,
+            IOException, HttpCacheUpdateException {
+        final String url = "foo";
+        final HttpCacheEntry cacheEntry = new CacheEntry();
+        EasyMock.expect(mockMemcachedClient.get(url)).andReturn(new byte[] {});
+        EasyMock.expect(
+                mockSerializer.readFrom(EasyMock.isA(InputStream.class)))
+                .andReturn(cacheEntry);
+        replayMocks();
+        HttpCacheEntry resultingEntry = impl.getEntry(url);
+        verifyMocks();
+        assertSame(cacheEntry, resultingEntry);
+    }
+
+    @Test
+    public void testCacheGetNullEntry() throws IOException {
+        final String url = "foo";
+
+        EasyMock.expect(mockMemcachedClient.get(url)).andReturn(null);
+
+        replayMocks();
+        HttpCacheEntry resultingEntry = impl.getEntry(url);
+        verifyMocks();
+
+        assertNull(resultingEntry);
+    }
+
+    @Test
+    public void testCacheRemove() throws IOException, HttpCacheUpdateException {
+        final String url = "foo";
+        EasyMock.expect(mockMemcachedClient.delete(url)).andReturn(null);
+        replayMocks();
+        impl.removeEntry(url);
+        verifyMocks();
+    }
+
+    @Test
+    public void testCacheUpdateNullEntry() throws IOException,
+            HttpCacheUpdateException {
+        final String url = "foo";
+        final HttpCacheEntry updatedValue = new CacheEntry();
+
+        HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
+            public HttpCacheEntry update(HttpCacheEntry old) {
+                assertNull(old);
+                return updatedValue;
+            }
+        };
+
+        // get empty old entry
+        EasyMock.expect(mockMemcachedClient.gets(url)).andReturn(null);
+        // EasyMock.expect(mockCache.get(key)).andReturn(null);
+
+        // put new entry
+        mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock
+                .isA(OutputStream.class));
+        EasyMock.expect(
+                mockMemcachedClient.set(EasyMock.eq(url), EasyMock.eq(0),
+                        EasyMock.aryEq(new byte[0]))).andReturn(null);
+
+        replayMocks();
+        impl.updateEntry(url, callback);
+        verifyMocks();
+    }
+
+    @Test
+    public void testCacheUpdate() throws IOException, HttpCacheUpdateException {
+        final String url = "foo";
+        final HttpCacheEntry existingValue = new CacheEntry();
+        final HttpCacheEntry updatedValue = new CacheEntry();
+
+        CASValue<Object> v = new CASValue<Object>(1234, new byte[] {});
+
+        HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
+            public HttpCacheEntry update(HttpCacheEntry old) {
+                assertEquals(existingValue, old);
+                return updatedValue;
+            }
+        };
+
+        // get existing old entry
+        EasyMock.expect(mockMemcachedClient.gets(url)).andReturn(v);
+        EasyMock.expect(
+                mockSerializer.readFrom(EasyMock.isA(InputStream.class)))
+                .andReturn(existingValue);
+
+        // update
+        EasyMock.expect(
+                mockMemcachedClient.cas(EasyMock.eq(url), EasyMock.eq(v
+                        .getCas()), EasyMock.aryEq(new byte[0]))).andReturn(
+                CASResponse.OK);
+        mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock
+                .isA(OutputStream.class));
+
+        replayMocks();
+        impl.updateEntry(url, callback);
+        verifyMocks();
+    }
+
+    @Test
+    public void testSingleCacheUpdateRetry() throws IOException,
+            HttpCacheUpdateException {
+        final String url = "foo";
+        final HttpCacheEntry existingValue = new CacheEntry();
+        final HttpCacheEntry updatedValue = new CacheEntry();
+        CASValue<Object> v = new CASValue<Object>(1234, new byte[] {});
+
+        HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
+            public HttpCacheEntry update(HttpCacheEntry old) {
+                assertEquals(existingValue, old);
+                return updatedValue;
+            }
+        };
+        // get existing old entry, will happen twice
+        EasyMock.expect(mockMemcachedClient.gets(url)).andReturn(v).times(2);
+        EasyMock.expect(
+                mockSerializer.readFrom(EasyMock.isA(InputStream.class)))
+                .andReturn(existingValue).times(2);
+
+        // update but fail
+        mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock
+                .isA(OutputStream.class));
+        EasyMock.expectLastCall().times(2);
+        EasyMock.expect(
+                mockMemcachedClient.cas(EasyMock.eq(url), EasyMock.eq(v
+                        .getCas()), EasyMock.aryEq(new byte[0]))).andReturn(
+                CASResponse.NOT_FOUND);
+
+        // update again and succeed
+        EasyMock.expect(
+                mockMemcachedClient.cas(EasyMock.eq(url), EasyMock.eq(v
+                        .getCas()), EasyMock.aryEq(new byte[0]))).andReturn(
+                CASResponse.OK);
+
+        replayMocks();
+        impl.updateEntry(url, callback);
+        verifyMocks();
+    }
+
+    @Test
+    public void testCacheUpdateFail() throws IOException,
+            HttpCacheUpdateException {
+        final String url = "foo";
+        final HttpCacheEntry existingValue = new CacheEntry();
+        final HttpCacheEntry updatedValue = new CacheEntry();
+        CASValue<Object> v = new CASValue<Object>(1234, new byte[] {});
+
+        HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
+            public HttpCacheEntry update(HttpCacheEntry old) {
+                assertEquals(existingValue, old);
+                return updatedValue;
+            }
+        };
+
+        // get existing old entry
+        EasyMock.expect(mockMemcachedClient.gets(url)).andReturn(v).times(2);
+        EasyMock.expect(
+                mockSerializer.readFrom(EasyMock.isA(InputStream.class)))
+                .andReturn(existingValue).times(2);
+
+        // update but fail
+        mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock
+                .isA(OutputStream.class));
+        EasyMock.expectLastCall().times(2);
+        EasyMock.expect(
+                mockMemcachedClient.cas(EasyMock.eq(url), EasyMock.eq(v
+                        .getCas()), EasyMock.aryEq(new byte[0]))).andReturn(
+                CASResponse.NOT_FOUND).times(2);
+
+        replayMocks();
+        try {
+            impl.updateEntry(url, callback);
+            fail("Expected HttpCacheUpdateException");
+        } catch (HttpCacheUpdateException e) {
+        }
+        verifyMocks();
+    }
+
+}

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message