unwound a few stack traces by making DelegatesToInvocationFunction an invocation handler directly

This commit is contained in:
Adrian Cole 2013-01-13 22:41:06 -08:00
parent f18d3b433f
commit b07984435f
10 changed files with 136 additions and 160 deletions

View File

@ -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

View File

@ -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();
}
}
} }

View File

@ -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);
} }
} }

View File

@ -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);
} }
} }

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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();

View File

@ -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) {

View File

@ -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",

View File

@ -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() {