mirror of https://github.com/apache/jclouds.git
refactored retry/shortcircuit logic from vcloud as it is also used in aws
This commit is contained in:
parent
7f38672a4c
commit
10c2a0e36b
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue