diff --git a/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
index 69f4205122..65af8d2360 100644
--- a/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
+++ b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
@@ -29,59 +29,51 @@ import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.rest.AuthorizationException;
import com.google.common.base.Objects;
+import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ForwardingObject;
-import com.google.common.util.concurrent.UncheckedExecutionException;
/**
* This will retry the supplier if it encounters a timeout exception, but not if it encounters an
* AuthorizationException.
*
- * A shared exception reference is used so that anyone who encounters an authorizationexception will
- * be short-circuited. This prevents accounts from being locked out.
+ * A shared exception reference is used so that anyone who encounters an authorizationexception will be short-circuited.
+ * This prevents accounts from being locked out.
*
* details
- * http://code.google.com/p/google-guice/issues/detail?id=483 guice doesn't remember when singleton
- * providers throw exceptions. in this case, if the supplier fails with an authorization exception,
- * it is called again for each provider method that depends on it. To short-circuit this, we
- * remember the last exception trusting that guice is single-threaded.
+ * http://code.google.com/p/google-guice/issues/detail?id=483 guice doesn't remember when singleton providers throw
+ * exceptions. in this case, if the supplier fails with an authorization exception, it is called again for each provider
+ * method that depends on it. To short-circuit this, we remember the last exception trusting that guice is
+ * single-threaded.
*
- * Note this implementation is folded into the same class, vs being decorated as stacktraces are
- * exceptionally long and difficult to grok otherwise. We use {@link LoadingCache} to deal with
- * concurrency issues related to the supplier.
+ * Note this implementation is folded into the same class, vs being decorated as stacktraces are exceptionally long and
+ * difficult to grok otherwise. We use {@link LoadingCache} to deal with concurrency issues related to the supplier.
*
* @author Adrian Cole
*/
public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier extends ForwardingObject implements
- Supplier {
+ Supplier {
- static class NullValueException extends RuntimeException {
-
- }
-
- static class SetAndThrowAuthorizationExceptionSupplierBackedLoader extends CacheLoader {
+ static class SetAndThrowAuthorizationExceptionSupplierBackedLoader extends CacheLoader> {
private final Supplier delegate;
private final AtomicReference authException;
public SetAndThrowAuthorizationExceptionSupplierBackedLoader(Supplier delegate,
- AtomicReference authException) {
+ AtomicReference authException) {
this.delegate = checkNotNull(delegate, "delegate");
this.authException = checkNotNull(authException, "authException");
}
@Override
- public V load(String key) {
+ public Optional load(String key) {
if (authException.get() != null)
throw authException.get();
try {
- V value = delegate.get();
- if (value == null)
- throw new NullValueException();
- return value;
+ return Optional.fromNullable(delegate.get());
} catch (Exception e) {
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
if (aex != null) {
@@ -102,21 +94,21 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier ext
private final Supplier delegate;
private final long duration;
private final TimeUnit unit;
- private final LoadingCache cache;
+ private final LoadingCache> cache;
public static MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier create(
- AtomicReference authException, Supplier delegate, long duration, TimeUnit unit) {
+ AtomicReference authException, Supplier delegate, long duration, TimeUnit unit) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(authException, delegate, duration,
- unit);
+ unit);
}
MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(AtomicReference authException,
- Supplier delegate, long duration, TimeUnit unit) {
+ Supplier delegate, long duration, TimeUnit unit) {
this.delegate = delegate;
this.duration = duration;
this.unit = unit;
this.cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit)
- .build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader(delegate, authException));
+ .build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader(delegate, authException));
}
@Override
@@ -127,14 +119,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier ext
@Override
public T get() {
try {
- T returnVal = cache.get("FOO");
- return returnVal;
- } catch (UncheckedExecutionException e) {
- NullValueException nullV = getFirstThrowableOfType(e, NullValueException.class);
- if (nullV != null) {
- return null;
- }
- throw propagate(e.getCause());
+ return cache.get("FOO").orNull();
} catch (ExecutionException e) {
throw propagate(e.getCause());
}
@@ -143,7 +128,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier ext
@Override
public String toString() {
return Objects.toStringHelper(this).add("delegate", delegate).add("duration", duration).add("unit", unit)
- .toString();
+ .toString();
}
}
diff --git a/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java b/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
index faa32f8a39..aafdf47b5c 100644
--- a/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
+++ b/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
@@ -18,52 +18,38 @@
*/
package org.jclouds.rest.suppliers;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Suppliers.ofInstance;
+import static com.google.common.util.concurrent.Atomics.newReference;
import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertSame;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.SetAndThrowAuthorizationExceptionSupplierBackedLoader;
import org.testng.annotations.Test;
-import com.google.common.base.Function;
import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.util.concurrent.Atomics;
/**
*
* @author Adrian Cole
*/
-@SuppressWarnings({ "unchecked", "rawtypes" })
@Test(groups = "unit", testName = "MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest")
public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
@Test
public void testLoaderNormal() {
- AtomicReference authException = Atomics.newReference();
- assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader(Suppliers.ofInstance("foo"),
- authException).load("KEY"), "foo");
+ AtomicReference authException = newReference();
+ assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader(ofInstance("foo"),
+ authException).load("KEY").get(), "foo");
assertEquals(authException.get(), null);
}
@Test(expectedExceptions = AuthorizationException.class)
public void testLoaderThrowsAuthorizationExceptionAndAlsoSetsExceptionType() {
- AtomicReference authException = Atomics.newReference();
+ AtomicReference authException = newReference();
try {
new SetAndThrowAuthorizationExceptionSupplierBackedLoader(new Supplier() {
-
- @Override
public String get() {
throw new AuthorizationException();
}
@@ -75,11 +61,9 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
@Test(expectedExceptions = AuthorizationException.class)
public void testLoaderThrowsAuthorizationExceptionAndAlsoSetsExceptionTypeWhenNested() {
- AtomicReference authException = Atomics.newReference();
+ AtomicReference authException = newReference();
try {
new SetAndThrowAuthorizationExceptionSupplierBackedLoader(new Supplier() {
-
- @Override
public String get() {
throw new RuntimeException(new ExecutionException(new AuthorizationException()));
}
@@ -91,11 +75,9 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
@Test(expectedExceptions = RuntimeException.class)
public void testLoaderThrowsOriginalExceptionAndAlsoSetsExceptionTypeWhenNestedAndNotAuthorizationException() {
- AtomicReference authException = Atomics.newReference();
+ AtomicReference authException = newReference();
try {
new SetAndThrowAuthorizationExceptionSupplierBackedLoader(new Supplier() {
-
- @Override
public String get() {
throw new RuntimeException(new IllegalArgumentException("foo"));
}
@@ -104,163 +86,4 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
assertEquals(authException.get().getClass(), RuntimeException.class);
}
}
-
- @Test
- public void testMemoizeKeepsValueForFullDurationWhenDelegateCallIsSlow() {
- final long SLEEP_TIME = 250;
- final long EXPIRATION_TIME = 200;
-
- Supplier slowSupplier = new CountingSupplier() {
-
- @Override
- public Integer get() {
- try {
- Thread.sleep(SLEEP_TIME);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- return super.get();
- }
- };
-
- Supplier memoizedSupplier = new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
- new AtomicReference(), slowSupplier, EXPIRATION_TIME, TimeUnit.MILLISECONDS);
-
- assertEquals(memoizedSupplier.get(), (Integer) 10);
- assertEquals(memoizedSupplier.get(), (Integer) 10);
- }
-
- // =================================
- //
- // TODO Everything below this point is taken from SuppliersTest, to test our version of the
- // Suppliers2.memoizeWithExpiration
- // It should be deleted when we can switch back to using the google
- // Supplier.memoizeWithExpiration.
-
- private static class CountingSupplier implements Supplier {
- transient int calls = 0;
-
- @Override
- public Integer get() {
- calls++;
- return calls * 10;
- }
- }
-
- @Test
- public void testMemoizeWithExpiration() throws InterruptedException {
- CountingSupplier countingSupplier = new CountingSupplier();
-
- Supplier memoizedSupplier = new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
- new AtomicReference(), countingSupplier, 75, TimeUnit.MILLISECONDS);
-
- checkExpiration(countingSupplier, memoizedSupplier);
- }
-
- private void checkExpiration(CountingSupplier countingSupplier, Supplier memoizedSupplier)
- throws InterruptedException {
- // the underlying supplier hasn't executed yet
- assertEquals(0, countingSupplier.calls);
-
- assertEquals(10, (int) memoizedSupplier.get());
- // now it has
- assertEquals(1, countingSupplier.calls);
-
- assertEquals(10, (int) memoizedSupplier.get());
- // it still should only have executed once due to memoization
- assertEquals(1, countingSupplier.calls);
-
- Thread.sleep(150);
-
- assertEquals(20, (int) memoizedSupplier.get());
- // old value expired
- assertEquals(2, countingSupplier.calls);
-
- assertEquals(20, (int) memoizedSupplier.get());
- // it still should only have executed twice due to memoization
- assertEquals(2, countingSupplier.calls);
- }
-
- @Test
- public void testExpiringMemoizedSupplierThreadSafe() throws Throwable {
- Function, Supplier> memoizer = new Function, Supplier>() {
- @Override
- public Supplier apply(Supplier supplier) {
- return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
- new AtomicReference(), supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
- }
- };
- supplierThreadSafe(memoizer);
- }
-
- private void supplierThreadSafe(Function, Supplier> memoizer) throws Throwable {
- final AtomicInteger count = new AtomicInteger(0);
- final AtomicReference thrown = Atomics.newReference(null);
- final int numThreads = 3;
- final Thread[] threads = new Thread[numThreads];
- final long timeout = TimeUnit.SECONDS.toNanos(60);
-
- final Supplier supplier = new Supplier() {
- boolean isWaiting(Thread thread) {
- switch (thread.getState()) {
- case BLOCKED:
- case WAITING:
- case TIMED_WAITING:
- return true;
- default:
- return false;
- }
- }
-
- int waitingThreads() {
- int waitingThreads = 0;
- for (Thread thread : threads) {
- if (isWaiting(thread)) {
- waitingThreads++;
- }
- }
- return waitingThreads;
- }
-
- @Override
- public Boolean get() {
- // Check that this method is called exactly once, by the first
- // thread to synchronize.
- long t0 = System.nanoTime();
- while (waitingThreads() != numThreads - 1) {
- if (System.nanoTime() - t0 > timeout) {
- thrown.set(new TimeoutException("timed out waiting for other threads to block"
- + " synchronizing on supplier"));
- break;
- }
- Thread.yield();
- }
- count.getAndIncrement();
- return Boolean.TRUE;
- }
- };
-
- final Supplier memoizedSupplier = memoizer.apply(supplier);
-
- for (int i = 0; i < numThreads; i++) {
- threads[i] = new Thread() {
- @Override
- public void run() {
- assertSame(Boolean.TRUE, memoizedSupplier.get());
- }
- };
- }
- for (Thread t : threads) {
- t.start();
- }
- for (Thread t : threads) {
- t.join();
- }
-
- if (thrown.get() != null) {
- throw thrown.get();
- }
- assertEquals(1, count.get());
- }
-
}