mirror of https://github.com/apache/jclouds.git
refactored tests and internal code due to removing custom guava code
This commit is contained in:
parent
145c2a750c
commit
202b9be5e5
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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>
|
||||
* {
|
||||
* @code
|
||||
* Method factoryMethod = Person.class.getMethod("create");
|
||||
* Invokable<?, Person> 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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())));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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())
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>>(){}));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue