mirror of https://github.com/apache/jclouds.git
unwound a few stack traces by making DelegatesToInvocationFunction an invocation handler directly
This commit is contained in:
parent
f18d3b433f
commit
b07984435f
|
@ -32,8 +32,6 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jclouds.reflect.Invocation.Result;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
@ -77,13 +75,13 @@ public final class FunctionalReflection {
|
||||||
* @see com.google.common.reflect.AbstractInvocationHandler#invoke(Object, Method, Object[])
|
* @see com.google.common.reflect.AbstractInvocationHandler#invoke(Object, Method, Object[])
|
||||||
* @see com.google.common.reflect.Reflection#newProxy(Class, java.lang.reflect.InvocationHandler)
|
* @see com.google.common.reflect.Reflection#newProxy(Class, java.lang.reflect.InvocationHandler)
|
||||||
*/
|
*/
|
||||||
public static <T> T newProxy(TypeToken<T> enclosingType, Function<Invocation, Result> invocationFunction) {
|
public static <T> T newProxy(TypeToken<T> enclosingType, Function<Invocation, Object> invocationFunction) {
|
||||||
checkNotNull(enclosingType, "enclosingType");
|
checkNotNull(enclosingType, "enclosingType");
|
||||||
checkNotNull(invocationFunction, "invocationFunction");
|
checkNotNull(invocationFunction, "invocationFunction");
|
||||||
return newProxy(enclosingType.getRawType(), new FunctionalInvocationHandler<T>(enclosingType, invocationFunction));
|
return newProxy(enclosingType.getRawType(), new FunctionalInvocationHandler<T>(enclosingType, invocationFunction));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T newProxy(Class<T> enclosingType, Function<Invocation, Result> invocationFunction) {
|
public static <T> T newProxy(Class<T> enclosingType, Function<Invocation, Object> invocationFunction) {
|
||||||
checkNotNull(invocationFunction, "invocationFunction");
|
checkNotNull(invocationFunction, "invocationFunction");
|
||||||
return newProxy(enclosingType,
|
return newProxy(enclosingType,
|
||||||
new FunctionalInvocationHandler<T>(TypeToken.of(enclosingType), invocationFunction));
|
new FunctionalInvocationHandler<T>(TypeToken.of(enclosingType), invocationFunction));
|
||||||
|
@ -100,9 +98,9 @@ public final class FunctionalReflection {
|
||||||
private static final class FunctionalInvocationHandler<T> extends
|
private static final class FunctionalInvocationHandler<T> extends
|
||||||
com.google.common.reflect.AbstractInvocationHandler {
|
com.google.common.reflect.AbstractInvocationHandler {
|
||||||
private final TypeToken<T> enclosingType;
|
private final TypeToken<T> enclosingType;
|
||||||
private final Function<Invocation, Result> invocationFunction;
|
private final Function<Invocation, Object> invocationFunction;
|
||||||
|
|
||||||
private FunctionalInvocationHandler(TypeToken<T> enclosingType, Function<Invocation, Result> invocationFunction) {
|
private FunctionalInvocationHandler(TypeToken<T> enclosingType, Function<Invocation, Object> invocationFunction) {
|
||||||
this.enclosingType = enclosingType;
|
this.enclosingType = enclosingType;
|
||||||
this.invocationFunction = invocationFunction;
|
this.invocationFunction = invocationFunction;
|
||||||
}
|
}
|
||||||
|
@ -117,17 +115,12 @@ public final class FunctionalReflection {
|
||||||
Invokable<?, Object> invokable = Invokable.from(invoked);
|
Invokable<?, Object> invokable = Invokable.from(invoked);
|
||||||
// not yet support the proxy arg
|
// not yet support the proxy arg
|
||||||
Invocation invocation = Invocation.create(invokable, args);
|
Invocation invocation = Invocation.create(invokable, args);
|
||||||
Result result;
|
|
||||||
try {
|
try {
|
||||||
result = invocationFunction.apply(invocation);
|
return invocationFunction.apply(invocation);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
result = Result.fail(e);
|
propagateIfPossible(e, invocation.getInvokable().getExceptionTypes());
|
||||||
|
throw propagate(e);
|
||||||
}
|
}
|
||||||
if (result.getThrowable().isPresent()) {
|
|
||||||
propagateIfPossible(result.getThrowable().get(), invocation.getInvokable().getExceptionTypes());
|
|
||||||
throw propagate(result.getThrowable().get());
|
|
||||||
}
|
|
||||||
return result.getResult().orNull();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -23,11 +23,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Optional;
|
|
||||||
import com.google.common.reflect.Invokable;
|
import com.google.common.reflect.Invokable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,63 +87,4 @@ public final class Invocation {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s%s", invokable, args);
|
return String.format("%s%s", invokable, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* result of an invocation which is either successful or failed, but not both.
|
|
||||||
*/
|
|
||||||
@Beta
|
|
||||||
public final static class Result {
|
|
||||||
public static Result success(@Nullable Object result) {
|
|
||||||
return new Result(Optional.fromNullable(result), Optional.<Throwable> absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Result fail(Throwable throwable) {
|
|
||||||
return new Result(Optional.absent(), Optional.of(throwable));
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Optional<Object> result;
|
|
||||||
private final Optional<Throwable> throwable;
|
|
||||||
|
|
||||||
private Result(Optional<Object> result, Optional<Throwable> throwable) {
|
|
||||||
this.result = checkNotNull(result, "result");
|
|
||||||
this.throwable = checkNotNull(throwable, "throwable");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* result of{@link Invokable#invoke(Object, Object...)}
|
|
||||||
*/
|
|
||||||
public Optional<Object> getResult() {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* throwable received during {@link Invokable#invoke(Object, Object...)}
|
|
||||||
*/
|
|
||||||
public Optional<Throwable> getThrowable() {
|
|
||||||
return throwable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o)
|
|
||||||
return true;
|
|
||||||
if (o == null || getClass() != o.getClass())
|
|
||||||
return false;
|
|
||||||
Result that = Result.class.cast(o);
|
|
||||||
return equal(this.result.orNull(), that.result.orNull())
|
|
||||||
&& equal(this.throwable.orNull(), that.throwable.orNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(result.orNull(), throwable.orNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Objects.toStringHelper("").omitNullValues().add("result", result.orNull())
|
|
||||||
.add("throwable", throwable.orNull()).toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest.config;
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.reflect.FunctionalReflection;
|
import org.jclouds.rest.internal.DelegatesToInvocationFunction;
|
||||||
import org.jclouds.rest.internal.DelegatingInvocationFunction;
|
|
||||||
import org.jclouds.rest.internal.InvokeHttpMethod;
|
import org.jclouds.rest.internal.InvokeHttpMethod;
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
@ -35,10 +36,10 @@ import com.google.inject.TypeLiteral;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AsyncHttpApiProvider<A> implements Provider<A> {
|
public class AsyncHttpApiProvider<A> implements Provider<A> {
|
||||||
private final Class<? super A> asyncApiType;
|
private final Class<? super A> asyncApiType;
|
||||||
private final DelegatingInvocationFunction<A, A, InvokeHttpMethod<A, A>> httpInvoker;
|
private final DelegatesToInvocationFunction<A, A, InvokeHttpMethod<A, A>> httpInvoker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private AsyncHttpApiProvider(DelegatingInvocationFunction<A, A, InvokeHttpMethod<A, A>> httpInvoker,
|
private AsyncHttpApiProvider(DelegatesToInvocationFunction<A, A, InvokeHttpMethod<A, A>> httpInvoker,
|
||||||
TypeLiteral<A> asyncApiType) {
|
TypeLiteral<A> asyncApiType) {
|
||||||
this.httpInvoker = httpInvoker;
|
this.httpInvoker = httpInvoker;
|
||||||
this.asyncApiType = asyncApiType.getRawType();
|
this.asyncApiType = asyncApiType.getRawType();
|
||||||
|
@ -47,7 +48,6 @@ public class AsyncHttpApiProvider<A> implements Provider<A> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public A get() {
|
public A get() {
|
||||||
return (A) FunctionalReflection.newProxy(asyncApiType, httpInvoker);
|
return (A) Proxy.newProxyInstance(asyncApiType.getClassLoader(), new Class<?>[] { asyncApiType }, httpInvoker);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,15 +18,16 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest.config;
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.reflect.FunctionalReflection;
|
import org.jclouds.rest.internal.DelegatesToInvocationFunction;
|
||||||
import com.google.common.reflect.Invokable;
|
|
||||||
import org.jclouds.rest.internal.DelegatingInvocationFunction;
|
|
||||||
import org.jclouds.rest.internal.InvokeAndCallGetOnFutures;
|
import org.jclouds.rest.internal.InvokeAndCallGetOnFutures;
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.reflect.Invokable;
|
||||||
import com.google.common.reflect.TypeToken;
|
import com.google.common.reflect.TypeToken;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
@ -37,11 +38,11 @@ import com.google.inject.Provider;
|
||||||
public class CallGetOnFuturesProvider<S, A> implements Provider<S> {
|
public class CallGetOnFuturesProvider<S, A> implements Provider<S> {
|
||||||
|
|
||||||
private final Class<? super S> apiType;
|
private final Class<? super S> apiType;
|
||||||
private final DelegatingInvocationFunction<S, A, InvokeAndCallGetOnFutures<A>> syncInvoker;
|
private final DelegatesToInvocationFunction<S, A, InvokeAndCallGetOnFutures<A>> syncInvoker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private CallGetOnFuturesProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
|
private CallGetOnFuturesProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
|
||||||
DelegatingInvocationFunction<S, A, InvokeAndCallGetOnFutures<A>> syncInvoker, Class<S> apiType,
|
DelegatesToInvocationFunction<S, A, InvokeAndCallGetOnFutures<A>> syncInvoker, Class<S> apiType,
|
||||||
Class<A> asyncApiType) {
|
Class<A> asyncApiType) {
|
||||||
this.syncInvoker = syncInvoker;
|
this.syncInvoker = syncInvoker;
|
||||||
this.apiType = apiType;
|
this.apiType = apiType;
|
||||||
|
@ -52,6 +53,6 @@ public class CallGetOnFuturesProvider<S, A> implements Provider<S> {
|
||||||
@Override
|
@Override
|
||||||
@Singleton
|
@Singleton
|
||||||
public S get() {
|
public S get() {
|
||||||
return (S) FunctionalReflection.newProxy(apiType, syncInvoker);
|
return (S) Proxy.newProxyInstance(apiType.getClassLoader(), new Class<?>[] { apiType }, syncInvoker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,15 +18,16 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest.config;
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.reflect.FunctionalReflection;
|
import org.jclouds.rest.internal.DelegatesToInvocationFunction;
|
||||||
import com.google.common.reflect.Invokable;
|
|
||||||
import org.jclouds.rest.internal.DelegatingInvocationFunction;
|
|
||||||
import org.jclouds.rest.internal.InvokeHttpMethod;
|
import org.jclouds.rest.internal.InvokeHttpMethod;
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.reflect.Invokable;
|
||||||
import com.google.common.reflect.TypeToken;
|
import com.google.common.reflect.TypeToken;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
@ -37,11 +38,11 @@ import com.google.inject.Provider;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class HttpApiProvider<S, A> implements Provider<S> {
|
public class HttpApiProvider<S, A> implements Provider<S> {
|
||||||
private final Class<? super S> apiType;
|
private final Class<? super S> apiType;
|
||||||
private final DelegatingInvocationFunction<S, A, InvokeHttpMethod<S, A>> httpInvoker;
|
private final DelegatesToInvocationFunction<S, A, InvokeHttpMethod<S, A>> httpInvoker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private HttpApiProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
|
private HttpApiProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
|
||||||
DelegatingInvocationFunction<S, A, InvokeHttpMethod<S, A>> httpInvoker, Class<S> apiType, Class<A> asyncApiType) {
|
DelegatesToInvocationFunction<S, A, InvokeHttpMethod<S, A>> httpInvoker, Class<S> apiType, Class<A> asyncApiType) {
|
||||||
this.httpInvoker = httpInvoker;
|
this.httpInvoker = httpInvoker;
|
||||||
this.apiType = apiType;
|
this.apiType = apiType;
|
||||||
RestModule.putInvokables(TypeToken.of(apiType), TypeToken.of(asyncApiType), invokables);
|
RestModule.putInvokables(TypeToken.of(apiType), TypeToken.of(asyncApiType), invokables);
|
||||||
|
@ -51,7 +52,7 @@ public class HttpApiProvider<S, A> implements Provider<S> {
|
||||||
@Override
|
@Override
|
||||||
@Singleton
|
@Singleton
|
||||||
public S get() {
|
public S get() {
|
||||||
return (S) FunctionalReflection.newProxy(apiType, httpInvoker);
|
return (S) Proxy.newProxyInstance(apiType.getClassLoader(), new Class<?>[] { apiType }, httpInvoker);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ import javax.inject.Named;
|
||||||
|
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.reflect.Invocation;
|
import org.jclouds.reflect.Invocation;
|
||||||
import org.jclouds.reflect.Invocation.Result;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
@ -46,7 +45,7 @@ import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||||
import com.google.common.util.concurrent.UncheckedTimeoutException;
|
import com.google.common.util.concurrent.UncheckedTimeoutException;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
public class BlockOnFuture implements Function<ListenableFuture<?>, Result> {
|
public class BlockOnFuture implements Function<ListenableFuture<?>, Object> {
|
||||||
|
|
||||||
public static interface Factory {
|
public static interface Factory {
|
||||||
/**
|
/**
|
||||||
|
@ -73,19 +72,19 @@ public class BlockOnFuture implements Function<ListenableFuture<?>, Result> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result apply(ListenableFuture<?> future) {
|
public Object apply(ListenableFuture<?> future) {
|
||||||
Optional<Long> timeoutNanos = timeoutInNanos(invocation.getInvokable(), timeouts);
|
Optional<Long> timeoutNanos = timeoutInNanos(invocation.getInvokable(), timeouts);
|
||||||
return block(future, timeoutNanos);
|
return block(future, timeoutNanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result block(ListenableFuture<?> future, Optional<Long> timeoutNanos) {
|
private Object block(ListenableFuture<?> future, Optional<Long> timeoutNanos) {
|
||||||
try {
|
try {
|
||||||
if (timeoutNanos.isPresent()) {
|
if (timeoutNanos.isPresent()) {
|
||||||
logger.debug(">> blocking on %s for %s", future, timeoutNanos);
|
logger.debug(">> blocking on %s for %s", future, timeoutNanos);
|
||||||
return Result.success(getUninterruptibly(future, timeoutNanos.get(), NANOSECONDS));
|
return getUninterruptibly(future, timeoutNanos.get(), NANOSECONDS);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(">> blocking on %s", future);
|
logger.debug(">> blocking on %s", future);
|
||||||
return Result.success(getUninterruptibly(future));
|
return getUninterruptibly(future);
|
||||||
}
|
}
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
throw propagateCause(e);
|
throw propagateCause(e);
|
||||||
|
|
|
@ -19,25 +19,33 @@
|
||||||
package org.jclouds.rest.internal;
|
package org.jclouds.rest.internal;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Predicates.notNull;
|
||||||
import static com.google.common.base.Throwables.propagate;
|
import static com.google.common.base.Throwables.propagate;
|
||||||
|
import static com.google.common.collect.Iterables.all;
|
||||||
import static com.google.common.collect.Iterables.find;
|
import static com.google.common.collect.Iterables.find;
|
||||||
import static com.google.inject.util.Types.newParameterizedType;
|
import static com.google.inject.util.Types.newParameterizedType;
|
||||||
import static org.jclouds.util.Optionals2.isReturnTypeOptional;
|
import static org.jclouds.util.Optionals2.isReturnTypeOptional;
|
||||||
import static org.jclouds.util.Optionals2.unwrapIfOptional;
|
import static org.jclouds.util.Optionals2.unwrapIfOptional;
|
||||||
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
|
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
|
||||||
|
import static org.jclouds.util.Throwables2.propagateIfPossible;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Qualifier;
|
import javax.inject.Qualifier;
|
||||||
|
|
||||||
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
import org.jclouds.reflect.FunctionalReflection;
|
import org.jclouds.reflect.FunctionalReflection;
|
||||||
import org.jclouds.reflect.Invocation;
|
import org.jclouds.reflect.Invocation;
|
||||||
import org.jclouds.reflect.Invocation.Result;
|
|
||||||
import org.jclouds.reflect.InvocationSuccess;
|
import org.jclouds.reflect.InvocationSuccess;
|
||||||
import com.google.common.reflect.Invokable;
|
|
||||||
import org.jclouds.rest.AuthorizationException;
|
import org.jclouds.rest.AuthorizationException;
|
||||||
import org.jclouds.rest.annotations.Delegate;
|
import org.jclouds.rest.annotations.Delegate;
|
||||||
import org.jclouds.rest.config.SetCaller;
|
import org.jclouds.rest.config.SetCaller;
|
||||||
|
@ -49,6 +57,8 @@ import com.google.common.base.Optional;
|
||||||
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.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.reflect.AbstractInvocationHandler;
|
||||||
|
import com.google.common.reflect.Invokable;
|
||||||
import com.google.common.reflect.TypeToken;
|
import com.google.common.reflect.TypeToken;
|
||||||
import com.google.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
import com.google.inject.ConfigurationException;
|
import com.google.inject.ConfigurationException;
|
||||||
|
@ -60,7 +70,6 @@ import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.util.Types;
|
import com.google.inject.util.Types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param <S>
|
* @param <S>
|
||||||
* The enclosing type of the interface that a dynamic proxy like this implements
|
* The enclosing type of the interface that a dynamic proxy like this implements
|
||||||
* @param <A>
|
* @param <A>
|
||||||
|
@ -69,8 +78,67 @@ import com.google.inject.util.Types;
|
||||||
* The function that implements this dynamic proxy
|
* The function that implements this dynamic proxy
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
public final class DelegatingInvocationFunction<S, A, F extends Function<Invocation, Result>> implements
|
public final class DelegatesToInvocationFunction<S, A, F extends Function<Invocation, Object>> implements
|
||||||
Function<Invocation, Result> {
|
InvocationHandler {
|
||||||
|
|
||||||
|
private static final Object[] NO_ARGS = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code proxy.hashCode()} delegates to {@link AbstractInvocationHandler#hashCode}
|
||||||
|
* <li>{@code proxy.toString()} delegates to {@link AbstractInvocationHandler#toString}
|
||||||
|
* <li>{@code proxy.equals(argument)} returns true if:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code proxy} and {@code argument} are of the same type
|
||||||
|
* <li>and {@link AbstractInvocationHandler#equals} returns true for the {@link InvocationHandler} of
|
||||||
|
* {@code argument}
|
||||||
|
* </ul>
|
||||||
|
* <li>other method calls are dispatched to {@link #handleInvocation}.
|
||||||
|
* </ul>
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final Object invoke(Object proxy, Method invoked, @Nullable Object[] argv) throws Throwable {
|
||||||
|
if (argv == null) {
|
||||||
|
argv = NO_ARGS;
|
||||||
|
}
|
||||||
|
if (argv.length == 0 && invoked.getName().equals("hashCode")) {
|
||||||
|
return hashCode();
|
||||||
|
}
|
||||||
|
if (argv.length == 1 && invoked.getName().equals("equals") && invoked.getParameterTypes()[0] == Object.class) {
|
||||||
|
Object arg = argv[0];
|
||||||
|
return proxy.getClass().isInstance(arg) && equals(Proxy.getInvocationHandler(arg));
|
||||||
|
}
|
||||||
|
if (argv.length == 0 && invoked.getName().equals("toString")) {
|
||||||
|
return toString();
|
||||||
|
}
|
||||||
|
List<Object> args = Arrays.asList(argv);
|
||||||
|
if (all(args, notNull()))
|
||||||
|
args = ImmutableList.copyOf(args);
|
||||||
|
else
|
||||||
|
args = Collections.unmodifiableList(args);
|
||||||
|
Invokable<?, Object> invokable = Invokable.from(invoked);
|
||||||
|
// not yet support the proxy arg
|
||||||
|
Invocation invocation = Invocation.create(invokable, args);
|
||||||
|
try {
|
||||||
|
return handle(invocation);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
propagateIfPossible(e, invocation.getInvokable().getExceptionTypes());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object handle(Invocation invocation) {
|
||||||
|
if (invocation.getInvokable().isAnnotationPresent(Provides.class))
|
||||||
|
return lookupValueFromGuice(invocation.getInvokable());
|
||||||
|
else if (invocation.getInvokable().isAnnotationPresent(Delegate.class))
|
||||||
|
return propagateContextToDelegate(invocation);
|
||||||
|
return methodInvoker.apply(invocation);
|
||||||
|
}
|
||||||
|
|
||||||
private final Injector injector;
|
private final Injector injector;
|
||||||
private final TypeToken<S> enclosingType;
|
private final TypeToken<S> enclosingType;
|
||||||
private final SetCaller setCaller;
|
private final SetCaller setCaller;
|
||||||
|
@ -80,7 +148,7 @@ public final class DelegatingInvocationFunction<S, A, F extends Function<Invocat
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Inject
|
@Inject
|
||||||
DelegatingInvocationFunction(Injector injector, SetCaller setCaller, Map<Class<?>, Class<?>> syncToAsync,
|
DelegatesToInvocationFunction(Injector injector, SetCaller setCaller, Map<Class<?>, Class<?>> syncToAsync,
|
||||||
TypeLiteral<S> enclosingType, Function<InvocationSuccess, Optional<Object>> optionalConverter, F methodInvoker) {
|
TypeLiteral<S> enclosingType, Function<InvocationSuccess, Optional<Object>> optionalConverter, F methodInvoker) {
|
||||||
this.injector = checkNotNull(injector, "injector");
|
this.injector = checkNotNull(injector, "injector");
|
||||||
this.enclosingType = (TypeToken<S>) TypeToken.of(checkNotNull(enclosingType, "enclosingType").getType());
|
this.enclosingType = (TypeToken<S>) TypeToken.of(checkNotNull(enclosingType, "enclosingType").getType());
|
||||||
|
@ -90,22 +158,13 @@ public final class DelegatingInvocationFunction<S, A, F extends Function<Invocat
|
||||||
this.methodInvoker = checkNotNull(methodInvoker, "methodInvoker");
|
this.methodInvoker = checkNotNull(methodInvoker, "methodInvoker");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Result apply(Invocation in) {
|
|
||||||
if (in.getInvokable().isAnnotationPresent(Provides.class))
|
|
||||||
return Result.success(lookupValueFromGuice(in.getInvokable()));
|
|
||||||
else if (in.getInvokable().isAnnotationPresent(Delegate.class))
|
|
||||||
return Result.success(propagateContextToDelegate(in));
|
|
||||||
return methodInvoker.apply(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object propagateContextToDelegate(Invocation caller) {
|
private Object propagateContextToDelegate(Invocation caller) {
|
||||||
Class<?> returnType = unwrapIfOptional(caller.getInvokable().getReturnType());
|
Class<?> returnType = unwrapIfOptional(caller.getInvokable().getReturnType());
|
||||||
Function<Invocation, Result> delegate;
|
Function<Invocation, Object> delegate;
|
||||||
setCaller.enter(enclosingType, caller);
|
setCaller.enter(enclosingType, caller);
|
||||||
try {
|
try {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Key<Function<Invocation, Result>> delegateType = (Key<Function<Invocation, Result>>) methodInvokerFor(returnType);
|
Key<Function<Invocation, Object>> delegateType = (Key<Function<Invocation, Object>>) methodInvokerFor(returnType);
|
||||||
delegate = injector.getInstance(delegateType);
|
delegate = injector.getInstance(delegateType);
|
||||||
} finally {
|
} finally {
|
||||||
setCaller.exit();
|
setCaller.exit();
|
|
@ -19,6 +19,8 @@
|
||||||
package org.jclouds.rest.internal;
|
package org.jclouds.rest.internal;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Throwables.propagate;
|
||||||
|
import static com.google.common.util.concurrent.Futures.getUnchecked;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
@ -27,20 +29,18 @@ import javax.inject.Inject;
|
||||||
|
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.reflect.Invocation;
|
import org.jclouds.reflect.Invocation;
|
||||||
import org.jclouds.reflect.Invocation.Result;
|
|
||||||
import com.google.common.reflect.Invokable;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.reflect.Invokable;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public final class InvokeAndCallGetOnFutures<R> implements Function<Invocation, Result> {
|
public final class InvokeAndCallGetOnFutures<R> implements Function<Invocation, Object> {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private Logger logger = Logger.NULL;
|
private Logger logger = Logger.NULL;
|
||||||
|
@ -62,7 +62,7 @@ public final class InvokeAndCallGetOnFutures<R> implements Function<Invocation,
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public Result apply(Invocation in) {
|
public Object apply(Invocation in) {
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
Invokable target = checkNotNull(sync2AsyncInvokables.getIfPresent(in.getInvokable()), "invokable %s not in %s",
|
Invokable target = checkNotNull(sync2AsyncInvokables.getIfPresent(in.getInvokable()), "invokable %s not in %s",
|
||||||
in.getInvokable(), sync2AsyncInvokables);
|
in.getInvokable(), sync2AsyncInvokables);
|
||||||
|
@ -70,13 +70,13 @@ public final class InvokeAndCallGetOnFutures<R> implements Function<Invocation,
|
||||||
try {
|
try {
|
||||||
returnVal = target.invoke(receiver, in.getArgs().toArray());
|
returnVal = target.invoke(receiver, in.getArgs().toArray());
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
return Result.fail(e);
|
throw propagate(e.getCause());
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
return Result.fail(e);
|
throw new Error("Method became inaccessible: " + toString(), e);
|
||||||
}
|
}
|
||||||
if (!isFuture(target))
|
if (!isFuture(target))
|
||||||
return Result.success(returnVal);
|
return returnVal;
|
||||||
return Result.success(Futures.getUnchecked(ListenableFuture.class.cast(returnVal)));
|
return getUnchecked(ListenableFuture.class.cast(returnVal));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFuture(Invokable<?, ?> target) {
|
private boolean isFuture(Invokable<?, ?> target) {
|
||||||
|
|
|
@ -35,7 +35,6 @@ import org.jclouds.http.HttpCommandExecutorService;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.reflect.Invocation;
|
import org.jclouds.reflect.Invocation;
|
||||||
import org.jclouds.reflect.Invocation.Result;
|
|
||||||
import org.jclouds.rest.InvocationContext;
|
import org.jclouds.rest.InvocationContext;
|
||||||
import org.jclouds.rest.annotations.Fallback;
|
import org.jclouds.rest.annotations.Fallback;
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@ import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
public class InvokeHttpMethod<S, A> implements Function<Invocation, Result> {
|
public class InvokeHttpMethod<S, A> implements Function<Invocation, Object> {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private Logger logger = Logger.NULL;
|
private Logger logger = Logger.NULL;
|
||||||
|
@ -98,9 +97,9 @@ public class InvokeHttpMethod<S, A> implements Function<Invocation, Result> {
|
||||||
});
|
});
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result apply(Invocation in) {
|
public Object apply(Invocation in) {
|
||||||
if (isFuture(in.getInvokable())) {
|
if (isFuture(in.getInvokable())) {
|
||||||
return Result.success(createFuture(in));
|
return createFuture(in);
|
||||||
}
|
}
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
Invokable async = checkNotNull(sync2AsyncInvokables.getIfPresent(in.getInvokable()), "invokable %s not in %s",
|
Invokable async = checkNotNull(sync2AsyncInvokables.getIfPresent(in.getInvokable()), "invokable %s not in %s",
|
||||||
|
|
|
@ -27,9 +27,7 @@ import static org.testng.Assert.assertTrue;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.jclouds.reflect.Invocation.Result;
|
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
@ -45,8 +43,8 @@ public class FunctionalReflectionTest {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Test(expectedExceptions = UnsupportedOperationException.class)
|
@Test(expectedExceptions = UnsupportedOperationException.class)
|
||||||
public void testNullArgsAreAllowedAndUnmodifiable() {
|
public void testNullArgsAreAllowedAndUnmodifiable() {
|
||||||
final Function<Invocation, Result> test = new Function<Invocation, Result>() {
|
final Function<Invocation, Object> test = new Function<Invocation, Object>() {
|
||||||
public Result apply(Invocation e) {
|
public Object apply(Invocation e) {
|
||||||
assertNotNull(e.getArgs());
|
assertNotNull(e.getArgs());
|
||||||
assertNull(e.getArgs().get(0));
|
assertNull(e.getArgs().get(0));
|
||||||
e.getArgs().add("foo");
|
e.getArgs().add("foo");
|
||||||
|
@ -59,8 +57,8 @@ public class FunctionalReflectionTest {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Test(expectedExceptions = UnsupportedOperationException.class)
|
@Test(expectedExceptions = UnsupportedOperationException.class)
|
||||||
public void testImmutableListWhenArgsAreNotNull() {
|
public void testImmutableListWhenArgsAreNotNull() {
|
||||||
final Function<Invocation, Result> test = new Function<Invocation, Result>() {
|
final Function<Invocation, Object> test = new Function<Invocation, Object>() {
|
||||||
public Result apply(Invocation e) {
|
public Object apply(Invocation e) {
|
||||||
assertNotNull(e.getArgs());
|
assertNotNull(e.getArgs());
|
||||||
assertTrue(e.getArgs() instanceof ImmutableList);
|
assertTrue(e.getArgs() instanceof ImmutableList);
|
||||||
assertEquals(e.getArgs().get(0), "foo");
|
assertEquals(e.getArgs().get(0), "foo");
|
||||||
|
@ -73,9 +71,9 @@ public class FunctionalReflectionTest {
|
||||||
|
|
||||||
@Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "io")
|
@Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "io")
|
||||||
public void testPropagatesDeclaredException() throws IOException {
|
public void testPropagatesDeclaredException() throws IOException {
|
||||||
final Function<Invocation, Result> test = new Function<Invocation, Result>() {
|
final Function<Invocation, Object> test = new Function<Invocation, Object>() {
|
||||||
public Result apply(Invocation e) {
|
public Object apply(Invocation e) {
|
||||||
return Result.fail(new IOException("io"));
|
throw new RuntimeException(new IOException("io"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Closeable closeable = FunctionalReflection.newProxy(Closeable.class, test);
|
Closeable closeable = FunctionalReflection.newProxy(Closeable.class, test);
|
||||||
|
@ -87,21 +85,9 @@ public class FunctionalReflectionTest {
|
||||||
*/
|
*/
|
||||||
@Test(expectedExceptions = AssertionError.class, expectedExceptionsMessageRegExp = "assert")
|
@Test(expectedExceptions = AssertionError.class, expectedExceptionsMessageRegExp = "assert")
|
||||||
public void testPropagatesError() throws IOException {
|
public void testPropagatesError() throws IOException {
|
||||||
final Function<Invocation, Result> test = new Function<Invocation, Result>() {
|
final Function<Invocation, Object> test = new Function<Invocation, Object>() {
|
||||||
public Result apply(Invocation e) {
|
public Object apply(Invocation e) {
|
||||||
return Result.fail(new AssertionError("assert"));
|
throw new AssertionError("assert");
|
||||||
}
|
|
||||||
};
|
|
||||||
Closeable closeable = FunctionalReflection.newProxy(Closeable.class, test);
|
|
||||||
closeable.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: coerce things like this to UncheckedTimeoutException and friends
|
|
||||||
@Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = ".*timeout")
|
|
||||||
public void testWrapsDeclaredException() throws IOException {
|
|
||||||
final Function<Invocation, Result> test = new Function<Invocation, Result>() {
|
|
||||||
public Result apply(Invocation e) {
|
|
||||||
return Result.fail(new TimeoutException("timeout"));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Closeable closeable = FunctionalReflection.newProxy(Closeable.class, test);
|
Closeable closeable = FunctionalReflection.newProxy(Closeable.class, test);
|
||||||
|
@ -109,9 +95,9 @@ public class FunctionalReflectionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testToStringEqualsFunction() {
|
public void testToStringEqualsFunction() {
|
||||||
final Function<Invocation, Result> test = new Function<Invocation, Result>() {
|
final Function<Invocation, Object> test = new Function<Invocation, Object>() {
|
||||||
public Result apply(Invocation e) {
|
public Object apply(Invocation e) {
|
||||||
return Result.success("foo");
|
return "foo";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -123,9 +109,9 @@ public class FunctionalReflectionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testHashCodeDifferentiatesOnInterface() {
|
public void testHashCodeDifferentiatesOnInterface() {
|
||||||
final Function<Invocation, Result> test = new Function<Invocation, Result>() {
|
final Function<Invocation, Object> test = new Function<Invocation, Object>() {
|
||||||
public Result apply(Invocation e) {
|
public Object apply(Invocation e) {
|
||||||
return Result.success(null);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
|
Loading…
Reference in New Issue