diff --git a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java index dff7a76f..560596b7 100644 --- a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java +++ b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java @@ -121,17 +121,19 @@ public class AuthMeServiceInitializer { if (Annotation.class.isAssignableFrom(clazz)) { throw new UnsupportedOperationException("Cannot retrieve annotated elements in this way!"); } else if (objects.containsKey(clazz)) { - return getObject(clazz); + return clazz.cast(objects.get(clazz)); } - // First time we come across clazz, need to instantiate it. Add the clazz to the list of traversed - // classes in a new list, so each path we need to take has its own Set. + // First time we come across clazz, need to instantiate it. Validate that we can do so validatePackage(clazz); validateInstantiable(clazz); + // Add the clazz to the list of traversed classes in a new Set, so each path we take has its own Set. traversedClasses = new HashSet<>(traversedClasses); traversedClasses.add(clazz); - return instantiate(clazz, traversedClasses); + T object = instantiate(clazz, traversedClasses); + storeObject(object); + return object; } /** @@ -154,7 +156,6 @@ public class AuthMeServiceInitializer { validateInjectionHasNoCircularDependencies(injection.getDependencies(), traversedClasses); Object[] dependencies = resolveDependencies(injection, traversedClasses); T object = injection.instantiateWith(dependencies); - storeObject(object); executePostConstructMethods(object); return object; } @@ -186,27 +187,6 @@ public class AuthMeServiceInitializer { return values; } - - /** - * Internal method to retrieve an object from the objects map for non-annotation classes. - * In such cases, the type of the entry always corresponds to the key, i.e. the entry of key - * {@code Class} is guaranteed to be of type {@code T}. - *

- * To retrieve values identified with an annotation, use {@code objects.get(clazz)} directly. - * We do not know or control the type of the value of keys of annotation classes. - * - * @param clazz the class to retrieve the implementation of - * @param the type - * @return the implementation - */ - private T getObject(Class clazz) { - Object o = objects.get(clazz); - if (o == null) { - throw new NullPointerException("No instance of " + clazz + " available"); - } - return clazz.cast(o); - } - /** * Stores the given object with its class as key. Throws an exception if the key already has * a value associated to it. @@ -262,7 +242,7 @@ public class AuthMeServiceInitializer { private static void executePostConstructMethods(Object object) { for (Method method : object.getClass().getDeclaredMethods()) { if (method.isAnnotationPresent(PostConstruct.class)) { - if (method.getParameterTypes().length == 0) { + if (method.getParameterTypes().length == 0 && !Modifier.isStatic(method.getModifiers())) { try { method.setAccessible(true); method.invoke(object); @@ -270,8 +250,9 @@ public class AuthMeServiceInitializer { throw new UnsupportedOperationException(e); } } else { - throw new IllegalStateException("@PostConstruct methods must have an empty parameter list. " + - "Found parameters in " + method + " belonging to " + object.getClass()); + throw new IllegalStateException(String.format("@PostConstruct methods may not be static or have " + + " any parameters. Method '%s' of class '%s' is either static or has parameters", + method.getName(), object.getClass().getSimpleName())); } } } diff --git a/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java b/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java index 61322291..54789ef0 100644 --- a/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java +++ b/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java @@ -1,5 +1,6 @@ package fr.xephi.authme.initialization; +import fr.xephi.authme.initialization.samples.AlphaService; import fr.xephi.authme.initialization.samples.BadFieldInjection; import fr.xephi.authme.initialization.samples.BetaManager; import fr.xephi.authme.initialization.samples.CircularClasses; @@ -18,6 +19,7 @@ import org.junit.Test; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertThat; /** @@ -47,12 +49,18 @@ public class AuthMeServiceInitializerTest { } } - @Test(expected = IllegalStateException.class) + @Test(expected = RuntimeException.class) public void shouldThrowForInvalidPackage() { // given / when / then initializer.get(InvalidClass.class); } + @Test(expected = RuntimeException.class) + public void shouldThrowForUnregisteredPrimitiveType() { + // given / when / then + initializer.get(int.class); + } + @Test public void shouldPassValueByAnnotation() { // given @@ -169,7 +177,19 @@ public class AuthMeServiceInitializerTest { @Test(expected = RuntimeException.class) public void shouldThrowForInvalidPostConstructMethod() { // given / when / then - initializer.get(InvalidPostConstruct.class); + initializer.get(InvalidPostConstruct.WithParams.class); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForStaticPostConstructMethod() { + // given / when / then + initializer.get(InvalidPostConstruct.Static.class); + } + + @Test(expected = RuntimeException.class) + public void shouldForwardExceptionFromPostConstruct() { + // given / when / then + initializer.get(InvalidPostConstruct.ThrowsException.class); } @Test(expected = RuntimeException.class) @@ -191,4 +211,26 @@ public class AuthMeServiceInitializerTest { assertThat(cwad.getAbstractDependency() == concrete, equalTo(true)); assertThat(cwad.getAlphaService(), not(nullValue())); } + + @Test(expected = RuntimeException.class) + public void shouldThrowForAlreadyRegisteredClass() { + // given + initializer.register(new BetaManager()); + + // when / then + initializer.register(BetaManager.class, new BetaManager()); + } + + @Test + public void shouldCreateNewUntrackedInstance() { + // given / when + AlphaService singletonScoped = initializer.get(AlphaService.class); + AlphaService requestScoped = initializer.newInstance(AlphaService.class); + + // then + assertThat(singletonScoped.getProvidedClass(), not(nullValue())); + assertThat(singletonScoped.getProvidedClass(), equalTo(requestScoped.getProvidedClass())); + assertThat(singletonScoped, not(sameInstance(requestScoped))); + } + } diff --git a/src/test/java/fr/xephi/authme/initialization/FieldInjectionTest.java b/src/test/java/fr/xephi/authme/initialization/FieldInjectionTest.java new file mode 100644 index 00000000..5f0b45c2 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/FieldInjectionTest.java @@ -0,0 +1,54 @@ +package fr.xephi.authme.initialization; + +import fr.xephi.authme.initialization.samples.AlphaService; +import fr.xephi.authme.initialization.samples.BetaManager; +import fr.xephi.authme.initialization.samples.ClassWithAnnotations; +import fr.xephi.authme.initialization.samples.Duration; +import fr.xephi.authme.initialization.samples.FieldInjectionWithAnnotations; +import fr.xephi.authme.initialization.samples.GammaService; +import fr.xephi.authme.initialization.samples.ProvidedClass; +import fr.xephi.authme.initialization.samples.Size; +import org.junit.Test; + +import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link FieldInjection}. + */ +public class FieldInjectionTest { + + @Test + public void shouldReturnDependencies() { + // given + FieldInjection injection = + FieldInjection.provide(FieldInjectionWithAnnotations.class).get(); + + // when + Class[] dependencies = injection.getDependencies(); + Class[] annotations = injection.getDependencyAnnotations(); + + // then + assertThat(dependencies, arrayContaining(BetaManager.class, int.class, long.class, ClassWithAnnotations.class)); + assertThat(annotations, arrayContaining((Class) null, Size.class, Duration.class, null)); + } + + @Test + public void shouldInstantiateClass() { + // given + FieldInjection injection = FieldInjection.provide(BetaManager.class).get(); + ProvidedClass providedClass = new ProvidedClass(""); + AlphaService alphaService = AlphaService.newInstance(providedClass); + GammaService gammaService = new GammaService(alphaService); + + // when + BetaManager betaManager = injection.instantiateWith(providedClass, gammaService, alphaService); + + // then + assertThat(betaManager, not(nullValue())); + assertThat(betaManager.getDependencies(), arrayContaining(providedClass, gammaService, alphaService)); + } + +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/AlphaService.java b/src/test/java/fr/xephi/authme/initialization/samples/AlphaService.java index 2a93a43c..21dd3b32 100644 --- a/src/test/java/fr/xephi/authme/initialization/samples/AlphaService.java +++ b/src/test/java/fr/xephi/authme/initialization/samples/AlphaService.java @@ -17,4 +17,14 @@ public class AlphaService { public ProvidedClass getProvidedClass() { return providedClass; } + + /** + * Creates a new instance (for instantiations in tests). + * + * @param providedClass . + * @return created instance + */ + public static AlphaService newInstance(ProvidedClass providedClass) { + return new AlphaService(providedClass); + } } diff --git a/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java b/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java index 8ee97032..9f9dd229 100644 --- a/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java +++ b/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java @@ -10,7 +10,7 @@ public class GammaService { private AlphaService alphaService; @Inject - GammaService(AlphaService alphaService) { + public GammaService(AlphaService alphaService) { this.alphaService = alphaService; } diff --git a/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java b/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java index 85c7f4c4..c80a0c6c 100644 --- a/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java +++ b/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java @@ -8,12 +8,35 @@ import javax.inject.Inject; */ public class InvalidPostConstruct { - @Inject - private AlphaService alphaService; - @Inject - private ProvidedClass providedClass; + public static final class WithParams { + @Inject + private AlphaService alphaService; + @Inject + private ProvidedClass providedClass; - @PostConstruct - public void invalidPostConstr(BetaManager betaManager) { + WithParams() { } + + @PostConstruct + public void invalidPostConstr(BetaManager betaManager) { + } + } + + public static final class Static { + @Inject + Static(BetaManager betaManager) { + // -- + } + + @PostConstruct + public static void invalidMethod() { + // -- + } + } + + public static final class ThrowsException { + @PostConstruct + public void throwingPostConstruct() { + throw new IllegalStateException("Exception in post construct"); + } } }