refactored tests and internal code due to removing custom guava code

This commit is contained in:
Adrian Cole 2013-01-08 22:23:47 -08:00
parent 145c2a750c
commit 202b9be5e5
43 changed files with 1251 additions and 2074 deletions

View File

@ -55,7 +55,7 @@ import com.google.common.base.Optional;
public abstract class CallerArg0ToPagedIterable<T, I extends CallerArg0ToPagedIterable<T, I>> implements
Function<IterableWithMarker<T>, PagedIterable<T>>, InvocationContext<I> {
private GeneratedHttpRequest request;
private GeneratedHttpRequest<?> request;
@Override
public PagedIterable<T> apply(IterableWithMarker<T> input) {

View File

@ -66,7 +66,7 @@ import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.ImmutableSet.Builder;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
import com.google.inject.Inject;
/**

View File

@ -1,180 +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.reflect;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.reflect.Invokable;
/**
*
* based on the {@link com.google.reflect.AccessibleObject} copied in as {@link com.google.reflect.Invokable} is package
* private.
*
* @author Adrian Cole
* @since 1.6
*/
class Element extends AccessibleObject implements Member {
private final AccessibleObject accessibleObject;
protected final Member member;
<M extends AccessibleObject & Member> Element( M member) {
this.member = checkNotNull(member, "member");
this.accessibleObject = member;
}
@Override
public final boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return accessibleObject.isAnnotationPresent(annotationClass);
}
@Override
public final <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
return accessibleObject.getAnnotation(annotationClass);
}
@Override
public final Annotation[] getAnnotations() {
return accessibleObject.getAnnotations();
}
@Override
public final Annotation[] getDeclaredAnnotations() {
return accessibleObject.getDeclaredAnnotations();
}
@Override
public final void setAccessible(boolean flag) throws SecurityException {
accessibleObject.setAccessible(flag);
}
@Override
public final boolean isAccessible() {
return accessibleObject.isAccessible();
}
@Override
public Class<?> getDeclaringClass() {
return member.getDeclaringClass();
}
@Override
public final String getName() {
return member.getName();
}
@Override
public final int getModifiers() {
return member.getModifiers();
}
@Override
public final boolean isSynthetic() {
return member.isSynthetic();
}
/** Returns true if the element is public. */
public final boolean isPublic() {
return Modifier.isPublic(getModifiers());
}
/** Returns true if the element is protected. */
public final boolean isProtected() {
return Modifier.isProtected(getModifiers());
}
/** Returns true if the element is package-private. */
public final boolean isPackagePrivate() {
return !isPrivate() && !isPublic() && !isProtected();
}
/** Returns true if the element is private. */
public final boolean isPrivate() {
return Modifier.isPrivate(getModifiers());
}
/** Returns true if the element is static. */
public final boolean isStatic() {
return Modifier.isStatic(getModifiers());
}
/**
* Returns {@code true} if this method is final, per {@code Modifier.isFinal(getModifiers())}.
*
* <p>
* Note that a method may still be effectively "final", or non-overridable when it has no {@code final} keyword. For
* example, it could be private, or it could be declared by a final class. To tell whether a method is overridable,
* use {@link Invokable#isOverridable}.
*/
public final boolean isFinal() {
return Modifier.isFinal(getModifiers());
}
/** Returns true if the method is abstract. */
public final boolean isAbstract() {
return Modifier.isAbstract(getModifiers());
}
/** Returns true if the element is native. */
public final boolean isNative() {
return Modifier.isNative(getModifiers());
}
/** Returns true if the method is synchronized. */
public final boolean isSynchronized() {
return Modifier.isSynchronized(getModifiers());
}
/** Returns true if the field is volatile. */
final boolean isVolatile() {
return Modifier.isVolatile(getModifiers());
}
/** Returns true if the field is transient. */
final boolean isTransient() {
return Modifier.isTransient(getModifiers());
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof Element) {
Element that = (Element) obj;
return member.equals(that.member);
}
return false;
}
@Override
public int hashCode() {
return member.hashCode();
}
@Override
public String toString() {
return member.toString();
}
}

View File

@ -38,6 +38,7 @@ import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.TypeToken;
/**
@ -113,7 +114,7 @@ public final class FunctionalReflection {
args = ImmutableList.copyOf(args);
else
args = Collections.unmodifiableList(args);
Invokable<T, ?> invokable = Invokable.from(enclosingType, invoked);
Invokable<?, Object> invokable = Invokable.from(invoked);
// not yet support the proxy arg
Invocation invocation = Invocation.create(invokable, args);
Result result;

View File

@ -28,9 +28,10 @@ import org.jclouds.javax.annotation.Nullable;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.reflect.Invokable;
/**
* Context needed to call {@link org.jclouds.reflect.Invokable#invoke(Object, Object...)}
* Context needed to call {@link com.google.common.reflect.Invokable#invoke(Object, Object...)}
*
* @author Adrian Cole
*/

View File

@ -29,7 +29,7 @@ import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
/**
* Holds the context of a successful call to {@link org.jclouds.reflect.Invokable#invoke(Object, Object...)}
* Holds the context of a successful call to {@link com.google.common.reflect.Invokable#invoke(Object, Object...)}
*
* @author Adrian Cole
*/

View File

@ -1,347 +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.reflect;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.reflect.TypeToken;
/**
* based on the {@link com.google.reflect.AccessibleObject} copied in as {@link com.google.reflect.Invokable} is package
* private. This adds access to {#link {@link #getEnclosingType()} and folds in ability to lookup methods and
* constructors by name.
*
* @author Adrian Cole
* @since 1.6
*/
@Beta
public abstract class Invokable<T, R> extends Element implements GenericDeclaration {
protected final TypeToken<?> enclosingType;
<M extends AccessibleObject & Member> Invokable(TypeToken<?> enclosingType, M member) {
super(member);
this.enclosingType = checkNotNull(enclosingType, "enclosingType");
}
/** Returns {@link Invokable} of {@code method}. */
public static Invokable<?, Object> from(Method method) {
return from(TypeToken.of(method.getDeclaringClass()), method);
}
/** Returns {@link Invokable} of {@code constructor}. */
public static <T> Invokable<T, T> from(Constructor<T> constructor) {
return from(TypeToken.of(constructor.getDeclaringClass()), constructor);
}
/** Returns {@link Invokable} of {@code method}. */
public static <T> Invokable<T, Object> from(TypeToken<T> enclosingType, Method method) {
return new MethodInvokable<T>(enclosingType, method);
}
/** Returns {@link Invokable} of {@code constructor}. */
public static <T> Invokable<T, T> from(TypeToken<T> enclosingType, Constructor<T> constructor) {
return new ConstructorInvokable<T>(enclosingType, constructor);
}
/**
* different than {@link #getDeclaringClass()} when this is a member of a class it was not declared in.
*/
public TypeToken<?> getEnclosingType() {
return enclosingType;
}
/**
* Returns {@code true} if this is an overridable method. Constructors, private, static or final methods, or methods
* declared by final classes are not overridable.
*/
public abstract boolean isOverridable();
/** Returns {@code true} if this was declared to take a variable number of arguments. */
public abstract boolean isVarArgs();
/**
* Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method and returns the return
* value; or calls the underlying constructor with {@code args} and returns the constructed instance.
*
* @throws IllegalAccessException
* if this {@code Constructor} object enforces Java language access control and the underlying method or
* constructor is inaccessible.
* @throws IllegalArgumentException
* if the number of actual and formal parameters differ; if an unwrapping conversion for primitive
* arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the
* corresponding formal parameter type by a method invocation conversion.
* @throws InvocationTargetException
* if the underlying method or constructor throws an exception.
*/
// All subclasses are owned by us and we'll make sure to get the R type right.
@SuppressWarnings("unchecked")
public final R invoke(@Nullable T receiver, Object... args) throws InvocationTargetException, IllegalAccessException {
return (R) invokeInternal(receiver, checkNotNull(args, "args"));
}
/** Returns the return type of this {@code Invokable}. */
// All subclasses are owned by us and we'll make sure to get the R type right.
@SuppressWarnings("unchecked")
public final TypeToken<? extends R> getReturnType() {
return (TypeToken<? extends R>) TypeToken.of(getGenericReturnType());
}
/**
* Returns all declared parameters of this {@code Invokable}. Note that if this is a constructor of a non-static
* inner class, unlike {@link Constructor#getParameterTypes}, the hidden {@code this} parameter of the enclosing
* class is excluded from the returned parameters.
*/
public final ImmutableList<Parameter> getParameters() {
Type[] parameterTypes = getGenericParameterTypes();
Annotation[][] annotations = getParameterAnnotations();
ImmutableList.Builder<Parameter> builder = ImmutableList.builder();
for (int i = 0; i < parameterTypes.length; i++) {
builder.add(new Parameter(this, i, TypeToken.of(parameterTypes[i]), annotations[i]));
}
return builder.build();
}
/** Returns all declared exception types of this {@code Invokable}. */
public final ImmutableList<TypeToken<? extends Throwable>> getExceptionTypes() {
ImmutableList.Builder<TypeToken<? extends Throwable>> builder = ImmutableList.builder();
for (Type type : getGenericExceptionTypes()) {
// getGenericExceptionTypes() will never return a type that's not exception
@SuppressWarnings("unchecked")
TypeToken<? extends Throwable> exceptionType = (TypeToken<? extends Throwable>) TypeToken.of(type);
builder.add(exceptionType);
}
return builder.build();
}
/**
* Explicitly specifies the return type of this {@code Invokable}. For example:
*
* <pre>
* {
* &#064;code
* Method factoryMethod = Person.class.getMethod(&quot;create&quot;);
* Invokable&lt;?, Person&gt; factory = Invokable.of(getNameMethod).returning(Person.class);
* }
* </pre>
*/
public final <R1 extends R> Invokable<T, R1> returning(Class<R1> returnType) {
return returning(TypeToken.of(returnType));
}
/** Explicitly specifies the return type of this {@code Invokable}. */
public final <R1 extends R> Invokable<T, R1> returning(TypeToken<R1> returnType) {
if (!returnType.isAssignableFrom(getReturnType())) {
throw new IllegalArgumentException("Invokable is known to return " + getReturnType() + ", not " + returnType);
}
@SuppressWarnings("unchecked")
// guarded by previous check
Invokable<T, R1> specialized = (Invokable<T, R1>) this;
return specialized;
}
@SuppressWarnings("unchecked")
// The declaring class is T's raw class, or one of its supertypes.
@Override
public final Class<? super T> getDeclaringClass() {
return (Class<? super T>) super.getDeclaringClass();
}
abstract Object invokeInternal(@Nullable Object receiver, Object[] args) throws InvocationTargetException,
IllegalAccessException;
abstract Type[] getGenericParameterTypes();
/** This should never return a type that's not a subtype of Throwable. */
abstract Type[] getGenericExceptionTypes();
abstract Annotation[][] getParameterAnnotations();
abstract Type getGenericReturnType();
static class MethodInvokable<T> extends Invokable<T, Object> {
private final Method method;
MethodInvokable(TypeToken<?> enclosingType, Method method) {
super(enclosingType, method);
this.method = method;
checkArgument(TypeToken.of(method.getDeclaringClass()).isAssignableFrom(enclosingType),
"%s not declared by %s", method, enclosingType);
}
@Override
final Object invokeInternal(@Nullable Object receiver, Object[] args) throws InvocationTargetException,
IllegalAccessException {
return method.invoke(receiver, args);
}
@Override
Type getGenericReturnType() {
return resolveType(method.getGenericReturnType()).getType();
}
@Override
Type[] getGenericParameterTypes() {
return resolveInPlace(method.getGenericParameterTypes());
}
@Override
Type[] getGenericExceptionTypes() {
return resolveInPlace(method.getGenericExceptionTypes());
}
@Override
final Annotation[][] getParameterAnnotations() {
return method.getParameterAnnotations();
}
@Override
public final TypeVariable<?>[] getTypeParameters() {
return method.getTypeParameters();
}
@Override
public final boolean isOverridable() {
return !(isFinal() || isPrivate() || isStatic() || Modifier.isFinal(getDeclaringClass().getModifiers()));
}
@Override
public final boolean isVarArgs() {
return method.isVarArgs();
}
}
protected TypeToken<?> resolveType(Type type) {
return enclosingType.resolveType(type);
}
protected Type[] resolveInPlace(Type[] types) {
for (int i = 0; i < types.length; i++) {
types[i] = resolveType(types[i]).getType();
}
return types;
}
static class ConstructorInvokable<T> extends Invokable<T, T> {
private final Constructor<?> constructor;
ConstructorInvokable(TypeToken<?> enclosingType, Constructor<?> constructor) {
super(enclosingType, constructor);
this.constructor = constructor;
checkArgument(constructor.getDeclaringClass() == enclosingType.getRawType(), "%s not declared by %s",
constructor, enclosingType.getRawType());
}
@Override
final Object invokeInternal(@Nullable Object receiver, Object[] args) throws InvocationTargetException,
IllegalAccessException {
try {
return constructor.newInstance(args);
} catch (InstantiationException e) {
throw new RuntimeException(constructor + " failed.", e);
}
}
@Override
Type getGenericReturnType() {
return resolveType(constructor.getDeclaringClass()).getType();
}
@Override
Type[] getGenericParameterTypes() {
Type[] types = constructor.getGenericParameterTypes();
Class<?> declaringClass = constructor.getDeclaringClass();
if (!Modifier.isStatic(declaringClass.getModifiers()) && declaringClass.getEnclosingClass() != null) {
if (types.length == constructor.getParameterTypes().length) {
// first parameter is the hidden 'this'
return Arrays.copyOfRange(types, 1, types.length);
}
}
return resolveInPlace(types);
}
@Override
Type[] getGenericExceptionTypes() {
return resolveInPlace(constructor.getGenericExceptionTypes());
}
@Override
final Annotation[][] getParameterAnnotations() {
return constructor.getParameterAnnotations();
}
@Override
public final TypeVariable<?>[] getTypeParameters() {
return constructor.getTypeParameters();
}
@Override
public final boolean isOverridable() {
return false;
}
@Override
public final boolean isVarArgs() {
return constructor.isVarArgs();
}
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Invokable<?, ?> that = Invokable.class.cast(o);
return equal(this.enclosingType, that.enclosingType) && super.equals(o);
}
@Override
public int hashCode() {
return Objects.hashCode(enclosingType, super.hashCode());
}
@Override
public String toString() {
int parametersTypeHashCode = 0;
for (Parameter param : getParameters())
parametersTypeHashCode += param.getType().hashCode();
return String.format("%s.%s[%s]", enclosingType.getRawType().getSimpleName(), getName(), parametersTypeHashCode);
}
}

View File

@ -1,115 +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.reflect;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableList;
import com.google.common.reflect.TypeToken;
/**
*
* based on the {@link com.google.reflect.AccessibleObject} copied in as {@link com.google.reflect.Parameter} is package
* private and decided not to add {@link #getPosition}
*
* @author Adrian Cole
* @since 1.6
*/
@Beta
public final class Parameter implements AnnotatedElement {
private final Invokable<?, ?> declaration;
private final int position;
private final TypeToken<?> type;
private final ImmutableList<Annotation> annotations;
Parameter(Invokable<?, ?> declaration, int position, TypeToken<?> type, Annotation[] annotations) {
this.declaration = declaration;
this.position = position;
this.type = type;
this.annotations = ImmutableList.copyOf(annotations);
}
/** Returns the position of the parameter. */
public int getPosition() {
return position;
}
/** Returns the type of the parameter. */
public TypeToken<?> getType() {
return type;
}
/** Returns the {@link Invokable} that declares this parameter. */
public Invokable<?, ?> getDeclaringInvokable() {
return declaration;
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
return getAnnotation(annotationType) != null;
}
@Override
@Nullable
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
checkNotNull(annotationType);
for (Annotation annotation : annotations) {
if (annotationType.isInstance(annotation)) {
return annotationType.cast(annotation);
}
}
return null;
}
@Override
public Annotation[] getAnnotations() {
return getDeclaredAnnotations();
}
@Override
public Annotation[] getDeclaredAnnotations() {
return annotations.toArray(new Annotation[annotations.size()]);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof Parameter) {
Parameter that = (Parameter) obj;
return position == that.position && declaration.equals(that.declaration);
}
return false;
}
@Override
public int hashCode() {
return position;
}
@Override
public String toString() {
return type + " arg" + position;
}
}

View File

@ -27,12 +27,12 @@ import javax.inject.Inject;
import org.jclouds.predicates.Validator;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Parameter;
import org.jclouds.rest.annotations.ParamValidators;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.reflect.Parameter;
import com.google.inject.Injector;
/**

View File

@ -32,7 +32,7 @@ import org.jclouds.rest.MapBinder;
import org.jclouds.rest.annotations.Payload;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
/**
*

View File

@ -22,7 +22,8 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.reflect.FunctionalReflection;
import org.jclouds.rest.internal.InvokeAsyncApi;
import org.jclouds.rest.internal.DelegatingInvocationFunction;
import org.jclouds.rest.internal.InvokeHttpMethod;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
@ -32,21 +33,21 @@ import com.google.inject.TypeLiteral;
* @author Adrian Cole
*/
@Singleton
public class AsyncClientProvider<A> implements Provider<A> {
private final Class<? super A> asyncClientType;
private final InvokeAsyncApi proxy;
public class AsyncHttpApiProvider<A> implements Provider<A> {
private final Class<? super A> asyncApiType;
private final DelegatingInvocationFunction<A, A, InvokeHttpMethod<A, A>> httpInvoker;
@Inject
private AsyncClientProvider(InvokeAsyncApi proxy, TypeLiteral<A> asyncClientType) {
this.proxy = proxy;
this.asyncClientType = asyncClientType.getRawType();
private AsyncHttpApiProvider(DelegatingInvocationFunction<A, A, InvokeHttpMethod<A, A>> httpInvoker,
TypeLiteral<A> asyncApiType) {
this.httpInvoker = httpInvoker;
this.asyncApiType = asyncApiType.getRawType();
}
@SuppressWarnings("unchecked")
@Override
@Singleton
public A get() {
return (A) FunctionalReflection.newProxy(asyncClientType, proxy);
return (A) FunctionalReflection.newProxy(asyncApiType, httpInvoker);
}
}

View File

@ -45,13 +45,31 @@ public class BinderUtils {
* @param async
* type type that returns {@link ListenableFuture}
*/
public static <S, A> void bindClientAndAsyncClient(Binder binder, Class<S> sync, Class<A> async) {
public static <S, A> void bindHttpApi(Binder binder, Class<S> sync, Class<A> async) {
bindClass(binder, sync);
bindClass(binder, async);
bindAsyncClientProvider(binder, async);
bindClientProvider(binder, sync, async);
bindAsyncHttpApiProvider(binder, async);
bindHttpApiProvider(binder, sync, async);
}
@SuppressWarnings("unchecked")
private static <T> void bindAsyncHttpApiProvider(Binder binder, Class<T> async) {
TypeToken<AsyncHttpApiProvider<T>> token = new TypeToken<AsyncHttpApiProvider<T>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<T>() {
}, async);
binder.bind(async).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
}
@SuppressWarnings("unchecked")
private static <S, A> void bindHttpApiProvider(Binder binder, Class<S> sync, Class<A> async) {
TypeToken<HttpApiProvider<S, A>> token = new TypeToken<HttpApiProvider<S, A>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<S>() {
}, sync).where(new TypeParameter<A>() {
}, async);
binder.bind(sync).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
}
/**
* adds an explicit binding for an interface which synchronously blocks on similar calls to an {@code async} type.
*
@ -66,15 +84,15 @@ public class BinderUtils {
* @param async
* type type that returns {@link ListenableFuture}
*/
public static <S, A> void bindClient(Binder binder, Class<S> sync, Class<A> async) {
public static <S, A> void bindBlockingApi(Binder binder, Class<S> sync, Class<A> async) {
bindClass(binder, sync);
bindClass(binder, async);
bindClientProvider(binder, sync, async);
bindCallGetOnFutures(binder, sync, async);
}
@SuppressWarnings("unchecked")
private static <S, A> void bindClientProvider(Binder binder, Class<S> sync, Class<A> async) {
TypeToken<ClientProvider<S, A>> token = new TypeToken<ClientProvider<S, A>>() {
private static <S, A> void bindCallGetOnFutures(Binder binder, Class<S> sync, Class<A> async) {
TypeToken<CallGetOnFuturesProvider<S, A>> token = new TypeToken<CallGetOnFuturesProvider<S, A>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<S>() {
}, sync).where(new TypeParameter<A>() {
@ -89,19 +107,4 @@ public class BinderUtils {
}.where(new TypeParameter<K>() {
}, sync).getType()))).toInstance(sync);
}
public static <T> void bindAsyncClient(Binder binder, Class<T> async) {
bindClass(binder, async);
bindAsyncClientProvider(binder, async);
}
@SuppressWarnings("unchecked")
private static <T> void bindAsyncClientProvider(Binder binder, Class<T> async) {
TypeToken<AsyncClientProvider<T>> token = new TypeToken<AsyncClientProvider<T>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<T>() {
}, async);
binder.bind(async).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
}
}

View File

@ -0,0 +1,57 @@
/**
* 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.rest.config;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.reflect.FunctionalReflection;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.internal.DelegatingInvocationFunction;
import org.jclouds.rest.internal.InvokeAndCallGetOnFutures;
import com.google.common.cache.Cache;
import com.google.common.reflect.TypeToken;
import com.google.inject.Provider;
/**
* @author Adrian Cole
*/
@Singleton
public class CallGetOnFuturesProvider<S, A> implements Provider<S> {
private final Class<? super S> apiType;
private final DelegatingInvocationFunction<S, A, InvokeAndCallGetOnFutures<A>> syncInvoker;
@Inject
private CallGetOnFuturesProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
DelegatingInvocationFunction<S, A, InvokeAndCallGetOnFutures<A>> syncInvoker, Class<S> apiType,
Class<A> asyncApiType) {
this.syncInvoker = syncInvoker;
this.apiType = apiType;
RestModule.putInvokables(TypeToken.of(apiType), TypeToken.of(asyncApiType), invokables);
}
@SuppressWarnings("unchecked")
@Override
@Singleton
public S get() {
return (S) FunctionalReflection.newProxy(apiType, syncInvoker);
}
}

View File

@ -22,38 +22,36 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.reflect.FunctionalReflection;
import org.jclouds.reflect.Invokable;
import org.jclouds.rest.internal.InvokeSyncApi;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.internal.DelegatingInvocationFunction;
import org.jclouds.rest.internal.InvokeHttpMethod;
import com.google.common.cache.Cache;
import com.google.common.reflect.TypeToken;
import com.google.inject.Provider;
/**
* ClientProvider makes the primary interface for the provider context. ex.
* {@code context.getProviderSpecificContext().getApi()} is created by ClientProvider, which is a singleton
*
* @author Adrian Cole
*/
@Singleton
public class ClientProvider<S, A> implements Provider<S> {
private final InvokeSyncApi.Factory factory;
private final Class<S> syncClientType;
private final A asyncClient;
public class HttpApiProvider<S, A> implements Provider<S> {
private final Class<? super S> apiType;
private final DelegatingInvocationFunction<S, A, InvokeHttpMethod<S, A>> httpInvoker;
@Inject
private ClientProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables, InvokeSyncApi.Factory factory,
Class<S> syncClientType, Class<A> asyncClientType, A asyncClient) {
this.factory = factory;
this.asyncClient = asyncClient;
this.syncClientType = syncClientType;
RestModule.putInvokables(TypeToken.of(syncClientType), TypeToken.of(asyncClientType), invokables);
private HttpApiProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
DelegatingInvocationFunction<S, A, InvokeHttpMethod<S, A>> httpInvoker, Class<S> apiType, Class<A> asyncApiType) {
this.httpInvoker = httpInvoker;
this.apiType = apiType;
RestModule.putInvokables(TypeToken.of(apiType), TypeToken.of(asyncApiType), invokables);
}
@SuppressWarnings("unchecked")
@Override
@Singleton
public S get() {
return FunctionalReflection.newProxy(syncClientType, factory.create(asyncClient));
return (S) FunctionalReflection.newProxy(apiType, httpInvoker);
}
}

View File

