Create delayed injection test runner

- Test runner supporting new "DelayedInjection" annotation: such fields are only initialized with instantiation right before the first time they're used in tests, allowing to set up mock behavior beforehand
This commit is contained in:
ljacqu
2016-06-18 13:19:07 +02:00
parent cd1acfde1b
commit a1c62e7c04
13 changed files with 296 additions and 126 deletions
@@ -5,7 +5,6 @@ import com.google.common.collect.ImmutableSet;
import fr.xephi.authme.settings.NewSetting;
import javax.annotation.PostConstruct;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -167,8 +166,7 @@ public class AuthMeServiceInitializer {
* @return the instantiated object
*/
private <T> T instantiate(Class<T> clazz, Set<Class<?>> traversedClasses) {
Injection<T> injection = firstNotNull(
ConstructorInjection.provide(clazz), FieldInjection.provide(clazz), InstantiationFallback.provide(clazz));
Injection<T> injection = InjectionHelper.getInjection(clazz);
if (injection == null) {
throw new IllegalStateException("Did not find injection method for " + clazz + ". Make sure you have "
+ "a constructor with @Inject or fields with @Inject. Fields with @Inject require "
@@ -268,7 +266,7 @@ public class AuthMeServiceInitializer {
* @param object the object to execute the post construct method for
*/
private static void executePostConstructMethod(Object object) {
Method postConstructMethod = getAndValidatePostConstructMethod(object.getClass());
Method postConstructMethod = InjectionHelper.getAndValidatePostConstructMethod(object.getClass());
if (postConstructMethod != null) {
try {
postConstructMethod.setAccessible(true);
@@ -285,40 +283,4 @@ public class AuthMeServiceInitializer {
}
}
/**
* Validate and locate the given class' post construct method. Returns {@code null} if none present.
*
* @param clazz the class to search
* @return post construct method, or null
*/
private static Method getAndValidatePostConstructMethod(Class<?> clazz) {
Method postConstructMethod = null;
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
if (postConstructMethod != null) {
throw new IllegalStateException("Multiple methods with @PostConstruct on " + clazz);
} else if (method.getParameterTypes().length > 0 || Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@PostConstruct method may not be static or have any parameters. "
+ "Invalid method in " + clazz);
} else if (method.getReturnType() != void.class) {
throw new IllegalStateException("@PostConstruct method must have return type void. "
+ "Offending class: " + clazz);
} else {
postConstructMethod = method;
}
}
}
return postConstructMethod;
}
@SafeVarargs
private static <T> Injection<T> firstNotNull(Provider<? extends Injection<T>>... providers) {
for (Provider<? extends Injection<T>> provider : providers) {
Injection<T> object = provider.get();
if (object != null) {
return object;
}
}
return null;
}
}
@@ -11,7 +11,7 @@ import java.lang.reflect.InvocationTargetException;
/**
* Functionality for constructor injection.
*/
public class ConstructorInjection<T> implements Injection<T> {
class ConstructorInjection<T> implements Injection<T> {
private final Constructor<T> constructor;
@@ -16,7 +16,7 @@ import java.util.List;
/**
* Functionality for field injection.
*/
public class FieldInjection<T> implements Injection<T> {
class FieldInjection<T> implements Injection<T> {
private final Field[] fields;
private final Constructor<T> defaultConstructor;
@@ -0,0 +1,66 @@
package fr.xephi.authme.initialization;
import javax.annotation.PostConstruct;
import javax.inject.Provider;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Helper class for functions relating to injecting.
*/
public class InjectionHelper {
private InjectionHelper() {
}
/**
* Returns the {@link Injection} for the given class, or null if none applicable.
*
* @param clazz the class to process
* @param <T> the class' type
* @return injection of the class or null if none detected
*/
public static <T> Injection<T> getInjection(Class<T> clazz) {
return firstNotNull(
ConstructorInjection.provide(clazz),
FieldInjection.provide(clazz),
InstantiationFallback.provide(clazz));
}
/**
* Validates and locates the given class' post construct method. Returns {@code null} if none present.
*
* @param clazz the class to search
* @return post construct method, or null
*/
public static Method getAndValidatePostConstructMethod(Class<?> clazz) {
Method postConstructMethod = null;
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
if (postConstructMethod != null) {
throw new IllegalStateException("Multiple methods with @PostConstruct on " + clazz);
} else if (method.getParameterTypes().length > 0 || Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@PostConstruct method may not be static or have any parameters. "
+ "Invalid method in " + clazz);
} else if (method.getReturnType() != void.class) {
throw new IllegalStateException("@PostConstruct method must have return type void. "
+ "Offending class: " + clazz);
} else {
postConstructMethod = method;
}
}
}
return postConstructMethod;
}
@SafeVarargs
private static <T> Injection<T> firstNotNull(Provider<? extends Injection<T>>... providers) {
for (Provider<? extends Injection<T>> provider : providers) {
Injection<T> object = provider.get();
if (object != null) {
return object;
}
}
return null;
}
}
@@ -11,7 +11,7 @@ import java.lang.reflect.InvocationTargetException;
* Fallback instantiation method for classes with an accessible no-args constructor
* and no elements whatsoever annotated with {@link Inject} or {@link PostConstruct}.
*/
public class InstantiationFallback<T> implements Injection<T> {
class InstantiationFallback<T> implements Injection<T> {
private final Constructor<T> constructor;