From d2fccdeb806a3ad094fe85288c604f1ddcbbdad8 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sun, 5 Feb 2017 16:52:35 +0100 Subject: [PATCH] Update Injector and create injectable object factory - Using e.g. Factory instead of the injector directly makes its purpose more specific and disallows any future abuse of the injector's functions --- pom.xml | 2 +- src/main/java/fr/xephi/authme/AuthMe.java | 6 ++- .../xephi/authme/command/CommandHandler.java | 14 +++--- .../executable/authme/ConverterCommand.java | 6 +-- .../initialization/factory/Factory.java | 19 ++++++++ .../factory/FactoryDependencyHandler.java | 46 +++++++++++++++++++ .../authme/security/PasswordSecurity.java | 6 +-- .../authme/AuthMeInitializationTest.java | 6 ++- .../authme/command/CommandHandlerTest.java | 7 +-- .../authme/ConverterCommandTest.java | 16 +++---- .../authme/security/PasswordSecurityTest.java | 5 +- .../tools/dependencygraph/DrawDependency.java | 4 +- src/test/java/tools/utils/InjectorUtils.java | 23 +--------- 13 files changed, 109 insertions(+), 51 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/initialization/factory/Factory.java create mode 100644 src/main/java/fr/xephi/authme/initialization/factory/FactoryDependencyHandler.java diff --git a/pom.xml b/pom.xml index 622af6bde..d275b20dd 100644 --- a/pom.xml +++ b/pom.xml @@ -433,7 +433,7 @@ ch.jalu injector - 0.3 + 0.4 compile true diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 1b987080b..8ed9ffb36 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -14,6 +14,7 @@ import fr.xephi.authme.initialization.OnShutdownPlayerSaver; import fr.xephi.authme.initialization.OnStartupTasks; import fr.xephi.authme.initialization.SettingsProvider; import fr.xephi.authme.initialization.TaskCloser; +import fr.xephi.authme.initialization.factory.FactoryDependencyHandler; import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.listener.EntityListener; import fr.xephi.authme.listener.PlayerListener; @@ -196,7 +197,10 @@ public class AuthMe extends JavaPlugin { getDataFolder().mkdir(); // Create injector, provide elements from the Bukkit environment and register providers - injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); + injector = new InjectorBuilder() + .addHandlers(new FactoryDependencyHandler()) + .addDefaultHandlers("fr.xephi.authme") + .create(); injector.register(AuthMe.class, this); injector.register(Server.class, getServer()); injector.register(PluginManager.class, getServer().getPluginManager()); diff --git a/src/main/java/fr/xephi/authme/command/CommandHandler.java b/src/main/java/fr/xephi/authme/command/CommandHandler.java index 394b6f144..083d8a531 100644 --- a/src/main/java/fr/xephi/authme/command/CommandHandler.java +++ b/src/main/java/fr/xephi/authme/command/CommandHandler.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command; -import ch.jalu.injector.Injector; import fr.xephi.authme.AuthMe; import fr.xephi.authme.command.help.HelpProvider; +import fr.xephi.authme.initialization.factory.Factory; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; @@ -40,13 +40,13 @@ public class CommandHandler { private Map, ExecutableCommand> commands = new HashMap<>(); @Inject - CommandHandler(Injector injector, CommandMapper commandMapper, PermissionsManager permissionsManager, - Messages messages, HelpProvider helpProvider) { + CommandHandler(Factory commandFactory, CommandMapper commandMapper, + PermissionsManager permissionsManager, Messages messages, HelpProvider helpProvider) { this.commandMapper = commandMapper; this.permissionsManager = permissionsManager; this.messages = messages; this.helpProvider = helpProvider; - initializeCommands(injector, commandMapper.getCommandClasses()); + initializeCommands(commandFactory, commandMapper.getCommandClasses()); } /** @@ -94,13 +94,13 @@ public class CommandHandler { /** * Initialize all required ExecutableCommand objects. * - * @param injector the injector + * @param commandFactory factory to create command objects * @param commandClasses the classes to instantiate */ - private void initializeCommands(Injector injector, + private void initializeCommands(Factory commandFactory, Set> commandClasses) { for (Class clazz : commandClasses) { - commands.put(clazz, injector.newInstance(clazz)); + commands.put(clazz, commandFactory.newInstance(clazz)); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java index efa27ff09..e1c0661ce 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java @@ -1,6 +1,5 @@ package fr.xephi.authme.command.executable.authme; -import ch.jalu.injector.Injector; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import fr.xephi.authme.ConsoleLogger; @@ -13,6 +12,7 @@ import fr.xephi.authme.datasource.converter.RoyalAuthConverter; import fr.xephi.authme.datasource.converter.SqliteToSql; import fr.xephi.authme.datasource.converter.vAuthConverter; import fr.xephi.authme.datasource.converter.xAuthConverter; +import fr.xephi.authme.initialization.factory.Factory; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; @@ -37,7 +37,7 @@ public class ConverterCommand implements ExecutableCommand { private BukkitService bukkitService; @Inject - private Injector injector; + private Factory converterFactory; @Override public void executeCommand(final CommandSender sender, List arguments) { @@ -52,7 +52,7 @@ public class ConverterCommand implements ExecutableCommand { } // Get the proper converter instance - final Converter converter = injector.newInstance(converterClass); + final Converter converter = converterFactory.newInstance(converterClass); // Run the convert job bukkitService.runTaskAsynchronously(new Runnable() { diff --git a/src/main/java/fr/xephi/authme/initialization/factory/Factory.java b/src/main/java/fr/xephi/authme/initialization/factory/Factory.java new file mode 100644 index 000000000..0f4ae62ad --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/factory/Factory.java @@ -0,0 +1,19 @@ +package fr.xephi.authme.initialization.factory; + +/** + * Injectable factory that creates new instances of a certain type. + * + * @param

