mirror of https://github.com/apache/jclouds.git
Updated invokable to include enclosing type and started refactoring SyncProxy
This commit is contained in:
parent
6193fc25bd
commit
d38ae1420d
|
@ -1,238 +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.concurrent.internal;
|
||||
|
||||
import static com.google.common.base.Optional.fromNullable;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.internal.ForwardInvocationToInterface;
|
||||
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.rest.annotations.Delegate;
|
||||
import org.jclouds.rest.internal.AsyncRestClientProxy;
|
||||
import org.jclouds.util.Optionals2;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
/**
|
||||
* Generates RESTful clients from appropriately annotated interfaces.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public final class SyncProxy implements Function<Invocation, Result> {
|
||||
|
||||
public static interface Factory {
|
||||
/**
|
||||
* @param declaring
|
||||
* type of the interface where all invokeds match those of {@code async} except the return values are
|
||||
* dereferenced
|
||||
* @param async
|
||||
* object whose interface matched {@code declaring} except all invokeds return {@link ListenableFuture}
|
||||
* @return blocking invocation handler
|
||||
*/
|
||||
SyncProxy create(Class<?> declaring, Object async);
|
||||
}
|
||||
|
||||
/**
|
||||
* CreateClientForCaller is parameterized, so clients it creates aren't singletons. For example,
|
||||
* CreateClientForCaller satisfies a call like this:
|
||||
* {@code context.getProviderSpecificContext().getApi().getOrgClientForName(name)}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public static class CreateClientForCaller implements Function<ForwardInvocationToInterface, Object> {
|
||||
private final SyncProxy.Factory factory;
|
||||
private final AsyncRestClientProxy.Caller.Factory asyncFactory;
|
||||
|
||||
@Inject
|
||||
private CreateClientForCaller(SyncProxy.Factory factory, AsyncRestClientProxy.Caller.Factory asyncFactory) {
|
||||
this.factory = factory;
|
||||
this.asyncFactory = asyncFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(ForwardInvocationToInterface from) {
|
||||
Object asyncClient = FunctionalReflection.newProxy(from.getInterfaceType(),
|
||||
asyncFactory.caller(from.getInvocation(), from.getInterfaceType()));
|
||||
checkState(asyncClient != null, "configuration error, sync client for " + from + " not found");
|
||||
Class<?> type = Optionals2.unwrapIfOptional(from.getInvocation().getInvokable().getReturnType());
|
||||
return FunctionalReflection.newProxy(type, factory.create(type, asyncClient));
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private Logger logger = Logger.NULL;
|
||||
|
||||
private final Function<InvocationSuccess, Optional<Object>> optionalConverter;
|
||||
private final Object delegate;
|
||||
private final Class<?> declaring;
|
||||
private final Map<Invokable<?, ?>, Invokable<Object, ListenableFuture<?>>> invokedMap;
|
||||
private final Map<Invokable<?, ?>, Invokable<Object, ?>> syncMethodMap;
|
||||
private final Map<Invokable<?, ?>, Optional<Long>> timeoutMap;
|
||||
private final Function<ForwardInvocationToInterface, Object> createClientForCaller;
|
||||
private final Map<Class<?>, Class<?>> sync2Async;
|
||||
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Inject
|
||||
@VisibleForTesting
|
||||
SyncProxy(Function<InvocationSuccess, Optional<Object>> optionalConverter,
|
||||
Function<ForwardInvocationToInterface, Object> createClientForCaller, Map<Class<?>, Class<?>> sync2Async,
|
||||
@Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted Class<?> declaring, @Assisted Object async)
|
||||
throws SecurityException, NoSuchMethodException {
|
||||
this.optionalConverter = optionalConverter;
|
||||
this.createClientForCaller = createClientForCaller;
|
||||
this.delegate = async;
|
||||
this.declaring = declaring;
|
||||
this.sync2Async = ImmutableMap.copyOf(sync2Async);
|
||||
|
||||
ImmutableMap.Builder<Invokable<?, ?>, Invokable<Object, ListenableFuture<?>>> invokedMapBuilder = ImmutableMap
|
||||
.builder();
|
||||
ImmutableMap.Builder<Invokable<?, ?>, Invokable<Object, ?>> syncMethodMapBuilder = ImmutableMap.builder();
|
||||
|
||||
for (Method invoked : declaring.getMethods()) {
|
||||
if (!objectMethods.contains(invoked)) {
|
||||
Method delegatedMethod = delegate.getClass().getMethod(invoked.getName(), invoked.getParameterTypes());
|
||||
if (!Arrays.equals(delegatedMethod.getExceptionTypes(), invoked.getExceptionTypes()))
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"invoked %s has different typed exceptions than delegated invoked %s", invoked, delegatedMethod));
|
||||
if (delegatedMethod.getReturnType().isAssignableFrom(ListenableFuture.class)) {
|
||||
invokedMapBuilder.put(Invokable.from(invoked), Invokable.class.cast(Invokable.from(delegatedMethod)));
|
||||
} else {
|
||||
syncMethodMapBuilder.put(Invokable.from(invoked), Invokable.class.cast(Invokable.from(delegatedMethod)));
|
||||
}
|
||||
}
|
||||
}
|
||||
invokedMap = invokedMapBuilder.build();
|
||||
syncMethodMap = syncMethodMapBuilder.build();
|
||||
|
||||
ImmutableMap.Builder<Invokable<?, ?>, Optional<Long>> timeoutMapBuilder = ImmutableMap.builder();
|
||||
for (Invokable<?, ?> invoked : invokedMap.keySet()) {
|
||||
timeoutMapBuilder.put(invoked, timeoutInNanos(invoked, timeouts));
|
||||
}
|
||||
timeoutMap = timeoutMapBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result apply(Invocation invocation) {
|
||||
if (invocation.getInvokable().isAnnotationPresent(Delegate.class))
|
||||
return forwardToDelegate(invocation);
|
||||
if (syncMethodMap.containsKey(invocation.getInvokable()))
|
||||
return invokeOnDelegate(invocation);
|
||||
return invokeFutureAndBlock(invocation);
|
||||
}
|
||||
|
||||
private Result forwardToDelegate(Invocation invocation) {
|
||||
Class<?> returnType = Optionals2.unwrapIfOptional(invocation.getInvokable().getReturnType());
|
||||
// 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"
|
||||
ForwardInvocationToInterface cma = ForwardInvocationToInterface.create(invocation, asyncClass);
|
||||
Object result = createClientForCaller.apply(cma);
|
||||
if (Optionals2.isReturnTypeOptional(invocation.getInvokable())) {
|
||||
result = optionalConverter.apply(InvocationSuccess.create(invocation, result));
|
||||
}
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
private Result invokeFutureAndBlock(Invocation invocation) {
|
||||
try {
|
||||
Invokable<Object, ListenableFuture<?>> asyncMethod = invokedMap.get(invocation.getInvokable());
|
||||
ListenableFuture<?> future = asyncMethod.invoke(delegate, invocation.getArgs().toArray());
|
||||
Optional<Long> timeoutNanos = timeoutMap.get(invocation.getInvokable());
|
||||
return block(future, timeoutNanos);
|
||||
} catch (InvocationTargetException e) {
|
||||
return Result.fail(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
return Result.fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Result block(ListenableFuture<?> future, Optional<Long> timeoutNanos) {
|
||||
try {
|
||||
if (timeoutNanos.isPresent()) {
|
||||
logger.debug(">> blocking on %s for %s", future, timeoutNanos);
|
||||
return Result.success(future.get(timeoutNanos.get(), TimeUnit.NANOSECONDS));
|
||||
} else {
|
||||
logger.debug(">> blocking on %s", future);
|
||||
return Result.success(future.get());
|
||||
}
|
||||
} catch (ExecutionException e) {
|
||||
return Result.fail(e.getCause());
|
||||
} catch (InterruptedException e) {
|
||||
return Result.fail(e); // TODO: should we kill the future?
|
||||
} catch (TimeoutException e) {
|
||||
return Result.fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Result invokeOnDelegate(Invocation invocation) {
|
||||
Invokable<Object, ?> toInvoke = syncMethodMap.get(invocation.getInvokable());
|
||||
try {
|
||||
return Result.success(toInvoke.invoke(delegate, invocation.getArgs().toArray()));
|
||||
} catch (InvocationTargetException e) {
|
||||
return Result.fail(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
return Result.fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
// override timeout by values configured in properties(in ms)
|
||||
private Optional<Long> timeoutInNanos(Invokable<?, ?> invoked, Map<String, Long> timeouts) {
|
||||
String className = declaring.getSimpleName();
|
||||
Optional<Long> timeoutMillis = fromNullable(timeouts.get(className + "." + invoked.getName())).or(
|
||||
fromNullable(timeouts.get(className))).or(fromNullable(timeouts.get("default")));
|
||||
if (timeoutMillis.isPresent())
|
||||
return Optional.of(TimeUnit.MILLISECONDS.toNanos(timeoutMillis.get()));
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s->%s", declaring.getClass().getSimpleName(), delegate.getClass().getSimpleName());
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.reflect.Invokable;
|
||||
|
||||
/**
|
||||
* Command whose endpoint is an http service.
|
||||
|
@ -134,9 +133,7 @@ public class HttpCommand {
|
|||
public String toString() {
|
||||
if (request instanceof GeneratedHttpRequest) {
|
||||
GeneratedHttpRequest gRequest = GeneratedHttpRequest.class.cast(request);
|
||||
return String.format("[method=%s.%s, request=%s]",
|
||||
gRequest.getInvocation().getInterfaceType().getSimpleName(), gRequest.getInvocation().getInvokable()
|
||||
.getName(), gRequest.getRequestLine());
|
||||
return String.format("[method=%s, request=%s]", gRequest.getInvocation(), gRequest.getRequestLine());
|
||||
}
|
||||
return "[request=" + request.getRequestLine() + "]";
|
||||
}
|
||||
|
|
|
@ -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 com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,84 +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.internal;
|
||||
|
||||
import static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.jclouds.reflect.Invocation;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Objects.ToStringHelper;
|
||||
|
||||
/**
|
||||
* internal type to {@link SyncProxy} which is likely to be removed
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Beta
|
||||
public final class ForwardInvocationToInterface {
|
||||
/**
|
||||
* @param interfaceType
|
||||
* {@link #getInterfaceType()}
|
||||
*/
|
||||
public static ForwardInvocationToInterface create(Invocation invocation, Class<?> interfaceType) {
|
||||
return new ForwardInvocationToInterface(invocation, interfaceType);
|
||||
}
|
||||
|
||||
private final Invocation invocation;
|
||||
private final Class<?> interfaceType;
|
||||
|
||||
private ForwardInvocationToInterface(Invocation invocation, Class<?> interfaceType) {
|
||||
this.invocation = checkNotNull(invocation, "invocation");
|
||||
this.interfaceType = checkNotNull(interfaceType, "interfaceType");
|
||||
}
|
||||
|
||||
public Invocation getInvocation() {
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public Class<?> getInterfaceType() {
|
||||
return interfaceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
ForwardInvocationToInterface that = ForwardInvocationToInterface.class.cast(o);
|
||||
return equal(this.invocation, that.invocation) && equal(this.interfaceType, that.interfaceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(invocation, interfaceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
|
||||
protected ToStringHelper string() {
|
||||
return Objects.toStringHelper("").omitNullValues().add("invocation", invocation).add("interfaceType", interfaceType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/**
|
||||
* 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,7 +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;
|
||||
|
||||
/**
|
||||
* Static utilities relating to functional Java reflection.
|
||||
|
@ -48,8 +48,8 @@ import com.google.common.reflect.Invokable;
|
|||
@Beta
|
||||
public final class FunctionalReflection {
|
||||
/**
|
||||
* Returns a proxy instance that implements {@code interfaceType} by dispatching method invocations to
|
||||
* {@code invocationFunction}. The class loader of {@code interfaceType} will be used to define the proxy class.
|
||||
* Returns a proxy instance that implements {@code enclosingType} by dispatching method invocations to
|
||||
* {@code invocationFunction}. The class loader of {@code enclosingType} will be used to define the proxy class.
|
||||
* <p>
|
||||
* Usage example:
|
||||
*
|
||||
|
@ -72,26 +72,37 @@ public final class FunctionalReflection {
|
|||
* @param invocationFunction
|
||||
* returns a result or a top-level exception, or result
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code interfaceType} does not specify the type of a Java interface
|
||||
* if {@code enclosingType} does not specify the type of a Java interface
|
||||
* @see com.google.common.reflect.AbstractInvocationHandler#invoke(Object, Method, Object[])
|
||||
* @see com.google.common.reflect.Reflection#newProxy(Class, java.lang.reflect.InvocationHandler)
|
||||
*/
|
||||
public static <T> T newProxy(Class<T> interfaceType, Function<Invocation, Result> invocationFunction) {
|
||||
checkNotNull(interfaceType, "interfaceType");
|
||||
public static <T> T newProxy(TypeToken<T> enclosingType, Function<Invocation, Result> invocationFunction) {
|
||||
checkNotNull(enclosingType, "enclosingType");
|
||||
checkNotNull(invocationFunction, "invocationFunction");
|
||||
checkArgument(interfaceType.isInterface(), "%s is not an interface", interfaceType);
|
||||
Object object = Proxy.newProxyInstance(interfaceType.getClassLoader(), new Class<?>[] { interfaceType },
|
||||
new FunctionalInvocationHandler<T>(interfaceType, invocationFunction));
|
||||
return interfaceType.cast(object);
|
||||
return newProxy(enclosingType.getRawType(), new FunctionalInvocationHandler<T>(enclosingType, invocationFunction));
|
||||
}
|
||||
|
||||
public static <T> T newProxy(Class<T> enclosingType, Function<Invocation, Result> invocationFunction) {
|
||||
checkNotNull(invocationFunction, "invocationFunction");
|
||||
return newProxy(enclosingType,
|
||||
new FunctionalInvocationHandler<T>(TypeToken.of(enclosingType), invocationFunction));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T newProxy(Class<? super T> enclosingType, FunctionalInvocationHandler<T> invocationHandler) {
|
||||
checkNotNull(enclosingType, "enclosingType");
|
||||
checkArgument(enclosingType.isInterface(), "%s is not an interface", enclosingType);
|
||||
return (T) Proxy.newProxyInstance(enclosingType.getClassLoader(), new Class<?>[] { enclosingType },
|
||||
invocationHandler);
|
||||
}
|
||||
|
||||
private static final class FunctionalInvocationHandler<T> extends
|
||||
com.google.common.reflect.AbstractInvocationHandler {
|
||||
private final Class<T> interfaceType;
|
||||
private final TypeToken<T> enclosingType;
|
||||
private final Function<Invocation, Result> invocationFunction;
|
||||
|
||||
private FunctionalInvocationHandler(Class<T> interfaceType, Function<Invocation, Result> invocationFunction) {
|
||||
this.interfaceType = interfaceType;
|
||||
private FunctionalInvocationHandler(TypeToken<T> enclosingType, Function<Invocation, Result> invocationFunction) {
|
||||
this.enclosingType = enclosingType;
|
||||
this.invocationFunction = invocationFunction;
|
||||
}
|
||||
|
||||
|
@ -102,9 +113,9 @@ public final class FunctionalReflection {
|
|||
args = ImmutableList.copyOf(args);
|
||||
else
|
||||
args = Collections.unmodifiableList(args);
|
||||
Invokable<?, ?> invokable = Invokable.class.cast(Invokable.from(invoked));
|
||||
Invokable<T, ?> invokable = Invokable.from(enclosingType, invoked);
|
||||
// not yet support the proxy arg
|
||||
Invocation invocation = Invocation.create(interfaceType, invokable, args);
|
||||
Invocation invocation = Invocation.create(invokable, args);
|
||||
Result result;
|
||||
try {
|
||||
result = invocationFunction.apply(invocation);
|
||||
|
@ -125,13 +136,13 @@ public final class FunctionalReflection {
|
|||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
FunctionalInvocationHandler<?> that = FunctionalInvocationHandler.class.cast(o);
|
||||
return equal(this.interfaceType, that.interfaceType)
|
||||
return equal(this.enclosingType, that.enclosingType)
|
||||
&& equal(this.invocationFunction, that.invocationFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(interfaceType, invocationFunction);
|
||||
return Objects.hashCode(enclosingType, invocationFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
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.util.List;
|
||||
|
@ -28,12 +27,10 @@ import org.jclouds.javax.annotation.Nullable;
|
|||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Objects.ToStringHelper;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.reflect.Invokable;
|
||||
|
||||
/**
|
||||
* Context needed to call {@link com.google.common.reflect.Invokable#invoke(Object, Object...)}
|
||||
* Context needed to call {@link org.jclouds.reflect.Invokable#invoke(Object, Object...)}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
|
@ -41,48 +38,21 @@ import com.google.common.reflect.Invokable;
|
|||
public final class Invocation {
|
||||
|
||||
/**
|
||||
* Use this class when the invokable could be inherited. For example, a method is inherited when it cannot be
|
||||
* retrieved by {@link Class#getDeclaredMethods()}, but it can be retrieved by {@link Class#getMethods()}.
|
||||
*
|
||||
* @param interfaceType
|
||||
* type that either declared or inherited {@code invokable}, or was forwarded a call to it.
|
||||
* @param args
|
||||
* as these represent parameters, can contain nulls
|
||||
*/
|
||||
public static Invocation create(Class<?> interfaceType, Invokable<?, ?> invokable, List<Object> args) {
|
||||
checkArgument(invokable.getDeclaringClass().isAssignableFrom(interfaceType), "%s isn't assignable from %s",
|
||||
invokable.getDeclaringClass(), interfaceType);
|
||||
return new Invocation(interfaceType, invokable, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: use {@link #create(Class, Invokable, List)} when the invokable was inherited.
|
||||
*
|
||||
* @param args
|
||||
* as these represent parameters, can contain nulls
|
||||
*/
|
||||
public static Invocation create(Invokable<?, ?> invokable, List<Object> args) {
|
||||
return new Invocation(invokable.getDeclaringClass(), invokable, args);
|
||||
return new Invocation(invokable, args);
|
||||
}
|
||||
|
||||
private final Class<?> interfaceType;
|
||||
private final Invokable<?, ?> invokable;
|
||||
private final List<Object> args;
|
||||
|
||||
private Invocation(Class<?> interfaceType, Invokable<?, ?> invokable, List<Object> args) {
|
||||
this.interfaceType = checkNotNull(interfaceType, "interfaceType");
|
||||
private Invocation(Invokable<?, ?> invokable, List<Object> args) {
|
||||
this.invokable = checkNotNull(invokable, "invokable");
|
||||
this.args = checkNotNull(args, "args");
|
||||
}
|
||||
|
||||
/**
|
||||
* different than {@link Invokable#getDeclaringClass()} when {@link #getInvokable()} is a member of a class it was
|
||||
* not declared in.
|
||||
*/
|
||||
public Class<?> getInterfaceType() {
|
||||
return interfaceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* what we can invoke
|
||||
*/
|
||||
|
@ -107,19 +77,17 @@ public final class Invocation {
|
|||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
Invocation that = Invocation.class.cast(o);
|
||||
return equal(this.interfaceType, that.interfaceType) && equal(this.invokable, that.invokable)
|
||||
&& equal(this.args, that.args);
|
||||
return equal(this.invokable, that.invokable) && equal(this.args, that.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(interfaceType, invokable, args);
|
||||
return Objects.hashCode(invokable, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toStringHelper("").omitNullValues().add("interfaceType", interfaceType)
|
||||
.add("invokable", invokable).add("args", args.size() != 0 ? args : null).toString();
|
||||
return String.format("%s%s", invokable, args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,12 +143,8 @@ public final class Invocation {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string().toString();
|
||||
}
|
||||
|
||||
protected ToStringHelper string() {
|
||||
return Objects.toStringHelper("").omitNullValues().add("result", result.orNull())
|
||||
.add("throwable", throwable.orNull());
|
||||
.add("throwable", throwable.orNull()).toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 com.google.common.reflect.Invokable#invoke(Object, Object...)}
|
||||
* Holds the context of a successful call to {@link org.jclouds.reflect.Invokable#invoke(Object, Object...)}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,347 @@
|
|||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* 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 com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -22,7 +22,7 @@ import javax.inject.Inject;
|
|||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.reflect.FunctionalReflection;
|
||||
import org.jclouds.rest.internal.AsyncRestClientProxy;
|
||||
import org.jclouds.rest.internal.InvokeAsyncApi;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
@ -34,10 +34,10 @@ import com.google.inject.TypeLiteral;
|
|||
@Singleton
|
||||
public class AsyncClientProvider<A> implements Provider<A> {
|
||||
private final Class<? super A> asyncClientType;
|
||||
private final AsyncRestClientProxy proxy;
|
||||
private final InvokeAsyncApi proxy;
|
||||
|
||||
@Inject
|
||||
private AsyncClientProvider(AsyncRestClientProxy proxy, TypeLiteral<A> asyncClientType) {
|
||||
private AsyncClientProvider(InvokeAsyncApi proxy, TypeLiteral<A> asyncClientType) {
|
||||
this.proxy = proxy;
|
||||
this.asyncClientType = asyncClientType.getRawType();
|
||||
}
|
||||
|
|
|
@ -46,8 +46,10 @@ public class BinderUtils {
|
|||
* type type that returns {@link ListenableFuture}
|
||||
*/
|
||||
public static <S, A> void bindClientAndAsyncClient(Binder binder, Class<S> sync, Class<A> async) {
|
||||
bindAsyncClient(binder, async);
|
||||
bindClient(binder, sync, async);
|
||||
bindClass(binder, sync);
|
||||
bindClass(binder, async);
|
||||
bindAsyncClientProvider(binder, async);
|
||||
bindClientProvider(binder, sync, async);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,9 +66,14 @@ public class BinderUtils {
|
|||
* @param async
|
||||
* type type that returns {@link ListenableFuture}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <S, A> void bindClient(Binder binder, Class<S> sync, Class<A> async) {
|
||||
bindClass(binder, sync);
|
||||
bindClass(binder, async);
|
||||
bindClientProvider(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 final long serialVersionUID = 1L;
|
||||
}.where(new TypeParameter<S>() {
|
||||
|
@ -83,9 +90,13 @@ public class BinderUtils {
|
|||
}, sync).getType()))).toInstance(sync);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> void bindAsyncClient(Binder binder, Class<T> async) {
|
||||
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>() {
|
||||
|
|
|
@ -21,9 +21,12 @@ package org.jclouds.rest.config;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.concurrent.internal.SyncProxy;
|
||||
import org.jclouds.reflect.FunctionalReflection;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import org.jclouds.rest.internal.InvokeSyncApi;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
/**
|
||||
|
@ -35,20 +38,22 @@ import com.google.inject.Provider;
|
|||
@Singleton
|
||||
public class ClientProvider<S, A> implements Provider<S> {
|
||||
|
||||
private final SyncProxy.Factory factory;
|
||||
private final InvokeSyncApi.Factory factory;
|
||||
private final Class<S> syncClientType;
|
||||
private final A asyncClient;
|
||||
|
||||
@Inject
|
||||
private ClientProvider(SyncProxy.Factory factory, Class<S> syncClientType, A asyncClient) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Singleton
|
||||
public S get() {
|
||||
return FunctionalReflection.newProxy(syncClientType, factory.create(syncClientType, asyncClient));
|
||||
return FunctionalReflection.newProxy(syncClientType, factory.create(asyncClient));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ public class RestClientModule<S, A> extends RestModule {
|
|||
"unbound type variable: %s, use ctor that explicitly assigns this", type);
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #RestClientModule(Map)
|
||||
*/
|
||||
|
@ -91,8 +92,6 @@ public class RestClientModule<S, A> extends RestModule {
|
|||
bindRetryHandlers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* overrides this to change the default retry handlers for the http engine
|
||||
*
|
||||
|
|
|
@ -18,39 +18,60 @@
|
|||
*/
|
||||
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.util.Maps2.transformKeys;
|
||||
import static org.jclouds.util.Predicates2.startsWith;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.jclouds.concurrent.internal.SyncProxy;
|
||||
import org.jclouds.concurrent.internal.SyncProxy.CreateClientForCaller;
|
||||
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.internal.ForwardInvocationToInterface;
|
||||
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 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.AsyncRestClientProxy;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.jclouds.util.Maps2;
|
||||
import org.jclouds.util.Predicates2;
|
||||
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 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.Maps;
|
||||
import com.google.common.util.concurrent.Atomics;
|
||||
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;
|
||||
|
@ -60,31 +81,105 @@ public class RestModule extends AbstractModule {
|
|||
|
||||
public static final TypeLiteral<Supplier<URI>> URI_SUPPLIER_TYPE = new TypeLiteral<Supplier<URI>>() {
|
||||
};
|
||||
private final Map<Class<?>, Class<?>> sync2Async;
|
||||
protected final AtomicReference<AuthorizationException> authException = Atomics.newReference();
|
||||
|
||||
protected final Map<Class<?>, Class<?>> sync2Async;
|
||||
protected final AtomicReference<AuthorizationException> authException = newReference();
|
||||
|
||||
public RestModule() {
|
||||
this(ImmutableMap.<Class<?>, Class<?>> of());
|
||||
}
|
||||
|
||||
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
|
||||
|
||||
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.
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
protected Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables() {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncBuilder = CacheBuilder.newBuilder().build();
|
||||
putInvokables(TypeToken.of(HttpClient.class), TypeToken.of(HttpAsyncClient.class), sync2AsyncBuilder);
|
||||
for (Class<?> s : sync2Async.keySet()) {
|
||||
putInvokables(TypeToken.of(s), TypeToken.of(sync2Async.get(s)), sync2AsyncBuilder);
|
||||
}
|
||||
return sync2AsyncBuilder;
|
||||
}
|
||||
|
||||
// accessible for ClientProvider
|
||||
public static void putInvokables(TypeToken<?> sync, TypeToken<?> async, Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
||||
for (Method invoked : sync.getRawType().getMethods()) {
|
||||
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);
|
||||
invoked.setAccessible(true);
|
||||
delegatedMethod.setAccessible(true);
|
||||
cache.put(Invokable.from(sync, invoked), Invokable.from(async, delegatedMethod));
|
||||
} catch (SecurityException e) {
|
||||
throw propagate(e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void installLocations() {
|
||||
install(new LocationModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Map<Class<?>, Class<?>>>(){}).toInstance(sync2Async);
|
||||
bind(new TypeLiteral<Map<Class<?>, Class<?>>>() {
|
||||
}).toInstance(sync2Async);
|
||||
install(new SaxParserModule());
|
||||
install(new GsonModule());
|
||||
install(new FactoryModuleBuilder().build(BindToJsonPayloadWrappedWith.Factory.class));
|
||||
install(new FactoryModuleBuilder().build(RestAnnotationProcessor.Caller.Factory.class));
|
||||
install(new FactoryModuleBuilder().build(AsyncRestClientProxy.Caller.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));
|
||||
bind(IdentityFunction.class).toInstance(IdentityFunction.INSTANCE);
|
||||
install(new FactoryModuleBuilder().build(SyncProxy.Factory.class));
|
||||
install(new FactoryModuleBuilder().build(InvokeSyncApi.Factory.class));
|
||||
bindClientAndAsyncClient(binder(), HttpClient.class, HttpAsyncClient.class);
|
||||
// this will help short circuit scenarios that can otherwise lock out users
|
||||
bind(new TypeLiteral<AtomicReference<AuthorizationException>>() {
|
||||
|
@ -93,8 +188,10 @@ public class RestModule extends AbstractModule {
|
|||
}).to(FilterStringsBoundToInjectorByName.class);
|
||||
bind(new TypeLiteral<Function<Predicate<String>, Map<String, String>>>() {
|
||||
}).to(FilterStringsBoundToInjectorByName.class);
|
||||
bind(new TypeLiteral<Function<ForwardInvocationToInterface, Object>>() {
|
||||
}).to(CreateClientForCaller.class);
|
||||
bind(new TypeLiteral<Function<Invocation, ListenableFuture<?>>>() {
|
||||
}).to(InvokeListenableFutureViaHttp.class);
|
||||
bind(new TypeLiteral<Function<GeneratedHttpRequest, Function<HttpResponse, ?>>>() {
|
||||
}).to(TransformerForRequest.class);
|
||||
installLocations();
|
||||
}
|
||||
|
||||
|
@ -102,22 +199,16 @@ 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(Predicates2.startsWith(PROPERTY_TIMEOUTS_PREFIX));
|
||||
Map<String, Long> longsByName = Maps.transformValues(stringBoundWithTimeoutPrefix, new Function<String, Long>() {
|
||||
|
||||
@Override
|
||||
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));
|
||||
}
|
||||
|
||||
});
|
||||
return Maps2.transformKeys(longsByName, new Function<String, String>() {
|
||||
|
||||
@Override
|
||||
return transformKeys(longsByName, new Function<String, String>() {
|
||||
public String apply(String input) {
|
||||
return input.replaceFirst(PROPERTY_TIMEOUTS_PREFIX, "");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -1,529 +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.Functions.compose;
|
||||
import static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Predicates.in;
|
||||
import static com.google.common.base.Predicates.not;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Iterables.any;
|
||||
import static com.google.common.collect.Iterables.find;
|
||||
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
|
||||
import static com.google.common.util.concurrent.Futures.transform;
|
||||
import static com.google.common.util.concurrent.Futures.withFallback;
|
||||
import static com.google.inject.util.Types.newParameterizedType;
|
||||
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
|
||||
import static javax.ws.rs.core.MediaType.APPLICATION_XML;
|
||||
import static org.jclouds.concurrent.Futures.makeListenable;
|
||||
import static org.jclouds.http.HttpUtils.tryFindHttpMethod;
|
||||
import static org.jclouds.util.Optionals2.isReturnTypeOptional;
|
||||
import static org.jclouds.util.Optionals2.unwrapIfOptional;
|
||||
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Qualifier;
|
||||
import javax.inject.Singleton;
|
||||
import javax.lang.model.type.NullType;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
|
||||
import org.jclouds.functions.IdentityFunction;
|
||||
import org.jclouds.functions.OnlyElementOrNull;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpCommandExecutorService;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
||||
import org.jclouds.http.functions.ParseJson;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
|
||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
||||
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
||||
import org.jclouds.http.functions.ReturnInputStream;
|
||||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||
import org.jclouds.http.functions.ReturnTrueIf2xx;
|
||||
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
|
||||
import org.jclouds.json.internal.GsonWrapper;
|
||||
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.rest.AuthorizationException;
|
||||
import org.jclouds.rest.InvocationContext;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
import org.jclouds.rest.annotations.Fallback;
|
||||
import org.jclouds.rest.annotations.JAXBResponseParser;
|
||||
import org.jclouds.rest.annotations.OnlyElement;
|
||||
import org.jclouds.rest.annotations.ResponseParser;
|
||||
import org.jclouds.rest.annotations.SelectJson;
|
||||
import org.jclouds.rest.annotations.Transform;
|
||||
import org.jclouds.rest.annotations.Unwrap;
|
||||
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.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.FutureFallback;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
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.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 AsyncRestClientProxy implements Function<Invocation, Result> {
|
||||
|
||||
public final static class Caller extends AsyncRestClientProxy {
|
||||
|
||||
public static interface Factory {
|
||||
Caller caller(Invocation caller, Class<?> interfaceType);
|
||||
}
|
||||
|
||||
@Inject
|
||||
private Caller(Injector injector, Function<InvocationSuccess, Optional<Object>> optionalConverter,
|
||||
HttpCommandExecutorService http, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
|
||||
Caller.Factory factory, RestAnnotationProcessor.Caller.Factory rap, ParseSax.Factory parserFactory,
|
||||
@Assisted Invocation caller) {
|
||||
super(injector, optionalConverter, http, userThreads, factory, rap.caller(caller), parserFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private Logger logger = Logger.NULL;
|
||||
|
||||
private final Injector injector;
|
||||
private final HttpCommandExecutorService http;
|
||||
private final ExecutorService userThreads;
|
||||
private final Function<InvocationSuccess, Optional<Object>> optionalConverter;
|
||||
private final Caller.Factory factory;
|
||||
private final RestAnnotationProcessor annotationProcessor;
|
||||
private final ParseSax.Factory parserFactory;
|
||||
|
||||
private static final LoadingCache<Class<?>, Set<InterfaceNameAndParameters>> delegationMapCache = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<Class<?>, Set<InterfaceNameAndParameters>>() {
|
||||
public Set<InterfaceNameAndParameters> load(final Class<?> interfaceType) throws ExecutionException {
|
||||
return FluentIterable.from(ImmutableSet.copyOf(interfaceType.getMethods()))
|
||||
.filter(not(in(ImmutableSet.copyOf(Object.class.getMethods()))))
|
||||
.transform(new Function<Method, Invokable<?, ?>>() {
|
||||
public Invokable<?, ?> apply(Method in) {
|
||||
return Invokable.from(in);
|
||||
}
|
||||
}).filter(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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).filter(new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> in) {
|
||||
return in.getReturnType().getRawType().isAssignableFrom(ListenableFuture.class);
|
||||
}
|
||||
}).transform(new Function<Invokable<?, ?>, InterfaceNameAndParameters>() {
|
||||
public InterfaceNameAndParameters apply(Invokable<?, ?> in) {
|
||||
return new InterfaceNameAndParameters(interfaceType, in.getName(), in.getParameters());
|
||||
}
|
||||
}).toSet();
|
||||
}
|
||||
});
|
||||
|
||||
private static final class InterfaceNameAndParameters {
|
||||
private final Class<?> interfaceType;
|
||||
private final String name;
|
||||
private final int parametersTypeHashCode;
|
||||
|
||||
private InterfaceNameAndParameters(Class<?> interfaceType, String name, ImmutableList<Parameter> parameters) {
|
||||
this.interfaceType = interfaceType;
|
||||
this.name = name;
|
||||
int parametersTypeHashCode = 0;
|
||||
for (Parameter param : parameters)
|
||||
parametersTypeHashCode += param.getType().hashCode();
|
||||
this.parametersTypeHashCode = parametersTypeHashCode;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(interfaceType, name, parametersTypeHashCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
InterfaceNameAndParameters that = InterfaceNameAndParameters.class.cast(o);
|
||||
return equal(this.interfaceType, that.interfaceType) && equal(this.name, that.name)
|
||||
&& equal(this.parametersTypeHashCode, that.parametersTypeHashCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
private AsyncRestClientProxy(Injector injector, Function<InvocationSuccess, Optional<Object>> optionalConverter,
|
||||
HttpCommandExecutorService http, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
|
||||
Caller.Factory factory, RestAnnotationProcessor annotationProcessor, ParseSax.Factory parserFactory) {
|
||||
this.injector = injector;
|
||||
this.optionalConverter = optionalConverter;
|
||||
this.http = http;
|
||||
this.userThreads = userThreads;
|
||||
this.factory = factory;
|
||||
this.annotationProcessor = annotationProcessor;
|
||||
this.parserFactory = parserFactory;
|
||||
}
|
||||
|
||||
private static final Predicate<Annotation> isQualifierPresent = new Predicate<Annotation>() {
|
||||
public boolean apply(Annotation input) {
|
||||
return input.annotationType().isAnnotationPresent(Qualifier.class);
|
||||
}
|
||||
};
|
||||
|
||||
@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));
|
||||
} else if (isAsyncOrDelegate(invocation)) {
|
||||
return Result.success(createListenableFutureForHttpRequestMappedToMethodAndArgs(invocation));
|
||||
} else {
|
||||
return Result.fail(new IllegalStateException(String.format(
|
||||
"Method is not annotated as either http or provider invoked: %s", invocation.getInvokable())));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAsyncOrDelegate(Invocation invocation) {
|
||||
return delegationMapCache.getUnchecked(invocation.getInterfaceType()).contains(
|
||||
new InterfaceNameAndParameters(invocation.getInterfaceType(), invocation.getInvokable().getName(),
|
||||
invocation.getInvokable().getParameters()));
|
||||
}
|
||||
|
||||
private Object propagateContextToDelegate(Invocation invocation) {
|
||||
Class<?> returnType = unwrapIfOptional(invocation.getInvokable().getReturnType());
|
||||
Object result = FunctionalReflection.newProxy(returnType, factory.caller(invocation, returnType));
|
||||
if (isReturnTypeOptional(invocation.getInvokable())) {
|
||||
return optionalConverter.apply(InvocationSuccess.create(invocation, result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
|
||||
private 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));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@VisibleForTesting
|
||||
static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
|
||||
Invocation invocation, HttpRequest request) {
|
||||
Function<HttpResponse, ?> transformer;
|
||||
Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(invocation.getInvokable());
|
||||
if (handler != null) {
|
||||
transformer = parserFactory.create(injector.getInstance(handler));
|
||||
} else {
|
||||
transformer = getTransformerForMethod(invocation, injector);
|
||||
}
|
||||
if (transformer instanceof InvocationContext<?>) {
|
||||
((InvocationContext<?>) transformer).setContext(request);
|
||||
}
|
||||
if (invocation.getInvokable().isAnnotationPresent(Transform.class)) {
|
||||
Function<?, ?> wrappingTransformer = injector.getInstance(invocation.getInvokable()
|
||||
.getAnnotation(Transform.class).value());
|
||||
if (wrappingTransformer instanceof InvocationContext<?>) {
|
||||
((InvocationContext<?>) wrappingTransformer).setContext(request);
|
||||
}
|
||||
transformer = compose(Function.class.cast(wrappingTransformer), transformer);
|
||||
}
|
||||
return transformer;
|
||||
}
|
||||
|
||||
private static Class<? extends HandlerWithResult<?>> getSaxResponseParserClassOrNull(Invokable<?, ?> invoked) {
|
||||
XMLResponseParser annotation = invoked.getAnnotation(XMLResponseParser.class);
|
||||
if (annotation != null) {
|
||||
return annotation.value();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: refactor this out of here
|
||||
@VisibleForTesting
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static Function<HttpResponse, ?> getTransformerForMethod(Invocation invocation, Injector injector) {
|
||||
Invokable<?, ?> invoked = invocation.getInvokable();
|
||||
Function<HttpResponse, ?> transformer;
|
||||
if (invoked.isAnnotationPresent(SelectJson.class)) {
|
||||
Type returnVal = getReturnTypeFor(invoked.getReturnType());
|
||||
if (invoked.isAnnotationPresent(OnlyElement.class))
|
||||
returnVal = newParameterizedType(Set.class, returnVal);
|
||||
transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class),
|
||||
TypeLiteral.get(returnVal), invoked.getAnnotation(SelectJson.class).value());
|
||||
if (invoked.isAnnotationPresent(OnlyElement.class))
|
||||
transformer = compose(new OnlyElementOrNull(), transformer);
|
||||
} else {
|
||||
transformer = injector.getInstance(getParserOrThrowException(invocation));
|
||||
}
|
||||
return transformer;
|
||||
}
|
||||
|
||||
private ListenableFuture<?> createListenableFutureForHttpRequestMappedToMethodAndArgs(Invocation invocation) {
|
||||
String name = invocation.getInterfaceType().getSimpleName() + "." + invocation.getInvokable().getName();
|
||||
logger.trace(">> converting %s", name);
|
||||
FutureFallback<?> fallback = fallbacks.getUnchecked(invocation.getInvokable());
|
||||
// in case there is an exception creating the request, we should at least pass in args
|
||||
if (fallback instanceof InvocationContext) {
|
||||
InvocationContext.class.cast(fallback).setContext((HttpRequest) null);
|
||||
}
|
||||
ListenableFuture<?> result;
|
||||
try {
|
||||
GeneratedHttpRequest request = annotationProcessor.apply(invocation);
|
||||
if (fallback instanceof InvocationContext) {
|
||||
InvocationContext.class.cast(fallback).setContext(request);
|
||||
}
|
||||
logger.trace("<< converted %s to %s", name, request.getRequestLine());
|
||||
|
||||
Function<HttpResponse, ?> transformer = createResponseParser(parserFactory, injector, invocation, request);
|
||||
logger.trace("<< response from %s is parsed by %s", name, transformer.getClass().getSimpleName());
|
||||
|
||||
logger.debug(">> invoking %s", name);
|
||||
result = transform(makeListenable(http.submit(new HttpCommand(request)), userThreads), transformer);
|
||||
} catch (RuntimeException e) {
|
||||
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
|
||||
if (aex != null)
|
||||
e = aex;
|
||||
try {
|
||||
return fallback.create(e);
|
||||
} catch (Exception ex) {
|
||||
return immediateFailedFuture(ex);
|
||||
}
|
||||
}
|
||||
logger.trace("<< exceptions from %s are parsed by %s", name, fallback.getClass().getSimpleName());
|
||||
return withFallback(result, fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("async->http");
|
||||
}
|
||||
|
||||
private final LoadingCache<Invokable<?, ?>, FutureFallback<?>> fallbacks = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Invokable<?, ?>, FutureFallback<?>>() {
|
||||
|
||||
@Override
|
||||
public FutureFallback<?> load(Invokable<?, ?> key) throws Exception {
|
||||
Fallback annotation = key.getAnnotation(Fallback.class);
|
||||
if (annotation != null) {
|
||||
return injector.getInstance(annotation.value());
|
||||
}
|
||||
return injector.getInstance(MapHttp4xxCodesToExceptions.class);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
private static final TypeToken<ListenableFuture<Boolean>> futureBooleanLiteral = new TypeToken<ListenableFuture<Boolean>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<String>> futureStringLiteral = new TypeToken<ListenableFuture<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<Void>> futureVoidLiteral = new TypeToken<ListenableFuture<Void>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<URI>> futureURILiteral = new TypeToken<ListenableFuture<URI>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<InputStream>> futureInputStreamLiteral = new TypeToken<ListenableFuture<InputStream>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeToken<ListenableFuture<HttpResponse>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@VisibleForTesting
|
||||
static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Invocation invocation) {
|
||||
Invokable<?, ?> invoked = invocation.getInvokable();
|
||||
ResponseParser annotation = invoked.getAnnotation(ResponseParser.class);
|
||||
if (annotation == null) {
|
||||
if (invoked.getReturnType().equals(void.class) || invoked.getReturnType().equals(futureVoidLiteral)) {
|
||||
return Key.get(ReleasePayloadAndReturn.class);
|
||||
} else if (invoked.getReturnType().equals(boolean.class) || invoked.getReturnType().equals(Boolean.class)
|
||||
|| invoked.getReturnType().equals(futureBooleanLiteral)) {
|
||||
return Key.get(ReturnTrueIf2xx.class);
|
||||
} else if (invoked.getReturnType().equals(InputStream.class)
|
||||
|| invoked.getReturnType().equals(futureInputStreamLiteral)) {
|
||||
return Key.get(ReturnInputStream.class);
|
||||
} 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)) {
|
||||
return getJsonParserKeyForMethod(invoked);
|
||||
} else if (RestAnnotationProcessor.getAcceptHeaders(invocation).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);
|
||||
} else if (invoked.getReturnType().equals(URI.class) || invoked.getReturnType().equals(futureURILiteral)) {
|
||||
return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
|
||||
} else {
|
||||
throw new IllegalStateException("You must specify a ResponseParser annotation on: " + invoked.toString());
|
||||
}
|
||||
}
|
||||
return Key.get(annotation.value());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Key<? extends Function<HttpResponse, ?>> getJAXBParserKeyForMethod(Invokable<?, ?> invoked) {
|
||||
Optional<Type> configuredReturnVal = Optional.absent();
|
||||
if (invoked.isAnnotationPresent(JAXBResponseParser.class)) {
|
||||
Type configuredClass = invoked.getAnnotation(JAXBResponseParser.class).value();
|
||||
configuredReturnVal = configuredClass.equals(NullType.class) ? Optional.<Type> absent() : Optional
|
||||
.<Type> of(configuredClass);
|
||||
}
|
||||
Type returnVal = configuredReturnVal.or(getReturnTypeFor(invoked.getReturnType()));
|
||||
Type parserType = newParameterizedType(ParseXMLWithJAXB.class, returnVal);
|
||||
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
private static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethod(Invokable<?, ?> invoked) {
|
||||
ParameterizedType parserType;
|
||||
if (invoked.isAnnotationPresent(Unwrap.class)) {
|
||||
parserType = newParameterizedType(UnwrapOnlyJsonValue.class, getReturnTypeFor(invoked.getReturnType()));
|
||||
} else {
|
||||
parserType = newParameterizedType(ParseJson.class, getReturnTypeFor(invoked.getReturnType()));
|
||||
}
|
||||
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
|
||||
}
|
||||
|
||||
private static Type getReturnTypeFor(TypeToken<?> typeToken) {
|
||||
Type returnVal = typeToken.getType();
|
||||
if (typeToken.getRawType().getTypeParameters().length == 0) {
|
||||
returnVal = typeToken.getRawType();
|
||||
} else if (typeToken.getRawType().equals(ListenableFuture.class)) {
|
||||
ParameterizedType futureType = (ParameterizedType) typeToken.getType();
|
||||
returnVal = futureType.getActualTypeArguments()[0];
|
||||
if (returnVal instanceof WildcardType)
|
||||
returnVal = WildcardType.class.cast(returnVal).getUpperBounds()[0];
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
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));
|
||||
}
|
||||
}
|
|
@ -37,15 +37,15 @@ import com.google.common.collect.Multimap;
|
|||
* @author Adrian Cole
|
||||
*/
|
||||
public final class GeneratedHttpRequest extends HttpRequest {
|
||||
public static Builder builder() {
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
|
||||
public Builder toBuilder() {
|
||||
return new Builder().fromGeneratedHttpRequest(this);
|
||||
}
|
||||
|
||||
public final static class Builder extends HttpRequest.Builder<Builder> {
|
||||
public final static class Builder extends HttpRequest.Builder<Builder> {
|
||||
protected Invocation invocation;
|
||||
protected Optional<Invocation> caller = Optional.absent();
|
||||
|
||||
|
@ -57,7 +57,7 @@ public final class GeneratedHttpRequest extends HttpRequest {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @see GeneratedHttpRequest#getCaller()
|
||||
*/
|
||||
public Builder caller(@Nullable Invocation caller) {
|
||||
|
@ -71,9 +71,15 @@ public final class GeneratedHttpRequest extends HttpRequest {
|
|||
}
|
||||
|
||||
public Builder fromGeneratedHttpRequest(GeneratedHttpRequest in) {
|
||||
return super.fromHttpRequest(in)
|
||||
.invocation(in.invocation)
|
||||
.caller(in.getCaller().orNull());
|
||||
return super.fromHttpRequest(in).invocation(in.invocation).caller(in.getCaller().orNull());
|
||||
}
|
||||
|
||||
Invocation getInvocation() {
|
||||
return invocation;
|
||||
}
|
||||
|
||||
Optional<Invocation> getCaller() {
|
||||
return caller;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,7 +87,7 @@ public final class GeneratedHttpRequest extends HttpRequest {
|
|||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Invocation invocation;
|
||||
private final Optional<Invocation> caller;
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* 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.Optional.fromNullable;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
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.util.concurrent.ListenableFuture;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
public class InvokeFutureAndBlock implements Function<Invocation, Result> {
|
||||
|
||||
public static interface Factory {
|
||||
/**
|
||||
* @param receiver
|
||||
* object whose interface matched {@code declaring} except all invokeds return {@link ListenableFuture}
|
||||
* @return blocking invocation handler
|
||||
*/
|
||||
InvokeFutureAndBlock create(Object async);
|
||||
}
|
||||
|
||||
@Resource
|
||||
private Logger logger = Logger.NULL;
|
||||
|
||||
private final Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables;
|
||||
private final Map<String, Long> timeouts;
|
||||
private final Object receiver;
|
||||
|
||||
@Inject
|
||||
@VisibleForTesting
|
||||
InvokeFutureAndBlock(Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncInvokables,
|
||||
@Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted Object receiver) {
|
||||
this.receiver = receiver;
|
||||
this.sync2AsyncInvokables = sync2AsyncInvokables;
|
||||
this.timeouts = timeouts;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Result block(ListenableFuture<?> future, Optional<Long> timeoutNanos) {
|
||||
try {
|
||||
if (timeoutNanos.isPresent()) {
|
||||
logger.debug(">> blocking on %s for %s", future, timeoutNanos);
|
||||
return Result.success(future.get(timeoutNanos.get(), TimeUnit.NANOSECONDS));
|
||||
} else {
|
||||
logger.debug(">> blocking on %s", future);
|
||||
return Result.success(future.get());
|
||||
}
|
||||
} catch (ExecutionException e) {
|
||||
return Result.fail(e.getCause());
|
||||
} catch (InterruptedException e) {
|
||||
return Result.fail(e); // TODO: should we kill the future?
|
||||
} catch (TimeoutException e) {
|
||||
return Result.fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
Optional<Long> timeoutMillis = fromNullable(timeouts.get(className + "." + invoked.getName())).or(
|
||||
fromNullable(timeouts.get(className))).or(fromNullable(timeouts.get("default")));
|
||||
if (timeoutMillis.isPresent())
|
||||
return Optional.of(TimeUnit.MILLISECONDS.toNanos(timeoutMillis.get()));
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* 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.Objects.equal;
|
||||
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;
|
||||
|
||||
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;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
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.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.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
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;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private Logger logger = Logger.NULL;
|
||||
|
||||
private final Injector injector;
|
||||
private final RestAnnotationProcessor annotationProcessor;
|
||||
private final HttpCommandExecutorService http;
|
||||
private final Function<GeneratedHttpRequest, Function<HttpResponse, ?>> transformerForRequest;
|
||||
private final ExecutorService userThreads;
|
||||
|
||||
@Inject
|
||||
private InvokeListenableFutureViaHttp(Injector injector, RestAnnotationProcessor annotationProcessor,
|
||||
HttpCommandExecutorService http,
|
||||
Function<GeneratedHttpRequest, Function<HttpResponse, ?>> transformerForRequest,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads) {
|
||||
this.injector = injector;
|
||||
this.annotationProcessor = annotationProcessor;
|
||||
this.http = http;
|
||||
this.userThreads = userThreads;
|
||||
this.transformerForRequest = transformerForRequest;
|
||||
}
|
||||
|
||||
private final LoadingCache<Invokable<?, ?>, FutureFallback<?>> fallbacks = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Invokable<?, ?>, FutureFallback<?>>() {
|
||||
|
||||
@Override
|
||||
public FutureFallback<?> load(Invokable<?, ?> key) throws Exception {
|
||||
Fallback annotation = key.getAnnotation(Fallback.class);
|
||||
if (annotation != null) {
|
||||
return injector.getInstance(annotation.value());
|
||||
}
|
||||
return injector.getInstance(MapHttp4xxCodesToExceptions.class);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> apply(Invocation invocation) {
|
||||
String name = invocation.getInvokable().toString();
|
||||
logger.trace(">> converting %s", name);
|
||||
GeneratedHttpRequest 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);
|
||||
|
||||
FutureFallback<?> fallback = fallbacks.getUnchecked(invocation.getInvokable());
|
||||
if (fallback instanceof InvocationContext) {
|
||||
InvocationContext.class.cast(fallback).setContext(request);
|
||||
}
|
||||
logger.trace("<< exceptions from %s are parsed by %s", name, fallback.getClass().getSimpleName());
|
||||
return withFallback(result, fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
InvokeListenableFutureViaHttp that = InvokeListenableFutureViaHttp.class.cast(o);
|
||||
return equal(this.annotationProcessor, that.annotationProcessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(annotationProcessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toStringHelper("").omitNullValues().add("annotationParser", annotationProcessor).toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -78,6 +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 org.jclouds.rest.Binder;
|
||||
import org.jclouds.rest.InputParamValidator;
|
||||
import org.jclouds.rest.annotations.ApiVersion;
|
||||
|
@ -119,13 +121,10 @@ 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.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
/**
|
||||
* Creates http invocation.getInvoked()s based on annotations on a class or interface.
|
||||
|
@ -134,44 +133,6 @@ import com.google.inject.assistedinject.Assisted;
|
|||
*/
|
||||
public class RestAnnotationProcessor implements Function<Invocation, GeneratedHttpRequest> {
|
||||
|
||||
public static final class Caller extends RestAnnotationProcessor {
|
||||
|
||||
public static interface Factory {
|
||||
Caller caller(Invocation caller);
|
||||
}
|
||||
|
||||
private final Invocation caller;
|
||||
|
||||
@Inject
|
||||
private Caller(Injector injector, @ApiVersion String apiVersion, @BuildVersion String buildVersion,
|
||||
HttpUtils utils, ContentMetadataCodec contentMetadataCodec, InputParamValidator inputParamValidator,
|
||||
@Assisted Invocation caller) {
|
||||
super(injector, apiVersion, buildVersion, utils, contentMetadataCodec, inputParamValidator);
|
||||
this.caller = caller;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GeneratedHttpRequest.Builder requestBuilder() {
|
||||
return super.requestBuilder().caller(caller);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<URI> findEndpoint(Invocation invocation) {
|
||||
Optional<URI> endpoint = getEndpointFor(caller);
|
||||
if (endpoint.isPresent())
|
||||
logger.trace("using endpoint %s from caller %s for %s", endpoint, caller, invocation);
|
||||
else
|
||||
endpoint = super.findEndpoint(invocation);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Multimap<String, Object> addPathAndGetTokens(Invocation invocation, UriBuilder uriBuilder) {
|
||||
return ImmutableMultimap.<String, Object> builder().putAll(super.addPathAndGetTokens(caller, uriBuilder))
|
||||
.putAll(super.addPathAndGetTokens(invocation, uriBuilder)).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
|
@ -208,6 +169,13 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
public GeneratedHttpRequest 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) {
|
||||
|
@ -220,14 +188,20 @@ 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 {
|
||||
} 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 {
|
||||
endpoint = findEndpoint(invocation);
|
||||
}
|
||||
|
||||
if (!endpoint.isPresent())
|
||||
throw new NoSuchElementException(format("no endpoint found for %s", invocation));
|
||||
|
||||
GeneratedHttpRequest.Builder requestBuilder = requestBuilder();
|
||||
GeneratedHttpRequest.Builder requestBuilder = GeneratedHttpRequest.builder().caller(caller);
|
||||
if (r != null) {
|
||||
requestBuilder.fromHttpRequest(r);
|
||||
} else {
|
||||
|
@ -245,6 +219,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
|
||||
overridePathEncoding(uriBuilder, invocation);
|
||||
|
||||
if (caller != null)
|
||||
tokenValues.putAll(addPathAndGetTokens(caller, uriBuilder));
|
||||
tokenValues.putAll(addPathAndGetTokens(invocation, uriBuilder));
|
||||
|
||||
Multimap<String, Object> formParams = addFormParams(tokenValues, invocation);
|
||||
|
@ -353,9 +329,9 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
}
|
||||
|
||||
private void overridePathEncoding(UriBuilder uriBuilder, Invocation invocation) {
|
||||
if (invocation.getInterfaceType().isAnnotationPresent(SkipEncoding.class)) {
|
||||
uriBuilder.skipPathEncoding(Chars.asList(invocation.getInterfaceType().getAnnotation(SkipEncoding.class)
|
||||
.value()));
|
||||
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(SkipEncoding.class)) {
|
||||
uriBuilder.skipPathEncoding(Chars.asList(invocation.getInvokable().getEnclosingType().getRawType()
|
||||
.getAnnotation(SkipEncoding.class).value()));
|
||||
}
|
||||
if (invocation.getInvokable().isAnnotationPresent(SkipEncoding.class)) {
|
||||
uriBuilder.skipPathEncoding(Chars.asList(invocation.getInvokable().getAnnotation(SkipEncoding.class).value()));
|
||||
|
@ -387,9 +363,10 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
return endpoint;
|
||||
}
|
||||
|
||||
protected Multimap<String, Object> addPathAndGetTokens(Invocation invocation, UriBuilder uriBuilder) {
|
||||
if (invocation.getInterfaceType().isAnnotationPresent(Path.class))
|
||||
uriBuilder.appendPath(invocation.getInterfaceType().getAnnotation(Path.class).value());
|
||||
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());
|
||||
if (invocation.getInvokable().isAnnotationPresent(Path.class))
|
||||
uriBuilder.appendPath(invocation.getInvokable().getAnnotation(Path.class).value());
|
||||
return getPathParamKeyValues(invocation);
|
||||
|
@ -397,8 +374,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.getInterfaceType().isAnnotationPresent(FormParams.class)) {
|
||||
FormParams form = invocation.getInterfaceType().getAnnotation(FormParams.class);
|
||||
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(FormParams.class)) {
|
||||
FormParams form = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(FormParams.class);
|
||||
addForm(formMap, form, tokenValues);
|
||||
}
|
||||
|
||||
|
@ -415,8 +392,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.getInterfaceType().isAnnotationPresent(QueryParams.class)) {
|
||||
QueryParams query = invocation.getInterfaceType().getAnnotation(QueryParams.class);
|
||||
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(QueryParams.class)) {
|
||||
QueryParams query = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(QueryParams.class);
|
||||
addQuery(queryMap, query, tokenValues);
|
||||
}
|
||||
|
||||
|
@ -466,12 +443,13 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
|
||||
private List<HttpRequestFilter> getFiltersIfAnnotated(Invocation invocation) {
|
||||
List<HttpRequestFilter> filters = newArrayList();
|
||||
if (invocation.getInterfaceType().isAnnotationPresent(RequestFilters.class)) {
|
||||
for (Class<? extends HttpRequestFilter> clazz : invocation.getInterfaceType()
|
||||
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(RequestFilters.class)) {
|
||||
for (Class<? extends HttpRequestFilter> clazz : invocation.getInvokable().getEnclosingType().getRawType()
|
||||
.getAnnotation(RequestFilters.class).value()) {
|
||||
HttpRequestFilter instance = injector.getInstance(clazz);
|
||||
filters.add(instance);
|
||||
logger.trace("adding filter %s from annotation on %s", instance, invocation.getInterfaceType().getName());
|
||||
logger.trace("adding filter %s from annotation on %s", instance, invocation.getInvokable()
|
||||
.getEnclosingType().getRawType().getName());
|
||||
}
|
||||
}
|
||||
if (invocation.getInvokable().isAnnotationPresent(RequestFilters.class)) {
|
||||
|
@ -526,8 +504,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
Endpoint annotation;
|
||||
if (invocation.getInvokable().isAnnotationPresent(Endpoint.class)) {
|
||||
annotation = invocation.getInvokable().getAnnotation(Endpoint.class);
|
||||
} else if (invocation.getInterfaceType().isAnnotationPresent(Endpoint.class)) {
|
||||
annotation = invocation.getInterfaceType().getAnnotation(Endpoint.class);
|
||||
} else if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(Endpoint.class)) {
|
||||
annotation = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(Endpoint.class);
|
||||
} else {
|
||||
logger.trace("no annotations on class or invocation.getInvoked(): %s", invocation.getInvokable());
|
||||
return Optional.absent();
|
||||
|
@ -587,8 +565,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
}
|
||||
|
||||
private boolean shouldAddHostHeader(Invocation invocation) {
|
||||
return (invocation.getInterfaceType().isAnnotationPresent(VirtualHost.class) || invocation.getInvokable()
|
||||
.isAnnotationPresent(VirtualHost.class));
|
||||
return (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(VirtualHost.class) || invocation
|
||||
.getInvokable().isAnnotationPresent(VirtualHost.class));
|
||||
}
|
||||
|
||||
private GeneratedHttpRequest decorateRequest(GeneratedHttpRequest request) throws NegativeArraySizeException {
|
||||
|
@ -610,15 +588,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()) { // TODO && varinvocation.getArgs() guava issue 1244
|
||||
if (!argType.isArray() && parameterType.isArray() && 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()) { // TODO && varinvocation.getArgs() guava issue
|
||||
// 1244
|
||||
} else if (argType.isArray() && parameterType.isArray() && invocation.getInvokable().isVarArgs()) {
|
||||
} else {
|
||||
if (arg.getClass().isArray()) {
|
||||
Object[] payloadArray = (Object[]) arg;
|
||||
|
@ -631,8 +608,9 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
if (shouldBreak)
|
||||
break OUTER;
|
||||
} else {
|
||||
if (position + 1 == invocation.getInvokable().getParameters().size() && entry.getType().isArray())
|
||||
continue OUTER; // TODO should only skip on null when varinvocation.getArgs(): guava issue 1244
|
||||
if (position + 1 == invocation.getInvokable().getParameters().size() && entry.getType().isArray()
|
||||
&& invocation.getInvokable().isVarArgs())
|
||||
continue OUTER;
|
||||
|
||||
if (entry.isAnnotationPresent(Nullable.class)) {
|
||||
continue OUTER;
|
||||
|
@ -652,7 +630,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.hashCode()); // TODO position guava issue 1243
|
||||
toReturn.add(param.getPosition());
|
||||
}
|
||||
return toReturn.build();
|
||||
}
|
||||
|
@ -685,7 +663,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.hashCode()).toString(); // TODO position guava issue 1243
|
||||
String value = invocation.getArgs().get(headerParam.getPosition()).toString();
|
||||
value = replaceTokens(value, tokenValues);
|
||||
headers.put(((HeaderParam) key).value(), value);
|
||||
}
|
||||
|
@ -703,13 +681,14 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
// TODO: refactor this out
|
||||
static Set<String> getAcceptHeaders(Invocation invocation) {
|
||||
Optional<Consumes> accept = Optional.fromNullable(invocation.getInvokable().getAnnotation(Consumes.class)).or(
|
||||
Optional.fromNullable(invocation.getInterfaceType().getAnnotation(Consumes.class)));
|
||||
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.getInterfaceType().isAnnotationPresent(Produces.class)) {
|
||||
Produces header = invocation.getInterfaceType().getAnnotation(Produces.class);
|
||||
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(Produces.class)) {
|
||||
Produces header = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(Produces.class);
|
||||
headers.replaceValues(CONTENT_TYPE, asList(header.value()));
|
||||
}
|
||||
if (invocation.getInvokable().isAnnotationPresent(Produces.class)) {
|
||||
|
@ -720,8 +699,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
|
||||
private static void addHeaderIfAnnotationPresentOnMethod(Multimap<String, String> headers, Invocation invocation,
|
||||
Multimap<String, ?> tokenValues) {
|
||||
if (invocation.getInterfaceType().isAnnotationPresent(Headers.class)) {
|
||||
Headers header = invocation.getInterfaceType().getAnnotation(Headers.class);
|
||||
if (invocation.getInvokable().getEnclosingType().getRawType().isAnnotationPresent(Headers.class)) {
|
||||
Headers header = invocation.getInvokable().getEnclosingType().getRawType().getAnnotation(Headers.class);
|
||||
addHeader(headers, header, tokenValues);
|
||||
}
|
||||
if (invocation.getInvokable().isAnnotationPresent(Headers.class)) {
|
||||
|
@ -747,7 +726,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.hashCode()); // TODO position guava issue 1243
|
||||
Object arg = invocation.getArgs().get(param.getPosition());
|
||||
checkNotNull(arg, partParam.name());
|
||||
Part part = Part.create(partParam.name(), newPayload(arg), options);
|
||||
parts.add(part);
|
||||
|
@ -760,8 +739,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
for (Parameter param : parametersWithAnnotation(invocation.getInvokable(), PathParam.class)) {
|
||||
PathParam pathParam = param.getAnnotation(PathParam.class);
|
||||
String paramKey = pathParam.value();
|
||||
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class), param.hashCode(),
|
||||
paramKey); // TODO position guava issue 1243
|
||||
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class),
|
||||
param.getPosition(), paramKey);
|
||||
if (paramValue.isPresent())
|
||||
pathParamValues.put(paramKey, paramValue.get().toString());
|
||||
}
|
||||
|
@ -780,8 +759,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
|
||||
private static 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
|
||||
.getInterfaceType().getSimpleName(), invocation.getInvokable().getName()));
|
||||
throw new NullPointerException(format("param{%s} for invocation %s.%s", paramKey, invocation.getInvokable()
|
||||
.getEnclosingType().getRawType().getSimpleName(), invocation.getInvokable().getName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -790,8 +769,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
for (Parameter param : parametersWithAnnotation(invocation.getInvokable(), FormParam.class)) {
|
||||
FormParam formParam = param.getAnnotation(FormParam.class);
|
||||
String paramKey = formParam.value();
|
||||
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class), param.hashCode(),
|
||||
paramKey); // TODO position guava issue 1243
|
||||
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class),
|
||||
param.getPosition(), paramKey);
|
||||
if (paramValue.isPresent())
|
||||
formParamValues.put(paramKey, paramValue.get().toString());
|
||||
}
|
||||
|
@ -803,8 +782,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
for (Parameter param : parametersWithAnnotation(invocation.getInvokable(), QueryParam.class)) {
|
||||
QueryParam queryParam = param.getAnnotation(QueryParam.class);
|
||||
String paramKey = queryParam.value();
|
||||
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class), param.hashCode(),
|
||||
paramKey); // TODO position guava issue 1243
|
||||
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class),
|
||||
param.getPosition(), paramKey);
|
||||
if (paramValue.isPresent())
|
||||
if (paramValue.get() instanceof Iterable) {
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -822,8 +801,8 @@ public class RestAnnotationProcessor implements Function<Invocation, GeneratedHt
|
|||
for (Parameter param : parametersWithAnnotation(invocation.getInvokable(), PayloadParam.class)) {
|
||||
PayloadParam payloadParam = param.getAnnotation(PayloadParam.class);
|
||||
String paramKey = payloadParam.value();
|
||||
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class), param.hashCode(),
|
||||
paramKey); // TODO position guava issue 1243
|
||||
Optional<?> paramValue = getParamValue(invocation, param.getAnnotation(ParamParser.class),
|
||||
param.getPosition(), paramKey);
|
||||
if (paramValue.isPresent())
|
||||
payloadParamValues.put(paramKey, paramValue.get());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
/**
|
||||
* 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.Functions.compose;
|
||||
import static com.google.inject.util.Types.newParameterizedType;
|
||||
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
|
||||
import static javax.ws.rs.core.MediaType.APPLICATION_XML;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.lang.model.type.NullType;
|
||||
|
||||
import org.jclouds.functions.IdentityFunction;
|
||||
import org.jclouds.functions.OnlyElementOrNull;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
||||
import org.jclouds.http.functions.ParseJson;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.http.functions.ParseSax.Factory;
|
||||
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
|
||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
||||
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
||||
import org.jclouds.http.functions.ReturnInputStream;
|
||||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||
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 org.jclouds.rest.InvocationContext;
|
||||
import org.jclouds.rest.annotations.JAXBResponseParser;
|
||||
import org.jclouds.rest.annotations.OnlyElement;
|
||||
import org.jclouds.rest.annotations.ResponseParser;
|
||||
import org.jclouds.rest.annotations.SelectJson;
|
||||
import org.jclouds.rest.annotations.Transform;
|
||||
import org.jclouds.rest.annotations.Unwrap;
|
||||
import org.jclouds.rest.annotations.XMLResponseParser;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
public class TransformerForRequest implements Function<GeneratedHttpRequest, Function<HttpResponse, ?>> {
|
||||
private final ParseSax.Factory parserFactory;
|
||||
private final Injector injector;
|
||||
|
||||
@Inject
|
||||
TransformerForRequest(Injector injector, Factory parserFactory) {
|
||||
this.injector = injector;
|
||||
this.parserFactory = parserFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<HttpResponse, ?> apply(GeneratedHttpRequest request) {
|
||||
return createResponseParser(parserFactory, injector, request);
|
||||
}
|
||||
|
||||
private static final TypeToken<ListenableFuture<Boolean>> futureBooleanLiteral = new TypeToken<ListenableFuture<Boolean>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<String>> futureStringLiteral = new TypeToken<ListenableFuture<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<Void>> futureVoidLiteral = new TypeToken<ListenableFuture<Void>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<URI>> futureURILiteral = new TypeToken<ListenableFuture<URI>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<InputStream>> futureInputStreamLiteral = new TypeToken<ListenableFuture<InputStream>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeToken<ListenableFuture<HttpResponse>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@VisibleForTesting
|
||||
protected static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Invocation invocation) {
|
||||
Invokable<?, ?> invoked = invocation.getInvokable();
|
||||
ResponseParser annotation = invoked.getAnnotation(ResponseParser.class);
|
||||
if (annotation == null) {
|
||||
if (invoked.getReturnType().equals(void.class) || invoked.getReturnType().equals(futureVoidLiteral)) {
|
||||
return Key.get(ReleasePayloadAndReturn.class);
|
||||
} else if (invoked.getReturnType().equals(boolean.class) || invoked.getReturnType().equals(Boolean.class)
|
||||
|| invoked.getReturnType().equals(futureBooleanLiteral)) {
|
||||
return Key.get(ReturnTrueIf2xx.class);
|
||||
} else if (invoked.getReturnType().equals(InputStream.class)
|
||||
|| invoked.getReturnType().equals(futureInputStreamLiteral)) {
|
||||
return Key.get(ReturnInputStream.class);
|
||||
} 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)) {
|
||||
return getJsonParserKeyForMethod(invoked);
|
||||
} else if (RestAnnotationProcessor.getAcceptHeaders(invocation).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);
|
||||
} else if (invoked.getReturnType().equals(URI.class) || invoked.getReturnType().equals(futureURILiteral)) {
|
||||
return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
|
||||
} else {
|
||||
throw new IllegalStateException("You must specify a ResponseParser annotation on: " + invoked.toString());
|
||||
}
|
||||
}
|
||||
return Key.get(annotation.value());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Key<? extends Function<HttpResponse, ?>> getJAXBParserKeyForMethod(Invokable<?, ?> invoked) {
|
||||
Optional<Type> configuredReturnVal = Optional.absent();
|
||||
if (invoked.isAnnotationPresent(JAXBResponseParser.class)) {
|
||||
Type configuredClass = invoked.getAnnotation(JAXBResponseParser.class).value();
|
||||
configuredReturnVal = configuredClass.equals(NullType.class) ? Optional.<Type> absent() : Optional
|
||||
.<Type> of(configuredClass);
|
||||
}
|
||||
Type returnVal = configuredReturnVal.or(getReturnTypeFor(invoked.getReturnType()));
|
||||
Type parserType = newParameterizedType(ParseXMLWithJAXB.class, returnVal);
|
||||
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
private static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethod(Invokable<?, ?> invoked) {
|
||||
ParameterizedType parserType;
|
||||
if (invoked.isAnnotationPresent(Unwrap.class)) {
|
||||
parserType = newParameterizedType(UnwrapOnlyJsonValue.class, getReturnTypeFor(invoked.getReturnType()));
|
||||
} else {
|
||||
parserType = newParameterizedType(ParseJson.class, getReturnTypeFor(invoked.getReturnType()));
|
||||
}
|
||||
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
|
||||
}
|
||||
|
||||
static Type getReturnTypeFor(TypeToken<?> typeToken) {
|
||||
Type returnVal = typeToken.getType();
|
||||
if (typeToken.getRawType().getTypeParameters().length == 0) {
|
||||
returnVal = typeToken.getRawType();
|
||||
} else if (typeToken.getRawType().equals(ListenableFuture.class)) {
|
||||
ParameterizedType futureType = (ParameterizedType) typeToken.getType();
|
||||
returnVal = futureType.getActualTypeArguments()[0];
|
||||
if (returnVal instanceof WildcardType)
|
||||
returnVal = WildcardType.class.cast(returnVal).getUpperBounds()[0];
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
// TODO: refactor this out of here
|
||||
@VisibleForTesting
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static Function<HttpResponse, ?> getTransformerForMethod(Invocation invocation, Injector injector) {
|
||||
Invokable<?, ?> invoked = invocation.getInvokable();
|
||||
Function<HttpResponse, ?> transformer;
|
||||
if (invoked.isAnnotationPresent(SelectJson.class)) {
|
||||
Type returnVal = getReturnTypeFor(invoked.getReturnType());
|
||||
if (invoked.isAnnotationPresent(OnlyElement.class))
|
||||
returnVal = newParameterizedType(Set.class, returnVal);
|
||||
transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class),
|
||||
TypeLiteral.get(returnVal), invoked.getAnnotation(SelectJson.class).value());
|
||||
if (invoked.isAnnotationPresent(OnlyElement.class))
|
||||
transformer = compose(new OnlyElementOrNull(), transformer);
|
||||
} else {
|
||||
transformer = injector.getInstance(getParserOrThrowException(invocation));
|
||||
}
|
||||
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) {
|
||||
return annotation.value();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ import java.lang.reflect.Type;
|
|||
import java.lang.reflect.WildcardType;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,16 +28,9 @@ import java.util.concurrent.ExecutionException;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.jclouds.internal.ForwardInvocationToInterface;
|
||||
import org.jclouds.reflect.FunctionalReflection;
|
||||
import org.jclouds.rest.functions.AlwaysPresentImplicitOptionalConverter;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
|
@ -46,7 +39,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", singleThreaded = true)
|
||||
@Test(groups = "unit", enabled = false, singleThreaded = true)
|
||||
public class SyncProxyTest {
|
||||
|
||||
static ListenableFuture<String> future;
|
||||
|
@ -100,10 +93,15 @@ public class SyncProxyTest {
|
|||
}
|
||||
|
||||
private Sync syncProxyForTimeouts(ImmutableMap<String, Long> timeouts) throws NoSuchMethodException {
|
||||
LoadingCache<ForwardInvocationToInterface, Object> cache = CacheBuilder.newBuilder().build(
|
||||
CacheLoader.from(Functions.<Object> constant(null)));
|
||||
return FunctionalReflection.newProxy(Sync.class, new SyncProxy(new AlwaysPresentImplicitOptionalConverter(),
|
||||
cache, ImmutableMap.<Class<?>, Class<?>> of(Sync.class, Async.class), timeouts, Sync.class, new Async()));
|
||||
// LoadingCache<ForwardInvocationToInterface, Object> cache = CacheBuilder.newBuilder().build(
|
||||
// CacheLoader.from(Functions.<Object> constant(null)));
|
||||
// return FunctionalReflection.newProxy(Sync.class, new SyncProxy(new AlwaysPresentImplicitOptionalConverter(),
|
||||
// cache, ImmutableMap.<Class<?>, Class<?>> of(Sync.class, Async.class), timeouts, Sync.class, new Async()));
|
||||
////
|
||||
// Function<InvocationSuccess, Optional<Object>> optionalConverter, SyncProxy.Factory factory,
|
||||
// AsyncRestClientProxy.Caller.Factory asyncFactory, Map<Class<?>, Class<?>> sync2Async,
|
||||
// @Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted Class<?> declaring, @Assisted Object async
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.testng.annotations.AfterTest;
|
|||
import org.testng.annotations.BeforeTest;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ import org.testng.annotations.Test;
|
|||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
|
||||
@Test(groups = "unit", testName = "BackoffLimitedRetryHandlerTest")
|
||||
public class BackoffLimitedRetryHandlerTest {
|
||||
|
|
|
@ -41,7 +41,7 @@ import org.jclouds.rest.internal.GeneratedHttpRequest;
|
|||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
|
|
@ -32,13 +32,13 @@ import org.jclouds.io.Payload;
|
|||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.jclouds.reflect.Invocation;
|
||||
import org.jclouds.rest.internal.AsyncRestClientProxy;
|
||||
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 com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
|
@ -58,7 +58,7 @@ public abstract class BaseParserTest<T, G> {
|
|||
@SuppressWarnings("unchecked")
|
||||
protected Function<HttpResponse, T> parser(Injector i) {
|
||||
try {
|
||||
return (Function<HttpResponse, T>) AsyncRestClientProxy.getTransformerForMethod(
|
||||
return (Function<HttpResponse, T>) TransformerForRequest.getTransformerForMethod(
|
||||
Invocation.create(Invokable.from(getClass().getMethod("expected")), ImmutableList.of()), i);
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
|
|
|
@ -48,11 +48,11 @@ public class FunctionalReflectionTest {
|
|||
* access to the actual proxied interface
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testCanAccessInterfaceTypeInsideFunction() {
|
||||
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.getInterfaceType(), SortedSet.class);
|
||||
assertEquals(e.getInvokable().getEnclosingType().getRawType(), SortedSet.class);
|
||||
return Result.success(true);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
/**
|
||||
* 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,7 +34,7 @@ import org.testng.annotations.BeforeClass;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
@Test(groups = "unit")
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.testng.annotations.Test;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code BindMapToStringPayload}
|
||||
|
|
|
@ -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 com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
|
||||
/**
|
||||
* Allows you to use simple api version comparison to determine if a feature is
|
||||
|
|
|
@ -40,17 +40,15 @@ 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.Invocation;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import org.jclouds.rest.annotations.Fallback;
|
||||
import org.jclouds.rest.annotations.XMLResponseParser;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.SortedSetMultimap;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.name.Names;
|
||||
|
@ -177,9 +175,9 @@ public abstract class BaseRestApiTest {
|
|||
assertEquals(expected, parserClass);
|
||||
}
|
||||
|
||||
protected void assertResponseParserClassEquals(Invokable<?, ?> method, HttpRequest request, @Nullable Class<?> parserClass) {
|
||||
assertEquals(
|
||||
AsyncRestClientProxy.createResponseParser(parserFactory, injector,
|
||||
Invocation.create(method, ImmutableList.of()), request).getClass(), parserClass);
|
||||
protected void assertResponseParserClassEquals(Invokable<?, ?> method, GeneratedHttpRequest request,
|
||||
@Nullable Class<?> parserClass) {
|
||||
assertEquals(TransformerForRequest.createResponseParser(parserFactory, injector, request).getClass(),
|
||||
parserClass);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ import com.google.common.collect.ImmutableSortedSet;
|
|||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
@ -492,7 +492,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testQuery() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("foo"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17&x-ms-rubbish=bin");
|
||||
|
@ -501,7 +501,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testQuery2() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("foo2"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17&foo=bar&fooble=baz");
|
||||
|
@ -510,7 +510,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testQuery3() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("foo3", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("wonder"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("wonder"));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17&foo=bar&fooble=baz&robbie=wonder");
|
||||
|
@ -525,7 +525,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testNoNPEOnQueryParamWithNullable() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("foo3Nullable", String.class));
|
||||
HttpRequest request = processor.createRequest(method, Lists.<Object> newArrayList((String) null));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, Lists.<Object> newArrayList((String) null));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17&foo=bar&fooble=baz");
|
||||
|
@ -535,7 +535,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
public void testQueryParamIterableOneString() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("queryParamIterable", Iterable.class));
|
||||
Set<String> bars = ImmutableSortedSet.<String> of("1");
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(bars));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(bars));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17&foo=1");
|
||||
|
@ -545,7 +545,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
public void testQueryParamIterableString() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("queryParamIterable", Iterable.class));
|
||||
Set<String> bars = ImmutableSortedSet.<String> of("1", "2", "3");
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(bars));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(bars));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17&foo=1&foo=2&foo=3");
|
||||
|
@ -555,7 +555,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
public void testQueryParamIterableInteger() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("queryParamIterable", Iterable.class));
|
||||
Set<Integer> bars = ImmutableSortedSet.<Integer> of(1, 2, 3);
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(bars));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(bars));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17&foo=1&foo=2&foo=3");
|
||||
|
@ -565,7 +565,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
public void testQueryParamIterableEmpty() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("queryParamIterable", Iterable.class));
|
||||
Set<String> bars = Collections.emptySet();
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(bars));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(bars));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17");
|
||||
|
@ -574,7 +574,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testQueryParamIterableNull() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestQuery.class.getMethod("queryParamIterable", Iterable.class));
|
||||
HttpRequest request = processor.createRequest(method, Lists.<Object> newArrayList((String) null));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, Lists.<Object> newArrayList((String) null));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/");
|
||||
assertEquals(request.getEndpoint().getQuery(), "x-ms-version=2009-07-17");
|
||||
|
@ -602,7 +602,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testHttpRequestOptionsNoPayloadParam() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPayloadParamVarargs.class.getMethod("post"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "", "application/octet-stream", false);
|
||||
|
@ -616,7 +616,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testHttpRequestOptionsPayloadParam() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPayloadParamVarargs.class.getMethod("post", Payload.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(Payloads.newStringPayload("foo")));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(Payloads.newStringPayload("foo")));
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "foo", "application/octet-stream", false);
|
||||
|
@ -624,7 +624,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testHttpRequestWithOnlyContentType() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPayloadParamVarargs.class.getMethod("post", HttpRequestOptions.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(new TestHttpRequestOptions().payload("fooya")));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(new TestHttpRequestOptions().payload("fooya")));
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "fooya", "application/unknown", false);
|
||||
|
@ -632,7 +632,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testHeaderAndQueryVarargs() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPayloadParamVarargs.class.getMethod("varargs", HttpRequestOptions[].class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||
new TestHttpRequestOptions().payload("fooya"),
|
||||
new TestHttpRequestOptions().headerParams(ImmutableMultimap.of("X-header-1", "fooya")),
|
||||
new TestHttpRequestOptions().queryParams(ImmutableMultimap.of("key", "value"))));
|
||||
|
@ -643,7 +643,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testHeaderAndQueryVarargsPlusReq() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPayloadParamVarargs.class.getMethod("varargsWithReq", String.class, HttpRequestOptions[].class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("required param",
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("required param",
|
||||
new TestHttpRequestOptions().payload("fooya"),
|
||||
new TestHttpRequestOptions().headerParams(ImmutableMultimap.of("X-header-1", "fooya")),
|
||||
new TestHttpRequestOptions().queryParams(ImmutableMultimap.of("key", "value"))));
|
||||
|
@ -654,7 +654,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testDuplicateHeaderAndQueryVarargs() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPayloadParamVarargs.class.getMethod("varargs", HttpRequestOptions[].class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||
new TestHttpRequestOptions().queryParams(ImmutableMultimap.of("key", "value")),
|
||||
new TestHttpRequestOptions().payload("fooya"),
|
||||
new TestHttpRequestOptions().headerParams(ImmutableMultimap.of("X-header-1", "fooya")),
|
||||
|
@ -674,7 +674,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCustomMethod() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestCustomMethod.class.getMethod("foo"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "");
|
||||
assertEquals(request.getMethod(), "FOO");
|
||||
|
@ -692,7 +692,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testOverriddenMethod() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestOverridden.class.getMethod("foo"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "");
|
||||
assertEquals(request.getMethod(), "POST");
|
||||
|
@ -712,7 +712,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testOverriddenEndpointMethod() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestOverriddenEndpoint.class.getMethod("foo"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPort(), 1111);
|
||||
assertEquals(request.getEndpoint().getPath(), "");
|
||||
|
@ -721,7 +721,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testOverriddenEndpointParameter() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestOverriddenEndpoint.class.getMethod("foo", URI.class));
|
||||
HttpRequest request = processor.createRequest(method,
|
||||
GeneratedHttpRequest request = processor.createRequest(method,
|
||||
ImmutableList.<Object> of(URI.create("http://wowsa:8001")));
|
||||
assertEquals(request.getEndpoint().getHost(), "wowsa");
|
||||
assertEquals(request.getEndpoint().getPort(), 8001);
|
||||
|
@ -764,7 +764,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePostRequest() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("post", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -773,7 +773,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePostRequestNullOk1() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("post", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -782,7 +782,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePostRequestNullOk2() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("post", String.class));
|
||||
HttpRequest request = processor.createRequest(method, Lists.<Object> newArrayList((String) null));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, Lists.<Object> newArrayList((String) null));
|
||||
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -792,7 +792,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
public void testCreatePostRequestNullNotOk1() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("postNonnull", String.class));
|
||||
try {
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
Assert.fail("call should have failed with illegal null parameter, not permitted " + request + " to be created");
|
||||
} catch (NullPointerException e) {
|
||||
Assert.assertTrue(e.toString().indexOf("postNonnull parameter 1") >= 0,
|
||||
|
@ -808,7 +808,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePostJsonRequest() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("postAsJson", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -817,7 +817,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePostWithPathRequest() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("postWithPath", String.class, MapBinder.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data", new org.jclouds.rest.MapBinder() {
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data", new org.jclouds.rest.MapBinder() {
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
|
||||
request.setPayload((String) postParams.get("fooble"));
|
||||
|
@ -835,7 +835,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePostWithMethodBinder() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("postWithMethodBinder", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999/data HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -844,7 +844,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePostWithMethodBinderAndDefaults() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("postWithMethodBinderAndDefaults", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999/data HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -853,7 +853,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePostWithPayload() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPost.class.getMethod("testPayload", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999/data HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -1101,7 +1101,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testAlternateHttpMethod() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("rowdy", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "ROWDY http://localhost:9999/strings/data HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -1110,7 +1110,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testAlternateHttpMethodSameArity() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("rowdy", int.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "ROWDY http://localhost:9999/ints/data HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -1119,7 +1119,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePutWithMethodBinder() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("putWithMethodBinder", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999/data HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -1128,7 +1128,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePutWithMethodProduces() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("putWithMethodBinderProduces", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999/data HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -1138,7 +1138,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testCreatePutWithMethodConsumes() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("putWithMethodBinderConsumes", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("data"));
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999/data HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
|
||||
|
@ -1147,8 +1147,8 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, ParseJson.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, View> parser = (Function<HttpResponse, View>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, View> parser = (Function<HttpResponse, View>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()).foo, "bar");
|
||||
|
||||
|
@ -1157,13 +1157,13 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testGeneric1() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testGeneric"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, ParseJson.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
|
||||
ImmutableMap.of("foo", "bar"));
|
||||
|
@ -1173,13 +1173,13 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testGeneric2() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testGeneric2"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, ParseJson.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
|
||||
ImmutableMap.of("foo", "bar"));
|
||||
|
@ -1189,13 +1189,13 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testGeneric3() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testGeneric3"));
|
||||
HttpRequest request = processor.createRequest(method ,ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method ,ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, ParseJson.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
|
||||
ImmutableMap.of("foo", "bar"));
|
||||
|
@ -1205,13 +1205,13 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testUnwrap1() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testUnwrap"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
|
||||
|
||||
|
@ -1220,13 +1220,13 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testUnwrapValueNamed() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testUnwrapValueNamed"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
|
||||
|
||||
|
@ -1234,20 +1234,20 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testWrapWith() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testWrapWith", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("bar"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("bar"));
|
||||
assertPayloadEquals(request, "{\"foo\":\"bar\"}", "application/json", false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testUnwrap2() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testUnwrap2"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
|
||||
|
||||
|
@ -1256,13 +1256,13 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testUnwrap3() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testUnwrap3"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{\"runit\":[\"0.7.0\",\"0.7.1\"]}").build()),
|
||||
ImmutableSet.of("0.7.0", "0.7.1"));
|
||||
|
@ -1271,13 +1271,13 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testUnwrap4() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("testUnwrap4"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{\"runit\":[\"0.7.0\",\"0.7.1\"]}").build()),
|
||||
ImmutableSet.of("0.7.0", "0.7.1"));
|
||||
|
@ -1286,13 +1286,13 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void selectLong() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("selectLong"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok")
|
||||
.payload("{ \"destroyvirtualmachineresponse\" : {\"jobid\":4} }").build()), Long.valueOf(4));
|
||||
|
@ -1301,10 +1301,10 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public void selectLongAddOne() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPut.class.getMethod("selectLongAddOne"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, Invocation.create(method, ImmutableList.of()), request);
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) TransformerForRequest
|
||||
.createResponseParser(parserFactory, injector, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok")
|
||||
.payload("{ \"destroyvirtualmachineresponse\" : {\"jobid\":4} }").build()), Long.valueOf(5));
|
||||
|
@ -1341,7 +1341,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testRequestFilter() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequestFilter.class.getMethod("get"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
assertEquals(request.getFilters().size(), 2);
|
||||
assertEquals(request.getFilters().get(0).getClass(), TestRequestFilter1.class);
|
||||
assertEquals(request.getFilters().get(1).getClass(), TestRequestFilter2.class);
|
||||
|
@ -1349,14 +1349,14 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testRequestFilterOverride() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequestFilter.class.getMethod("getOverride"));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.of());
|
||||
assertEquals(request.getFilters().size(), 1);
|
||||
assertEquals(request.getFilters().get(0).getClass(), TestRequestFilter2.class);
|
||||
}
|
||||
|
||||
public void testRequestFilterOverrideOnRequest() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequestFilter.class.getMethod("getOverride", HttpRequest.class));
|
||||
HttpRequest request = processor.createRequest(
|
||||
GeneratedHttpRequest request = processor.createRequest(
|
||||
method, ImmutableList.<Object> of(
|
||||
HttpRequest.builder().method("GET").endpoint("http://localhost")
|
||||
.addHeader("foo", "bar").build()));
|
||||
|
@ -1375,7 +1375,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testSkipEncoding() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestEncoding.class.getMethod("twoPaths", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", "localhost"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", "localhost"));
|
||||
assertEquals(request.getEndpoint().getPath(), "/1/localhost");
|
||||
assertEquals(request.getMethod(), HttpMethod.GET);
|
||||
assertEquals(request.getHeaders().size(), 0);
|
||||
|
@ -1384,7 +1384,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testEncodingPath() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestEncoding.class.getMethod("twoPaths", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("/", "localhost" ));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("/", "localhost" ));
|
||||
assertEquals(request.getEndpoint().getPath(), "///localhost");
|
||||
assertEquals(request.getMethod(), HttpMethod.GET);
|
||||
assertEquals(request.getHeaders().size(), 0);
|
||||
|
@ -1404,7 +1404,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test(enabled = false)
|
||||
public void testConstantPathParam() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestConstantPathParam.class.getMethod("twoPaths", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method,
|
||||
GeneratedHttpRequest request = processor.createRequest(method,
|
||||
ImmutableList.<Object> of("1", "localhost"));
|
||||
assertRequestLineEquals(request, "GET http://localhost:9999/v1/ralphie/1/localhost HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -1457,7 +1457,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testPathParamExtractor() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPath.class.getMethod("onePathParamExtractor", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("localhost"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("localhost"));
|
||||
assertRequestLineEquals(request, "GET http://localhost:9999/l HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, null, null, false);
|
||||
|
@ -1466,7 +1466,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testQueryParamExtractor() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPath.class.getMethod("oneQueryParamExtractor", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("localhost"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("localhost"));
|
||||
assertRequestLineEquals(request, "GET http://localhost:9999/?one=l HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, null, null, false);
|
||||
|
@ -1475,7 +1475,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testFormParamExtractor() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestPath.class.getMethod("oneFormParamExtractor", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("localhost"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("localhost"));
|
||||
assertRequestLineEquals(request, "POST http://localhost:9999/ HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "one=l", "application/x-www-form-urlencoded", false);
|
||||
|
@ -1704,7 +1704,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testPutPayloadEnclosing() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", PayloadEnclosing.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||
new PayloadEnclosingImpl(newStringPayload("whoops"))));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -1715,7 +1715,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", PayloadEnclosing.class));
|
||||
PayloadEnclosing payloadEnclosing = new PayloadEnclosingImpl(newStringPayload("whoops"));
|
||||
calculateMD5(payloadEnclosing);
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payloadEnclosing));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payloadEnclosing));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
||||
|
@ -1729,7 +1729,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
newInputStreamPayload(Strings2.toInputStream("whoops")));
|
||||
|
||||
calculateMD5(payloadEnclosing);
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payloadEnclosing));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payloadEnclosing));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
||||
|
@ -1738,7 +1738,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testPutPayloadChunkedNoContentLength() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("putXfer", Payload.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(newStringPayload("whoops")));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(newStringPayload("whoops")));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "Transfer-Encoding: chunked\n");
|
||||
assertPayloadEquals(request, "whoops", "application/unknown", false);
|
||||
|
@ -1746,7 +1746,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testPutPayload() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", Payload.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(newStringPayload("whoops")));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(newStringPayload("whoops")));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "whoops", "application/unknown", false);
|
||||
|
@ -1756,7 +1756,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", Payload.class));
|
||||
Payload payload = newStringPayload("whoops");
|
||||
payload.getContentMetadata().setContentDisposition("attachment; filename=photo.jpg");
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "whoops", "application/unknown", "attachment; filename=photo.jpg", null, null, false);
|
||||
|
@ -1766,7 +1766,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", Payload.class));
|
||||
Payload payload = newStringPayload("whoops");
|
||||
payload.getContentMetadata().setContentEncoding("gzip");
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "whoops", "application/unknown", null, "gzip", null, false);
|
||||
|
@ -1776,7 +1776,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", Payload.class));
|
||||
Payload payload = newStringPayload("whoops");
|
||||
payload.getContentMetadata().setContentLanguage("en");
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "whoops", "application/unknown", null, null, "en", false);
|
||||
|
@ -1787,7 +1787,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Payload payload = newStringPayload("whoops");
|
||||
calculateMD5(payload);
|
||||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", Payload.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "whoops", "application/unknown", true);
|
||||
|
@ -1797,7 +1797,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Payload payload = newInputStreamPayload(Strings2.toInputStream("whoops"));
|
||||
payload.getContentMetadata().setContentLength((long) "whoops".length());
|
||||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", Payload.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "whoops", "application/unknown", false);
|
||||
|
@ -1808,7 +1808,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Payload payload = newStringPayload("whoops");
|
||||
calculateMD5(payload);
|
||||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("put", Payload.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(payload));
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
assertPayloadEquals(request, "whoops", "application/unknown", true);
|
||||
|
@ -1822,7 +1822,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Class<? extends Function<HttpResponse, ?>> unwrap(RestAnnotationProcessor processor, Invokable<?, ?> method) {
|
||||
return (Class<? extends Function<HttpResponse, ?>>) AsyncRestClientProxy
|
||||
return (Class<? extends Function<HttpResponse, ?>>) TransformerForRequest
|
||||
.getParserOrThrowException(Invocation.create(method, ImmutableList.of())).getTypeLiteral().getRawType();
|
||||
}
|
||||
|
||||
|
@ -1847,7 +1847,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test(expectedExceptions = { RuntimeException.class })
|
||||
public void testNoTransformer() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestTransformers.class.getMethod("noTransformer"));
|
||||
AsyncRestClientProxy.getParserOrThrowException(Invocation.create(method, ImmutableList.of()));
|
||||
TransformerForRequest.getParserOrThrowException(Invocation.create(method, ImmutableList.of()));
|
||||
}
|
||||
|
||||
public void oneTransformerWithContext() throws SecurityException, NoSuchMethodException {
|
||||
|
@ -1859,8 +1859,8 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Invocation.create(Invokable.from(TestTransformers.class.getMethod("oneTransformerWithContext")),
|
||||
ImmutableList.of()))
|
||||
.build();
|
||||
Function<HttpResponse, ?> transformer = AsyncRestClientProxy.createResponseParser(parserFactory, injector,
|
||||
request.getInvocation(), request);
|
||||
Function<HttpResponse, ?> transformer = TransformerForRequest.createResponseParser(parserFactory, injector,
|
||||
request);
|
||||
assertEquals(transformer.getClass(), ReturnStringIf200Context.class);
|
||||
assertEquals(((ReturnStringIf200Context) transformer).request, request);
|
||||
}
|
||||
|
@ -1924,7 +1924,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Date date = new Date();
|
||||
GetOptions options = GetOptions.Builder.ifModifiedSince(date);
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("get", String.class, HttpRequestOptions[].class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", options));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", options));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/1");
|
||||
assertEquals(request.getMethod(), HttpMethod.GET);
|
||||
|
@ -1938,7 +1938,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Date date = new Date();
|
||||
GetOptions options = GetOptions.Builder.ifModifiedSince(date);
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("get", String.class, HttpRequestOptions.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", options));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", options));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/1");
|
||||
assertEquals(request.getMethod(), HttpMethod.GET);
|
||||
|
@ -1958,7 +1958,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
public void testCreateGetOptionsThatProducesQuery() throws SecurityException, NoSuchMethodException, IOException {
|
||||
PrefixOptions options = new PrefixOptions().withPrefix("1");
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("get", String.class, HttpRequestOptions.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", options));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", options));
|
||||
assertRequestLineEquals(request, "GET http://localhost:9999/1?prefix=1 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "Host: localhost:9999\n");
|
||||
assertPayloadEquals(request, null, null, false);
|
||||
|
@ -1966,7 +1966,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreateGetQuery() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("getQuery", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1"));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/1");
|
||||
assertEquals(request.getEndpoint().getQuery(), "max-keys=0");
|
||||
|
@ -1976,7 +1976,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreateGetQueryNull() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("getQueryNull", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1"));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/1");
|
||||
assertEquals(request.getEndpoint().getQuery(), "acl");
|
||||
|
@ -1986,7 +1986,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreateGetQueryEmpty() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("getQueryEmpty", String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1"));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/1");
|
||||
assertEquals(request.getEndpoint().getQuery(), "acl=");
|
||||
|
@ -2004,7 +2004,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
public void testCreateGetOptionsThatProducesPayload() throws SecurityException, NoSuchMethodException, IOException {
|
||||
PayloadOptions options = new PayloadOptions();
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("putOptions", String.class, HttpRequestOptions.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", options));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", options));
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999/1 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "Host: localhost:9999\n");
|
||||
|
@ -2020,7 +2020,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
public void testCreateGetRequest(String key) throws SecurityException, NoSuchMethodException,
|
||||
UnsupportedEncodingException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("get", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(key, "localhost"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(key, "localhost"));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
String expectedPath = "/" + URLEncoder.encode(key, "UTF-8").replaceAll("\\+", "%20");
|
||||
assertEquals(request.getEndpoint().getRawPath(), expectedPath);
|
||||
|
@ -2032,7 +2032,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePutRequest() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("put", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("111", "data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("111", "data"));
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999/1 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "");
|
||||
|
@ -2041,7 +2041,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public void testCreatePutHeader() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Invokable<?, ?> method = Invokable.from(TestRequest.class.getMethod("putHeader", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", "data"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", "data"));
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://localhost:9999/1 HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "foo: --1--\n");
|
||||
|
@ -2060,7 +2060,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testVirtualHostMethod() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestVirtualHostMethod.class.getMethod("get", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method,
|
||||
GeneratedHttpRequest request = processor.createRequest(method,
|
||||
ImmutableList.<Object> of("1", "localhost"));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/1");
|
||||
|
@ -2084,7 +2084,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testVirtualHost() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestVirtualHost.class.getMethod("get", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", "localhost"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", "localhost"));
|
||||
assertEquals(request.getEndpoint().getHost(), "localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/1");
|
||||
assertEquals(request.getMethod(), HttpMethod.GET);
|
||||
|
@ -2095,7 +2095,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test
|
||||
public void testHostPrefix() throws SecurityException, NoSuchMethodException {
|
||||
Invokable<?, ?> method = Invokable.from(TestVirtualHost.class.getMethod("getPrefix", String.class, String.class));
|
||||
HttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", "holy"));
|
||||
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of("1", "holy"));
|
||||
assertEquals(request.getEndpoint().getHost(), "holy.localhost");
|
||||
assertEquals(request.getEndpoint().getPath(), "/1");
|
||||
assertEquals(request.getMethod(), HttpMethod.GET);
|
||||
|
|
|
@ -25,7 +25,7 @@ import static org.testng.Assert.assertTrue;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.reflect.Invokable;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
|
|
Loading…
Reference in New Issue