TimeoutException is controllable via upping timeout properties; don't add more retry loops and stacktraces

This commit is contained in:
Adrian Cole 2012-12-31 17:09:27 -08:00
parent b7f5999a22
commit c39503b682
16 changed files with 63 additions and 519 deletions

View File

@ -887,7 +887,7 @@ public class CloudServersAsyncClientTest extends BaseAsyncClientTest<CloudServer
GetAuth provideGetAuth() { GetAuth provideGetAuth() {
return new GetAuth(null) { return new GetAuth(null) {
@Override @Override
public Auth apply(Credentials in) { public Auth load(Credentials in) {
return new ParseAuthTest().expected(); return new ParseAuthTest().expected();
} }
}; };

View File

@ -116,10 +116,9 @@ import org.jclouds.cloudstack.features.ZoneClient;
import org.jclouds.cloudstack.filters.AddSessionKeyAndJSessionIdToRequest; import org.jclouds.cloudstack.filters.AddSessionKeyAndJSessionIdToRequest;
import org.jclouds.cloudstack.filters.AuthenticationFilter; import org.jclouds.cloudstack.filters.AuthenticationFilter;
import org.jclouds.cloudstack.filters.QuerySigner; import org.jclouds.cloudstack.filters.QuerySigner;
import org.jclouds.cloudstack.functions.LoginWithPasswordCredentials;
import org.jclouds.cloudstack.handlers.CloudStackErrorHandler; import org.jclouds.cloudstack.handlers.CloudStackErrorHandler;
import org.jclouds.cloudstack.handlers.InvalidateSessionAndRetryOn401AndLogoutOnClose; import org.jclouds.cloudstack.handlers.InvalidateSessionAndRetryOn401AndLogoutOnClose;
import org.jclouds.concurrent.RetryOnTimeOutExceptionFunction; import org.jclouds.cloudstack.loaders.LoginWithPasswordCredentials;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
@ -134,10 +133,8 @@ import org.jclouds.rest.RestContext;
import org.jclouds.rest.config.RestClientModule; import org.jclouds.rest.config.RestClientModule;
import org.jclouds.rest.internal.RestContextImpl; import org.jclouds.rest.internal.RestContextImpl;
import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -269,24 +266,13 @@ public class CloudStackRestClientModule extends RestClientModule<CloudStackClien
} }
} }
@Provides
@Singleton
protected Function<Credentials, LoginResponse> makeSureFilterRetriesOnTimeout(
LoginWithPasswordCredentials loginWithPasswordCredentials) {
// we should retry on timeout exception logging in.
return new RetryOnTimeOutExceptionFunction<Credentials, LoginResponse>(loginWithPasswordCredentials);
}
// TODO: not sure we can action the timeout from loginresponse without extra code? modify default
// accordingly
// PROPERTY_SESSION_INTERVAL is default to 60 seconds // PROPERTY_SESSION_INTERVAL is default to 60 seconds
@Provides @Provides
@Singleton @Singleton
public LoadingCache<Credentials, LoginResponse> provideLoginResponseCache( protected LoadingCache<Credentials, LoginResponse> provideLoginResponseCache(
Function<Credentials, LoginResponse> getLoginResponse, LoginWithPasswordCredentials getLoginResponse,
@Named(Constants.PROPERTY_SESSION_INTERVAL) int seconds) { @Named(Constants.PROPERTY_SESSION_INTERVAL) int seconds) {
return CacheBuilder.newBuilder().expireAfterWrite(seconds, TimeUnit.SECONDS).build( return CacheBuilder.newBuilder().expireAfterWrite(seconds, TimeUnit.SECONDS).build(getLoginResponse);
CacheLoader.from(getLoginResponse));
} }
// Temporary conversion of a cache to a supplier until there is a single-element cache // Temporary conversion of a cache to a supplier until there is a single-element cache

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.cloudstack.functions; package org.jclouds.cloudstack.loaders;
import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.hash.Hashing.md5; import static com.google.common.hash.Hashing.md5;
@ -31,10 +31,10 @@ import org.jclouds.cloudstack.domain.LoginResponse;
import org.jclouds.cloudstack.features.SessionClient; import org.jclouds.cloudstack.features.SessionClient;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import com.google.common.base.Function; import com.google.common.cache.CacheLoader;
@Singleton @Singleton
public class LoginWithPasswordCredentials implements Function<Credentials, LoginResponse> { public class LoginWithPasswordCredentials extends CacheLoader<Credentials, LoginResponse> {
private final SessionClient client; private final SessionClient client;
@Inject @Inject
@ -43,7 +43,7 @@ public class LoginWithPasswordCredentials implements Function<Credentials, Login
} }
@Override @Override
public LoginResponse apply(Credentials input) { public LoginResponse load(Credentials input) {
String username = input.identity; String username = input.identity;
String domain = ""; // empty = ROOT domain String domain = ""; // empty = ROOT domain

View File

@ -29,10 +29,11 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.config.BaseComputeServiceContextModule; import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.extensions.ImageExtension; import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier; import org.jclouds.ec2.compute.EC2ComputeService;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.loaders.RegionAndIdToImage; import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier; import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier;
@ -118,12 +119,7 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod
} }
} }
}; };
return new SetAndThrowAuthorizationExceptionSupplier<Image>(rawSupplier, authException).get();
// wrap in retry logic
Supplier<Image> retryingSupplier = new RetryOnTimeOutExceptionSupplier<Image>(
new SetAndThrowAuthorizationExceptionSupplier<Image>(rawSupplier, authException));
return retryingSupplier.get();
} }
}); });

View File

@ -30,7 +30,6 @@ import java.util.concurrent.TimeoutException;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.concurrent.RetryOnTimeOutExceptionFunction;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.ClientError;
@ -47,8 +46,8 @@ import org.jclouds.location.suppliers.derived.RegionIdsFromRegionIdToURIKeySet;
import org.jclouds.location.suppliers.derived.ZoneIdsFromZoneIdToURIKeySet; import org.jclouds.location.suppliers.derived.ZoneIdsFromZoneIdToURIKeySet;
import org.jclouds.location.suppliers.implicit.FirstRegion; import org.jclouds.location.suppliers.implicit.FirstRegion;
import org.jclouds.location.suppliers.implicit.FirstZone; import org.jclouds.location.suppliers.implicit.FirstZone;
import org.jclouds.openstack.keystone.v2_0.AuthenticationAsyncApi;
import org.jclouds.openstack.keystone.v2_0.AuthenticationApi; import org.jclouds.openstack.keystone.v2_0.AuthenticationApi;
import org.jclouds.openstack.keystone.v2_0.AuthenticationAsyncApi;
import org.jclouds.openstack.keystone.v2_0.domain.Access; import org.jclouds.openstack.keystone.v2_0.domain.Access;
import org.jclouds.openstack.keystone.v2_0.functions.AuthenticateApiAccessKeyCredentials; import org.jclouds.openstack.keystone.v2_0.functions.AuthenticateApiAccessKeyCredentials;
import org.jclouds.openstack.keystone.v2_0.functions.AuthenticatePasswordCredentials; import org.jclouds.openstack.keystone.v2_0.functions.AuthenticatePasswordCredentials;
@ -179,9 +178,7 @@ public class KeystoneAuthenticationModule extends AbstractModule {
Map<String, Function<Credentials, Access>> authenticationMethods) { Map<String, Function<Credentials, Access>> authenticationMethods) {
checkArgument(authenticationMethods.containsKey(credentialType), "credential type %s not in supported list: %s", checkArgument(authenticationMethods.containsKey(credentialType), "credential type %s not in supported list: %s",
credentialType, authenticationMethods.keySet()); credentialType, authenticationMethods.keySet());
// regardless of how we authenticate, we should retry if there is a timeout exception logging return authenticationMethods.get(credentialType);
// in.
return new RetryOnTimeOutExceptionFunction<Credentials, Access>(authenticationMethods.get(credentialType));
} }
// TODO: what is the timeout of the session token? modify default accordingly // TODO: what is the timeout of the session token? modify default accordingly

View File

@ -29,7 +29,6 @@ import java.util.concurrent.TimeoutException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.concurrent.RetryOnTimeOutExceptionFunction;
import org.jclouds.date.TimeStamp; import org.jclouds.date.TimeStamp;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
@ -42,14 +41,12 @@ import org.jclouds.openstack.internal.Authentication;
import org.jclouds.openstack.internal.OpenStackAuthAsyncClient; import org.jclouds.openstack.internal.OpenStackAuthAsyncClient;
import org.jclouds.openstack.internal.OpenStackAuthClient; import org.jclouds.openstack.internal.OpenStackAuthClient;
import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.assistedinject.FactoryModuleBuilder;
/** /**
@ -61,8 +58,6 @@ public class OpenStackAuthenticationModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(new TypeLiteral<Function<Credentials, AuthenticationResponse>>() {
}).to(GetAuthenticationResponse.class);
// OpenStackAuthClient is used directly for filters and retry handlers, so let's bind it explicitly // OpenStackAuthClient is used directly for filters and retry handlers, so let's bind it explicitly
bindClientAndAsyncClient(binder(), OpenStackAuthClient.class, OpenStackAuthAsyncClient.class); bindClientAndAsyncClient(binder(), OpenStackAuthClient.class, OpenStackAuthAsyncClient.class);
install(new FactoryModuleBuilder().build(URIFromAuthenticationResponseForService.Factory.class)); install(new FactoryModuleBuilder().build(URIFromAuthenticationResponseForService.Factory.class));
@ -85,15 +80,16 @@ public class OpenStackAuthenticationModule extends AbstractModule {
} }
@Singleton @Singleton
public static class GetAuthenticationResponse extends public static class GetAuthenticationResponse extends CacheLoader<Credentials, AuthenticationResponse> {
RetryOnTimeOutExceptionFunction<Credentials, AuthenticationResponse> { private final OpenStackAuthClient client;
@Inject @Inject
public GetAuthenticationResponse(final OpenStackAuthClient client) { public GetAuthenticationResponse(final OpenStackAuthClient client) {
super(new Function<Credentials, AuthenticationResponse>() { this.client = client;
}
@Override @Override
public AuthenticationResponse apply(Credentials input) { public AuthenticationResponse load(Credentials input) {
return client.authenticate(input.identity, input.credential); return client.authenticate(input.identity, input.credential);
} }
@ -101,17 +97,14 @@ public class OpenStackAuthenticationModule extends AbstractModule {
public String toString() { public String toString() {
return "authenticate()"; return "authenticate()";
} }
});
} }
}
@Provides @Provides
@Singleton @Singleton
public LoadingCache<Credentials, AuthenticationResponse> provideAuthenticationResponseCache( public LoadingCache<Credentials, AuthenticationResponse> provideAuthenticationResponseCache(
Function<Credentials, AuthenticationResponse> getAuthenticationResponse) { GetAuthenticationResponse getAuthenticationResponse) {
return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS).build( return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS).build(getAuthenticationResponse);
CacheLoader.from(getAuthenticationResponse));
} }
@Provides @Provides

View File

@ -28,7 +28,6 @@ import java.util.concurrent.TimeoutException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.concurrent.RetryOnTimeOutExceptionFunction;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.ClientError;
@ -43,14 +42,12 @@ import org.jclouds.openstack.keystone.v1_1.handlers.RetryOnRenew;
import org.jclouds.openstack.keystone.v1_1.suppliers.RegionIdToURIFromAuthForServiceSupplier; import org.jclouds.openstack.keystone.v1_1.suppliers.RegionIdToURIFromAuthForServiceSupplier;
import org.jclouds.openstack.keystone.v1_1.suppliers.V1DefaultRegionIdSupplier; import org.jclouds.openstack.keystone.v1_1.suppliers.V1DefaultRegionIdSupplier;
import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.assistedinject.FactoryModuleBuilder;
/** /**
@ -61,8 +58,6 @@ public class AuthenticationServiceModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(new TypeLiteral<Function<Credentials, Auth>>() {
}).to(GetAuth.class);
// ServiceClient is used directly for filters and retry handlers, so let's bind it explicitly // ServiceClient is used directly for filters and retry handlers, so let's bind it explicitly
bindClientAndAsyncClient(binder(), AuthenticationClient.class, AuthenticationAsyncClient.class); bindClientAndAsyncClient(binder(), AuthenticationClient.class, AuthenticationAsyncClient.class);
install(new FactoryModuleBuilder().implement(RegionIdToURISupplier.class, install(new FactoryModuleBuilder().implement(RegionIdToURISupplier.class,
@ -88,14 +83,17 @@ public class AuthenticationServiceModule extends AbstractModule {
} }
@Singleton @Singleton
public static class GetAuth extends RetryOnTimeOutExceptionFunction<Credentials, Auth> { public static class GetAuth extends CacheLoader<Credentials, Auth> {
private final AuthenticationClient client;
@Inject @Inject
public GetAuth(final AuthenticationClient client) { public GetAuth(final AuthenticationClient client) {
super(new Function<Credentials, Auth>() { this.client = client;
}
@Override @Override
public Auth apply(Credentials input) { public Auth load(Credentials input) {
return client.authenticate(input.identity, input.credential); return client.authenticate(input.identity, input.credential);
} }
@ -103,15 +101,12 @@ public class AuthenticationServiceModule extends AbstractModule {
public String toString() { public String toString() {
return "authenticate()"; return "authenticate()";
} }
});
}
} }
@Provides @Provides
@Singleton @Singleton
public LoadingCache<Credentials, Auth> provideAuthCache(Function<Credentials, Auth> getAuth) { protected LoadingCache<Credentials, Auth> provideAuthCache(GetAuth getAuth) {
return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS).build(CacheLoader.from(getAuth)); return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS).build(getAuth);
} }
@Provides @Provides

View File

@ -52,7 +52,7 @@ public class TestOpenStackAuthenticationModule extends OpenStackAuthenticationMo
} }
@Override @Override
public AuthenticationResponse apply(Credentials input) { public AuthenticationResponse load(Credentials input) {
return new AuthenticationResponse("authToken", ImmutableMap.<String, URI> of( return new AuthenticationResponse("authToken", ImmutableMap.<String, URI> of(
AuthHeaders.SERVER_MANAGEMENT_URL, URI.create("http://endpoint/vapi-version"), AuthHeaders.SERVER_MANAGEMENT_URL, URI.create("http://endpoint/vapi-version"),
AuthHeaders.STORAGE_URL, URI.create("http://storage"))); AuthHeaders.STORAGE_URL, URI.create("http://storage")));

View File

@ -1,77 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.
*/
package org.jclouds.concurrent;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import java.util.concurrent.TimeoutException;
import org.jclouds.util.Throwables2;
import com.google.common.base.Function;
import com.google.common.collect.ForwardingObject;
/**
*
* @author Adrian Cole
*/
public class RetryOnTimeOutExceptionFunction<K,V> extends ForwardingObject implements Function<K,V>{
private final Function<K,V> delegate;
public RetryOnTimeOutExceptionFunction(Function<K,V> delegate) {
this.delegate = checkNotNull(delegate, "delegate");
}
//TODO: backoff limited retry handler
@Override
public V apply(K key) {
TimeoutException ex = null;
for (int i = 0; i < 3; i++) {
try {
ex = null;
return delegate().apply(key);
} catch (Exception e) {
if ((ex = Throwables2.getFirstThrowableOfType(e, TimeoutException.class)) != null)
continue;
throw propagate(e);
}
}
if (ex != null)
throw propagate(ex);
assert false;
return null;
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
protected Function<K,V> delegate() {
return delegate;
}
}

View File

@ -1,65 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.
*/
package org.jclouds.concurrent;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import java.util.concurrent.TimeoutException;
import org.jclouds.util.Throwables2;
import com.google.common.base.Supplier;
/**
*
* @author Adrian Cole
*/
public class RetryOnTimeOutExceptionSupplier<T> implements Supplier<T> {
private final Supplier<T> delegate;
public RetryOnTimeOutExceptionSupplier(Supplier<T> delegate) {
this.delegate = checkNotNull(delegate, "delegate");
}
@Override
public T get() {
TimeoutException ex = null;
for (int i = 0; i < 3; i++) {
try {
ex = null;
return delegate.get();
} catch (Exception e) {
if ((ex = Throwables2.getFirstThrowableOfType(e, TimeoutException.class)) != null)
continue;
throw propagate(e);
}
}
if (ex != null)
propagate(ex);
assert false;
return null;
}
@Override
public String toString() {
return "RetryOnTimeOutExceptionSupplier(" + delegate + ")";
}
}

View File

@ -1,127 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.
*/
package org.jclouds.concurrent;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeoutException;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Function;
/**
* Tests behavior of RetryOnTimeOutExceptionFunction
*
* @author Adrian Cole
*/
@Test(groups = "unit", singleThreaded = true, testName = "RetryOnTimeOutExceptionFunctionTest")
public class RetryOnTimeOutExceptionFunctionTest {
ExecutorService executorService = MoreExecutors.sameThreadExecutor();
@SuppressWarnings("unchecked")
@Test
public void testGetThrowsOriginalExceptionButRetriesOnTimeoutException() throws InterruptedException, ExecutionException {
Function<String, String> delegate = createMock(Function.class);
TimeoutException timeout = createMock(TimeoutException.class);
RuntimeException throwable = new RuntimeException(timeout);
expect(delegate.apply("baz")).andThrow(throwable);
expect(timeout.getCause()).andReturn(null).anyTimes();
expect(delegate.apply("baz")).andThrow(throwable);
expect(delegate.apply("baz")).andThrow(throwable);
replay(delegate);
replay(timeout);
RetryOnTimeOutExceptionFunction<String, String> supplier = new RetryOnTimeOutExceptionFunction<String, String>(
delegate);
try {
supplier.apply("baz");
fail();
} catch (RuntimeException e) {
assertEquals(e.getCause(), timeout);
}
verify(delegate);
verify(timeout);
}
@SuppressWarnings("unchecked")
@Test
public void testGetAllowsTwoFailuresOnTimeoutException() throws InterruptedException, ExecutionException {
Function<String, String> delegate = createMock(Function.class);
TimeoutException timeout = createMock(TimeoutException.class);
RuntimeException throwable = new RuntimeException(timeout);
expect(delegate.apply("baz")).andThrow(throwable);
expect(timeout.getCause()).andReturn(null).anyTimes();
expect(delegate.apply("baz")).andThrow(throwable);
expect(delegate.apply("baz")).andReturn("foo");
replay(delegate);
replay(timeout);
RetryOnTimeOutExceptionFunction<String, String> supplier = new RetryOnTimeOutExceptionFunction<String, String>(
delegate);
assertEquals(supplier.apply("baz"), "foo");
verify(delegate);
verify(timeout);
}
@SuppressWarnings("unchecked")
@Test
public void testGetAllowsNoFailuresOnOtherExceptions() throws InterruptedException, ExecutionException {
Function<String, String> delegate = createMock(Function.class);
AuthorizationException auth = createMock(AuthorizationException.class);
RuntimeException throwable = new RuntimeException(auth);
expect(delegate.apply("baz")).andThrow(throwable);
expect(auth.getCause()).andReturn(null).anyTimes();
replay(delegate);
replay(auth);
RetryOnTimeOutExceptionFunction<String, String> supplier = new RetryOnTimeOutExceptionFunction<String, String>(
delegate);
try {
supplier.apply("baz");
fail();
} catch (RuntimeException e) {
assertEquals(e.getCause(), auth);
}
verify(delegate);
verify(auth);
}
}

View File

@ -1,127 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.
*/
package org.jclouds.concurrent;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeoutException;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
/**
* Tests behavior of RetryOnTimeOutExceptionSupplier
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public class RetryOnTimeOutExceptionSupplierTest {
ExecutorService executorService = MoreExecutors.sameThreadExecutor();
@SuppressWarnings("unchecked")
@Test
public void testGetThrowsOriginalExceptionButRetriesOnTimeoutException() throws InterruptedException, ExecutionException {
Supplier<String> delegate = createMock(Supplier.class);
TimeoutException timeout = createMock(TimeoutException.class);
RuntimeException throwable = new RuntimeException(timeout);
expect(delegate.get()).andThrow(throwable);
expect(timeout.getCause()).andReturn(null).anyTimes();
expect(delegate.get()).andThrow(throwable);
expect(delegate.get()).andThrow(throwable);
replay(delegate);
replay(timeout);
RetryOnTimeOutExceptionSupplier<String> supplier = new RetryOnTimeOutExceptionSupplier<String>(
delegate);
try {
supplier.get();
fail();
} catch (RuntimeException e) {
assertEquals(e.getCause(), timeout);
}
verify(delegate);
verify(timeout);
}
@SuppressWarnings("unchecked")
@Test
public void testGetAllowsTwoFailuresOnTimeoutException() throws InterruptedException, ExecutionException {
Supplier<String> delegate = createMock(Supplier.class);
TimeoutException timeout = createMock(TimeoutException.class);
RuntimeException throwable = new RuntimeException(timeout);
expect(delegate.get()).andThrow(throwable);
expect(timeout.getCause()).andReturn(null).anyTimes();
expect(delegate.get()).andThrow(throwable);
expect(delegate.get()).andReturn("foo");
replay(delegate);
replay(timeout);
RetryOnTimeOutExceptionSupplier<String> supplier = new RetryOnTimeOutExceptionSupplier<String>(
delegate);
assertEquals(supplier.get(), "foo");
verify(delegate);
verify(timeout);
}
@SuppressWarnings("unchecked")
@Test
public void testGetAllowsNoFailuresOnOtherExceptions() throws InterruptedException, ExecutionException {
Supplier<String> delegate = createMock(Supplier.class);
AuthorizationException auth = createMock(AuthorizationException.class);
RuntimeException throwable = new RuntimeException(auth);
expect(delegate.get()).andThrow(throwable);
expect(auth.getCause()).andReturn(null).anyTimes();
replay(delegate);
replay(auth);
RetryOnTimeOutExceptionSupplier<String> supplier = new RetryOnTimeOutExceptionSupplier<String>(
delegate);
try {
supplier.get();
fail();
} catch (RuntimeException e) {
assertEquals(e.getCause(), auth);
}
verify(delegate);
verify(auth);
}
}

View File

@ -19,6 +19,7 @@
package org.jclouds.vcloud.director.v1_5.config; package org.jclouds.vcloud.director.v1_5.config;
import static com.google.common.base.Throwables.propagate; import static com.google.common.base.Throwables.propagate;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient; import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
import java.net.URI; import java.net.URI;
@ -26,8 +27,6 @@ import java.util.Map;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.jclouds.Constants;
import org.jclouds.concurrent.RetryOnTimeOutExceptionFunction;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
@ -84,10 +83,10 @@ import org.jclouds.vcloud.director.v1_5.features.admin.GroupApi;
import org.jclouds.vcloud.director.v1_5.features.admin.GroupAsyncApi; import org.jclouds.vcloud.director.v1_5.features.admin.GroupAsyncApi;
import org.jclouds.vcloud.director.v1_5.features.admin.UserApi; import org.jclouds.vcloud.director.v1_5.features.admin.UserApi;
import org.jclouds.vcloud.director.v1_5.features.admin.UserAsyncApi; import org.jclouds.vcloud.director.v1_5.features.admin.UserAsyncApi;
import org.jclouds.vcloud.director.v1_5.functions.LoginUserInOrgWithPassword;
import org.jclouds.vcloud.director.v1_5.functions.href.ResolveEntity;
import org.jclouds.vcloud.director.v1_5.handlers.InvalidateSessionAndRetryOn401AndLogoutOnClose; import org.jclouds.vcloud.director.v1_5.handlers.InvalidateSessionAndRetryOn401AndLogoutOnClose;
import org.jclouds.vcloud.director.v1_5.handlers.VCloudDirectorErrorHandler; import org.jclouds.vcloud.director.v1_5.handlers.VCloudDirectorErrorHandler;
import org.jclouds.vcloud.director.v1_5.loaders.LoginUserInOrgWithPassword;
import org.jclouds.vcloud.director.v1_5.loaders.ResolveEntity;
import org.jclouds.vcloud.director.v1_5.login.SessionApi; import org.jclouds.vcloud.director.v1_5.login.SessionApi;
import org.jclouds.vcloud.director.v1_5.login.SessionAsyncApi; import org.jclouds.vcloud.director.v1_5.login.SessionAsyncApi;
import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorApi; import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorApi;
@ -96,7 +95,6 @@ import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorAsyncApi;
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.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.inject.Provides; import com.google.inject.Provides;
@ -214,33 +212,15 @@ public class VCloudDirectorRestClientModule extends RestClientModule<VCloudDirec
@Provides @Provides
@Singleton @Singleton
protected Function<String, Entity> makeSureResolveEntityRetriesOnTimeout(ResolveEntity resolveEntity) { LoadingCache<String, Entity> resolveEntityCache(ResolveEntity loader, @Named(PROPERTY_SESSION_INTERVAL) int seconds) {
// we should retry on timeout exception logging in. return CacheBuilder.newBuilder().expireAfterWrite(seconds, TimeUnit.SECONDS).build(loader);
return new RetryOnTimeOutExceptionFunction<String, Entity>(resolveEntity);
} }
@Provides @Provides
@Singleton @Singleton
public LoadingCache<String, Entity> resolveEntityCache(Function<String, Entity> getEntity, LoadingCache<Credentials, SessionWithToken> provideSessionWithTokenCache(LoginUserInOrgWithPassword loader,
@Named(Constants.PROPERTY_SESSION_INTERVAL) int seconds) { @Named(PROPERTY_SESSION_INTERVAL) int seconds) {
return CacheBuilder.newBuilder().expireAfterWrite(seconds, TimeUnit.SECONDS).build(CacheLoader.from(getEntity)); return CacheBuilder.newBuilder().expireAfterWrite(seconds, TimeUnit.SECONDS).build(loader);
}
@Provides
@Singleton
protected Function<Credentials, SessionWithToken> makeSureFilterRetriesOnTimeout(
LoginUserInOrgWithPassword loginWithPasswordCredentials) {
// we should retry on timeout exception logging in.
return new RetryOnTimeOutExceptionFunction<Credentials, SessionWithToken>(loginWithPasswordCredentials);
}
@Provides
@Singleton
public LoadingCache<Credentials, SessionWithToken> provideSessionWithTokenCache(
Function<Credentials, SessionWithToken> getSessionWithToken,
@Named(Constants.PROPERTY_SESSION_INTERVAL) int seconds) {
return CacheBuilder.newBuilder().expireAfterWrite(seconds, TimeUnit.SECONDS).build(
CacheLoader.from(getSessionWithToken));
} }
// Temporary conversion of a cache to a supplier until there is a single-element cache // Temporary conversion of a cache to a supplier until there is a single-element cache

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.vcloud.director.v1_5.functions; package org.jclouds.vcloud.director.v1_5.loaders;
import java.net.URI; import java.net.URI;
@ -28,11 +28,11 @@ import org.jclouds.vcloud.director.v1_5.annotations.Login;
import org.jclouds.vcloud.director.v1_5.domain.SessionWithToken; import org.jclouds.vcloud.director.v1_5.domain.SessionWithToken;
import org.jclouds.vcloud.director.v1_5.login.SessionApi; import org.jclouds.vcloud.director.v1_5.login.SessionApi;
import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.CacheLoader;
@Singleton @Singleton
public class LoginUserInOrgWithPassword implements Function<Credentials, SessionWithToken> { public class LoginUserInOrgWithPassword extends CacheLoader<Credentials, SessionWithToken> {
private final SessionApi api; private final SessionApi api;
private final Supplier<URI> loginUrl; private final Supplier<URI> loginUrl;
@ -43,11 +43,10 @@ public class LoginUserInOrgWithPassword implements Function<Credentials, Session
} }
@Override @Override
public SessionWithToken apply(Credentials input) { public SessionWithToken load(Credentials input) {
String user = input.identity.substring(0, input.identity.lastIndexOf('@')); String user = input.identity.substring(0, input.identity.lastIndexOf('@'));
String org = input.identity.substring(input.identity.lastIndexOf('@') + 1); String org = input.identity.substring(input.identity.lastIndexOf('@') + 1);
String password = input.credential; String password = input.credential;
return api.loginUserInOrgWithPassword(loginUrl.get(), user, org, password); return api.loginUserInOrgWithPassword(loginUrl.get(), user, org, password);
} }

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.vcloud.director.v1_5.functions.href; package org.jclouds.vcloud.director.v1_5.loaders;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -26,10 +26,10 @@ import javax.inject.Singleton;
import org.jclouds.vcloud.director.v1_5.domain.Entity; import org.jclouds.vcloud.director.v1_5.domain.Entity;
import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorApi; import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorApi;
import com.google.common.base.Function; import com.google.common.cache.CacheLoader;
@Singleton @Singleton
public class ResolveEntity implements Function<String, Entity> { public class ResolveEntity extends CacheLoader<String, Entity> {
private final VCloudDirectorApi api; private final VCloudDirectorApi api;
@Inject @Inject
@ -38,7 +38,7 @@ public class ResolveEntity implements Function<String, Entity> {
} }
@Override @Override
public Entity apply(String input) { public Entity load(String input) {
return api.resolveEntity(checkNotNull(input, "urn")); return api.resolveEntity(checkNotNull(input, "urn"));
} }

View File

@ -42,7 +42,6 @@ import org.jclouds.compute.config.BaseComputeServiceContextModule;
import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.Image;
import org.jclouds.compute.extensions.ImageExtension; import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier;
import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass; import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass;
import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata;
@ -147,12 +146,7 @@ public class AWSEC2ComputeServiceContextModule extends BaseComputeServiceContext
} }
} }
}; };
return new SetAndThrowAuthorizationExceptionSupplier<Image>(rawSupplier, authException).get();
// wrap in retry logic
Supplier<Image> retryingSupplier = new RetryOnTimeOutExceptionSupplier<Image>(
new SetAndThrowAuthorizationExceptionSupplier<Image>(rawSupplier, authException));
return retryingSupplier.get();
} }
}); });