From d791fcba9413e48bcb2f030c0754cb76041bbc9f Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 18 Jul 2016 21:47:26 +0200 Subject: [PATCH] #835 Remove all injector classes from AuthMe, update installer tasks --- .../initialization/ConstructorInjection.java | 86 ------------ .../authme/initialization/FieldInjection.java | 128 ------------------ .../authme/initialization/Injection.java | 36 ----- .../initialization/InjectionHelper.java | 66 --------- .../initialization/InstantiationFallback.java | 85 ------------ .../tools/checktestmocks/CheckTestMocks.java | 17 +-- .../tools/dependencygraph/DrawDependency.java | 21 ++- src/test/java/tools/utils/InjectorUtils.java | 55 ++++++++ 8 files changed, 74 insertions(+), 420 deletions(-) delete mode 100644 src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java delete mode 100644 src/main/java/fr/xephi/authme/initialization/FieldInjection.java delete mode 100644 src/main/java/fr/xephi/authme/initialization/Injection.java delete mode 100644 src/main/java/fr/xephi/authme/initialization/InjectionHelper.java delete mode 100644 src/main/java/fr/xephi/authme/initialization/InstantiationFallback.java create mode 100644 src/test/java/tools/utils/InjectorUtils.java diff --git a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java deleted file mode 100644 index e80ea128c..000000000 --- a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java +++ /dev/null @@ -1,86 +0,0 @@ -package fr.xephi.authme.initialization; - -import com.google.common.base.Preconditions; - -import javax.inject.Inject; -import javax.inject.Provider; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; - -/** - * Functionality for constructor injection. - */ -class ConstructorInjection implements Injection { - - private final Constructor constructor; - - private ConstructorInjection(Constructor constructor) { - this.constructor = constructor; - } - - @Override - public Class[] getDependencies() { - return constructor.getParameterTypes(); - } - - @Override - public Class[] getDependencyAnnotations() { - Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); - Class[] annotations = new Class[parameterAnnotations.length]; - for (int i = 0; i < parameterAnnotations.length; ++i) { - annotations[i] = parameterAnnotations[i].length > 0 - ? parameterAnnotations[i][0].annotationType() - : null; - } - return annotations; - } - - @Override - public T instantiateWith(Object... values) { - validateNoNullValues(values); - try { - return constructor.newInstance(values); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new UnsupportedOperationException(e); - } - } - - public static Provider> provide(final Class clazz) { - return new Provider>() { - @Override - public ConstructorInjection get() { - Constructor constructor = getInjectionConstructor(clazz); - return constructor == null ? null : new ConstructorInjection<>(constructor); - } - }; - } - - - /** - * Gets the first found constructor annotated with {@link Inject} of the given class - * and marks it as accessible. - * - * @param clazz the class to process - * @param the class' type - * @return injection constructor for the class, null if not applicable - */ - @SuppressWarnings("unchecked") - private static Constructor getInjectionConstructor(Class clazz) { - Constructor[] constructors = clazz.getDeclaredConstructors(); - for (Constructor constructor : constructors) { - if (constructor.isAnnotationPresent(Inject.class)) { - constructor.setAccessible(true); - return (Constructor) constructor; - } - } - return null; - } - - private static void validateNoNullValues(Object[] array) { - for (Object entry : array) { - Preconditions.checkNotNull(entry); - } - } - -} diff --git a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java deleted file mode 100644 index 096c5f528..000000000 --- a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java +++ /dev/null @@ -1,128 +0,0 @@ -package fr.xephi.authme.initialization; - -import com.google.common.base.Preconditions; - -import javax.inject.Inject; -import javax.inject.Provider; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Functionality for field injection. - */ -class FieldInjection implements Injection { - - private final Field[] fields; - private final Constructor defaultConstructor; - - private FieldInjection(Constructor defaultConstructor, Collection fields) { - this.fields = fields.toArray(new Field[fields.size()]); - this.defaultConstructor = defaultConstructor; - } - - @Override - public Class[] getDependencies() { - Class[] types = new Class[fields.length]; - for (int i = 0; i < fields.length; ++i) { - types[i] = fields[i].getType(); - } - return types; - } - - @Override - public Class[] getDependencyAnnotations() { - Class[] annotations = new Class[fields.length]; - for (int i = 0; i < fields.length; ++i) { - annotations[i] = getFirstNonInjectAnnotation(fields[i]); - } - return annotations; - } - - @Override - public T instantiateWith(Object... values) { - Preconditions.checkArgument(values.length == fields.length, - "The number of values must be equal to the number of fields"); - - T instance; - try { - instance = defaultConstructor.newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new UnsupportedOperationException(e); - } - - for (int i = 0; i < fields.length; ++i) { - try { - Preconditions.checkNotNull(values[i]); - fields[i].set(instance, values[i]); - } catch (IllegalAccessException e) { - throw new UnsupportedOperationException(e); - } - } - return instance; - } - - /** - * Returns a provider for a {@code FieldInjection} instance, i.e. a provides an object - * with which field injection can be performed on the given class if applicable. The provided - * value is {@code null} if field injection cannot be applied to the class. - * - * @param clazz the class to provide field injection for - * @param the class' type - * @return field injection provider for the given class, or null if not applicable - */ - public static Provider> provide(final Class clazz) { - return new Provider>() { - @Override - public FieldInjection get() { - Constructor constructor = getDefaultConstructor(clazz); - if (constructor == null) { - return null; - } - List fields = getInjectionFields(clazz); - return fields.isEmpty() ? null : new FieldInjection<>(constructor, fields); - } - }; - } - - private static List getInjectionFields(Class clazz) { - List fields = new ArrayList<>(); - for (Field field : clazz.getDeclaredFields()) { - if (field.isAnnotationPresent(Inject.class)) { - if (Modifier.isStatic(field.getModifiers())) { - throw new IllegalStateException(String.format("Field '%s' in class '%s' is static but " - + "annotated with @Inject", field.getName(), clazz.getSimpleName())); - } - field.setAccessible(true); - fields.add(field); - } - } - return fields; - } - - private static Class getFirstNonInjectAnnotation(Field field) { - for (Annotation annotation : field.getAnnotations()) { - if (annotation.annotationType() != Inject.class) { - return annotation.annotationType(); - } - } - return null; - } - - @SuppressWarnings("unchecked") - private static Constructor getDefaultConstructor(Class clazz) { - try { - Constructor defaultConstructor = clazz.getDeclaredConstructor(); - defaultConstructor.setAccessible(true); - return (Constructor) defaultConstructor; - } catch (NoSuchMethodException ignore) { - // no default constructor available - } - return null; - } -} diff --git a/src/main/java/fr/xephi/authme/initialization/Injection.java b/src/main/java/fr/xephi/authme/initialization/Injection.java deleted file mode 100644 index 65acf7961..000000000 --- a/src/main/java/fr/xephi/authme/initialization/Injection.java +++ /dev/null @@ -1,36 +0,0 @@ -package fr.xephi.authme.initialization; - -/** - * Common interface for all injection methods. - * - * @param the type of the concerned object - */ -public interface Injection { - - /** - * Returns the dependencies that must be provided to instantiate the given item. - * - * @return list of dependencies - * @see #instantiateWith - */ - Class[] getDependencies(); - - /** - * Returns the annotation on each dependency if available. The indices of this - * array correspond to the ones of {@link #getDependencies()}. If no annotation - * is available, {@code null} is stored. If multiple annotations are present, only - * one is stored (no guarantee on which one). - * - * @return annotation for each dependency - */ - Class[] getDependencyAnnotations(); - - /** - * Creates a new instance with the given values as dependencies. The given values - * must correspond to {@link #getDependencies()} in size, order and type. - * - * @param values the values to set for the dependencies - * @return resulting object - */ - T instantiateWith(Object... values); -} diff --git a/src/main/java/fr/xephi/authme/initialization/InjectionHelper.java b/src/main/java/fr/xephi/authme/initialization/InjectionHelper.java deleted file mode 100644 index 04e186b70..000000000 --- a/src/main/java/fr/xephi/authme/initialization/InjectionHelper.java +++ /dev/null @@ -1,66 +0,0 @@ -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 the class' type - * @return injection of the class or null if none detected - */ - public static Injection getInjection(Class 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 Injection firstNotNull(Provider>... providers) { - for (Provider> provider : providers) { - Injection object = provider.get(); - if (object != null) { - return object; - } - } - return null; - } -} diff --git a/src/main/java/fr/xephi/authme/initialization/InstantiationFallback.java b/src/main/java/fr/xephi/authme/initialization/InstantiationFallback.java deleted file mode 100644 index 4861733f5..000000000 --- a/src/main/java/fr/xephi/authme/initialization/InstantiationFallback.java +++ /dev/null @@ -1,85 +0,0 @@ -package fr.xephi.authme.initialization; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import javax.inject.Provider; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -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}. - */ -class InstantiationFallback implements Injection { - - private final Constructor constructor; - - private InstantiationFallback(Constructor constructor) { - this.constructor = constructor; - } - - @Override - public Class[] getDependencies() { - return new Class[0]; - } - - @Override - public Class[] getDependencyAnnotations() { - return new Class[0]; - } - - @Override - public T instantiateWith(Object... values) { - if (values == null || values.length > 0) { - throw new UnsupportedOperationException("Instantiation fallback cannot have parameters"); - } - try { - return constructor.newInstance(); - } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) { - throw new UnsupportedOperationException(e); - } - } - - /** - * Returns an instantiation fallback if the class is applicable. - * - * @param clazz the class - * @param the class' type - * @return instantiation fallback provider for the given class, or null if not applicable - */ - public static Provider> provide(final Class clazz) { - return new Provider>() { - @Override - public InstantiationFallback get() { - Constructor noArgsConstructor = getNoArgsConstructor(clazz); - // Return fallback only if we have no args constructor and no @Inject annotation anywhere - if (noArgsConstructor != null - && !isInjectionAnnotationPresent(clazz.getDeclaredConstructors()) - && !isInjectionAnnotationPresent(clazz.getDeclaredFields()) - && !isInjectionAnnotationPresent(clazz.getDeclaredMethods())) { - return new InstantiationFallback<>(noArgsConstructor); - } - return null; - } - }; - } - - private static Constructor getNoArgsConstructor(Class clazz) { - try { - // Note ljacqu 20160504: getConstructor(), unlike getDeclaredConstructor(), only considers public members - return clazz.getConstructor(); - } catch (NoSuchMethodException e) { - return null; - } - } - - private static boolean isInjectionAnnotationPresent(A[] accessibles) { - for (A accessible : accessibles) { - if (accessible.isAnnotationPresent(Inject.class) || accessible.isAnnotationPresent(PostConstruct.class)) { - return true; - } - } - return false; - } -} diff --git a/src/test/java/tools/checktestmocks/CheckTestMocks.java b/src/test/java/tools/checktestmocks/CheckTestMocks.java index e27fe7df9..8cd0bad81 100644 --- a/src/test/java/tools/checktestmocks/CheckTestMocks.java +++ b/src/test/java/tools/checktestmocks/CheckTestMocks.java @@ -3,18 +3,16 @@ package tools.checktestmocks; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Sets; -import fr.xephi.authme.initialization.Injection; -import fr.xephi.authme.initialization.InjectionHelper; import fr.xephi.authme.util.StringUtils; import org.mockito.Mock; import tools.utils.AutoToolTask; +import tools.utils.InjectorUtils; import tools.utils.ToolsConstants; import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Scanner; @@ -76,8 +74,10 @@ public class CheckTestMocks implements AutoToolTask { Class realClass = returnRealClass(testClass); if (realClass != null) { Set> mockFields = getMocks(testClass); - Set> injectFields = getRealClassDependencies(realClass); - if (!injectFields.containsAll(mockFields)) { + Set> injectFields = InjectorUtils.getDependencies(realClass); + if (injectFields == null) { + addErrorEntry(testClass, "Could not find instantiation method"); + } else if (!injectFields.containsAll(mockFields)) { addErrorEntry(testClass, "Error - Found the following mocks absent as @Inject: " + formatClassList(Sets.difference(mockFields, injectFields))); } else if (!mockFields.containsAll(injectFields)) { @@ -137,13 +137,6 @@ public class CheckTestMocks implements AutoToolTask { } } - private static Set> getRealClassDependencies(Class realClass) { - Injection injection = InjectionHelper.getInjection(realClass); - return injection == null - ? Collections.>emptySet() - : Sets.>newHashSet(injection.getDependencies()); - } - private static boolean isTestClassWithMocks(Class clazz) { for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Mock.class)) { diff --git a/src/test/java/tools/dependencygraph/DrawDependency.java b/src/test/java/tools/dependencygraph/DrawDependency.java index 3dc003bdd..859e57761 100644 --- a/src/test/java/tools/dependencygraph/DrawDependency.java +++ b/src/test/java/tools/dependencygraph/DrawDependency.java @@ -1,16 +1,17 @@ package tools.dependencygraph; +import ch.jalu.injector.handlers.instantiation.DependencyDescription; +import ch.jalu.injector.handlers.instantiation.Instantiation; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.converter.Converter; -import fr.xephi.authme.initialization.Injection; -import fr.xephi.authme.initialization.InjectionHelper; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.SynchronousProcess; import fr.xephi.authme.security.crypts.EncryptionMethod; import org.bukkit.event.Listener; +import tools.utils.InjectorUtils; import tools.utils.ToolTask; import tools.utils.ToolsConstants; @@ -143,8 +144,8 @@ public class DrawDependency implements ToolTask { } private List getDependencies(Class clazz) { - Injection injection = InjectionHelper.getInjection(clazz); - return injection == null ? null : formatInjectionDependencies(injection); + Instantiation instantiation = InjectorUtils.getInstantiationMethod(clazz); + return instantiation == null ? null : formatInjectionDependencies(instantiation); } /** @@ -155,9 +156,15 @@ public class DrawDependency implements ToolTask { * @param injection the injection whose dependencies should be formatted * @return list of dependencies in a friendly format */ - private List formatInjectionDependencies(Injection injection) { - Class[] dependencies = injection.getDependencies(); - Class[] annotations = injection.getDependencyAnnotations(); + private List formatInjectionDependencies(Instantiation injection) { + List descriptions = injection.getDependencies(); + final int totalDependencies = descriptions.size(); + Class[] dependencies = new Class[totalDependencies]; + Class[] annotations = new Class[totalDependencies]; + for (int i = 0; i < descriptions.size(); ++i) { + dependencies[i] = descriptions.get(i).getType(); + annotations[i] = null; // FIXME #835 descriptions.get(i).getAnnotations(); + } List result = new ArrayList<>(dependencies.length); for (int i = 0; i < dependencies.length; ++i) { diff --git a/src/test/java/tools/utils/InjectorUtils.java b/src/test/java/tools/utils/InjectorUtils.java new file mode 100644 index 000000000..0dbd1f4a9 --- /dev/null +++ b/src/test/java/tools/utils/InjectorUtils.java @@ -0,0 +1,55 @@ +package tools.utils; + +import ch.jalu.injector.InjectorBuilder; +import ch.jalu.injector.handlers.instantiation.DependencyDescription; +import ch.jalu.injector.handlers.instantiation.Instantiation; +import ch.jalu.injector.handlers.instantiation.InstantiationProvider; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Utility for operations with the injector. + */ +public final class InjectorUtils { + + private InjectorUtils() { + } + + /** + * Returns a class' dependencies as determined by the injector. + * + * @param clazz the class to process + * @return the class' dependencies, or null if no instantiation method found + */ + public static Set> getDependencies(Class clazz) { + Instantiation instantiation = getInstantiationMethod(clazz); + if (instantiation == null) { + return null; + } + Set> dependencies = new HashSet<>(); + for (DependencyDescription description : instantiation.getDependencies()) { + dependencies.add(description.getType()); + } + return dependencies; + } + + /** + * Returns the instantiation method for the given class. + * + * @param clazz the class to process + * @return the instantiation method for the class, or null if none applicable + */ + public static Instantiation getInstantiationMethod(Class clazz) { + List providers = InjectorBuilder.createInstantiationProviders(); + for (InstantiationProvider provider : providers) { + Instantiation instantiation = provider.get(clazz); + if (instantiation != null) { + return instantiation; + } + } + return null; + } + +}