refactored retry/shortcircuit logic from vcloud as it is also used in aws

This commit is contained in:
Adrian Cole 2010-08-08 17:46:00 -07:00
parent 7f38672a4c
commit 10c2a0e36b
3 changed files with 112 additions and 112 deletions

View File

@ -0,0 +1,75 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.
* ====================================================================
*/
package org.jclouds.rest.suppliers;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier;
import org.jclouds.rest.AuthorizationException;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.base.Throwables;
/**
* This will retry the supplier if it encounters a timeout exception, but not if it encounters an
* AuthorizationException.
* <p/>
* A shared exception reference is used so that anyone who encounters an authorizationexception will
* be short-circuited. This prevents accounts from being locked out.
*
* <h3>details</h3>
* 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.
*
* @author Adrian Cole
*/
public class RetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> implements Supplier<T> {
private final Supplier<T> delegate;
public RetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
final AtomicReference<AuthorizationException> authException, long seconds, final Supplier<T> delegate) {
this.delegate = Suppliers.<T> memoizeWithExpiration(new RetryOnTimeOutExceptionSupplier<T>(new Supplier<T>() {
public T get() {
if (authException.get() != null)
throw authException.get();
try {
return delegate.get();
} catch (AuthorizationException e) {
authException.set(e);
throw e;
} catch (Exception e) {
Throwables.propagate(e);
assert false : e;
return null;
}
}
}), seconds, TimeUnit.SECONDS);
}
@Override
public T get() {
return delegate.get();
}
}

View File

