Update Injector and create injectable object factory

- Using e.g. Factory<Converter> instead of the injector directly makes its purpose more specific and disallows any future abuse of the injector's functions
This commit is contained in:
ljacqu 2017-02-05 16:52:35 +01:00
parent 8ae06ed480
commit d2fccdeb80
13 changed files with 109 additions and 51 deletions

View File

@ -433,7 +433,7 @@
<dependency> <dependency>
<groupId>ch.jalu</groupId> <groupId>ch.jalu</groupId>
<artifactId>injector</artifactId> <artifactId>injector</artifactId>
<version>0.3</version> <version>0.4</version>
<scope>compile</scope> <scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>

View File

@ -14,6 +14,7 @@ import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
import fr.xephi.authme.initialization.OnStartupTasks; import fr.xephi.authme.initialization.OnStartupTasks;
import fr.xephi.authme.initialization.SettingsProvider; import fr.xephi.authme.initialization.SettingsProvider;
import fr.xephi.authme.initialization.TaskCloser; import fr.xephi.authme.initialization.TaskCloser;
import fr.xephi.authme.initialization.factory.FactoryDependencyHandler;
import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.listener.EntityListener; import fr.xephi.authme.listener.EntityListener;
import fr.xephi.authme.listener.PlayerListener; import fr.xephi.authme.listener.PlayerListener;
@ -196,7 +197,10 @@ public class AuthMe extends JavaPlugin {
getDataFolder().mkdir(); getDataFolder().mkdir();
// Create injector, provide elements from the Bukkit environment and register providers // 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(AuthMe.class, this);
injector.register(Server.class, getServer()); injector.register(Server.class, getServer());
injector.register(PluginManager.class, getServer().getPluginManager()); injector.register(PluginManager.class, getServer().getPluginManager());

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.command; package fr.xephi.authme.command;
import ch.jalu.injector.Injector;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.help.HelpProvider; 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.MessageKey;
import fr.xephi.authme.message.Messages; import fr.xephi.authme.message.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
@ -40,13 +40,13 @@ public class CommandHandler {
private Map<Class<? extends ExecutableCommand>, ExecutableCommand> commands = new HashMap<>(); private Map<Class<? extends ExecutableCommand>, ExecutableCommand> commands = new HashMap<>();
@Inject @Inject
CommandHandler(Injector injector, CommandMapper commandMapper, PermissionsManager permissionsManager, CommandHandler(Factory<ExecutableCommand> commandFactory, CommandMapper commandMapper,
Messages messages, HelpProvider helpProvider) { PermissionsManager permissionsManager, Messages messages, HelpProvider helpProvider) {
this.commandMapper = commandMapper; this.commandMapper = commandMapper;
this.permissionsManager = permissionsManager; this.permissionsManager = permissionsManager;
this.messages = messages; this.messages = messages;
this.helpProvider = helpProvider; this.helpProvider = helpProvider;
initializeCommands(injector, commandMapper.getCommandClasses()); initializeCommands(commandFactory, commandMapper.getCommandClasses());
} }
/** /**
@ -94,13 +94,13 @@ public class CommandHandler {
/** /**
* Initialize all required ExecutableCommand objects. * Initialize all required ExecutableCommand objects.
* *
* @param injector the injector * @param commandFactory factory to create command objects
* @param commandClasses the classes to instantiate * @param commandClasses the classes to instantiate
*/ */
private void initializeCommands(Injector injector, private void initializeCommands(Factory<ExecutableCommand> commandFactory,
Set<Class<? extends ExecutableCommand>> commandClasses) { Set<Class<? extends ExecutableCommand>> commandClasses) {
for (Class<? extends ExecutableCommand> clazz : commandClasses) { for (Class<? extends ExecutableCommand> clazz : commandClasses) {
commands.put(clazz, injector.newInstance(clazz)); commands.put(clazz, commandFactory.newInstance(clazz));
} }
} }

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.Injector;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import fr.xephi.authme.ConsoleLogger; 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.SqliteToSql;
import fr.xephi.authme.datasource.converter.vAuthConverter; import fr.xephi.authme.datasource.converter.vAuthConverter;
import fr.xephi.authme.datasource.converter.xAuthConverter; import fr.xephi.authme.datasource.converter.xAuthConverter;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.CommonService;
@ -37,7 +37,7 @@ public class ConverterCommand implements ExecutableCommand {
private BukkitService bukkitService; private BukkitService bukkitService;
@Inject @Inject
private Injector injector; private Factory<Converter> converterFactory;
@Override @Override
public void executeCommand(final CommandSender sender, List<String> arguments) { public void executeCommand(final CommandSender sender, List<String> arguments) {
@ -52,7 +52,7 @@ public class ConverterCommand implements ExecutableCommand {
} }
// Get the proper converter instance // Get the proper converter instance
final Converter converter = injector.newInstance(converterClass); final Converter converter = converterFactory.newInstance(converterClass);
// Run the convert job // Run the convert job
bukkitService.runTaskAsynchronously(new Runnable() { bukkitService.runTaskAsynchronously(new Runnable() {

View File

@ -0,0 +1,19 @@
package fr.xephi.authme.initialization.factory;
/**
* Injectable factory that creates new instances of a certain type.
*
* @param <P> the parent type to which the factory is limited to
*/
public interface Factory<P> {
/**
* Creates an instance of the given class.
*
* @param clazz the class to instantiate
* @param <C> the class type
* @return new instance of the class
*/
<C extends P> C newInstance(Class<C> clazz);
}

View File

@ -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<P> implements Factory<P> {
private final Injector injector;
private final Class<P> parentClass;
FactoryImpl(Class<P> parentClass, Injector injector) {
this.parentClass = parentClass;
this.injector = injector;
}
@Override
public <C extends P> C newInstance(Class<C> clazz) {
if (parentClass.isAssignableFrom(clazz)) {
return injector.newInstance(clazz);
}
throw new IllegalArgumentException(clazz + " not child of " + parentClass);
}
}
}

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.security; package fr.xephi.authme.security;
import ch.jalu.injector.Injector;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.PasswordEncryptionEvent; import fr.xephi.authme.events.PasswordEncryptionEvent;
import fr.xephi.authme.initialization.Reloadable; 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.EncryptionMethod;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
@ -29,7 +29,7 @@ public class PasswordSecurity implements Reloadable {
private PluginManager pluginManager; private PluginManager pluginManager;
@Inject @Inject
private Injector injector; private Factory<EncryptionMethod> hashAlgorithmFactory;
private HashAlgorithm algorithm; private HashAlgorithm algorithm;
private Collection<HashAlgorithm> legacyAlgorithms; private Collection<HashAlgorithm> legacyAlgorithms;
@ -154,7 +154,7 @@ public class PasswordSecurity implements Reloadable {
if (HashAlgorithm.CUSTOM.equals(algorithm) || HashAlgorithm.PLAINTEXT.equals(algorithm)) { if (HashAlgorithm.CUSTOM.equals(algorithm) || HashAlgorithm.PLAINTEXT.equals(algorithm)) {
return null; return null;
} }
return injector.newInstance(algorithm.getClazz()); return hashAlgorithmFactory.newInstance(algorithm.getClazz());
} }
private void hashPasswordForNewAlgorithm(String password, String playerName) { private void hashPasswordForNewAlgorithm(String password, String playerName) {

View File

@ -8,6 +8,7 @@ import fr.xephi.authme.api.NewAPI;
import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.command.CommandHandler;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.factory.FactoryDependencyHandler;
import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
@ -91,7 +92,10 @@ public class AuthMeInitializationTest {
Settings settings = Settings settings =
new Settings(dataFolder, mock(PropertyResource.class), null, buildConfigurationData()); 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.provide(DataFolder.class, dataFolder);
injector.register(Server.class, server); injector.register(Server.class, server);
injector.register(PluginManager.class, pluginManager); injector.register(PluginManager.class, pluginManager);

View File

@ -6,6 +6,7 @@ import fr.xephi.authme.command.TestCommandsUtil.TestLoginCommand;
import fr.xephi.authme.command.TestCommandsUtil.TestRegisterCommand; import fr.xephi.authme.command.TestCommandsUtil.TestRegisterCommand;
import fr.xephi.authme.command.TestCommandsUtil.TestUnregisterCommand; import fr.xephi.authme.command.TestCommandsUtil.TestUnregisterCommand;
import fr.xephi.authme.command.help.HelpProvider; 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.MessageKey;
import fr.xephi.authme.message.Messages; import fr.xephi.authme.message.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
@ -56,7 +57,7 @@ public class CommandHandlerTest {
private CommandHandler handler; private CommandHandler handler;
@Mock @Mock
private Injector injector; private Factory<ExecutableCommand> commandFactory;
@Mock @Mock
private CommandMapper commandMapper; private CommandMapper commandMapper;
@Mock @Mock
@ -75,7 +76,7 @@ public class CommandHandlerTest {
ExecutableCommand.class, TestLoginCommand.class, TestRegisterCommand.class, TestUnregisterCommand.class)); ExecutableCommand.class, TestLoginCommand.class, TestRegisterCommand.class, TestUnregisterCommand.class));
setInjectorToMockExecutableCommandClasses(); 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") @SuppressWarnings("unchecked")
private void setInjectorToMockExecutableCommandClasses() { private void setInjectorToMockExecutableCommandClasses() {
given(injector.newInstance(any(Class.class))).willAnswer(new Answer<Object>() { given(commandFactory.newInstance(any(Class.class))).willAnswer(new Answer<Object>() {
@Override @Override
public Object answer(InvocationOnMock invocation) throws Throwable { public Object answer(InvocationOnMock invocation) throws Throwable {
Class<?> clazz = invocation.getArgument(0); Class<?> clazz = invocation.getArgument(0);

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.Injector;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.datasource.converter.Converter; import fr.xephi.authme.datasource.converter.Converter;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.CommonService;
@ -48,7 +48,7 @@ public class ConverterCommandTest {
private BukkitService bukkitService; private BukkitService bukkitService;
@Mock @Mock
private Injector injector; private Factory<Converter> converterFactory;
@BeforeClass @BeforeClass
public static void initLogger() { public static void initLogger() {
@ -66,7 +66,7 @@ public class ConverterCommandTest {
// then // then
verify(sender).sendMessage(argThat(containsString("Converter does not exist"))); verify(sender).sendMessage(argThat(containsString("Converter does not exist")));
verifyNoMoreInteractions(commandService); verifyNoMoreInteractions(commandService);
verifyZeroInteractions(injector); verifyZeroInteractions(converterFactory);
verifyZeroInteractions(bukkitService); verifyZeroInteractions(bukkitService);
} }
@ -100,8 +100,8 @@ public class ConverterCommandTest {
// then // then
verify(converter).execute(sender); verify(converter).execute(sender);
verifyNoMoreInteractions(converter); verifyNoMoreInteractions(converter);
verify(injector).newInstance(converterClass); verify(converterFactory).newInstance(converterClass);
verifyNoMoreInteractions(injector); verifyNoMoreInteractions(converterFactory);
} }
@Test @Test
@ -120,14 +120,14 @@ public class ConverterCommandTest {
// then // then
verify(converter).execute(sender); verify(converter).execute(sender);
verifyNoMoreInteractions(converter); verifyNoMoreInteractions(converter);
verify(injector).newInstance(converterClass); verify(converterFactory).newInstance(converterClass);
verifyNoMoreInteractions(injector); verifyNoMoreInteractions(converterFactory);
verify(commandService).send(sender, MessageKey.ERROR); verify(commandService).send(sender, MessageKey.ERROR);
} }
private <T extends Converter> T createMockReturnedByInjector(Class<T> clazz) { private <T extends Converter> T createMockReturnedByInjector(Class<T> clazz) {
T converter = mock(clazz); T converter = mock(clazz);
given(injector.newInstance(clazz)).willReturn(converter); given(converterFactory.newInstance(clazz)).willReturn(converter);
return converter; return converter;
} }

View File

@ -6,6 +6,7 @@ import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.PasswordEncryptionEvent; 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.EncryptionMethod;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.JOOMLA; import fr.xephi.authme.security.crypts.JOOMLA;
@ -84,7 +85,9 @@ public class PasswordSecurityTest {
return null; return null;
} }
}).when(pluginManager).callEvent(any(Event.class)); }).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(Settings.class, settings);
injector.register(DataSource.class, dataSource); injector.register(DataSource.class, dataSource);
injector.register(PluginManager.class, pluginManager); injector.register(PluginManager.class, pluginManager);

View File

@ -2,6 +2,7 @@ package tools.dependencygraph;
import ch.jalu.injector.handlers.instantiation.DependencyDescription; import ch.jalu.injector.handlers.instantiation.DependencyDescription;
import ch.jalu.injector.handlers.instantiation.Instantiation; 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.HashMultimap;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap; 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.process.SynchronousProcess;
import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.EncryptionMethod;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import tools.utils.InjectorUtils;
import tools.utils.ToolTask; import tools.utils.ToolTask;
import tools.utils.ToolsConstants; import tools.utils.ToolsConstants;
@ -114,7 +114,7 @@ public class DrawDependency implements ToolTask {
} }
private List<String> getDependencies(Class<?> clazz) { private List<String> getDependencies(Class<?> clazz) {
Instantiation<?> instantiation = InjectorUtils.getInstantiationMethod(clazz); Instantiation<?> instantiation = new StandardInjectionProvider().safeGet(clazz);
return instantiation == null ? null : formatInjectionDependencies(instantiation); return instantiation == null ? null : formatInjectionDependencies(instantiation);
} }

View File

@ -1,12 +1,10 @@
package tools.utils; package tools.utils;
import ch.jalu.injector.InjectorBuilder;
import ch.jalu.injector.handlers.instantiation.DependencyDescription; import ch.jalu.injector.handlers.instantiation.DependencyDescription;
import ch.jalu.injector.handlers.instantiation.Instantiation; 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.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
@ -24,7 +22,7 @@ public final class InjectorUtils {
* @return the class' dependencies, or null if no instantiation method found * @return the class' dependencies, or null if no instantiation method found
*/ */
public static Set<Class<?>> getDependencies(Class<?> clazz) { public static Set<Class<?>> getDependencies(Class<?> clazz) {
Instantiation<?> instantiation = getInstantiationMethod(clazz); Instantiation<?> instantiation = new StandardInjectionProvider().safeGet(clazz);
if (instantiation == null) { if (instantiation == null) {
return null; return null;
} }
@ -35,21 +33,4 @@ public final class InjectorUtils {
return dependencies; 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<InstantiationProvider> providers = InjectorBuilder.createInstantiationProviders();
for (InstantiationProvider provider : providers) {
Instantiation<?> instantiation = provider.get(clazz);
if (instantiation != null) {
return instantiation;
}
}
return null;
}
} }