diff --git a/core/src/main/java/org/jclouds/Constants.java b/core/src/main/java/org/jclouds/Constants.java
index 74e922cfe4..8b601a69d0 100644
--- a/core/src/main/java/org/jclouds/Constants.java
+++ b/core/src/main/java/org/jclouds/Constants.java
@@ -230,4 +230,23 @@ public interface Constants {
*/
public static final String PROPERTY_CREDENTIAL = "jclouds.credential";
+ /**
+ * Long properties
+ *
+ * Overrides timeouts on sync interfaces. Timeout value is in ms.
+ * Here's an example of an override for a single method:
+ *
+ *
+ * #10 seconds
+ * jclouds.timeouts.S3Client.bucketExists=10000
+ *
+ *
+ * Or for all methods:
+ *
+ *
+ * jclouds.timeouts.GridServerClient = 350000
+ *
+ */
+ public static final String PROPERTY_TIMEOUTS_PREFIX = "jclouds.timeouts.";
+
}
diff --git a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java
index 42d7a83f2f..6eb6b68ae9 100644
--- a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java
+++ b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java
@@ -32,6 +32,8 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
import org.jclouds.concurrent.Timeout;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.rest.annotations.Delegate;
@@ -52,9 +54,12 @@ import com.google.inject.ProvisionException;
public class SyncProxy implements InvocationHandler {
@SuppressWarnings("unchecked")
- public static T proxy(Class clazz, SyncProxy proxy) throws IllegalArgumentException, SecurityException,
+ public static T proxy(Class clazz, Object async,
+ @Named("sync") Cache delegateMap,
+ Map, Class>> sync2Async, Map timeouts) throws IllegalArgumentException, SecurityException,
NoSuchMethodException {
- return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class>[] { clazz }, proxy);
+ return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class>[] { clazz },
+ new SyncProxy(clazz, async, delegateMap, sync2Async, timeouts));
}
private final Object delegate;
@@ -67,8 +72,9 @@ public class SyncProxy implements InvocationHandler {
private static final Set objectMethods = ImmutableSet.of(Object.class.getMethods());
@Inject
- public SyncProxy(Class> declaring, Object async,
- @Named("sync") Cache delegateMap, Map, Class>> sync2Async)
+ private SyncProxy(Class> declaring, Object async,
+ @Named("sync") Cache delegateMap, Map,
+ Class>> sync2Async, final Map timeouts)
throws SecurityException, NoSuchMethodException {
this.delegateMap = delegateMap;
this.delegate = async;
@@ -90,13 +96,7 @@ public class SyncProxy implements InvocationHandler {
throw new IllegalArgumentException(String.format(
"method %s has different typed exceptions than delegated method %s", method, delegatedMethod));
if (delegatedMethod.getReturnType().isAssignableFrom(ListenableFuture.class)) {
- if (method.isAnnotationPresent(Timeout.class)) {
- Timeout methodTimeout = method.getAnnotation(Timeout.class);
- long methodNanos = convertToNanos(methodTimeout);
- timeoutMap.put(method, methodNanos);
- } else {
- timeoutMap.put(method, typeNanos);
- }
+ timeoutMap.put(method, getTimeout(method, typeNanos, timeouts));
methodMap.put(method, delegatedMethod);
} else {
syncMethodMap.put(method, delegatedMethod);
@@ -105,6 +105,16 @@ public class SyncProxy implements InvocationHandler {
}
}
+ private Long getTimeout(Method method, long typeNanos, final Map timeouts) {
+ Long timeout = overrideTimeout(method, timeouts);
+ if (timeout == null && method.isAnnotationPresent(Timeout.class)) {
+ Timeout methodTimeout = method.getAnnotation(Timeout.class);
+ timeout = convertToNanos(methodTimeout);
+ }
+ return timeout != null ? timeout : typeNanos;
+
+ }
+
static long convertToNanos(Timeout timeout) {
long methodNanos = TimeUnit.NANOSECONDS.convert(timeout.duration(), timeout.timeUnit());
return methodNanos;
@@ -139,6 +149,19 @@ public class SyncProxy implements InvocationHandler {
}
}
+ // override timeout by values configured in properties(in ms)
+ private Long overrideTimeout(final Method method, final Map timeouts) {
+ if (timeouts == null) {
+ return null;
+ }
+ final String className = declaring.getSimpleName();
+ Long timeout = timeouts.get(className + "." + method.getName());
+ if (timeout == null) {
+ timeout = timeouts.get(className);
+ }
+ return timeout != null ? TimeUnit.MILLISECONDS.toNanos(timeout) : null;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof SyncProxy))
diff --git a/core/src/main/java/org/jclouds/rest/RestContextBuilder.java b/core/src/main/java/org/jclouds/rest/RestContextBuilder.java
index 6df2524fa2..94894c6956 100644
--- a/core/src/main/java/org/jclouds/rest/RestContextBuilder.java
+++ b/core/src/main/java/org/jclouds/rest/RestContextBuilder.java
@@ -36,19 +36,17 @@ import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.Constants.PROPERTY_IDENTITY;
import static org.jclouds.Constants.PROPERTY_ISO3166_CODES;
import static org.jclouds.Constants.PROPERTY_PROVIDER;
+import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
+import java.util.*;
import java.util.Map.Entry;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
+import com.google.common.collect.*;
import org.jclouds.concurrent.MoreExecutors;
import org.jclouds.concurrent.SingleThreaded;
import org.jclouds.concurrent.config.ConfiguresExecutorService;
@@ -72,10 +70,6 @@ import org.jclouds.rest.internal.RestContextImpl;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@@ -117,6 +111,23 @@ public class RestContextBuilder {
return LinkedHashMultimap.create(builder.build());
}
+ @Provides
+ @Singleton
+ @Named("TIMEOUTS")
+ protected Map timeouts() {
+ final ImmutableMap.Builder builder = ImmutableMap. builder();
+ for (final Entry