@ -19,7 +19,7 @@
package org.jclouds.rest.config;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
import java.lang.reflect.TypeVariable;
import java.util.Map;
@ -87,7 +87,7 @@ public class RestClientModule<S, A> extends RestModule {
@Override
protected void configure() {
super.configure();
bindClientAndAsyncClient(binder(), syncClientType.getRawType(), asyncClientType.getRawType());
bindHttpApi(binder(), syncClientType.getRawType(), asyncClientType.getRawType());
bindErrorHandlers();
bindRetryHandlers();
}

View File

@ -20,12 +20,10 @@ package org.jclouds.rest.config;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Maps.transformValues;
import static com.google.common.util.concurrent.Atomics.newReference;
import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
import static org.jclouds.http.HttpUtils.tryFindHttpMethod;
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
import static org.jclouds.util.Maps2.transformKeys;
import static org.jclouds.util.Predicates2.startsWith;
@ -38,40 +36,27 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.ws.rs.Path;
import org.jclouds.functions.IdentityFunction;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.config.SaxParserModule;
import org.jclouds.internal.FilterStringsBoundToInjectorByName;
import org.jclouds.json.config.GsonModule;
import org.jclouds.location.config.LocationModule;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invokable;
import org.jclouds.reflect.Parameter;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.HttpAsyncClient;
import org.jclouds.rest.HttpClient;
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.rest.internal.InvokeAsyncApi;
import org.jclouds.rest.internal.InvokeFutureAndBlock;
import org.jclouds.rest.internal.InvokeListenableFutureViaHttp;
import org.jclouds.rest.internal.InvokeSyncApi;
import org.jclouds.rest.internal.TransformerForRequest;
import org.jclouds.rest.internal.BlockOnFuture;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
@ -94,42 +79,7 @@ public class RestModule extends AbstractModule {
public RestModule(Map<Class<?>, Class<?>> sync2Async) {
this.sync2Async = sync2Async;
}
@Provides
@Singleton
protected Predicate<Invokable<?, ?>> mapsToAsyncHttpRequest(final Cache<Invokable<?, ?>, Invokable<?, ?>> backing) {
return new Predicate<Invokable<?, ?>>() {
public boolean apply(Invokable<?, ?> in) { // TODO: this is dynamic, but perhaps needs to be cached
return FluentIterable.from(backing.asMap().values()).anyMatch(mapsToAsyncHttpRequest(in));
}
};
}
private static Predicate<Invokable<?, ?>> mapsToAsyncHttpRequest(Invokable<?, ?> toTest) {
final int soughtHash = hashEnclosingTypeNameAndParameters(toTest);
return new Predicate<Invokable<?, ?>>() {
public boolean apply(Invokable<?, ?> in) {
return in.isAnnotationPresent(Path.class) || tryFindHttpMethod(in).isPresent()
|| any(in.getParameters(), new Predicate<Parameter>() {
public boolean apply(Parameter in) {
return in.getType().getRawType().isAssignableFrom(HttpRequest.class);
}
}) && in.getReturnType().getRawType().isAssignableFrom(ListenableFuture.class)
&& soughtHash == hashEnclosingTypeNameAndParameters(in);
}
};
}
/**
* when looking for a match, we ignore the return type
*/
public static int hashEnclosingTypeNameAndParameters(Invokable<?, ?> in) {
int parametersTypeHashCode = 0;
for (Parameter param : in.getParameters())
parametersTypeHashCode += param.getType().hashCode();
return Objects.hashCode(in.getEnclosingType(), in.getName(), parametersTypeHashCode);
}
/**
* seeds well-known invokables.
*/
@ -150,11 +100,11 @@ public class RestModule extends AbstractModule {
if (!objectMethods.contains(invoked)) {
try {
Method delegatedMethod = async.getRawType().getMethod(invoked.getName(), invoked.getParameterTypes());
checkArgument(Arrays.equals(delegatedMethod.getExceptionTypes(), invoked.getExceptionTypes()), "invoked %s has different typed exceptions than delegated invoked %s", invoked,
delegatedMethod);
checkArgument(Arrays.equals(delegatedMethod.getExceptionTypes(), invoked.getExceptionTypes()),
"invoked %s has different typed exceptions than delegated invoked %s", invoked, delegatedMethod);
invoked.setAccessible(true);
delegatedMethod.setAccessible(true);
cache.put(Invokable.from(sync, invoked), Invokable.from(async, delegatedMethod));
cache.put(Invokable.from(invoked), Invokable.from(delegatedMethod));
} catch (SecurityException e) {
throw propagate(e);
} catch (NoSuchMethodException e) {
@ -174,13 +124,11 @@ public class RestModule extends AbstractModule {
}).toInstance(sync2Async);
install(new SaxParserModule());
install(new GsonModule());
install(new SetCaller.Module());
install(new FactoryModuleBuilder().build(BindToJsonPayloadWrappedWith.Factory.class));
install(new FactoryModuleBuilder().build(InvokeAsyncApi.Delegate.Factory.class));
install(new FactoryModuleBuilder().build(InvokeFutureAndBlock.Factory.class));
install(new FactoryModuleBuilder().build(InvokeListenableFutureViaHttp.Caller.Factory.class));
install(new FactoryModuleBuilder().build(BlockOnFuture.Factory.class));
bind(IdentityFunction.class).toInstance(IdentityFunction.INSTANCE);
install(new FactoryModuleBuilder().build(InvokeSyncApi.Factory.class));
bindClientAndAsyncClient(binder(), HttpClient.class, HttpAsyncClient.class);
bindHttpApi(binder(), HttpClient.class, HttpAsyncClient.class);
// this will help short circuit scenarios that can otherwise lock out users
bind(new TypeLiteral<AtomicReference<AuthorizationException>>() {
}).toInstance(authException);
@ -188,10 +136,6 @@ public class RestModule extends AbstractModule {
}).to(FilterStringsBoundToInjectorByName.class);
bind(new TypeLiteral<Function<Predicate<String>, Map<String, String>>>() {
}).to(FilterStringsBoundToInjectorByName.class);
bind(new TypeLiteral<Function<Invocation, ListenableFuture<?>>>() {
}).to(InvokeListenableFutureViaHttp.class);
bind(new TypeLiteral<Function<GeneratedHttpRequest, Function<HttpResponse, ?>>>() {
}).to(TransformerForRequest.class);
installLocations();
}
@ -199,7 +143,8 @@ public class RestModule extends AbstractModule {
@Singleton
@Named("TIMEOUTS")
protected Map<String, Long> timeouts(Function<Predicate<String>, Map<String, String>> filterStringsBoundByName) {
Map<String, String> stringBoundWithTimeoutPrefix = filterStringsBoundByName.apply(startsWith(PROPERTY_TIMEOUTS_PREFIX));
Map<String, String> stringBoundWithTimeoutPrefix = filterStringsBoundByName
.apply(startsWith(PROPERTY_TIMEOUTS_PREFIX));
Map<String, Long> longsByName = transformValues(stringBoundWithTimeoutPrefix, new Function<String, Long>() {
public Long apply(String input) {
return Long.valueOf(String.valueOf(input));

View File

@ -0,0 +1,83 @@
/**
* 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.rest.config;
import static com.google.common.base.Preconditions.checkState;
import org.jclouds.reflect.Invocation;
import com.google.common.reflect.TypeToken;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
/**
* Allows the provider to supply a value set in a threadlocal.
*
* @author Adrian Cole
*/
public class SetCaller {
private final ThreadLocal<TypeToken<?>> callerEnclosingType = new ThreadLocal<TypeToken<?>>();
private final ThreadLocal<Invocation> caller = new ThreadLocal<Invocation>();
public void enter(TypeToken<?> callerEnclosingType, Invocation caller) {
checkState(this.callerEnclosingType.get() == null, "A scoping block is already in progress");
this.callerEnclosingType.set(callerEnclosingType);
this.caller.set(caller);
}
public void exit() {
checkState(caller.get() != null, "No scoping block in progress");
callerEnclosingType.remove();
caller.remove();
}
public static class Module extends AbstractModule {
public void configure() {
SetCaller delegateScope = new SetCaller();
bind(CALLER_ENCLOSING_TYPE).toProvider(delegateScope.new CallerEnclosingTypeProvider());
bind(CALLER_INVOCATION).toProvider(delegateScope.new CallerInvocationProvider());
bind(SetCaller.class).toInstance(delegateScope);
}
}
private static final Key<TypeToken<?>> CALLER_ENCLOSING_TYPE = Key.get(new TypeLiteral<TypeToken<?>>() {
}, Names.named("caller"));
private static final Key<Invocation> CALLER_INVOCATION = Key.get(new TypeLiteral<Invocation>() {
}, Names.named("caller"));
class CallerEnclosingTypeProvider implements Provider<TypeToken<?>> {
@Override
public TypeToken<?> get() {
return callerEnclosingType.get();
}
}
class CallerInvocationProvider implements Provider<Invocation> {
@Override
public Invocation get() {
return caller.get();
}
}
}

View File

@ -1,127 +0,0 @@
package org.jclouds.rest.internal;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.find;
import static com.google.inject.util.Types.newParameterizedType;
import static org.jclouds.util.Optionals2.isReturnTypeOptional;
import static org.jclouds.util.Optionals2.unwrapIfOptional;
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.inject.Qualifier;
import org.jclouds.reflect.FunctionalReflection;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invocation.Result;
import org.jclouds.reflect.InvocationSuccess;
import org.jclouds.reflect.Invokable;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.annotations.Delegate;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.inject.Binding;
import com.google.inject.ConfigurationException;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
@Beta
abstract class BaseInvocationFunction implements Function<Invocation, Result> {
protected final Injector injector;
protected final Function<InvocationSuccess, Optional<Object>> optionalConverter;
protected BaseInvocationFunction(Injector injector, Function<InvocationSuccess, Optional<Object>> optionalConverter) {
this.injector = injector;
this.optionalConverter = optionalConverter;
}
protected abstract Result invoke(Invocation in);
protected abstract Function<Invocation, Result> forwardInvocations(Invocation invocation, Class<?> returnType);
@Override
public Result apply(Invocation invocation) {
if (invocation.getInvokable().isAnnotationPresent(Provides.class))
return Result.success(lookupValueFromGuice(invocation.getInvokable()));
else if (invocation.getInvokable().isAnnotationPresent(Delegate.class))
return Result.success(propagateContextToDelegate(invocation));
return invoke(invocation);
}
private Object propagateContextToDelegate(Invocation invocation) {
Class<?> returnType = unwrapIfOptional(invocation.getInvokable().getReturnType());
Object result = FunctionalReflection.newProxy(returnType, forwardInvocations(invocation, returnType));
if (isReturnTypeOptional(invocation.getInvokable())) {
result = optionalConverter.apply(InvocationSuccess.create(invocation, result));
}
return result;
}
static final Predicate<Annotation> isQualifierPresent = new Predicate<Annotation>() {
public boolean apply(Annotation input) {
return input.annotationType().isAnnotationPresent(Qualifier.class);
}
};
private Object lookupValueFromGuice(Invokable<?, ?> invoked) {
try {
Type genericReturnType = invoked.getReturnType().getType();
try {
Annotation qualifier = find(ImmutableList.copyOf(invoked.getAnnotations()), isQualifierPresent);
return getInstanceOfTypeWithQualifier(genericReturnType, qualifier);
} catch (ProvisionException e) {
throw propagate(e.getCause());
} catch (RuntimeException e) {
return instanceOfTypeOrPropagate(genericReturnType, e);
}
} catch (ProvisionException e) {
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
if (aex != null)
throw aex;
throw e;
}
}
Object instanceOfTypeOrPropagate(Type genericReturnType, RuntimeException e) {
try {
// look for an existing binding
Binding<?> binding = injector.getExistingBinding(Key.get(genericReturnType));
if (binding != null)
return binding.getProvider().get();
// then, try looking via supplier
binding = injector.getExistingBinding(Key.get(newParameterizedType(Supplier.class, genericReturnType)));
if (binding != null)
return Supplier.class.cast(binding.getProvider().get()).get();
// else try to create an instance
return injector.getInstance(Key.get(genericReturnType));
} catch (ConfigurationException ce) {
throw e;
}
}
Object getInstanceOfTypeWithQualifier(Type genericReturnType, Annotation qualifier) {
// look for an existing binding
Binding<?> binding = injector.getExistingBinding(Key.get(genericReturnType, qualifier));
if (binding != null)
return binding.getProvider().get();
// then, try looking via supplier
binding = injector
.getExistingBinding(Key.get(newParameterizedType(Supplier.class, genericReturnType), qualifier));
if (binding != null)
return Supplier.class.cast(binding.getProvider().get()).get();
// else try to create an instance
return injector.getInstance(Key.get(genericReturnType, qualifier));
}
}

View File

@ -20,7 +20,6 @@ package org.jclouds.rest.internal;
import static com.google.common.base.Optional.fromNullable;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@ -33,57 +32,45 @@ import javax.inject.Named;
import org.jclouds.logging.Logger;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invocation.Result;
import org.jclouds.reflect.Invokable;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.assistedinject.Assisted;
public class InvokeFutureAndBlock implements Function<Invocation, Result> {
public class BlockOnFuture implements Function<ListenableFuture<?>, Result> {
public static interface Factory {
/**
* @param receiver
* object whose interface matched {@code declaring} except all invokeds return {@link ListenableFuture}
* @return blocking invocation handler
* @param invocation
* context for how the future was created
*/
InvokeFutureAndBlock create(Object async);
BlockOnFuture create(TypeToken<?> enclosingType, Invocation invocation);
}
@Resource
private Logger logger = Logger.NULL;
private final Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables;
private final Map<String, Long> timeouts;
private final Object receiver;
private final TypeToken<?> enclosingType;
private final Invocation invocation;
@Inject
@VisibleForTesting
InvokeFutureAndBlock(Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables,
@Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted Object receiver) {
this.receiver = receiver;
this.sync2AsyncInvokables = sync2AsyncInvokables;
BlockOnFuture(@Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted TypeToken<?> enclosingType,
@Assisted Invocation invocation) {
this.timeouts = timeouts;
this.enclosingType = enclosingType;
this.invocation = invocation;
}
@Override
public Result apply(Invocation invocation) {
@SuppressWarnings("unchecked")
Invokable<? super Object, ListenableFuture<?>> asyncMethod = Invokable.class.cast(sync2AsyncInvokables
.getIfPresent(invocation.getInvokable()));
try {
ListenableFuture<?> future = asyncMethod.invoke(receiver, invocation.getArgs().toArray());
Optional<Long> timeoutNanos = timeoutInNanos(invocation.getInvokable(), timeouts);
return block(future, timeoutNanos);
} catch (InvocationTargetException e) {
return Result.fail(e);
} catch (IllegalAccessException e) {
return Result.fail(e);
}
public Result apply(ListenableFuture<?> future) {
Optional<Long> timeoutNanos = timeoutInNanos(invocation.getInvokable(), timeouts);
return block(future, timeoutNanos);
}
private Result block(ListenableFuture<?> future, Optional<Long> timeoutNanos) {
@ -106,7 +93,7 @@ public class InvokeFutureAndBlock implements Function<Invocation, Result> {
// override timeout by values configured in properties(in ms)
private Optional<Long> timeoutInNanos(Invokable<?, ?> invoked, Map<String, Long> timeouts) {
String className = invoked.getEnclosingType().getRawType().getSimpleName();
String className = enclosingType.getRawType().getSimpleName();
Optional<Long> timeoutMillis = fromNullable(timeouts.get(className + "." + invoked.getName())).or(
fromNullable(timeouts.get(className))).or(fromNullable(timeouts.get("default")));
if (timeoutMillis.isPresent())

View File

@ -0,0 +1,207 @@
/**
* 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.rest.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.find;
import static com.google.inject.util.Types.newParameterizedType;
import static org.jclouds.util.Optionals2.isReturnTypeOptional;
import static org.jclouds.util.Optionals2.unwrapIfOptional;
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Qualifier;
import org.jclouds.reflect.FunctionalReflection;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invocation.Result;
import org.jclouds.reflect.InvocationSuccess;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.config.SetCaller;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.reflect.TypeToken;
import com.google.inject.Binding;
import com.google.inject.ConfigurationException;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Types;
/**
*
* @param <S>
* The enclosing type of the interface that a dynamic proxy like this implements
* @param <A>
* The enclosing type that is processed by this proxy
* @param <F>
* The function that implements this dynamic proxy
*/
@Beta
public final class DelegatingInvocationFunction<S, A, F extends Function<Invocation, Result>> implements
Function<Invocation, Result> {
private final Injector injector;
private final TypeToken<S> enclosingType;
private final SetCaller setCaller;
private final Map<Class<?>, Class<?>> syncToAsync;
private final Function<InvocationSuccess, Optional<Object>> optionalConverter;
private final F methodInvoker;
@SuppressWarnings("unchecked")
@Inject
DelegatingInvocationFunction(Injector injector, SetCaller setCaller, Map<Class<?>, Class<?>> syncToAsync,
TypeLiteral<S> enclosingType, Function<InvocationSuccess, Optional<Object>> optionalConverter, F methodInvoker) {
this.injector = checkNotNull(injector, "injector");
this.enclosingType = (TypeToken<S>) TypeToken.of(checkNotNull(enclosingType, "enclosingType").getType());
this.setCaller = checkNotNull(setCaller, "setCaller");
this.syncToAsync = checkNotNull(syncToAsync, "syncToAsync");
this.optionalConverter = checkNotNull(optionalConverter, "optionalConverter");
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) {
Class<?> returnType = unwrapIfOptional(caller.getInvokable().getReturnType());
Function<Invocation, Result> delegate;
setCaller.enter(enclosingType, caller);
try {
@SuppressWarnings("unchecked")
Key<Function<Invocation, Result>> delegateType = (Key<Function<Invocation, Result>>) methodInvokerFor(returnType);
delegate = injector.getInstance(delegateType);
} finally {
setCaller.exit();
}
Object result = FunctionalReflection.newProxy(returnType, delegate);
if (isReturnTypeOptional(caller.getInvokable())) {
result = optionalConverter.apply(InvocationSuccess.create(caller, result));
}
return result;
}
/**
* attempts to guess the generic type params for the delegate's invocation function based on the supplied type
*/
private Key<?> methodInvokerFor(Class<?> returnType) {
switch (methodInvoker.getClass().getTypeParameters().length) {
case 0:
return Key.get(methodInvoker.getClass());
case 1:
return Key.get(Types.newParameterizedType(methodInvoker.getClass(), returnType));
case 2:
if (syncToAsync.containsValue(returnType))
return Key.get(Types.newParameterizedType(methodInvoker.getClass(), returnType, returnType));
return Key.get(Types.newParameterizedType(
methodInvoker.getClass(),
returnType,
checkNotNull(syncToAsync.get(returnType), "need async type of %s for %s", returnType,
methodInvoker.getClass())));
}
throw new IllegalArgumentException(returnType + " has too many type parameters");
}
static final Predicate<Annotation> isQualifierPresent = new Predicate<Annotation>() {
public boolean apply(Annotation input) {
return input.annotationType().isAnnotationPresent(Qualifier.class);
}
};
private Object lookupValueFromGuice(Invokable<?, ?> invoked) {
try {
Type genericReturnType = invoked.getReturnType().getType();
try {
Annotation qualifier = find(ImmutableList.copyOf(invoked.getAnnotations()), isQualifierPresent);
return getInstanceOfTypeWithQualifier(genericReturnType, qualifier);
} catch (ProvisionException e) {
throw propagate(e.getCause());
} catch (RuntimeException e) {
return instanceOfTypeOrPropagate(genericReturnType, e);
}
} catch (ProvisionException e) {
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
if (aex != null)
throw aex;
throw e;
}
}
Object instanceOfTypeOrPropagate(Type genericReturnType, RuntimeException e) {
try {
// look for an existing binding
Binding<?> binding = injector.getExistingBinding(Key.get(genericReturnType));
if (binding != null)
return binding.getProvider().get();
// then, try looking via supplier
binding = injector.getExistingBinding(Key.get(newParameterizedType(Supplier.class, genericReturnType)));
if (binding != null)
return Supplier.class.cast(binding.getProvider().get()).get();
// else try to create an instance
return injector.getInstance(Key.get(genericReturnType));
} catch (ConfigurationException ce) {
throw e;
}
}
Object getInstanceOfTypeWithQualifier(Type genericReturnType, Annotation qualifier) {
// look for an existing binding
Binding<?> binding = injector.getExistingBinding(Key.get(genericReturnType, qualifier));
if (binding != null)
return binding.getProvider().get();
// then, try looking via supplier
binding = injector
.getExistingBinding(Key.get(newParameterizedType(Supplier.class, genericReturnType), qualifier));
if (binding != null)
return Supplier.class.cast(binding.getProvider().get()).get();
// else try to create an instance
return injector.getInstance(Key.get(genericReturnType, qualifier));
}
@Override
public String toString() {
return Objects.toStringHelper("").omitNullValues()
.add("enclosingType", enclosingType.getRawType().getSimpleName()).add("methodInvoker", methodInvoker)
.toString();
}
}

View File

@ -30,75 +30,101 @@ import org.jclouds.reflect.Invocation;
import com.google.common.base.Optional;
import com.google.common.collect.Multimap;
import com.google.common.reflect.TypeToken;
/**
* Represents a request generated from annotations
*
* @author Adrian Cole
* @author adriancole
*
* @param <A>
* enclosing type of the interface parsed to generate this request.
*/
public final class GeneratedHttpRequest extends HttpRequest {
public static Builder builder() {
return new Builder();
public final class GeneratedHttpRequest<A> extends HttpRequest {
public static <A> Builder<A> builder(Class<A> enclosingType) {
return new Builder<A>(TypeToken.of(enclosingType));
}
public Builder toBuilder() {
return new Builder().fromGeneratedHttpRequest(this);
public static <A> Builder<A> builder(TypeToken<A> enclosingType) {
return new Builder<A>(enclosingType);
}
public final static class Builder extends HttpRequest.Builder<Builder> {
protected Invocation invocation;
protected Optional<Invocation> caller = Optional.absent();
public Builder<A> toBuilder() {
return new Builder<A>(enclosingType).fromGeneratedHttpRequest(this);
}
public final static class Builder<A> extends HttpRequest.Builder<Builder<A>> {
private final TypeToken<A> enclosingType;
private Builder(TypeToken<A> enclosingType) {
this.enclosingType = checkNotNull(enclosingType, "enclosingType");
}
private Invocation invocation;
private Optional<TypeToken<?>> callerEnclosingType = Optional.absent();
private Optional<Invocation> caller = Optional.absent();
/**
* @see GeneratedHttpRequest#getInvocation()
*/
public Builder invocation(Invocation invocation) {
public Builder<A> invocation(Invocation invocation) {
this.invocation = checkNotNull(invocation, "invocation");
return this;
}
/**
* @see GeneratedHttpRequest#getCallerEnclosingType()
*/
public Builder<A> callerEnclosingType(@Nullable TypeToken<?> callerEnclosingType) {
this.callerEnclosingType = Optional.<TypeToken<?>> fromNullable(callerEnclosingType);
return this;
}
/**
* @see GeneratedHttpRequest#getCaller()
*/
public Builder caller(@Nullable Invocation caller) {
public Builder<A> caller(@Nullable Invocation caller) {
this.caller = Optional.fromNullable(caller);
return this;
}
public GeneratedHttpRequest build() {
return new GeneratedHttpRequest(method, endpoint, headers.build(), payload, invocation, filters.build(),
caller);
public GeneratedHttpRequest<A> build() {
return new GeneratedHttpRequest<A>(method, endpoint, headers.build(), payload, filters.build(), enclosingType,
invocation, callerEnclosingType, caller);
}
public Builder fromGeneratedHttpRequest(GeneratedHttpRequest in) {
return super.fromHttpRequest(in).invocation(in.invocation).caller(in.getCaller().orNull());
}
Invocation getInvocation() {
return invocation;
}
Optional<Invocation> getCaller() {
return caller;
public Builder<A> fromGeneratedHttpRequest(GeneratedHttpRequest<A> in) {
return super.fromHttpRequest(in).invocation(in.invocation)
.callerEnclosingType(in.getCallerEnclosingType().orNull()).caller(in.getCaller().orNull());
}
@Override
protected Builder self() {
protected Builder<A> self() {
return this;
}
}
private final TypeToken<A> enclosingType;
private final Invocation invocation;
private final Optional<TypeToken<?>> callerEnclosingType;
private final Optional<Invocation> caller;
protected GeneratedHttpRequest(String method, URI endpoint, Multimap<String, String> headers,
@Nullable Payload payload, Invocation invocation, Iterable<HttpRequestFilter> filters,
Optional<Invocation> caller) {
@Nullable Payload payload, Iterable<HttpRequestFilter> filters, TypeToken<A> enclosingType,
Invocation invocation, Optional<TypeToken<?>> callerEnclosingType, Optional<Invocation> caller) {
super(method, endpoint, headers, payload, filters);
this.enclosingType = checkNotNull(enclosingType, "enclosingType");
this.invocation = checkNotNull(invocation, "invocation");
this.callerEnclosingType = checkNotNull(callerEnclosingType, "callerEnclosingType");
this.caller = checkNotNull(caller, "caller");
}
/**
* different than {@link #getDeclaringClass()} when this is a member of a class it was not declared in.
*/
public TypeToken<?> getEnclosingType() {
return enclosingType;
}
/**
* what was interpreted to create this request
*/
@ -106,6 +132,14 @@ public final class GeneratedHttpRequest extends HttpRequest {
return invocation;
}
/**
* different than {@link #getDeclaringClass()} when {@link #getCaller()} is a member of a class it was not declared
* in.
*/
public Optional<TypeToken<?>> getCallerEnclosingType() {
return callerEnclosingType;
}
public Optional<Invocation> getCaller() {
return caller;
}

View File

@ -0,0 +1,48 @@
/**
* 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.rest.internal;
import java.util.Set;
import javax.ws.rs.Consumes;
import org.jclouds.reflect.Invocation;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
class GetAcceptHeaders<T> implements Function<Invocation, Set<String>> {
private Class<T> enclosingType;
@SuppressWarnings("unchecked")
@Inject
GetAcceptHeaders(TypeLiteral<T> enclosingType) {
this.enclosingType = (Class<T>) enclosingType.getRawType();
}
@Override
public Set<String> apply(Invocation invocation) {
Optional<Consumes> accept = Optional.fromNullable(invocation.getInvokable().getAnnotation(Consumes.class)).or(
Optional.fromNullable(enclosingType.getAnnotation(Consumes.class)));
return (accept.isPresent()) ? ImmutableSet.copyOf(accept.get().value()) : ImmutableSet.<String> of();
}
}

View File

@ -0,0 +1,91 @@
/**
* 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.rest.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.InvocationTargetException;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.jclouds.logging.Logger;
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.base.Function;
import com.google.common.cache.Cache;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
/**
*
* @author Adrian Cole
*/
public final class InvokeAndCallGetOnFutures<R> implements Function<Invocation, Result> {
@Resource
private Logger logger = Logger.NULL;
private final Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables;
private final R receiver;
/**
* @param receiver
* will have any methods that return {@link ListenableFuture} unwrapped.
* @return blocking invocation handler
*/
@Inject
@VisibleForTesting
InvokeAndCallGetOnFutures(Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables, R receiver) {
this.sync2AsyncInvokables = sync2AsyncInvokables;
this.receiver = receiver;
}
@SuppressWarnings("unchecked")
@Override
public Result apply(Invocation in) {
@SuppressWarnings("rawtypes")
Invokable target = checkNotNull(sync2AsyncInvokables.getIfPresent(in.getInvokable()), "invokable %s not in %s",
in.getInvokable(), sync2AsyncInvokables);
Object returnVal;
try {
returnVal = target.invoke(receiver, in.getArgs().toArray());
} catch (InvocationTargetException e) {
return Result.fail(e);
} catch (IllegalAccessException e) {
return Result.fail(e);
}
if (!isFuture(target))
return Result.success(returnVal);
return Result.success(Futures.getUnchecked(ListenableFuture.class.cast(returnVal)));
}
private boolean isFuture(Invokable<?, ?> target) {
return target.getReturnType().getRawType().isAssignableFrom(ListenableFuture.class);
}
@Override
public String toString() {
return String.format("InvokeAndCallGetOnFutures(%s)", receiver);
}
}

View File

@ -1,122 +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.rest.internal;
import static com.google.common.base.Preconditions.checkState;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.logging.Logger;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invocation.Result;
import org.jclouds.reflect.InvocationSuccess;
import org.jclouds.reflect.Invokable;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.assistedinject.Assisted;
/**
* Generates RESTful clients from appropriately annotated interfaces.
* <p/>
* Particularly, this code delegates calls to other things.
* <ol>
* <li>if the invoked has a {@link Provides} annotation, it responds via a {@link Injector} lookup</li>
* <li>if the invoked has a {@link Delegate} annotation, it responds with an instance of interface set in returnVal,
* adding the current JAXrs annotations to whatever are on that class.</li>
* <ul>
* <li>ex. if the invoked with {@link Delegate} has a {@code Path} annotation, and the returnval interface also has
* {@code Path}, these values are combined.</li>
* </ul>
* <li>if {@link RestAnnotationProcessor#delegationMap} contains a mapping for this, and the returnVal is properly
* assigned as a {@link ListenableFuture}, it responds with an http implementation.</li>
* <li>otherwise a RuntimeException is thrown with a message including:
* {@code invoked is intended solely to set constants}</li>
* </ol>
*
* @author Adrian Cole
*/
@Singleton
public class InvokeAsyncApi extends BaseInvocationFunction {
public final static class Delegate extends InvokeAsyncApi {
public static interface Factory {
Delegate caller(Invocation caller);
}
private final String string;
@Inject
private Delegate(Injector injector, Function<InvocationSuccess, Optional<Object>> optionalConverter,
Predicate<Invokable<?, ?>> mapsToAsyncHttpRequest,
InvokeListenableFutureViaHttp.Caller.Factory httpCallerFactory, Delegate.Factory factory,
@Assisted Invocation caller) {
super(injector, optionalConverter, mapsToAsyncHttpRequest, httpCallerFactory.caller(caller), factory);
this.string = String.format("%s->%s", caller, caller.getInvokable().getReturnType().getRawType()
.getSimpleName());
}
@Override
public String toString() {
return string;
}
}
@Resource
private Logger logger = Logger.NULL;
private final Predicate<Invokable<?, ?>> mapsToAsyncHttpRequest;
private final Function<Invocation, ListenableFuture<?>> invokeMethod;
private final Delegate.Factory factory;
@Inject
private InvokeAsyncApi(Injector injector, Function<InvocationSuccess, Optional<Object>> optionalConverter,
Predicate<Invokable<?, ?>> mapsToAsyncHttpRequest, Function<Invocation, ListenableFuture<?>> invokeMethod,
Delegate.Factory factory) {
super(injector, optionalConverter);
this.mapsToAsyncHttpRequest = mapsToAsyncHttpRequest;
this.invokeMethod = invokeMethod;
this.factory = factory;
}
@Override
protected Result invoke(Invocation invocation) {
checkState(mapsToAsyncHttpRequest.apply(invocation.getInvokable()),
"please configure corresponding async class for %s in your RestClientModule", invocation.getInvokable());
return Result.success(invokeMethod.apply(invocation));
}
@Override
protected Function<Invocation, Result> forwardInvocations(Invocation invocation, Class<?> returnType) {
return factory.caller(invocation);
}
@Override
public String toString() {
return String.format("async->http");
}
}

View File

@ -19,6 +19,8 @@
package org.jclouds.rest.internal;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.util.concurrent.Futures.transform;
import static com.google.common.util.concurrent.Futures.withFallback;
import static org.jclouds.concurrent.Futures.makeListenable;
@ -28,7 +30,6 @@ import java.util.concurrent.ExecutorService;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.Constants;
import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
@ -37,55 +38,50 @@ import org.jclouds.http.HttpCommandExecutorService;
import org.jclouds.http.HttpResponse;
import org.jclouds.logging.Logger;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invokable;
import org.jclouds.reflect.Invocation.Result;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.annotations.Fallback;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.FutureFallback;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Injector;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.TypeLiteral;
@Singleton
public class InvokeListenableFutureViaHttp implements Function<Invocation, ListenableFuture<?>> {
public final static class Caller extends InvokeListenableFutureViaHttp {
public static interface Factory {
Caller caller(Invocation caller);
}
@Inject
private Caller(Injector injector, RestAnnotationProcessor annotationProcessor, HttpCommandExecutorService http,
Function<GeneratedHttpRequest, Function<HttpResponse, ?>> transformerForRequest,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, @Assisted Invocation caller) {
super(injector, annotationProcessor.caller(caller), http, transformerForRequest, userThreads);
}
}
public class InvokeHttpMethod<S, A> implements Function<Invocation, Result> {
@Resource
private Logger logger = Logger.NULL;
private final Injector injector;
private final RestAnnotationProcessor annotationProcessor;
private final TypeToken<A> enclosingType;
private final Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables;
private final RestAnnotationProcessor<A> annotationProcessor;
private final HttpCommandExecutorService http;
private final Function<GeneratedHttpRequest, Function<HttpResponse, ?>> transformerForRequest;
private final TransformerForRequest<A> transformerForRequest;
private final ExecutorService userThreads;
private final BlockOnFuture.Factory blocker;
@SuppressWarnings("unchecked")
@Inject
private InvokeListenableFutureViaHttp(Injector injector, RestAnnotationProcessor annotationProcessor,
HttpCommandExecutorService http,
Function<GeneratedHttpRequest, Function<HttpResponse, ?>> transformerForRequest,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads) {
private InvokeHttpMethod(Injector injector, TypeLiteral<A> enclosingType,
Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables, RestAnnotationProcessor<A> annotationProcessor,
HttpCommandExecutorService http, TransformerForRequest<A> transformerForRequest,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads, BlockOnFuture.Factory blocker) {
this.injector = injector;
this.enclosingType = (TypeToken<A>) TypeToken.of(enclosingType.getType());
this.sync2AsyncInvokables = sync2AsyncInvokables;
this.annotationProcessor = annotationProcessor;
this.http = http;
this.userThreads = userThreads;
this.blocker = blocker;
this.transformerForRequest = transformerForRequest;
}
@ -104,18 +100,34 @@ public class InvokeListenableFutureViaHttp implements Function<Invocation, Liste
});
@Override
public ListenableFuture<?> apply(Invocation invocation) {
public Result apply(Invocation in) {
if (isFuture(in.getInvokable())) {
return Result.success(createFuture(in));
}
@SuppressWarnings("rawtypes")
Invokable async = checkNotNull(sync2AsyncInvokables.getIfPresent(in.getInvokable()), "invokable %s not in %s",
in.getInvokable(), sync2AsyncInvokables);
checkState(isFuture(async), "not a future: %s", async);
return blocker.create(enclosingType, in).apply(createFuture(Invocation.create(async, in.getArgs())));
}
private boolean isFuture(Invokable<?, ?> in) {
return in.getReturnType().getRawType().equals(ListenableFuture.class);
}
public ListenableFuture<?> createFuture(Invocation invocation) {
String name = invocation.getInvokable().toString();
logger.trace(">> converting %s", name);
GeneratedHttpRequest request = annotationProcessor.apply(invocation);
GeneratedHttpRequest<A> request = annotationProcessor.apply(invocation);
logger.trace("<< converted %s to %s", name, request.getRequestLine());
Function<HttpResponse, ?> transformer = transformerForRequest.apply(request);
logger.trace("<< response from %s is parsed by %s", name, transformer.getClass().getSimpleName());
logger.debug(">> invoking %s", name);
ListenableFuture<?> result = transform(makeListenable(http.submit(new HttpCommand(request)), userThreads), transformer);
ListenableFuture<?> result = transform(makeListenable(http.submit(new HttpCommand(request)), userThreads),
transformer);
FutureFallback<?> fallback = fallbacks.getUnchecked(invocation.getInvokable());
if (fallback instanceof InvocationContext) {
InvocationContext.class.cast(fallback).setContext(request);
@ -130,7 +142,7 @@ public class InvokeListenableFutureViaHttp implements Function<Invocation, Liste
return true;
if (o == null || getClass() != o.getClass())
return false;
InvokeListenableFutureViaHttp that = InvokeListenableFutureViaHttp.class.cast(o);
InvokeHttpMethod<?, ?> that = InvokeHttpMethod.class.cast(o);
return equal(this.annotationProcessor, that.annotationProcessor);
}

View File

@ -1,123 +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.rest.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.jclouds.logging.Logger;
import org.jclouds.reflect.FunctionalReflection;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invocation.Result;
import org.jclouds.reflect.InvocationSuccess;
import org.jclouds.reflect.Invokable;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Injector;
import com.google.inject.assistedinject.Assisted;
/**
*
* @author Adrian Cole
*/
public final class InvokeSyncApi extends BaseInvocationFunction {
public static interface Factory {
/**
* @param receiver
* object whose interface matched {@code declaring} except all invokeds return {@link ListenableFuture}
* @return blocking invocation handler
*/
InvokeSyncApi create(Object receiver);
}
@Resource
private Logger logger = Logger.NULL;
private final InvokeSyncApi.Factory factory;
private final InvokeAsyncApi.Delegate.Factory asyncFactory;
private final Map<Class<?>, Class<?>> sync2Async;
private final Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables;
private final InvokeFutureAndBlock.Factory blocker;
private final Object receiver;
@Inject
@VisibleForTesting
InvokeSyncApi(Injector injector, Function<InvocationSuccess, Optional<Object>> optionalConverter,
InvokeSyncApi.Factory factory, InvokeAsyncApi.Delegate.Factory asyncFactory,
Map<Class<?>, Class<?>> sync2Async, Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables,
InvokeFutureAndBlock.Factory blocker, @Assisted Object receiver) {
super(injector, optionalConverter);
this.factory = factory;
this.asyncFactory = asyncFactory;
this.sync2Async = sync2Async;
this.sync2AsyncInvokables = sync2AsyncInvokables;
this.blocker = blocker;
this.receiver = receiver;
}
@SuppressWarnings("unchecked")
@Override
protected Result invoke(Invocation in) {
@SuppressWarnings("rawtypes")
Invokable async = checkNotNull(sync2AsyncInvokables.getIfPresent(in.getInvokable()), "invokable %s not in %s",
in.getInvokable(), sync2AsyncInvokables);
if (async.getReturnType().getRawType().isAssignableFrom(ListenableFuture.class)) {
return blocker.create(receiver).apply(in);
}
try { // try any method
return Result.success(async.invoke(receiver, in.getArgs().toArray()));
} catch (InvocationTargetException e) {
return Result.fail(e);
} catch (IllegalAccessException e) {
return Result.fail(e);
}
}
@Override
protected Function<Invocation, Result> forwardInvocations(Invocation invocation, Class<?> returnType) {
// get the return type of the asynchronous class associated with this client
// ex. FloatingIPClient is associated with FloatingIPAsyncClient
Class<?> asyncClass = sync2Async.get(returnType);
checkState(asyncClass != null, "please configure corresponding async class for %s in your RestClientModule",
returnType);
// pass any parameters necessary to get a relevant instance of that async class
// ex. getClientForRegion("north") might return an instance whose endpoint is
// different that "south"
Object asyncProxy = FunctionalReflection.newProxy(asyncClass, asyncFactory.caller(invocation));
checkState(asyncProxy != null, "configuration error, sync client for " + invocation + " not found");
return factory.create(asyncProxy);
}
@Override
public String toString() {
return String.format("syncProxy(%s)", receiver);
}
}

View File

@ -54,7 +54,7 @@ import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.Resource;
import javax.ws.rs.Consumes;
import javax.inject.Named;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
@ -78,8 +78,8 @@ import org.jclouds.io.payloads.Part.PartOptions;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.logging.Logger;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invokable;
import org.jclouds.reflect.Parameter;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.Parameter;
import org.jclouds.rest.Binder;
import org.jclouds.rest.InputParamValidator;
import org.jclouds.rest.annotations.ApiVersion;
@ -105,6 +105,7 @@ import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
@ -121,17 +122,19 @@ import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.primitives.Chars;
import com.google.common.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
/**
* Creates http invocation.getInvoked()s based on annotations on a class or interface.
*
* @author Adrian Cole
* @author adriancole
*
* @param <A>
*/
public class RestAnnotationProcessor implements Function<Invocation, GeneratedHttpRequest> {
public class RestAnnotationProcessor<A> implements Function<Invocation, GeneratedHttpRequest<A>> {
@Resource
protected Logger logger = Logger.NULL;
@ -149,16 +152,27 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
private final String apiVersion;
private final String buildVersion;
private final InputParamValidator inputParamValidator;
private final GetAcceptHeaders<A> getAcceptHeaders;
private final TypeToken<A> enclosingType;
private final TypeToken<?> callerEnclosingType;
private final Invocation caller;
@SuppressWarnings("unchecked")
@Inject
private RestAnnotationProcessor(Injector injector, @ApiVersion String apiVersion, @BuildVersion String buildVersion,
HttpUtils utils, ContentMetadataCodec contentMetadataCodec, InputParamValidator inputParamValidator) {
HttpUtils utils, ContentMetadataCodec contentMetadataCodec, InputParamValidator inputParamValidator,
GetAcceptHeaders<A> getAcceptHeaders, TypeLiteral<A> enclosingType,
@Nullable @Named("caller") TypeToken<?> callerEnclosingType, @Nullable @Named("caller") Invocation caller) {
this.injector = injector;
this.utils = utils;
this.contentMetadataCodec = contentMetadataCodec;
this.apiVersion = apiVersion;
this.buildVersion = buildVersion;
this.inputParamValidator = inputParamValidator;
this.getAcceptHeaders = getAcceptHeaders;
this.enclosingType = (TypeToken<A>) TypeToken.of(enclosingType.getType());
this.callerEnclosingType = callerEnclosingType;
this.caller = caller;
}
/**
@ -166,19 +180,12 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
* in lost data.
*/
@Deprecated
public GeneratedHttpRequest createRequest(Invokable<?, ?> invokable, List<Object> args) {
public GeneratedHttpRequest<A> createRequest(Invokable<?, ?> invokable, List<Object> args) {
return apply(Invocation.create(invokable, args));
}
private Invocation caller;
RestAnnotationProcessor caller(Invocation caller) {
this.caller = caller;
return this;
}
@Override
public GeneratedHttpRequest apply(Invocation invocation) {
public GeneratedHttpRequest<A> apply(Invocation invocation) {
checkNotNull(invocation, "invocation");
inputParamValidator.validateMethodParametersOrThrow(invocation);
@ -188,27 +195,27 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
endpoint = Optional.fromNullable(r.getEndpoint());
if (endpoint.isPresent())
logger.trace("using endpoint %s from invocation.getArgs() for %s", endpoint, invocation);
} else if (caller != null){
endpoint = getEndpointFor(caller);
if (endpoint.isPresent())
logger.trace("using endpoint %s from caller %s for %s", endpoint, caller, invocation);
else
endpoint = findEndpoint(invocation);
}else {
} else if (caller != null) {
endpoint = getEndpointFor(callerEnclosingType, caller);
if (endpoint.isPresent())
logger.trace("using endpoint %s from caller %s for %s", endpoint, caller, invocation);
else
endpoint = findEndpoint(invocation);
} else {
endpoint = findEndpoint(invocation);
}
if (!endpoint.isPresent())
throw new NoSuchElementException(format("no endpoint found for %s", invocation));
GeneratedHttpRequest.Builder requestBuilder = GeneratedHttpRequest.builder().caller(caller);
GeneratedHttpRequest.Builder<A> requestBuilder = GeneratedHttpRequest.builder(enclosingType)
.invocation(invocation).callerEnclosingType(callerEnclosingType).caller(caller);
if (r != null) {
requestBuilder.fromHttpRequest(r);
} else {
requestBuilder.method(tryFindHttpMethod(invocation.getInvokable()).get());
}
requestBuilder.invocation(invocation).filters(getFiltersIfAnnotated(invocation));
requestBuilder.filters(getFiltersIfAnnotated(invocation));
Multimap<String, Object> tokenValues = LinkedHashMultimap.create();
@ -220,8 +227,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
overridePathEncoding(uriBuilder, invocation);
if (caller != null)
tokenValues.putAll(addPathAndGetTokens(caller, uriBuilder));
tokenValues.putAll(addPathAndGetTokens(invocation, uriBuilder));
tokenValues.putAll(addPathAndGetTokens(callerEnclosingType, caller, uriBuilder));
tokenValues.putAll(addPathAndGetTokens(enclosingType, invocation, uriBuilder));
Multimap<String, Object> formParams = addFormParams(tokenValues, invocation);
Multimap<String, Object> queryParams = addQueryParams(tokenValues, invocation);
@ -291,7 +298,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
if (payload != null) {
requestBuilder.payload(payload);
}
GeneratedHttpRequest request = requestBuilder.build();
GeneratedHttpRequest<A> request = requestBuilder.build();
org.jclouds.rest.MapBinder mapBinder = getMapPayloadBinderOrNull(invocation);
if (mapBinder != null) {
@ -312,7 +319,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
return request;
}
private static <T> T findOrNull(Iterable<Object> args, Class<T> clazz) {
private static <A> A findOrNull(Iterable<Object> args, Class<A> clazz) {
return clazz.cast(tryFind(args, instanceOf(clazz)).orNull());
}
@ -324,14 +331,10 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
return ImmutableMap.copyOf(out);
}
protected org.jclouds.rest.internal.GeneratedHttpRequest.Builder requestBuilder() {
return GeneratedHttpRequest.builder();
}
private void overridePathEncoding(UriBuilder uriBuilder, Invocation invocation) {
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(SkipEncoding.class)) {
uriBuilder.skipPathEncoding(Chars.asList(invocation.getInvokable().getEnclosingType().getRawType()
.getAnnotation(SkipEncoding.class).value()));
if (enclosingType.getRawType().isAnnotationPresent(SkipEncoding.class)) {
uriBuilder
.skipPathEncoding(Chars.asList(enclosingType.getRawType().getAnnotation(SkipEncoding.class).value()));
}
if (invocation.getInvokable().isAnnotationPresent(SkipEncoding.class)) {
uriBuilder.skipPathEncoding(Chars.asList(invocation.getInvokable().getAnnotation(SkipEncoding.class).value()));
@ -350,7 +353,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
}
protected Optional<URI> findEndpoint(Invocation invocation) {
Optional<URI> endpoint = getEndpointFor(invocation);
Optional<URI> endpoint = getEndpointFor(enclosingType, invocation);
if (endpoint.isPresent())
logger.trace("using endpoint %s for %s", endpoint, invocation);
if (!endpoint.isPresent()) {
@ -363,10 +366,10 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
return endpoint;
}
private Multimap<String, Object> addPathAndGetTokens(Invocation invocation, UriBuilder uriBuilder) {
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(Path.class))
uriBuilder.appendPath(invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(Path.class)
.value());
private Multimap<String, Object> addPathAndGetTokens(TypeToken<?> enclosingType, Invocation invocation,
UriBuilder uriBuilder) {
if (enclosingType.getRawType().isAnnotationPresent(Path.class))
uriBuilder.appendPath(enclosingType.getRawType().getAnnotation(Path.class).value());
if (invocation.getInvokable().isAnnotationPresent(Path.class))
uriBuilder.appendPath(invocation.getInvokable().getAnnotation(Path.class).value());
return getPathParamKeyValues(invocation);
@ -374,8 +377,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
private Multimap<String, Object> addFormParams(Multimap<String, ?> tokenValues, Invocation invocation) {
Multimap<String, Object> formMap = LinkedListMultimap.create();
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(FormParams.class)) {
FormParams form = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(FormParams.class);
if (enclosingType.getRawType().isAnnotationPresent(FormParams.class)) {
FormParams form = enclosingType.getRawType().getAnnotation(FormParams.class);
addForm(formMap, form, tokenValues);
}
@ -392,8 +395,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
private Multimap<String, Object> addQueryParams(Multimap<String, ?> tokenValues, Invocation invocation) {
Multimap<String, Object> queryMap = LinkedListMultimap.create();
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(QueryParams.class)) {
QueryParams query = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(QueryParams.class);
if (enclosingType.getRawType().isAnnotationPresent(QueryParams.class)) {
QueryParams query = enclosingType.getRawType().getAnnotation(QueryParams.class);
addQuery(queryMap, query, tokenValues);
}
@ -443,13 +446,12 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
private List<HttpRequestFilter> getFiltersIfAnnotated(Invocation invocation) {
List<HttpRequestFilter> filters = newArrayList();
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(RequestFilters.class)) {
for (Class<? extends HttpRequestFilter> clazz : invocation.getInvokable().getEnclosingType().getRawType()
.getAnnotation(RequestFilters.class).value()) {
if (enclosingType.getRawType().isAnnotationPresent(RequestFilters.class)) {
for (Class<? extends HttpRequestFilter> clazz : enclosingType.getRawType().getAnnotation(RequestFilters.class)
.value()) {
HttpRequestFilter instance = injector.getInstance(clazz);
filters.add(instance);
logger.trace("adding filter %s from annotation on %s", instance, invocation.getInvokable()
.getEnclosingType().getRawType().getName());
logger.trace("adding filter %s from annotation on %s", instance, enclosingType.getRawType().getName());
}
}
if (invocation.getInvokable().isAnnotationPresent(RequestFilters.class)) {
@ -498,14 +500,14 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
private static final TypeLiteral<Supplier<URI>> uriSupplierLiteral = new TypeLiteral<Supplier<URI>>() {
};
protected Optional<URI> getEndpointFor(Invocation invocation) {
protected Optional<URI> getEndpointFor(TypeToken<?> enclosingType, Invocation invocation) {
URI endpoint = getEndpointInParametersOrNull(invocation, injector);
if (endpoint == null) {
Endpoint annotation;
if (invocation.getInvokable().isAnnotationPresent(Endpoint.class)) {
annotation = invocation.getInvokable().getAnnotation(Endpoint.class);
} else if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(Endpoint.class)) {
annotation = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(Endpoint.class);
} else if (enclosingType.getRawType().isAnnotationPresent(Endpoint.class)) {
annotation = enclosingType.getRawType().getAnnotation(Endpoint.class);
} else {
logger.trace("no annotations on class or invocation.getInvoked(): %s", invocation.getInvokable());
return Optional.absent();
@ -565,11 +567,11 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
}
private boolean shouldAddHostHeader(Invocation invocation) {
return (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(VirtualHost.class) || invocation
.getInvokable().isAnnotationPresent(VirtualHost.class));
return (enclosingType.getRawType().isAnnotationPresent(VirtualHost.class) || invocation.getInvokable()
.isAnnotationPresent(VirtualHost.class));
}
private GeneratedHttpRequest decorateRequest(GeneratedHttpRequest request) throws NegativeArraySizeException {
private GeneratedHttpRequest<A> decorateRequest(GeneratedHttpRequest<A> request) throws NegativeArraySizeException {
Invocation invocation = request.getInvocation();
List<Object> args = request.getInvocation().getArgs();
Set<Parameter> binderOrWrapWith = ImmutableSet.copyOf(concat(
@ -588,14 +590,14 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
if (args.size() >= position + 1 && arg != null) {
Class<?> parameterType = entry.getType().getRawType();
Class<? extends Object> argType = arg.getClass();
if (!argType.isArray() && parameterType.isArray() && invocation.getInvokable().isVarArgs()) {
if (!argType.isArray() && parameterType.isArray()) {// TODO: && invocation.getInvokable().isVarArgs()) {
int arrayLength = args.size() - invocation.getInvokable().getParameters().size() + 1;
if (arrayLength == 0)
break OUTER;
arg = (Object[]) Array.newInstance(arg.getClass(), arrayLength);
System.arraycopy(args.toArray(), position, arg, 0, arrayLength);
shouldBreak = true;
} else if (argType.isArray() && parameterType.isArray() && invocation.getInvokable().isVarArgs()) {
} else if (argType.isArray() && parameterType.isArray()){// TODO: && invocation.getInvokable().isVarArgs()) {
} else {
if (arg.getClass().isArray()) {
Object[] payloadArray = (Object[]) arg;
@ -608,8 +610,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
if (shouldBreak)
break OUTER;
} else {
if (position + 1 == invocation.getInvokable().getParameters().size() && entry.getType().isArray()
&& invocation.getInvokable().isVarArgs())
if (position + 1 == invocation.getInvokable().getParameters().size() && entry.getType().isArray())// TODO: && invocation.getInvokable().isVarArgs())
continue OUTER;
if (entry.isAnnotationPresent(Nullable.class)) {
@ -630,7 +631,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
Class<?> type = param.getType().getRawType();
if (HttpRequestOptions.class.isAssignableFrom(type)
|| HttpRequestOptions[].class.isAssignableFrom(type))
toReturn.add(param.getPosition());
toReturn.add(param.hashCode());
}
return toReturn.build();
}
@ -663,7 +664,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
addHeaderIfAnnotationPresentOnMethod(headers, invocation, tokenValues);
for (Parameter headerParam : parametersWithAnnotation(invocation.getInvokable(), HeaderParam.class)) {
Annotation key = headerParam.getAnnotation(HeaderParam.class);
String value = invocation.getArgs().get(headerParam.getPosition()).toString();
String value = invocation.getArgs().get(headerParam.hashCode()).toString();
value = replaceTokens(value, tokenValues);
headers.put(((HeaderParam) key).value(), value);
}
@ -672,23 +673,15 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
return headers;
}
private static void addConsumesIfPresentOnTypeOrMethod(Multimap<String, String> headers, Invocation invocation) {
Set<String> accept = getAcceptHeaders(invocation);
private void addConsumesIfPresentOnTypeOrMethod(Multimap<String, String> headers, Invocation invocation) {
Set<String> accept = getAcceptHeaders.apply(invocation);
if (!accept.isEmpty())
headers.replaceValues(ACCEPT, accept);
}
// TODO: refactor this out
static Set<String> getAcceptHeaders(Invocation invocation) {
Optional<Consumes> accept = Optional.fromNullable(invocation.getInvokable().getAnnotation(Consumes.class)).or(
Optional.fromNullable(invocation.getInvokable().getEnclosingType().getRawType()
.getAnnotation(Consumes.class)));
return (accept.isPresent()) ? ImmutableSet.copyOf(accept.get().value()) : ImmutableSet.<String> of();
}
private static void addProducesIfPresentOnTypeOrMethod(Multimap<String, String> headers, Invocation invocation) {
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(Produces.class)) {
Produces header = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(Produces.class);
private void addProducesIfPresentOnTypeOrMethod(Multimap<String, String> headers, Invocation invocation) {
if (enclosingType.getRawType().isAnnotationPresent(Produces.class)) {
Produces header = enclosingType.getRawType().getAnnotation(Produces.class);
headers.replaceValues(CONTENT_TYPE, asList(header.value()));
}
if (invocation.getInvokable().isAnnotationPresent(Produces.class)) {
@ -697,10 +690,10 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
}
}
private static void addHeaderIfAnnotationPresentOnMethod(Multimap<String, String> headers, Invocation invocation,
private void addHeaderIfAnnotationPresentOnMethod(Multimap<String, String> headers, Invocation invocation,
Multimap<String, ?> tokenValues) {
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(Headers.class)) {
Headers header = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(Headers.class);
if (enclosingType.getRawType().isAnnotationPresent(Headers.class)) {
Headers header = enclosingType.getRawType().getAnnotation(Headers.class);
addHeader(headers, header, tokenValues);
}
if (invocation.getInvokable().isAnnotationPresent(Headers.class)) {
@ -726,7 +719,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
options.contentType(partParam.contentType());
if (!PartParam.NO_FILENAME.equals(partParam.filename()))
options.filename(replaceTokens(partParam.filename(), tokenValues));
Object arg = invocation.getArgs().get(param.getPosition());
Object arg = invocation.getArgs().get(param.hashCode());
checkNotNull(arg, partParam.name());
Part part = Part.create(partParam.name(), newPayload(arg), options);
parts.add(part);
@ -740,7 +733,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
PathParam pathParam = param.getAnnotation(PathParam.class);
String paramKey = pathParam.value();
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class),
param.getPosition(), paramKey);
param.hashCode(), paramKey);
if (paramValue.isPresent())
pathParamValues.put(paramKey, paramValue.get().toString());
}
@ -757,10 +750,10 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
return Optional.fromNullable(arg);
}
private static boolean checkPresentOrNullable(Invocation invocation, String paramKey, int argIndex, Object arg) {
private boolean checkPresentOrNullable(Invocation invocation, String paramKey, int argIndex, Object arg) {
if (arg == null && !invocation.getInvokable().getParameters().get(argIndex).isAnnotationPresent(Nullable.class))
throw new NullPointerException(format("param{%s} for invocation %s.%s", paramKey, invocation.getInvokable()
.getEnclosingType().getRawType().getSimpleName(), invocation.getInvokable().getName()));
throw new NullPointerException(format("param{%s} for invocation %s.%s", paramKey, enclosingType.getRawType()
.getSimpleName(), invocation.getInvokable().getName()));
return true;
}
@ -770,7 +763,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
FormParam formParam = param.getAnnotation(FormParam.class);
String paramKey = formParam.value();
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class),
param.getPosition(), paramKey);
param.hashCode(), paramKey);
if (paramValue.isPresent())
formParamValues.put(paramKey, paramValue.get().toString());
}
@ -783,7 +776,7 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
QueryParam queryParam = param.getAnnotation(QueryParam.class);
String paramKey = queryParam.value();
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class),
param.getPosition(), paramKey);
param.hashCode(), paramKey);
if (paramValue.isPresent())
if (paramValue.get() instanceof Iterable) {
@SuppressWarnings("unchecked")
@ -802,10 +795,21 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
PayloadParam payloadParam = param.getAnnotation(PayloadParam.class);
String paramKey = payloadParam.value();
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class),
param.getPosition(), paramKey);
param.hashCode(), paramKey);
if (paramValue.isPresent())
payloadParamValues.put(paramKey, paramValue.get());
}
return payloadParamValues;
}
@Override
public String toString() {
String callerString = null;
if (callerEnclosingType != null) {
callerString = String.format("%s.%s%s", callerEnclosingType.getRawType().getSimpleName(), caller
.getInvokable().getName(), caller.getArgs());
}
return Objects.toStringHelper("").omitNullValues().add("caller", callerString)
.add("enclosingType", enclosingType.getRawType().getSimpleName()).toString();
}
}

View File

@ -50,7 +50,7 @@ import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.json.internal.GsonWrapper;
import org.jclouds.reflect.Invocation;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.annotations.JAXBResponseParser;
import org.jclouds.rest.annotations.OnlyElement;
@ -62,6 +62,7 @@ import org.jclouds.rest.annotations.XMLResponseParser;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ListenableFuture;
@ -69,19 +70,45 @@ import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
public class TransformerForRequest implements Function<GeneratedHttpRequest, Function<HttpResponse, ?>> {
public class TransformerForRequest<A> implements Function<GeneratedHttpRequest<A>, Function<HttpResponse, ?>> {
private final ParseSax.Factory parserFactory;
private final Injector injector;
private final GetAcceptHeaders<A> getAcceptHeaders;
private final Class<A> enclosingType;
@SuppressWarnings("unchecked")
@Inject
TransformerForRequest(Injector injector, Factory parserFactory) {
TransformerForRequest(Injector injector, Factory parserFactory, GetAcceptHeaders<A> getAcceptHeaders,
TypeLiteral<A> enclosingType) {
this.injector = injector;
this.parserFactory = parserFactory;
this.getAcceptHeaders = getAcceptHeaders;
this.enclosingType = (Class<A>) enclosingType.getRawType();
}
@SuppressWarnings("unchecked")
@Override
public Function<HttpResponse, ?> apply(GeneratedHttpRequest request) {
return createResponseParser(parserFactory, injector, request);
public Function<HttpResponse, ?> apply(GeneratedHttpRequest<A> request) {
Function<HttpResponse, ?> transformer;
Class<? extends HandlerWithResult<?>> handler = TransformerForRequest.getSaxResponseParserClassOrNull(request
.getInvocation().getInvokable());
if (handler != null) {
transformer = parserFactory.create(injector.getInstance(handler));
} else {
transformer = getTransformerForMethod(request.getInvocation(), injector);
}
if (transformer instanceof InvocationContext<?>) {
((InvocationContext<?>) transformer).setContext(request);
}
if (request.getInvocation().getInvokable().isAnnotationPresent(Transform.class)) {
Function<?, ?> wrappingTransformer = injector.getInstance(request.getInvocation().getInvokable()
.getAnnotation(Transform.class).value());
if (wrappingTransformer instanceof InvocationContext<?>) {
((InvocationContext<?>) wrappingTransformer).setContext(request);
}
transformer = compose(Function.class.cast(wrappingTransformer), transformer);
}
return transformer;
}
private static final TypeToken<ListenableFuture<Boolean>> futureBooleanLiteral = new TypeToken<ListenableFuture<Boolean>>() {
@ -105,8 +132,9 @@ public class TransformerForRequest implements Function<GeneratedHttpRequest, Fun
@SuppressWarnings("unchecked")
@VisibleForTesting
protected static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Invocation invocation) {
protected Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Invocation invocation) {
Invokable<?, ?> invoked = invocation.getInvokable();
Set<String> acceptHeaders = getAcceptHeaders.apply(invocation);
ResponseParser annotation = invoked.getAnnotation(ResponseParser.class);
if (annotation == null) {
if (invoked.getReturnType().equals(void.class) || invoked.getReturnType().equals(futureVoidLiteral)) {
@ -120,10 +148,9 @@ public class TransformerForRequest implements Function<GeneratedHttpRequest, Fun
} else if (invoked.getReturnType().equals(HttpResponse.class)
|| invoked.getReturnType().equals(futureHttpResponseLiteral)) {
return Key.get(Class.class.cast(IdentityFunction.class));
} else if (RestAnnotationProcessor.getAcceptHeaders(invocation).contains(APPLICATION_JSON)) {
} else if (acceptHeaders.contains(APPLICATION_JSON)) {
return getJsonParserKeyForMethod(invoked);
} else if (RestAnnotationProcessor.getAcceptHeaders(invocation).contains(APPLICATION_XML)
|| invoked.isAnnotationPresent(JAXBResponseParser.class)) {
} else if (acceptHeaders.contains(APPLICATION_XML) || invoked.isAnnotationPresent(JAXBResponseParser.class)) {
return getJAXBParserKeyForMethod(invoked);
} else if (invoked.getReturnType().equals(String.class) || invoked.getReturnType().equals(futureStringLiteral)) {
return Key.get(ReturnStringIf2xx.class);
@ -176,7 +203,7 @@ public class TransformerForRequest implements Function<GeneratedHttpRequest, Fun
// TODO: refactor this out of here
@VisibleForTesting
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Function<HttpResponse, ?> getTransformerForMethod(Invocation invocation, Injector injector) {
public Function<HttpResponse, ?> getTransformerForMethod(Invocation invocation, Injector injector) {
Invokable<?, ?> invoked = invocation.getInvokable();
Function<HttpResponse, ?> transformer;
if (invoked.isAnnotationPresent(SelectJson.class)) {
@ -193,32 +220,6 @@ public class TransformerForRequest implements Function<GeneratedHttpRequest, Fun
return transformer;
}
@SuppressWarnings("unchecked")
@Deprecated
public static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
GeneratedHttpRequest request) {
Function<HttpResponse, ?> transformer;
Class<? extends HandlerWithResult<?>> handler = TransformerForRequest.getSaxResponseParserClassOrNull(request
.getInvocation().getInvokable());
if (handler != null) {
transformer = parserFactory.create(injector.getInstance(handler));
} else {
transformer = getTransformerForMethod(request.getInvocation(), injector);
}
if (transformer instanceof InvocationContext<?>) {
((InvocationContext<?>) transformer).setContext(request);
}
if (request.getInvocation().getInvokable().isAnnotationPresent(Transform.class)) {
Function<?, ?> wrappingTransformer = injector.getInstance(request.getInvocation().getInvokable()
.getAnnotation(Transform.class).value());
if (wrappingTransformer instanceof InvocationContext<?>) {
((InvocationContext<?>) wrappingTransformer).setContext(request);
}
transformer = compose(Function.class.cast(wrappingTransformer), transformer);
}
return transformer;
}
static Class<? extends HandlerWithResult<?>> getSaxResponseParserClassOrNull(Invokable<?, ?> invoked) {
XMLResponseParser annotation = invoked.getAnnotation(XMLResponseParser.class);
if (annotation != null) {
@ -226,4 +227,9 @@ public class TransformerForRequest implements Function<GeneratedHttpRequest, Fun
}
return null;
}
@Override
public String toString() {
return Objects.toStringHelper("").omitNullValues().add("enclosingType", enclosingType.getSimpleName()).toString();
}
}

View File

@ -23,7 +23,7 @@ import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import com.google.common.base.Optional;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.TypeToken;
/**

View File

@ -24,12 +24,13 @@ import java.util.List;
import org.jclouds.http.functions.config.SaxParserModule;
import org.jclouds.reflect.Invocation;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import com.google.common.collect.ImmutableList;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.TypeToken;
import com.google.inject.Guice;
import com.google.inject.Injector;
@ -60,7 +61,7 @@ public class BaseHandlerTest {
} catch (NoSuchMethodException e) {
throw propagate(e);
}
request = GeneratedHttpRequest.builder().method("POST").endpoint("http://localhost/key").invocation(toString)
request = GeneratedHttpRequest.builder(TypeToken.of(String.class)).method("POST").endpoint("http://localhost/key").invocation(toString)
.build();
}
@ -71,7 +72,7 @@ public class BaseHandlerTest {
}
protected GeneratedHttpRequest requestForArgs(List<Object> args) {
return GeneratedHttpRequest.builder().method("POST").endpoint("http://localhost/key")
return GeneratedHttpRequest.builder(TypeToken.of(String.class)).method("POST").endpoint("http://localhost/key")
.invocation(Invocation.create(toString.getInvokable(), args)).build();
}
}

View File

@ -44,13 +44,15 @@ import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService;
import org.jclouds.io.ContentMetadataCodec;
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
import org.jclouds.io.Payloads;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import org.jclouds.reflect.Invokable;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
@Test(groups = "unit", testName = "BackoffLimitedRetryHandlerTest")
public class BackoffLimitedRetryHandlerTest {
@ -166,8 +168,8 @@ public class BackoffLimitedRetryHandlerTest {
assertEquals(response.getPayload().getInput().read(), -1);
}
private final RestAnnotationProcessor processor = BaseJettyTest.newBuilder(8100, new Properties()).buildInjector()
.getInstance(RestAnnotationProcessor.class);
private final RestAnnotationProcessor<IntegrationTestAsyncClient> processor = BaseJettyTest.newBuilder(8100, new Properties()).buildInjector()
.getInstance(Key.get(new TypeLiteral<RestAnnotationProcessor<IntegrationTestAsyncClient>>(){}));
private HttpCommand createCommand() throws SecurityException, NoSuchMethodException {
Invokable<?, Object> method = Invokable.from(IntegrationTestAsyncClient.class.getMethod("download", String.class));

View File

@ -41,7 +41,7 @@ import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;

View File

@ -28,19 +28,23 @@ import java.lang.annotation.Target;
import javax.inject.Qualifier;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.config.SaxParserModule;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule;
import org.jclouds.reflect.Invocation;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.internal.TransformerForRequest;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.TypeParameter;
import com.google.common.reflect.TypeToken;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
/**
*
@ -57,9 +61,19 @@ public abstract class BaseParserTest<T, G> {
@SuppressWarnings("unchecked")
protected Function<HttpResponse, T> parser(Injector i) {
TypeToken<TransformerForRequest<T>> token = new TypeToken<TransformerForRequest<T>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<T>() {
}, new TypeToken<T>(getClass()) {
private static final long serialVersionUID = 1L;
});
Key<TransformerForRequest<T>> xform = (Key<TransformerForRequest<T>>) Key.get(token.getType());
try {
return (Function<HttpResponse, T>) TransformerForRequest.getTransformerForMethod(
Invocation.create(Invokable.from(getClass().getMethod("expected")), ImmutableList.of()), i);
return (Function<HttpResponse, T>) i
.createChildInjector(new SaxParserModule())
.getInstance(xform)
.getTransformerForMethod(
Invocation.create(Invokable.from(getClass().getMethod("expected")), ImmutableList.of()), i);
} catch (Exception e) {
throw Throwables.propagate(e);
}
@ -83,13 +97,10 @@ public abstract class BaseParserTest<T, G> {
protected Injector injector() {
return Guice.createInjector(new GsonModule() {
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
super.configure();
}
});
}

View File

@ -27,7 +27,6 @@ import static org.testng.Assert.assertTrue;
import java.io.Closeable;
import java.io.IOException;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.TimeoutException;
import org.jclouds.reflect.Invocation.Result;
@ -43,22 +42,6 @@ import com.google.common.collect.ImmutableList;
@Test(singleThreaded = true)
public class FunctionalReflectionTest {
/**
* a method only has reference to its declaring type, not the interface specified to the proxy. this shows how to get
* access to the actual proxied interface
*/
@SuppressWarnings("unchecked")
public void testCanAccessContravariantTypeInsideFunction() {
final Function<Invocation, Result> test = new Function<Invocation, Result>() {
public Result apply(Invocation e) {
assertEquals(e.getInvokable().getDeclaringClass(), Set.class);
assertEquals(e.getInvokable().getEnclosingType().getRawType(), SortedSet.class);
return Result.success(true);
}
};
FunctionalReflection.newProxy(SortedSet.class, test).add(null);
}
@SuppressWarnings("unchecked")
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testNullArgsAreAllowedAndUnmodifiable() {

View File

@ -1,345 +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.reflect;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.reflect.TypeToken;
/**
* ported from {@link com.google.common.reflect.InvokableTest}
*
*/
@Test
@SuppressWarnings("serial")
public class InvokableTest {
public void testConstructor_returnType() throws Exception {
assertEquals(Prepender.class, Prepender.constructor().getReturnType().getType());
}
public void testConstructor_exceptionTypes() throws Exception {
assertEquals(ImmutableList.of(TypeToken.of(NullPointerException.class)),
Prepender.constructor(String.class, int.class).getExceptionTypes());
}
public void testConstructor_typeParameters() throws Exception {
TypeVariable<?>[] variables = Prepender.constructor().getTypeParameters();
assertEquals(1, variables.length);
assertEquals("A", variables[0].getName());
}
public void testConstructor_parameters() throws Exception {
Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
ImmutableList<Parameter> parameters = delegate.getParameters();
assertEquals(2, parameters.size());
assertEquals(String.class, parameters.get(0).getType().getType());
assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
assertEquals(int.class, parameters.get(1).getType().getType());
assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
}
public void testConstructor_call() throws Exception {
Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
Prepender prepender = delegate.invoke(null, "a", 1);
assertEquals("a", prepender.prefix);
assertEquals(1, prepender.times);
}
public void testConstructor_returning() throws Exception {
Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class).returning(Prepender.class);
Prepender prepender = delegate.invoke(null, "a", 1);
assertEquals("a", prepender.prefix);
assertEquals(1, prepender.times);
}
public void testConstructor_invalidReturning() throws Exception {
Invokable<?, Prepender> delegate = Prepender.constructor(String.class, int.class);
try {
delegate.returning(SubPrepender.class);
fail();
} catch (IllegalArgumentException expected) {
}
}
public void testStaticMethod_returnType() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
assertEquals(new TypeToken<Iterable<String>>() {
}, delegate.getReturnType());
}
public void testStaticMethod_exceptionTypes() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
assertEquals(ImmutableList.of(), delegate.getExceptionTypes());
}
public void testStaticMethod_typeParameters() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
TypeVariable<?>[] variables = delegate.getTypeParameters();
assertEquals(1, variables.length);
assertEquals("T", variables[0].getName());
}
public void testStaticMethod_parameters() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
ImmutableList<Parameter> parameters = delegate.getParameters();
assertEquals(2, parameters.size());
assertEquals(String.class, parameters.get(0).getType().getType());
assertTrue(parameters.get(0).isAnnotationPresent(NotBlank.class));
assertEquals(new TypeToken<Iterable<String>>() {
}, parameters.get(1).getType());
assertFalse(parameters.get(1).isAnnotationPresent(NotBlank.class));
}
public void testStaticMethod_call() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", String.class, Iterable.class);
@SuppressWarnings("unchecked")
// prepend() returns Iterable<String>
Iterable<String> result = (Iterable<String>) delegate.invoke(null, "a", ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
}
public void testStaticMethod_returning() throws Exception {
Invokable<?, Iterable<String>> delegate = Prepender.method("prepend", String.class, Iterable.class).returning(
new TypeToken<Iterable<String>>() {
});
assertEquals(new TypeToken<Iterable<String>>() {
}, delegate.getReturnType());
Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
}
public void testStaticMethod_returningRawType() throws Exception {
@SuppressWarnings("rawtypes")
// the purpose is to test raw type
Invokable<?, Iterable> delegate = Prepender.method("prepend", String.class, Iterable.class).returning(
Iterable.class);
assertEquals(new TypeToken<Iterable<String>>() {
}, delegate.getReturnType());
@SuppressWarnings("unchecked")
// prepend() returns Iterable<String>
Iterable<String> result = delegate.invoke(null, "a", ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "b", "c"), ImmutableList.copyOf(result));
}
public void testStaticMethod_invalidReturning() throws Exception {
Invokable<?, Object> delegate = Prepender.method("prepend", String.class, Iterable.class);
try {
delegate.returning(new TypeToken<Iterable<Integer>>() {
});
fail();
} catch (IllegalArgumentException expected) {
}
}
public void testInstanceMethod_returnType() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
assertEquals(new TypeToken<Iterable<String>>() {
}, delegate.getReturnType());
}
public void testInstanceMethod_exceptionTypes() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
assertEquals(
ImmutableList.of(TypeToken.of(IllegalArgumentException.class), TypeToken.of(NullPointerException.class)),
delegate.getExceptionTypes());
}
public void testInstanceMethod_typeParameters() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
assertEquals(0, delegate.getTypeParameters().length);
}
public void testInstanceMethod_parameters() throws Exception {
Invokable<?, ?> delegate = Prepender.method("prepend", Iterable.class);
ImmutableList<Parameter> parameters = delegate.getParameters();
assertEquals(1, parameters.size());
assertEquals(new TypeToken<Iterable<String>>() {
}, parameters.get(0).getType());
assertEquals(0, parameters.get(0).getAnnotations().length);
}
public void testInstanceMethod_call() throws Exception {
Invokable<Prepender, ?> delegate = Prepender.method("prepend", Iterable.class);
@SuppressWarnings("unchecked")
// prepend() returns Iterable<String>
Iterable<String> result = (Iterable<String>) delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
}
public void testInstanceMethod_returning() throws Exception {
Invokable<Prepender, Iterable<String>> delegate = Prepender.method("prepend", Iterable.class).returning(
new TypeToken<Iterable<String>>() {
});
assertEquals(new TypeToken<Iterable<String>>() {
}, delegate.getReturnType());
Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
}
public void testInstanceMethod_returningRawType() throws Exception {
@SuppressWarnings("rawtypes")
// the purpose is to test raw type
Invokable<Prepender, Iterable> delegate = Prepender.method("prepend", Iterable.class).returning(Iterable.class);
assertEquals(new TypeToken<Iterable<String>>() {
}, delegate.getReturnType());
@SuppressWarnings("unchecked")
// prepend() returns Iterable<String>
Iterable<String> result = delegate.invoke(new Prepender("a", 2), ImmutableList.of("b", "c"));
assertEquals(ImmutableList.of("a", "a", "b", "c"), ImmutableList.copyOf(result));
}
public void testInstanceMethod_invalidReturning() throws Exception {
Invokable<?, Object> delegate = Prepender.method("prepend", Iterable.class);
try {
delegate.returning(new TypeToken<Iterable<Integer>>() {
});
fail();
} catch (IllegalArgumentException expected) {
}
}
public void testPrivateInstanceMethod_isOverridable() throws Exception {
Invokable<?, ?> delegate = Prepender.method("privateMethod");
assertTrue(delegate.isPrivate());
assertFalse(delegate.isOverridable());
}
public void testPrivateFinalInstanceMethod_isOverridable() throws Exception {
Invokable<?, ?> delegate = Prepender.method("privateFinalMethod");
assertTrue(delegate.isPrivate());
assertTrue(delegate.isFinal());
assertFalse(delegate.isOverridable());
}
public void testStaticMethod_isOverridable() throws Exception {
Invokable<?, ?> delegate = Prepender.method("staticMethod");
assertTrue(delegate.isStatic());
assertFalse(delegate.isOverridable());
}
public void testStaticFinalMethod_isFinal() throws Exception {
Invokable<?, ?> delegate = Prepender.method("staticFinalMethod");
assertTrue(delegate.isStatic());
assertTrue(delegate.isFinal());
assertFalse(delegate.isOverridable());
}
static class Foo {
}
public void testConstructor_isOverridablel() throws Exception {
Invokable<?, ?> delegate = Invokable.from(Foo.class.getDeclaredConstructor());
assertFalse(delegate.isOverridable());
}
private static final class FinalClass {
@SuppressWarnings("unused")
// used by reflection
void notFinalMethod() {
}
}
public void testNonFinalMethodInFinalClass_isOverridable() throws Exception {
Invokable<?, ?> delegate = Invokable.from(FinalClass.class.getDeclaredMethod("notFinalMethod"));
assertFalse(delegate.isOverridable());
}
@Retention(RetentionPolicy.RUNTIME)
private @interface NotBlank {
}
/** Class for testing construcrtor, static method and instance method. */
@SuppressWarnings("unused")
// most are called by reflection
private static class Prepender {
private final String prefix;
private final int times;
Prepender(@NotBlank String prefix, int times) throws NullPointerException {
this.prefix = prefix;
this.times = times;
}
// just for testing
private <A> Prepender() {
this(null, 0);
}
static <T> Iterable<String> prepend(@NotBlank String first, Iterable<String> tail) {
return Iterables.concat(ImmutableList.of(first), tail);
}
Iterable<String> prepend(Iterable<String> tail) throws IllegalArgumentException, NullPointerException {
return Iterables.concat(Collections.nCopies(times, prefix), tail);
}
static Invokable<?, Prepender> constructor(Class<?>... parameterTypes) throws Exception {
Constructor<Prepender> constructor = Prepender.class.getDeclaredConstructor(parameterTypes);
return Invokable.from(constructor);
}
static Invokable<Prepender, Object> method(String name, Class<?>... parameterTypes) {
try {
Method method = Prepender.class.getDeclaredMethod(name, parameterTypes);
@SuppressWarnings("unchecked")
// The method is from Prepender.
Invokable<Prepender, Object> invokable = (Invokable<Prepender, Object>) Invokable.from(method);
return invokable;
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(e);
}
}
private void privateMethod() {
}
private final void privateFinalMethod() {
}
static void staticMethod() {
}
static final void staticFinalMethod() {
}
}
private static class SubPrepender extends Prepender {
@SuppressWarnings("unused")
// needed to satisfy compiler, never called
public SubPrepender() throws NullPointerException {
throw new AssertionError();
}
}
}

View File

@ -34,8 +34,10 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
@Test(groups = "unit")
public class InputParamValidatorTest {
@ -104,7 +106,7 @@ public class InputParamValidatorTest {
}
Injector injector;
RestAnnotationProcessor restAnnotationProcessor;
RestAnnotationProcessor<IntegrationTestAsyncClient> restAnnotationProcessor;
@BeforeClass
void setupFactory() {
@ -112,7 +114,7 @@ public class InputParamValidatorTest {
.newBuilder(
AnonymousProviderMetadata.forClientMappedToAsyncClientOnEndpoint(IntegrationTestClient.class, IntegrationTestAsyncClient.class,
"http://localhost:9999")).buildInjector();
restAnnotationProcessor = injector.getInstance(RestAnnotationProcessor.class);
restAnnotationProcessor = injector.getInstance(Key.get(new TypeLiteral<RestAnnotationProcessor<IntegrationTestAsyncClient>>(){}));
}
}

View File

@ -33,7 +33,9 @@ import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.TypeToken;
import com.google.common.reflect.Invokable;
/**
* Tests behavior of {@code BindMapToStringPayload}
@ -55,12 +57,12 @@ public class BindMapToStringPayloadTest {
@Test
public void testCorrect() throws SecurityException, NoSuchMethodException {
Invokable<?, Object> testPayload = Invokable.from(TestPayload.class.getMethod("testPayload", String.class));
GeneratedHttpRequest request = GeneratedHttpRequest.builder()
GeneratedHttpRequest<TestPayload> request = GeneratedHttpRequest.builder(TypeToken.of(TestPayload.class))
.invocation(Invocation.create(testPayload, ImmutableList.<Object> of("robot")))
.method("POST").endpoint("http://localhost").build();
GeneratedHttpRequest newRequest = binder()
.bindToRequest(request, ImmutableMap.<String,Object>of("fooble", "robot"));
GeneratedHttpRequest<TestPayload> newRequest = binder().bindToRequest(request,
ImmutableMap.<String, Object> of("fooble", "robot"));
assertEquals(newRequest.getRequestLine(), request.getRequestLine());
assertEquals(newRequest.getPayload().getRawContent(), "name robot");
@ -69,11 +71,11 @@ public class BindMapToStringPayloadTest {
@Test
public void testDecodes() throws SecurityException, NoSuchMethodException {
Invokable<?, Object> testPayload = Invokable.from(TestPayload.class.getMethod("changeAdminPass", String.class));
GeneratedHttpRequest request = GeneratedHttpRequest.builder()
GeneratedHttpRequest<TestPayload> request = GeneratedHttpRequest.builder(TypeToken.of(TestPayload.class))
.invocation(Invocation.create(testPayload, ImmutableList.<Object> of("foo")))
.method("POST").endpoint("http://localhost").build();
GeneratedHttpRequest newRequest = binder()
GeneratedHttpRequest<TestPayload> newRequest = binder()
.bindToRequest(request, ImmutableMap.<String,Object>of("adminPass", "foo"));
assertEquals(newRequest.getRequestLine(), request.getRequestLine());
@ -83,7 +85,7 @@ public class BindMapToStringPayloadTest {
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMustHavePayloadAnnotation() throws SecurityException, NoSuchMethodException {
Invokable<?, Object> noPayload = Invokable.from(TestPayload.class.getMethod("noPayload", String.class));
GeneratedHttpRequest request = GeneratedHttpRequest.builder()
GeneratedHttpRequest<TestPayload> request = GeneratedHttpRequest.builder(TypeToken.of(TestPayload.class))
.invocation(Invocation.create(noPayload, ImmutableList.<Object> of("robot")))
.method("POST").endpoint("http://localhost").build();
binder().bindToRequest(request, ImmutableMap.<String,Object>of("fooble", "robot"));

View File

@ -35,7 +35,7 @@ import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
/**
* Allows you to use simple api version comparison to determine if a feature is

View File

@ -33,8 +33,10 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
/**
@ -44,7 +46,7 @@ import com.google.inject.Module;
@Test(groups = "unit")
public abstract class BaseAsyncApiTest<T> extends BaseRestApiTest {
protected RestAnnotationProcessor processor;
protected RestAnnotationProcessor<T> processor;
protected abstract void checkFilters(HttpRequest request);
@ -63,31 +65,36 @@ public abstract class BaseAsyncApiTest<T> extends BaseRestApiTest {
protected void setupFactory() throws IOException {
injector = createInjector();
parserFactory = injector.getInstance(ParseSax.Factory.class);
processor = injector.getInstance(RestAnnotationProcessor.class);
processor = injector.getInstance(rapKey);
}
protected String identity = "identity";
protected String credential = "credential";
@SuppressWarnings("unchecked")
Key<RestAnnotationProcessor<T>> rapKey = (Key<RestAnnotationProcessor<T>>) Key
.get(new TypeToken<RestAnnotationProcessor<T>>(getClass()) {
private static final long serialVersionUID = 1L;
}.getType());
/**
* @see org.jclouds.providers.Providers#withId
*/
protected ProviderMetadata createProviderMetadata() {
return null;
}
/**
* @see org.jclouds.apis.Apis#withId
*/
protected ApiMetadata createApiMetadata() {
return null;
}
protected Injector createInjector() {
ProviderMetadata pm = createProviderMetadata();
ContextBuilder builder = pm != null ? ContextBuilder.newBuilder(pm) : ContextBuilder
.newBuilder(ApiMetadata.class.cast(checkNotNull(createApiMetadata(),
ContextBuilder builder = pm != null ? ContextBuilder.newBuilder(pm) : ContextBuilder.newBuilder(ApiMetadata.class
.cast(checkNotNull(createApiMetadata(),
"either createApiMetadata or createProviderMetadata must be overridden")));
return builder.credentials(identity, credential)

View File

@ -40,7 +40,7 @@ import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.util.Strings2;
@ -49,8 +49,11 @@ import org.testng.annotations.Test;
import com.google.common.collect.Multimap;
import com.google.common.collect.SortedSetMultimap;
import com.google.common.collect.TreeMultimap;
import com.google.common.reflect.TypeParameter;
import com.google.common.reflect.TypeToken;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
/**
@ -177,7 +180,25 @@ public abstract class BaseRestApiTest {
protected void assertResponseParserClassEquals(Invokable<?, ?> method, GeneratedHttpRequest request,
@Nullable Class<?> parserClass) {
assertEquals(TransformerForRequest.createResponseParser(parserFactory, injector, request).getClass(),
parserClass);
assertEquals(transformer(method.getDeclaringClass()).apply(request).getClass(), parserClass);
}
protected <T> RestAnnotationProcessor<T> processor(Class<T> type) {
TypeToken<RestAnnotationProcessor<T>> token = new TypeToken<RestAnnotationProcessor<T>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<T>() {
}, type);
Key<RestAnnotationProcessor<T>> rapKey = (Key<RestAnnotationProcessor<T>>) Key.get(token.getType());
return injector.getInstance(rapKey);
}
protected <T> TransformerForRequest<T> transformer(Class<T> type) {
TypeToken<TransformerForRequest<T>> token = new TypeToken<TransformerForRequest<T>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<T>() {
}, type);
Key<TransformerForRequest<T>> rapKey = (Key<TransformerForRequest<T>>) Key.get(token.getType());
return injector.getInstance(rapKey);
}
}

View File

@ -1,30 +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.rest.internal;
import org.testng.annotations.Test;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public abstract class BaseRestClientTest extends BaseRestApiTest {
}

View File

@ -25,7 +25,7 @@ import static org.testng.Assert.assertTrue;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import org.jclouds.reflect.Invokable;
import com.google.common.reflect.Invokable;
/**
* @author Adrian Cole