the parent type to which the factory is limited to + */ +public interface Factory

{ + + /** + * Creates an instance of the given class. + * + * @param clazz the class to instantiate + * @param the class type + * @return new instance of the class + */ + C newInstance(Class clazz); + +} diff --git a/src/main/java/fr/xephi/authme/initialization/factory/FactoryDependencyHandler.java b/src/main/java/fr/xephi/authme/initialization/factory/FactoryDependencyHandler.java new file mode 100644 index 000000000..04c11c68e --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/factory/FactoryDependencyHandler.java @@ -0,0 +1,46 @@ +package fr.xephi.authme.initialization.factory; + +import ch.jalu.injector.Injector; +import ch.jalu.injector.context.ResolvedInstantiationContext; +import ch.jalu.injector.handlers.dependency.DependencyHandler; +import ch.jalu.injector.handlers.instantiation.DependencyDescription; +import ch.jalu.injector.utils.ReflectionUtils; + +/** + * Dependency handler that builds {@link Factory} objects. + */ +public class FactoryDependencyHandler implements DependencyHandler { + + @Override + public Object resolveValue(ResolvedInstantiationContext context, DependencyDescription dependencyDescription) { + if (dependencyDescription.getType() == Factory.class) { + Class genericType = ReflectionUtils.getGenericType(dependencyDescription.getGenericType()); + if (genericType == null) { + throw new IllegalStateException("Factory fields must have concrete generic type. " + + "Cannot get generic type for field in '" + context.getMappedClass() + "'"); + } + + return new FactoryImpl<>(genericType, context.getInjector()); + } + return null; + } + + private static final class FactoryImpl

implements Factory

{ + + private final Injector injector; + private final Class

parentClass; + + FactoryImpl(Class

parentClass, Injector injector) { + this.parentClass = parentClass; + this.injector = injector; + } + + @Override + public C newInstance(Class clazz) { + if (parentClass.isAssignableFrom(clazz)) { + return injector.newInstance(clazz); + } + throw new IllegalArgumentException(clazz + " not child of " + parentClass); + } + } +} diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java index 8549b388b..d29471089 100644 --- a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java +++ b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java @@ -1,9 +1,9 @@ package fr.xephi.authme.security; -import ch.jalu.injector.Injector; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.PasswordEncryptionEvent; import fr.xephi.authme.initialization.Reloadable; +import fr.xephi.authme.initialization.factory.Factory; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; @@ -29,7 +29,7 @@ public class PasswordSecurity implements Reloadable { private PluginManager pluginManager; @Inject - private Injector injector; + private Factory hashAlgorithmFactory; private HashAlgorithm algorithm; private Collection legacyAlgorithms; @@ -154,7 +154,7 @@ public class PasswordSecurity implements Reloadable { if (HashAlgorithm.CUSTOM.equals(algorithm) || HashAlgorithm.PLAINTEXT.equals(algorithm)) { return null; } - return injector.newInstance(algorithm.getClazz()); + return hashAlgorithmFactory.newInstance(algorithm.getClazz()); } private void hashPasswordForNewAlgorithm(String password, String playerName) { diff --git a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java index aae925b51..89132fcfa 100644 --- a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java +++ b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java @@ -8,6 +8,7 @@ import fr.xephi.authme.api.NewAPI; import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.initialization.factory.FactoryDependencyHandler; import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; @@ -91,7 +92,10 @@ public class AuthMeInitializationTest { Settings settings = new Settings(dataFolder, mock(PropertyResource.class), null, buildConfigurationData()); - Injector injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); + Injector injector = new InjectorBuilder() + .addHandlers(new FactoryDependencyHandler()) + .addDefaultHandlers("fr.xephi.authme") + .create(); injector.provide(DataFolder.class, dataFolder); injector.register(Server.class, server); injector.register(PluginManager.class, pluginManager); diff --git a/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java b/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java index 1166cef23..e343199fe 100644 --- a/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java @@ -6,6 +6,7 @@ import fr.xephi.authme.command.TestCommandsUtil.TestLoginCommand; import fr.xephi.authme.command.TestCommandsUtil.TestRegisterCommand; import fr.xephi.authme.command.TestCommandsUtil.TestUnregisterCommand; import fr.xephi.authme.command.help.HelpProvider; +import fr.xephi.authme.initialization.factory.Factory; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; @@ -56,7 +57,7 @@ public class CommandHandlerTest { private CommandHandler handler; @Mock - private Injector injector; + private Factory commandFactory; @Mock private CommandMapper commandMapper; @Mock @@ -75,7 +76,7 @@ public class CommandHandlerTest { ExecutableCommand.class, TestLoginCommand.class, TestRegisterCommand.class, TestUnregisterCommand.class)); setInjectorToMockExecutableCommandClasses(); - handler = new CommandHandler(injector, commandMapper, permissionsManager, messages, helpProvider); + handler = new CommandHandler(commandFactory, commandMapper, permissionsManager, messages, helpProvider); } /** @@ -86,7 +87,7 @@ public class CommandHandlerTest { */ @SuppressWarnings("unchecked") private void setInjectorToMockExecutableCommandClasses() { - given(injector.newInstance(any(Class.class))).willAnswer(new Answer() { + given(commandFactory.newInstance(any(Class.class))).willAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Class clazz = invocation.getArgument(0); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java index cf306599d..0474eac1a 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command.executable.authme; -import ch.jalu.injector.Injector; import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.converter.Converter; +import fr.xephi.authme.initialization.factory.Factory; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; @@ -48,7 +48,7 @@ public class ConverterCommandTest { private BukkitService bukkitService; @Mock - private Injector injector; + private Factory converterFactory; @BeforeClass public static void initLogger() { @@ -66,7 +66,7 @@ public class ConverterCommandTest { // then verify(sender).sendMessage(argThat(containsString("Converter does not exist"))); verifyNoMoreInteractions(commandService); - verifyZeroInteractions(injector); + verifyZeroInteractions(converterFactory); verifyZeroInteractions(bukkitService); } @@ -100,8 +100,8 @@ public class ConverterCommandTest { // then verify(converter).execute(sender); verifyNoMoreInteractions(converter); - verify(injector).newInstance(converterClass); - verifyNoMoreInteractions(injector); + verify(converterFactory).newInstance(converterClass); + verifyNoMoreInteractions(converterFactory); } @Test @@ -120,14 +120,14 @@ public class ConverterCommandTest { // then verify(converter).execute(sender); verifyNoMoreInteractions(converter); - verify(injector).newInstance(converterClass); - verifyNoMoreInteractions(injector); + verify(converterFactory).newInstance(converterClass); + verifyNoMoreInteractions(converterFactory); verify(commandService).send(sender, MessageKey.ERROR); } private T createMockReturnedByInjector(Class clazz) { T converter = mock(clazz); - given(injector.newInstance(clazz)).willReturn(converter); + given(converterFactory.newInstance(clazz)).willReturn(converter); return converter; } diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java index e651ebea8..04d2bc0bb 100644 --- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java +++ b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java @@ -6,6 +6,7 @@ import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.PasswordEncryptionEvent; +import fr.xephi.authme.initialization.factory.FactoryDependencyHandler; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.JOOMLA; @@ -84,7 +85,9 @@ public class PasswordSecurityTest { return null; } }).when(pluginManager).callEvent(any(Event.class)); - injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); + injector = new InjectorBuilder() + .addHandlers(new FactoryDependencyHandler()) + .addDefaultHandlers("fr.xephi.authme").create(); injector.register(Settings.class, settings); injector.register(DataSource.class, dataSource); injector.register(PluginManager.class, pluginManager); diff --git a/src/test/java/tools/dependencygraph/DrawDependency.java b/src/test/java/tools/dependencygraph/DrawDependency.java index 6b2c9c8af..34756ebb3 100644 --- a/src/test/java/tools/dependencygraph/DrawDependency.java +++ b/src/test/java/tools/dependencygraph/DrawDependency.java @@ -2,6 +2,7 @@ package tools.dependencygraph; import ch.jalu.injector.handlers.instantiation.DependencyDescription; import ch.jalu.injector.handlers.instantiation.Instantiation; +import ch.jalu.injector.handlers.instantiation.StandardInjectionProvider; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; @@ -14,7 +15,6 @@ 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; @@ -114,7 +114,7 @@ public class DrawDependency implements ToolTask { } private List getDependencies(Class clazz) { - Instantiation instantiation = InjectorUtils.getInstantiationMethod(clazz); + Instantiation instantiation = new StandardInjectionProvider().safeGet(clazz); return instantiation == null ? null : formatInjectionDependencies(instantiation); } diff --git a/src/test/java/tools/utils/InjectorUtils.java b/src/test/java/tools/utils/InjectorUtils.java index 0dbd1f4a9..6b5d510e0 100644 --- a/src/test/java/tools/utils/InjectorUtils.java +++ b/src/test/java/tools/utils/InjectorUtils.java @@ -1,12 +1,10 @@ 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 ch.jalu.injector.handlers.instantiation.StandardInjectionProvider; import java.util.HashSet; -import java.util.List; import java.util.Set; /** @@ -24,7 +22,7 @@ public final class InjectorUtils { * @return the class' dependencies, or null if no instantiation method found */ public static Set> getDependencies(Class clazz) { - Instantiation instantiation = getInstantiationMethod(clazz); + Instantiation instantiation = new StandardInjectionProvider().safeGet(clazz); if (instantiation == null) { return null; } @@ -35,21 +33,4 @@ public final class InjectorUtils { 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; - } - }