@ -33,13 +33,13 @@ import java.util.Map.Entry;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier;
import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.ClientError;
@ -51,6 +51,7 @@ import org.jclouds.rest.AsyncClientFactory;
import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.config.RestClientModule; import org.jclouds.rest.config.RestClientModule;
import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import org.jclouds.vcloud.VCloudAsyncClient; import org.jclouds.vcloud.VCloudAsyncClient;
import org.jclouds.vcloud.VCloudClient; import org.jclouds.vcloud.VCloudClient;
import org.jclouds.vcloud.VCloudToken; import org.jclouds.vcloud.VCloudToken;
@ -74,7 +75,6 @@ import org.jclouds.vcloud.predicates.TaskSuccess;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -139,39 +139,20 @@ public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A exten
@VDC @VDC
protected Supplier<Map<String, String>> provideVDCtoORG(@Named(PROPERTY_SESSION_INTERVAL) long seconds, protected Supplier<Map<String, String>> provideVDCtoORG(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
final @VDC Supplier<Map<String, Map<String, NamedResource>>> orgToVDCSupplier) { final @VDC Supplier<Map<String, Map<String, NamedResource>>> orgToVDCSupplier) {
return Suppliers.memoizeWithExpiration(new RetryOnTimeOutExceptionSupplier<Map<String, String>>( return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, String>>(authException, seconds,
new Supplier<Map<String, String>>() { new Supplier<Map<String, String>>() {
@Override
public Map<String, String> get() { public Map<String, String> get() {
// http://code.google.com/p/google-guice/issues/detail?id=483 Map<String, String> returnVal = Maps.newLinkedHashMap();
// guice doesn't remember when singleton providers throw for (Entry<String, Map<String, NamedResource>> orgr : orgToVDCSupplier.get().entrySet()) {
// exceptions. for (String vdc : orgr.getValue().keySet()) {
// in this case, if describeRegions fails, it is called returnVal.put(vdc, orgr.getKey());
// again for
// each provider method that depends on it. To
// short-circuit this,
// we remember the last exception trusting that guice is
// single-threaded
if (authException != null)
throw authException;
try {
Map<String, String> returnVal = Maps.newLinkedHashMap();
for (Entry<String, Map<String, NamedResource>> orgr : orgToVDCSupplier.get().entrySet()) {
for (String vdc : orgr.getValue().keySet()) {
returnVal.put(vdc, orgr.getKey());
}
} }
return returnVal;
} catch (AuthorizationException e) {
authException = e;
throw e;
} catch (Exception e) {
Throwables.propagate(e);
assert false : e;
return null;
} }
return returnVal;
} }
});
}), seconds, TimeUnit.SECONDS);
} }
@Provides @Provides
@ -190,31 +171,19 @@ public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A exten
return URI.create(vcloudUri.toASCIIString().replace("/login", "")); return URI.create(vcloudUri.toASCIIString().replace("/login", ""));
} }
protected AuthorizationException authException = null; protected AtomicReference<AuthorizationException> authException = new AtomicReference<AuthorizationException>();
@Provides @Provides
@Singleton @Singleton
protected Supplier<VCloudSession> provideVCloudTokenCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, protected Supplier<VCloudSession> provideVCloudTokenCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
final VCloudLoginAsyncClient login) { final VCloudLoginAsyncClient login) {
return Suppliers.memoizeWithExpiration(new RetryOnTimeOutExceptionSupplier<VCloudSession>( return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier<VCloudSession>(authException, seconds,
new Supplier<VCloudSession>() { new Supplier<VCloudSession>() {
@Override
public VCloudSession get() { public VCloudSession get() {
// http://code.google.com/p/google-guice/issues/detail?id=483
// guice doesn't remember when singleton providers throw
// exceptions.
// in this case, if describeRegions fails, 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
if (authException != null)
throw authException;
try { try {
return login.login().get(10, TimeUnit.SECONDS); return login.login().get(10, TimeUnit.SECONDS);
} catch (AuthorizationException e) {
BaseVCloudRestClientModule.this.authException = e;
throw e;
} catch (Exception e) { } catch (Exception e) {
Throwables.propagate(e); Throwables.propagate(e);
assert false : e; assert false : e;
@ -222,7 +191,7 @@ public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A exten
} }
} }
}), seconds, TimeUnit.SECONDS); });
} }
@Provides @Provides
@ -230,34 +199,14 @@ public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A exten
@VDC @VDC
protected Supplier<Map<String, Map<String, NamedResource>>> provideOrgToVDCCache( protected Supplier<Map<String, Map<String, NamedResource>>> provideOrgToVDCCache(
@Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgNameToVDCSupplier supplier) { @Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgNameToVDCSupplier supplier) {
return Suppliers.memoizeWithExpiration( return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Map<String, NamedResource>>>(
new RetryOnTimeOutExceptionSupplier<Map<String, Map<String, NamedResource>>>( authException, seconds, new Supplier<Map<String, Map<String, NamedResource>>>() {
new Supplier<Map<String, Map<String, NamedResource>>>() { @Override
public Map<String, Map<String, NamedResource>> get() { public Map<String, Map<String, NamedResource>> get() {
// http://code.google.com/p/google-guice/issues/detail?id=483 return supplier.get();
// guice doesn't remember when singleton providers throw }
// exceptions.
// in this case, if describeRegions fails, 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
if (authException != null)
throw authException;
try {
return supplier.get();
} catch (AuthorizationException e) {
authException = e;
throw e;
} catch (Exception e) {
Throwables.propagate(e);
assert false : e;
return null;
}
}
}), seconds, TimeUnit.SECONDS); });
} }
@Singleton @Singleton
@ -348,12 +297,12 @@ public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A exten
@Singleton @Singleton
protected Organization provideOrganization(VCloudClient discovery) throws ExecutionException, TimeoutException, protected Organization provideOrganization(VCloudClient discovery) throws ExecutionException, TimeoutException,
InterruptedException { InterruptedException {
if (authException != null) if (authException.get() != null)
throw authException; throw authException.get();
try { try {
return discovery.getDefaultOrganization(); return discovery.getDefaultOrganization();
} catch (AuthorizationException e) { } catch (AuthorizationException e) {
BaseVCloudRestClientModule.this.authException = e; authException.set(e);
throw e; throw e;
} }
} }
@ -379,15 +328,15 @@ public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A exten
@Singleton @Singleton
protected URI provideDefaultNetwork(VCloudClient client) throws InterruptedException, ExecutionException, protected URI provideDefaultNetwork(VCloudClient client) throws InterruptedException, ExecutionException,
TimeoutException { TimeoutException {
if (authException != null) if (authException.get() != null)
throw authException; throw authException.get();
try { try {
org.jclouds.vcloud.domain.VDC vDC = client.getDefaultVDC(); org.jclouds.vcloud.domain.VDC vDC = client.getDefaultVDC();
Map<String, NamedResource> networks = vDC.getAvailableNetworks(); Map<String, NamedResource> networks = vDC.getAvailableNetworks();
checkState(networks.size() > 0, "No networks present in vDC: " + vDC.getName()); checkState(networks.size() > 0, "No networks present in vDC: " + vDC.getName());
return Iterables.get(networks.values(), 0).getLocation(); return Iterables.get(networks.values(), 0).getLocation();
} catch (AuthorizationException e) { } catch (AuthorizationException e) {
BaseVCloudRestClientModule.this.authException = e; authException.set(e);
throw e; throw e;
} }
} }

View File

@ -22,16 +22,14 @@ import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
import org.jclouds.vcloud.VCloudAsyncClient; import org.jclouds.vcloud.VCloudAsyncClient;
import org.jclouds.vcloud.VCloudClient; import org.jclouds.vcloud.VCloudClient;
@ -45,21 +43,18 @@ import org.jclouds.vcloud.terremark.endpoints.KeysList;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.inject.Provides; import com.google.inject.Provides;
/** /**
* Configures the VCloud authentication service connection, including logging * Configures the VCloud authentication service connection, including logging and http transport.
* and http transport.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@RequiresHttp @RequiresHttp
@ConfiguresRestClient @ConfiguresRestClient
public class TerremarkVCloudExpressRestClientModule extends public class TerremarkVCloudExpressRestClientModule extends
TerremarkRestClientModule<TerremarkVCloudExpressClient, TerremarkVCloudExpressAsyncClient> { TerremarkRestClientModule<TerremarkVCloudExpressClient, TerremarkVCloudExpressAsyncClient> {
public TerremarkVCloudExpressRestClientModule() { public TerremarkVCloudExpressRestClientModule() {
super(TerremarkVCloudExpressClient.class, TerremarkVCloudExpressAsyncClient.class); super(TerremarkVCloudExpressClient.class, TerremarkVCloudExpressAsyncClient.class);
@ -118,34 +113,15 @@ public class TerremarkVCloudExpressRestClientModule extends
@Singleton @Singleton
@KeysList @KeysList
protected Supplier<Map<String, NamedResource>> provideOrgToKeysListCache( protected Supplier<Map<String, NamedResource>> provideOrgToKeysListCache(
@Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgNameToKeysListSupplier supplier) { @Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgNameToKeysListSupplier supplier) {
return Suppliers.memoizeWithExpiration(new RetryOnTimeOutExceptionSupplier<Map<String, NamedResource>>( return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, NamedResource>>(authException,
new Supplier<Map<String, NamedResource>>() { seconds, new Supplier<Map<String, NamedResource>>() {
public Map<String, NamedResource> get() { @Override
// http://code.google.com/p/google-guice/issues/detail?id=483 public Map<String, NamedResource> get() {
// guice doesn't remember when singleton providers throw
// exceptions.
// in this case, if describeRegions fails, 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
if (TerremarkVCloudExpressRestClientModule.this.authException != null)
throw TerremarkVCloudExpressRestClientModule.this.authException;
try {
return supplier.get(); return supplier.get();
} catch (AuthorizationException e) {
TerremarkVCloudExpressRestClientModule.this.authException = e;
throw e;
} catch (Exception e) {
Throwables.propagate(e);
assert false : e;
return null;
} }
}
}), seconds, TimeUnit.SECONDS); });
} }
@Singleton @Singleton