Index: src/main/java/org/apache/http/impl/client/cache/memcached/CheckedOperationTimeoutException.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/memcached/CheckedOperationTimeoutException.java (revision 0) +++ src/main/java/org/apache/http/impl/client/cache/memcached/CheckedOperationTimeoutException.java (revision 0) @@ -0,0 +1,44 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.impl.client.cache.memcached; + +import java.io.IOException; + +public class CheckedOperationTimeoutException extends IOException { + public CheckedOperationTimeoutException(String message) { + super(message); + + } + + public CheckedOperationTimeoutException(String message, Throwable cause) { + super(message, cause); + } + + public CheckedOperationTimeoutException(Throwable cause) { + super(cause); + } +} Index: src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java =================================================================== --- src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java (revision 1226846) +++ src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java (working copy) @@ -35,6 +35,7 @@ import net.spy.memcached.CASResponse; import net.spy.memcached.CASValue; import net.spy.memcached.MemcachedClientIF; +import net.spy.memcached.OperationTimeoutException; import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.HttpCacheEntrySerializer; @@ -87,6 +88,26 @@ } @Test + public void testCachePutThrowsTimeoutException() throws IOException { + final String url = "foo"; + final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); + 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]))).andThrow( + new OperationTimeoutException("op timeout")); + replayMocks(); + try { + impl.putEntry(url, value); + fail("IOException not thrown"); + } catch (IOException ex) { + assertTrue(ex instanceof IOException); + } + verifyMocks(); + } + + @Test public void testCacheGet() throws UnsupportedEncodingException, IOException { final String url = "foo"; @@ -102,6 +123,22 @@ } @Test + public void testCacheGetThrowsTimeoutException() + throws UnsupportedEncodingException, IOException { + final String url = "foo"; + EasyMock.expect(mockMemcachedClient.get(url)).andThrow( + new OperationTimeoutException("op timeout")); + replayMocks(); + try { + impl.getEntry(url); + fail("IOException not thrown"); + } catch (IOException ex) { + assertTrue(ex instanceof IOException); + } + verifyMocks(); + } + + @Test public void testCacheGetNullEntry() throws IOException { final String url = "foo"; @@ -124,6 +161,21 @@ } @Test + public void testCacheRemoveThrowsTimeoutException() throws IOException { + final String url = "foo"; + EasyMock.expect(mockMemcachedClient.delete(url)).andThrow( + new OperationTimeoutException("op timeout")); + replayMocks(); + try { + impl.removeEntry(url); + fail("IOException not thrown"); + } catch (IOException ex) { + assertTrue(ex instanceof IOException); + } + verifyMocks(); + } + + @Test public void testCacheUpdateNullEntry() throws IOException, HttpCacheUpdateException { final String url = "foo"; @@ -187,6 +239,44 @@ } @Test + public void testCacheUpdateThrowsTimeoutException() throws IOException, HttpCacheUpdateException { + final String url = "foo"; + final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); + final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); + + CASValue v = new CASValue(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]))).andThrow( + new OperationTimeoutException("op timeout")); + mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock + .isA(OutputStream.class)); + replayMocks(); + try { + impl.updateEntry(url, callback); + fail("IOException not thrown"); + } catch(IOException ex) { + assertTrue(ex instanceof IOException); + } + verifyMocks(); + } + + @Test public void testSingleCacheUpdateRetry() throws IOException, HttpCacheUpdateException { final String url = "foo"; Index: src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java (revision 1226846) +++ src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java (working copy) @@ -36,6 +36,7 @@ import net.spy.memcached.CASValue; import net.spy.memcached.MemcachedClient; import net.spy.memcached.MemcachedClientIF; +import net.spy.memcached.OperationTimeoutException; import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.HttpCacheEntrySerializer; @@ -74,7 +75,7 @@ */ public class MemcachedHttpCacheStorage implements HttpCacheStorage { - private final MemcachedClientIF client; + private final CheckedMemcachedClient client; private final HttpCacheEntrySerializer serializer; private final int maxUpdateRetries; @@ -112,7 +113,7 @@ */ public MemcachedHttpCacheStorage(MemcachedClientIF client, CacheConfig config, HttpCacheEntrySerializer serializer) { - this.client = client; + this.client = new CheckedMemcachedClient(client); this.maxUpdateRetries = config.getMaxUpdateRetries(); this.serializer = serializer; } @@ -166,4 +167,62 @@ } while(numRetries <= maxUpdateRetries); throw new HttpCacheUpdateException("Failed to update"); } + + /** + * This class wraps the Memcached Client and throws a checked exception + * instead of a runtime exception. The HttpCacheStorage interface + * expects an IOException to be thrown when there are problems with the + * underlying cache storage. Since the memcached client throws a runtime exception, + * OperationTimeoutException, this class catches that exception and will throw an + * CheckedOperationTimeoutException which extends IOException. + */ + private class CheckedMemcachedClient { + private final MemcachedClientIF client; + + public CheckedMemcachedClient(MemcachedClientIF client) { + this.client = client; + } + + public CASResponse cas(String url, long cas, byte[] byteArray) throws IOException { + try { + return client.cas(url, cas, byteArray); + } catch (OperationTimeoutException ex) { + throw new CheckedOperationTimeoutException(ex); + } + } + + public CASValue gets(String url) throws IOException { + try { + return client.gets(url); + } catch (OperationTimeoutException ex) { + throw new CheckedOperationTimeoutException(ex); + } + } + + public void delete(String url) throws IOException { + try { + client.delete(url); + } catch (OperationTimeoutException ex) { + throw new CheckedOperationTimeoutException(ex); + } + } + + public void set(String url, int i, byte[] byteArray) throws IOException { + try { + client.set(url, i, byteArray); + } catch (OperationTimeoutException ex) { + throw new CheckedOperationTimeoutException(ex); + } + + } + + public Object get(String key) throws IOException { + try { + return client.get(key); + } catch (OperationTimeoutException ex) { + throw new CheckedOperationTimeoutException(ex); + } + } + + } }