mirror of https://github.com/apache/jclouds.git
added ability to look up constructors as Invokables
This commit is contained in:
parent
03fa9da761
commit
a625127fd2
|
@ -53,8 +53,7 @@ public class FilterStringsBoundToInjectorByName implements Function<Predicate<St
|
|||
|
||||
@Override
|
||||
public Map<String, String> apply(Predicate<String> filter) {
|
||||
List<Binding<String>> stringBindings = injector.findBindingsByType(new TypeLiteral<String>() {
|
||||
});
|
||||
List<Binding<String>> stringBindings = injector.findBindingsByType(TypeLiteral.get(String.class));
|
||||
Iterable<Binding<String>> annotatedWithName = Iterables.filter(stringBindings, new Predicate<Binding<String>>() {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,19 +19,18 @@
|
|||
package org.jclouds.lifecycle.config;
|
||||
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||
import static com.google.inject.matcher.Matchers.any;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.jclouds.Constants.PROPERTY_IO_WORKER_THREADS;
|
||||
import static org.jclouds.Constants.PROPERTY_SCHEDULER_THREADS;
|
||||
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
|
||||
import static org.jclouds.reflect.Reflection2.methods;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
@ -40,6 +39,8 @@ import javax.inject.Named;
|
|||
|
||||
import org.jclouds.lifecycle.Closer;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.util.concurrent.ExecutionList;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
@ -51,15 +52,15 @@ import com.google.inject.spi.TypeEncounter;
|
|||
import com.google.inject.spi.TypeListener;
|
||||
|
||||
/**
|
||||
* This associates java lifecycle annotations with guice hooks. For example, we invoke
|
||||
* {@link PostConstruct} after injection, and Associate {@link PreDestroy} with a global
|
||||
* {@link Closer} object.
|
||||
* This associates java lifecycle annotations with guice hooks. For example, we invoke {@link PostConstruct} after
|
||||
* injection, and Associate {@link PreDestroy} with a global {@link Closer} object.
|
||||
*
|
||||
* <h3>Important</h3> Make sure you create your injector with {@link Stage#PRODUCTION} and execute
|
||||
* the bound {@link ExecutionList} prior to using any other objects.
|
||||
* <h3>Important</h3> Make sure you create your injector with {@link Stage#PRODUCTION} and execute the bound
|
||||
* {@link ExecutionList} prior to using any other objects.
|
||||
*
|
||||
* <p/>
|
||||
* Ex.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* </pre>
|
||||
|
@ -103,67 +104,57 @@ public class LifeCycleModule extends AbstractModule {
|
|||
bind(ExecutionList.class).toInstance(list);
|
||||
}
|
||||
|
||||
private static final Predicate<Invokable<?, ?>> isPreDestroy = new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> in) {
|
||||
return in.isAnnotationPresent(PreDestroy.class);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Predicate<Invokable<?, ?>> isPostConstruct = new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> in) {
|
||||
return in.isAnnotationPresent(PostConstruct.class);
|
||||
}
|
||||
};
|
||||
|
||||
protected void bindPostInjectionInvoke(final Closer closer, final ExecutionList list) {
|
||||
bindListener(any(), new TypeListener() {
|
||||
public <I> void hear(TypeLiteral<I> injectableType, TypeEncounter<I> encounter) {
|
||||
Set<Method> methods = newHashSet();
|
||||
Class<? super I> type = injectableType.getRawType();
|
||||
while (type != null) {
|
||||
methods.addAll(asList(type.getDeclaredMethods()));
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
for (final Method method : methods) {
|
||||
invokePostConstructMethodAfterInjection(encounter, method);
|
||||
associatePreDestroyWithCloser(closer, encounter, method);
|
||||
}
|
||||
}
|
||||
|
||||
private <I> void associatePreDestroyWithCloser(final Closer closer, TypeEncounter<I> encounter,
|
||||
final Method method) {
|
||||
PreDestroy preDestroy = method.getAnnotation(PreDestroy.class);
|
||||
if (preDestroy != null) {
|
||||
encounter.register(new InjectionListener<I>() {
|
||||
public void afterInjection(final I injectee) {
|
||||
closer.addToClose(new Closeable() {
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
method.invoke(injectee);
|
||||
} catch (InvocationTargetException ie) {
|
||||
Throwable e = ie.getTargetException();
|
||||
throw new IOException(e.getMessage());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private <I> void invokePostConstructMethodAfterInjection(TypeEncounter<I> encounter, final Method method) {
|
||||
PostConstruct postConstruct = method.getAnnotation(PostConstruct.class);
|
||||
if (postConstruct != null) {
|
||||
Collection<? extends Invokable<? super I, Object>> methods = methods(injectableType.getRawType());
|
||||
for (final Invokable<? super I, Object> method : filter(methods, isPostConstruct)) {
|
||||
encounter.register(new InjectionListener<I>() {
|
||||
public void afterInjection(final I injectee) {
|
||||
list.add(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
method.invoke(injectee);
|
||||
} catch (InvocationTargetException ie) {
|
||||
Throwable e = ie.getTargetException();
|
||||
throw propagate(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
invokeOnInjectee(method, injectee);
|
||||
}
|
||||
|
||||
}, sameThreadExecutor());
|
||||
}
|
||||
});
|
||||
}
|
||||
for (final Invokable<? super I, Object> method : filter(methods, isPreDestroy)) {
|
||||
encounter.register(new InjectionListener<I>() {
|
||||
public void afterInjection(final I injectee) {
|
||||
closer.addToClose(new Closeable() {
|
||||
public void close() throws IOException {
|
||||
invokeOnInjectee(method, injectee);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static <I> void invokeOnInjectee(Invokable<? super I, Object> method, I injectee) {
|
||||
try {
|
||||
method.invoke(injectee);
|
||||
} catch (InvocationTargetException ie) {
|
||||
throw propagate(ie.getTargetException());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,23 +19,33 @@
|
|||
package org.jclouds.reflect;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.toArray;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Iterables.tryFind;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
/**
|
||||
* Utilities that allow access to {@link Invokable}s with {@link Invokable#getOwnerType() owner types}.
|
||||
|
@ -43,14 +53,50 @@ import com.google.common.reflect.TypeToken;
|
|||
* @since 1.6
|
||||
*/
|
||||
@Beta
|
||||
public final class Reflection2 {
|
||||
public class Reflection2 {
|
||||
|
||||
/**
|
||||
* gets a {@link TypeToken} for the given type.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> TypeToken<T> typeToken(Type in) {
|
||||
return (TypeToken<T>) get(typeTokenForType, checkNotNull(in, "class"));
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a {@link TypeToken} for the given class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> TypeToken<T> typeToken(Class<T> in) {
|
||||
return (TypeToken<T>) typeTokenForClass.apply(checkNotNull(in, "class"));
|
||||
return (TypeToken<T>) get(typeTokenForClass, checkNotNull(in, "class"));
|
||||
}
|
||||
|
||||
/**
|
||||
* returns an {@link Invokable} object that reflects a constructor present in the {@link TypeToken} type.
|
||||
*
|
||||
* @param ownerType
|
||||
* corresponds to {@link Invokable#getOwnerType()}
|
||||
* @param parameterTypes
|
||||
* corresponds to {@link Constructor#getParameterTypes()}
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if the constructor doesn't exist or a security exception occurred
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Invokable<T, T> constructor(Class<T> ownerType, Class<?>... parameterTypes) {
|
||||
return (Invokable<T, T>) get(constructorForParams, new TypeTokenAndParameterTypes(typeToken(ownerType),
|
||||
parameterTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* return all constructors present in the class as {@link Invokable}s.
|
||||
*
|
||||
* @param ownerType
|
||||
* corresponds to {@link Invokable#getOwnerType()}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Collection<Invokable<T, T>> constructors(TypeToken<T> ownerType) {
|
||||
return Collection.class.cast(get(constructorsForTypeToken, ownerType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,7 +109,7 @@ public final class Reflection2 {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, R> Invokable<T, R> method(TypeToken<T> ownerType, Method method) {
|
||||
return (Invokable<T, R>) methods.apply(new TypeTokenAndMethod(ownerType, method));
|
||||
return (Invokable<T, R>) method(ownerType.getRawType(), method.getName(), method.getParameterTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +125,7 @@ public final class Reflection2 {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, R> Invokable<T, R> method(Class<T> ownerType, String name, Class<?>... parameterTypes) {
|
||||
return (Invokable<T, R>) methodForArgs.apply(new TypeTokenNameAndParameterTypes(typeToken(ownerType), name,
|
||||
return (Invokable<T, R>) get(methodForParams, new TypeTokenNameAndParameterTypes(typeToken(ownerType), name,
|
||||
parameterTypes));
|
||||
}
|
||||
|
||||
|
@ -91,51 +137,65 @@ public final class Reflection2 {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Collection<Invokable<T, Object>> methods(Class<T> ownerType) {
|
||||
return Collection.class.cast(methodsForTypeToken.apply(typeToken(ownerType)).values());
|
||||
return Collection.class.cast(get(methodsForTypeToken, typeToken(ownerType)));
|
||||
}
|
||||
|
||||
private static final LoadingCache<TypeTokenAndMethod, Invokable<?, ?>> methods = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<TypeTokenAndMethod, Invokable<?, ?>>() {
|
||||
public Invokable<?, ?> load(TypeTokenAndMethod key) {
|
||||
return key.type.method(key.method);
|
||||
/**
|
||||
* this gets all declared constructors, not just public ones. makes them accessible, as well.
|
||||
*/
|
||||
private static LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> constructorsForTypeToken = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
|
||||
public Set<Invokable<?, ?>> load(TypeToken<?> key) {
|
||||
ImmutableSet.Builder<Invokable<?, ?>> builder = ImmutableSet.<Invokable<?, ?>> builder();
|
||||
for (Constructor<?> ctor : key.getRawType().getDeclaredConstructors()) {
|
||||
ctor.setAccessible(true);
|
||||
builder.add(key.constructor(ctor));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
|
||||
private static class TypeTokenAndMethod {
|
||||
|
||||
protected final TypeToken<?> type;
|
||||
protected final Method method;
|
||||
|
||||
public TypeTokenAndMethod(TypeToken<?> type, Method method) {
|
||||
this.type = checkNotNull(type, "type");
|
||||
this.method = checkNotNull(method, "method");
|
||||
protected static List<Class<?>> toClasses(ImmutableList<Parameter> params) {
|
||||
return Lists.transform(params, new Function<Parameter, Class<?>>() {
|
||||
public Class<?> apply(Parameter input) {
|
||||
return input.getType().getRawType();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(type, method);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
TypeTokenAndMethod that = TypeTokenAndMethod.class.cast(obj);
|
||||
return Objects.equal(this.type, that.type) && Objects.equal(this.method, that.method);
|
||||
}
|
||||
}
|
||||
|
||||
private static final LoadingCache<Class<?>, TypeToken<?>> typeTokenForClass = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Class<?>, TypeToken<?>>() {
|
||||
public TypeToken<?> load(final Class<?> key) {
|
||||
private static LoadingCache<Type, TypeToken<?>> typeTokenForType = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Type, TypeToken<?>>() {
|
||||
public TypeToken<?> load(Type key) {
|
||||
return TypeToken.of(key);
|
||||
}
|
||||
});
|
||||
|
||||
private static LoadingCache<Class<?>, TypeToken<?>> typeTokenForClass = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Class<?>, TypeToken<?>>() {
|
||||
public TypeToken<?> load(Class<?> key) {
|
||||
return TypeToken.of(key);
|
||||
}
|
||||
});
|
||||
|
||||
private static LoadingCache<TypeTokenAndParameterTypes, Invokable<?, ?>> constructorForParams = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeTokenAndParameterTypes, Invokable<?, ?>>() {
|
||||
public Invokable<?, ?> load(final TypeTokenAndParameterTypes key) {
|
||||
Set<Invokable<?, ?>> constructors = get(constructorsForTypeToken, key.type);
|
||||
Optional<Invokable<?, ?>> constructor = tryFind(constructors, new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> input) {
|
||||
return Objects.equal(toClasses(input.getParameters()), key.parameterTypes);
|
||||
}
|
||||
});
|
||||
if (constructor.isPresent())
|
||||
return constructor.get();
|
||||
throw new IllegalArgumentException("no such constructor " + key.toString() + "in: " + constructors);
|
||||
}
|
||||
});
|
||||
|
||||
private static class TypeTokenAndParameterTypes {
|
||||
|
||||
protected final TypeToken<?> type;
|
||||
protected final List<Class<?>> parameterTypes;
|
||||
protected TypeToken<?> type;
|
||||
protected List<Class<?>> parameterTypes;
|
||||
|
||||
public TypeTokenAndParameterTypes(TypeToken<?> type, Class<?>... parameterTypes) {
|
||||
this.type = checkNotNull(type, "type");
|
||||
|
@ -160,23 +220,25 @@ public final class Reflection2 {
|
|||
}
|
||||
}
|
||||
|
||||
private static final LoadingCache<TypeTokenNameAndParameterTypes, Invokable<?, ?>> methodForArgs = CacheBuilder
|
||||
private static LoadingCache<TypeTokenNameAndParameterTypes, Invokable<?, ?>> methodForParams = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeTokenNameAndParameterTypes, Invokable<?, ?>>() {
|
||||
public Invokable<?, ?> load(final TypeTokenNameAndParameterTypes key) {
|
||||
try {
|
||||
Method method = key.type.getRawType().getMethod(key.name, toArray(key.parameterTypes, Class.class));
|
||||
return methods.apply(new TypeTokenAndMethod(key.type, method));
|
||||
} catch (SecurityException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + " getting method " + key.toString(), e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("no such method " + key.toString(), e);
|
||||
Set<Invokable<?, ?>> methods = get(methodsForTypeToken, key.type);
|
||||
Optional<Invokable<?, ?>> method = tryFind(methods, new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> input) {
|
||||
return Objects.equal(input.getName(), key.name)
|
||||
&& Objects.equal(toClasses(input.getParameters()), key.parameterTypes);
|
||||
}
|
||||
});
|
||||
if (method.isPresent())
|
||||
return method.get();
|
||||
throw new IllegalArgumentException("no such method " + key.toString() + "in: " + methods);
|
||||
}
|
||||
});
|
||||
|
||||
private static class TypeTokenNameAndParameterTypes extends TypeTokenAndParameterTypes {
|
||||
|
||||
private final String name;
|
||||
private String name;
|
||||
|
||||
public TypeTokenNameAndParameterTypes(TypeToken<?> type, String name, Class<?>... parameterTypes) {
|
||||
super(type, parameterTypes);
|
||||
|
@ -201,14 +263,36 @@ public final class Reflection2 {
|
|||
}
|
||||
}
|
||||
|
||||
private static final LoadingCache<TypeToken<?>, Map<Method, Invokable<?, ?>>> methodsForTypeToken = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeToken<?>, Map<Method, Invokable<?, ?>>>() {
|
||||
public Map<Method, Invokable<?, ?>> load(final TypeToken<?> key) {
|
||||
Builder<Method, Invokable<?, ?>> builder = ImmutableMap.<Method, Invokable<?, ?>> builder();
|
||||
for (Method method : key.getRawType().getMethods())
|
||||
builder.put(method, method(key, method));
|
||||
/**
|
||||
* this gets all declared methods, not just public ones. makes them accessible. Does not include Object methods.
|
||||
*/
|
||||
private static LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> methodsForTypeToken = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
|
||||
public Set<Invokable<?, ?>> load(TypeToken<?> key) {
|
||||
ImmutableSet.Builder<Invokable<?, ?>> builder = ImmutableSet.<Invokable<?, ?>> builder();
|
||||
for (TypeToken<?> token : key.getTypes()) {
|
||||
Class<?> raw = token.getRawType();
|
||||
if (raw == Object.class)
|
||||
continue;
|
||||
for (Method method : raw.getDeclaredMethods()) {
|
||||
method.setAccessible(true);
|
||||
builder.add(key.method(method));
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* ensures that exceptions are not doubly-wrapped
|
||||
*/
|
||||
private static <K, V> V get(LoadingCache<K, V> cache, K key) {
|
||||
try {
|
||||
return cache.get(key);
|
||||
} catch (UncheckedExecutionException e) {
|
||||
throw propagate(e.getCause());
|
||||
} catch (ExecutionException e) {
|
||||
throw propagate(e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ package org.jclouds.rest.config;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Iterables.toArray;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.collect.Maps.transformValues;
|
||||
|
@ -32,11 +31,9 @@ import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
|||
import static org.jclouds.util.Maps2.transformKeys;
|
||||
import static org.jclouds.util.Predicates2.startsWith;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
@ -47,7 +44,6 @@ import org.jclouds.http.functions.config.SaxParserModule;
|
|||
import org.jclouds.internal.FilterStringsBoundToInjectorByName;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.jclouds.location.config.LocationModule;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.proxy.ProxyForURI;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.HttpAsyncClient;
|
||||
|
@ -55,13 +51,14 @@ import org.jclouds.rest.HttpClient;
|
|||
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
|
||||
import org.jclouds.rest.internal.BlockOnFuture;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
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.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
@ -80,8 +77,6 @@ public class RestModule extends AbstractModule {
|
|||
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;
|
||||
}
|
||||
|
@ -92,6 +87,11 @@ public class RestModule extends AbstractModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
protected Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables() {
|
||||
return seedKnownSync2AsyncInvokables(sync2Async);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables(Map<Class<?>, Class<?>> sync2Async) {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncBuilder = CacheBuilder.newBuilder().build();
|
||||
putInvokables(HttpClient.class, HttpAsyncClient.class, sync2AsyncBuilder);
|
||||
for (Class<?> s : sync2Async.keySet()) {
|
||||
|
@ -103,18 +103,10 @@ public class RestModule extends AbstractModule {
|
|||
// accessible for ClientProvider
|
||||
public static void putInvokables(Class<?> sync, Class<?> async, Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
||||
for (Invokable<?, ?> invoked : methods(sync)) {
|
||||
if (!objectMethods.contains(invoked)) {
|
||||
try {
|
||||
Invokable<?, ?> delegatedMethod = method(async, invoked.getName(), getParameterTypes(invoked));
|
||||
checkArgument(delegatedMethod.getExceptionTypes().equals(invoked.getExceptionTypes()),
|
||||
"invoked %s has different typed exceptions than delegated invoked %s", invoked, delegatedMethod);
|
||||
invoked.setAccessible(true);
|
||||
delegatedMethod.setAccessible(true);
|
||||
"invoked %s has different typed exceptions than target %s", invoked, delegatedMethod);
|
||||
cache.put(invoked, delegatedMethod);
|
||||
} catch (SecurityException e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,14 +19,13 @@
|
|||
package org.jclouds.rest.config;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.inject.name.Names.named;
|
||||
|
||||
import org.jclouds.reflect.Invocation;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
* Allows the provider to supply a value set in a threadlocal.
|
||||
|
@ -55,8 +54,7 @@ public class SetCaller {
|
|||
}
|
||||
}
|
||||
|
||||
private static final Key<Invocation> CALLER_INVOCATION = Key.get(new TypeLiteral<Invocation>() {
|
||||
}, Names.named("caller"));
|
||||
private static final Key<Invocation> CALLER_INVOCATION = Key.get(Invocation.class, named("caller"));
|
||||
|
||||
class CallerInvocationProvider implements Provider<Invocation> {
|
||||
@Override
|
||||
|
|
|
@ -19,12 +19,15 @@
|
|||
package org.jclouds.reflect;
|
||||
|
||||
import static com.google.common.base.Functions.toStringFunction;
|
||||
import static org.jclouds.reflect.Reflection2.constructors;
|
||||
import static org.jclouds.reflect.Reflection2.method;
|
||||
import static org.jclouds.reflect.Reflection2.methods;
|
||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
@ -32,7 +35,9 @@ import com.google.common.base.Function;
|
|||
import com.google.common.collect.FluentIterable;
|
||||
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.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -41,7 +46,31 @@ import com.google.common.reflect.TypeToken;
|
|||
@Test
|
||||
public class Reflection2Test {
|
||||
|
||||
public void testTypeToken() {
|
||||
/**
|
||||
* useful when converting to and from type literals from other libraries such as guice and gson.
|
||||
*/
|
||||
public void testTypeTokenForType() {
|
||||
TypeLiteral<Set<String>> guice = new TypeLiteral<Set<String>>() {
|
||||
};
|
||||
|
||||
assertEquals(typeToken(guice.getType()), new TypeToken<Set<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
});
|
||||
}
|
||||
|
||||
public void testConstructors() {
|
||||
Set<String> ctorParams = FluentIterable.from(constructors(TypeToken.of(HashSet.class)))
|
||||
.transform(new Function<Invokable<?, ?>, Iterable<Parameter>>() {
|
||||
public Iterable<Parameter> apply(Invokable<?, ?> input) {
|
||||
return input.getParameters();
|
||||
}
|
||||
}).transform(toStringFunction()).toSet();
|
||||
|
||||
assertEquals(ctorParams, ImmutableSet.of("[]", "[java.util.Collection<? extends E> arg0]",
|
||||
"[int arg0, float arg1]", "[int arg0]", "[int arg0, float arg1, boolean arg2]"));
|
||||
}
|
||||
|
||||
public void testTypeTokenForClass() {
|
||||
assertEquals(typeToken(String.class), TypeToken.of(String.class));
|
||||
}
|
||||
|
||||
|
@ -65,15 +94,28 @@ public class Reflection2Test {
|
|||
assertEquals(methodInSuper.getParameters().get(0).getType().getRawType(), Object.class);
|
||||
}
|
||||
|
||||
ImmutableSet<String> setMethods = ImmutableSet.of("add", "equals", "hashCode", "clear", "isEmpty", "contains",
|
||||
"addAll", "size", "toArray", "iterator", "remove", "removeAll", "containsAll", "retainAll");
|
||||
|
||||
public void testMethods() {
|
||||
Set<String> methodNames = FluentIterable.from(methods(Set.class))
|
||||
.transform(new Function<Invokable<?, ?>, String>() {
|
||||
Set<String> methodNames = FluentIterable.from(methods(Set.class)).transform(invokableToName)
|
||||
.transform(toStringFunction()).toSet();
|
||||
|
||||
assertEquals(methodNames, setMethods);
|
||||
}
|
||||
|
||||
public void testMethodsSubClass() {
|
||||
Set<String> methodNames = FluentIterable.from(methods(SortedSet.class)).transform(invokableToName)
|
||||
.transform(toStringFunction()).toSet();
|
||||
|
||||
assertEquals(methodNames,
|
||||
ImmutableSet.builder().add("comparator", "last", "first", "subSet", "headSet", "tailSet")
|
||||
.addAll(setMethods).build());
|
||||
}
|
||||
|
||||
static final Function<Invokable<?, ?>, String> invokableToName = new Function<Invokable<?, ?>, String>() {
|
||||
public String apply(Invokable<?, ?> input) {
|
||||
return input.getName();
|
||||
}
|
||||
}).transform(toStringFunction()).toSet();
|
||||
|
||||
assertEquals(methodNames, ImmutableSet.of("add", "equals", "hashCode", "clear", "isEmpty", "contains", "addAll",
|
||||
"size", "toArray", "iterator", "remove", "removeAll", "containsAll", "retainAll"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jclouds.rest.config;
|
||||
|
||||
import static com.google.common.base.Predicates.not;
|
||||
import static com.google.common.collect.Maps.filterEntries;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.jclouds.rest.HttpAsyncClient;
|
||||
import org.jclouds.rest.HttpClient;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class RestModuleTest {
|
||||
static interface Sync {
|
||||
String get();
|
||||
}
|
||||
|
||||
private static interface Async {
|
||||
ListenableFuture<String> get();
|
||||
}
|
||||
|
||||
public void testPutInvokablesWhenInterfacesMatch() {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||
RestModule.putInvokables(Sync.class, Async.class, cache);
|
||||
|
||||
assertEquals(cache.size(), 1);
|
||||
|
||||
Invokable<?, ?> sync = cache.asMap().keySet().iterator().next();
|
||||
assertEquals(sync.getOwnerType().getRawType(), Sync.class);
|
||||
assertEquals(sync.getName(), "get");
|
||||
assertEquals(sync.getReturnType(), TypeToken.of(String.class));
|
||||
|
||||
Invokable<?, ?> async = cache.getIfPresent(sync);
|
||||
assertEquals(async.getOwnerType().getRawType(), Async.class);
|
||||
assertEquals(async.getName(), "get");
|
||||
assertEquals(async.getReturnType(), new TypeToken<ListenableFuture<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
});
|
||||
}
|
||||
|
||||
private static interface AsyncWithException {
|
||||
ListenableFuture<String> get() throws IOException;
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = ".* has different typed exceptions than target .*")
|
||||
public void testPutInvokablesWhenInterfacesMatchExceptExceptions() {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||
RestModule.putInvokables(Sync.class, AsyncWithException.class, cache);
|
||||
}
|
||||
|
||||
private static interface AsyncWithMisnamedMethod {
|
||||
ListenableFuture<String> got();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "no such method .*")
|
||||
public void testPutInvokablesWhenTargetMethodNotFound() {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||
RestModule.putInvokables(Sync.class, AsyncWithMisnamedMethod.class, cache);
|
||||
}
|
||||
|
||||
static final Predicate<Entry<Invokable<?, ?>, Invokable<?, ?>>> isHttpInvokable = new Predicate<Map.Entry<Invokable<?, ?>, Invokable<?, ?>>>() {
|
||||
public boolean apply(Map.Entry<Invokable<?, ?>, Invokable<?, ?>> in) {
|
||||
return in.getKey().getOwnerType().getRawType().equals(HttpClient.class)
|
||||
&& in.getValue().getOwnerType().getRawType().equals(HttpAsyncClient.class);
|
||||
}
|
||||
};
|
||||
|
||||
public void testSeedKnownSync2AsyncIncludesHttpClientByDefault() {
|
||||
Map<Invokable<?, ?>, Invokable<?, ?>> cache = RestModule.seedKnownSync2AsyncInvokables(
|
||||
ImmutableMap.<Class<?>, Class<?>> of()).asMap();
|
||||
|
||||
assertEquals(cache.size(), 6);
|
||||
assertEquals(filterEntries(cache, isHttpInvokable), cache);
|
||||
}
|
||||
|
||||
public void testSeedKnownSync2AsyncInvokablesInterfacesMatch() {
|
||||
Map<Invokable<?, ?>, Invokable<?, ?>> cache = RestModule.seedKnownSync2AsyncInvokables(
|
||||
ImmutableMap.<Class<?>, Class<?>> of(Sync.class, Async.class)).asMap();
|
||||
|
||||
assertEquals(cache.size(), 7);
|
||||
|
||||
cache = filterEntries(cache, not(isHttpInvokable));
|
||||
|
||||
assertEquals(cache.size(), 1);
|
||||
}
|
||||
|
||||
}
|
|
@ -22,8 +22,9 @@ package org.jclouds.abiquo.domain;
|
|||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.concat;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static org.jclouds.reflect.Reflection2.constructor;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -44,6 +45,7 @@ import com.abiquo.server.core.task.TaskDto;
|
|||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.reflect.Invokable;
|
||||
|
||||
/**
|
||||
* This class is used to decorate transport objects with high level
|
||||
|
@ -103,13 +105,12 @@ public abstract class DomainWrapper<T extends SingleResourceTransportDto> {
|
|||
}
|
||||
|
||||
try {
|
||||
Constructor<W> cons = wrapperClass.getDeclaredConstructor(RestContext.class, target.getClass());
|
||||
if (!cons.isAccessible()) {
|
||||
cons.setAccessible(true);
|
||||
}
|
||||
return cons.newInstance(context, target);
|
||||
} catch (Exception ex) {
|
||||
throw new WrapperException(wrapperClass, target, ex);
|
||||
Invokable<W, W> cons = constructor(wrapperClass, RestContext.class, target.getClass());
|
||||
return cons.invoke(null, context, target);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new WrapperException(wrapperClass, target, e.getTargetException());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new WrapperException(wrapperClass, target, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue