From 02079f1f5ceede785b537073807bc4c67b68dbd0 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Wed, 27 Apr 2016 22:49:20 +0200 Subject: [PATCH 1/8] #432 Create custom dependency injector --- pom.xml | 7 + src/main/java/fr/xephi/authme/AntiBot.java | 2 + src/main/java/fr/xephi/authme/AuthMe.java | 154 ++++------ .../java/fr/xephi/authme/DataManager.java | 40 ++- src/main/java/fr/xephi/authme/api/API.java | 19 +- src/main/java/fr/xephi/authme/api/NewAPI.java | 3 + .../xephi/authme/command/CommandHandler.java | 9 +- .../xephi/authme/command/CommandMapper.java | 5 +- .../xephi/authme/command/CommandService.java | 58 ++-- .../authme/command/help/HelpProvider.java | 8 +- .../fr/xephi/authme/hooks/PluginHooks.java | 14 +- .../AuthMeServiceInitializer.java | 286 ++++++++++++++++++ .../authme/initialization/BaseCommands.java | 14 + .../initialization/ConstructorInjection.java | 77 +++++ .../authme/initialization/DataFolder.java | 14 + .../authme/initialization/FieldInjection.java | 112 +++++++ .../authme/initialization/Injection.java | 36 +++ .../{ => initialization}/MetricsStarter.java | 4 +- .../authme/listener/AuthMePlayerListener.java | 40 +-- .../authme/listener/AuthMeServerListener.java | 21 +- .../AuthMeTabCompletePacketAdapter.java | 3 + .../listener/AuthMeTablistPacketAdapter.java | 2 + .../authme/{ => mail}/ImageGenerator.java | 7 +- .../fr/xephi/authme/mail/SendMailSSL.java | 1 - .../authme/permission/PermissionsManager.java | 55 ++-- .../fr/xephi/authme/process/Management.java | 30 +- .../xephi/authme/process/ProcessService.java | 42 ++- .../authme/process/join/AsynchronousJoin.java | 7 +- .../authme/security/PasswordSecurity.java | 2 + .../fr/xephi/authme/settings/SpawnLoader.java | 5 +- .../fr/xephi/authme/util/BukkitService.java | 2 + src/main/java/fr/xephi/authme/util/Utils.java | 3 +- .../xephi/authme/util/ValidationService.java | 2 + .../authme/command/CommandServiceTest.java | 9 +- .../authme/command/help/HelpProviderTest.java | 6 +- .../AuthMeServiceInitializerTest.java | 194 ++++++++++++ .../initialization/samples/AlphaService.java | 20 ++ .../samples/BadFieldInjection.java | 16 + .../initialization/samples/BetaManager.java | 20 ++ .../samples/CircularClasses.java | 30 ++ .../samples/ClassWithAbstractDependency.java | 32 ++ .../samples/ClassWithAnnotations.java | 29 ++ .../initialization/samples/Duration.java | 14 + .../FieldInjectionWithAnnotations.java | 40 +++ .../initialization/samples/GammaService.java | 20 ++ .../initialization/samples/InvalidClass.java | 14 + .../samples/InvalidPostConstruct.java | 19 ++ .../samples/PostConstructTestClass.java | 37 +++ .../initialization/samples/ProvidedClass.java | 18 ++ .../authme/initialization/samples/Size.java | 14 + .../authme/process/ProcessServiceTest.java | 9 +- 51 files changed, 1347 insertions(+), 278 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java create mode 100644 src/main/java/fr/xephi/authme/initialization/BaseCommands.java create mode 100644 src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java create mode 100644 src/main/java/fr/xephi/authme/initialization/DataFolder.java create mode 100644 src/main/java/fr/xephi/authme/initialization/FieldInjection.java create mode 100644 src/main/java/fr/xephi/authme/initialization/Injection.java rename src/main/java/fr/xephi/authme/{ => initialization}/MetricsStarter.java (93%) rename src/main/java/fr/xephi/authme/{ => mail}/ImageGenerator.java (88%) create mode 100644 src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/AlphaService.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/BadFieldInjection.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/BetaManager.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/CircularClasses.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/ClassWithAbstractDependency.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/ClassWithAnnotations.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/Duration.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/FieldInjectionWithAnnotations.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/GammaService.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/InvalidClass.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/PostConstructTestClass.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/ProvidedClass.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/Size.java diff --git a/pom.xml b/pom.xml index ec273c536..a93987381 100644 --- a/pom.xml +++ b/pom.xml @@ -405,6 +405,13 @@ true + + + javax.inject + javax.inject + 1 + + com.maxmind.geoip diff --git a/src/main/java/fr/xephi/authme/AntiBot.java b/src/main/java/fr/xephi/authme/AntiBot.java index 75a2a3b59..fcdb2206d 100644 --- a/src/main/java/fr/xephi/authme/AntiBot.java +++ b/src/main/java/fr/xephi/authme/AntiBot.java @@ -9,6 +9,7 @@ import fr.xephi.authme.settings.properties.ProtectionSettings; import fr.xephi.authme.util.BukkitService; import org.bukkit.entity.Player; +import javax.inject.Inject; import java.util.ArrayList; import java.util.List; @@ -27,6 +28,7 @@ public class AntiBot { private final List antibotPlayers = new ArrayList<>(); private AntiBotStatus antiBotStatus = AntiBotStatus.DISABLED; + @Inject public AntiBot(NewSetting settings, Messages messages, PermissionsManager permissionsManager, BukkitService bukkitService) { this.settings = settings; diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 08a1945c1..812e88e9f 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -7,12 +7,8 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.backup.JsonCache; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboPlayer; -import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.command.CommandInitializer; -import fr.xephi.authme.command.CommandMapper; -import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.help.HelpProvider; import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSourceType; @@ -21,8 +17,11 @@ import fr.xephi.authme.datasource.MySQL; import fr.xephi.authme.datasource.SQLite; import fr.xephi.authme.hooks.BungeeCordMessage; import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.initialization.AuthMeServiceInitializer; +import fr.xephi.authme.initialization.BaseCommands; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.initialization.MetricsStarter; import fr.xephi.authme.listener.AuthMeBlockListener; -import fr.xephi.authme.listener.AuthMeEntityListener; import fr.xephi.authme.listener.AuthMeInventoryPacketAdapter; import fr.xephi.authme.listener.AuthMePlayerListener; import fr.xephi.authme.listener.AuthMePlayerListener16; @@ -38,7 +37,6 @@ import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.Management; -import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.SHA256; import fr.xephi.authme.settings.NewSetting; @@ -61,7 +59,6 @@ import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.MigrationService; import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; -import fr.xephi.authme.util.ValidationService; import org.apache.logging.log4j.LogManager; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -71,6 +68,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; import java.io.File; @@ -80,14 +78,12 @@ import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.List; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT; import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD; import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; -import static fr.xephi.authme.settings.properties.PluginSettings.HELP_HEADER; /** * The AuthMe main class. @@ -103,7 +99,6 @@ public class AuthMe extends JavaPlugin { // Private Instances private static AuthMe plugin; - private static Server server; /* * Maps and stuff */ @@ -138,7 +133,6 @@ public class AuthMe extends JavaPlugin { private DataSource database; private PluginHooks pluginHooks; private SpawnLoader spawnLoader; - private AntiBot antiBot; private boolean autoPurging; private BukkitService bukkitService; @@ -215,17 +209,15 @@ public class AuthMe extends JavaPlugin { @Override public void onEnable() { // Set various instances - server = getServer(); plugin = this; ConsoleLogger.setLogger(getLogger()); - setPluginInfos(); // Load settings and custom configurations, if it fails, stop the server due to security reasons. newSettings = createNewSetting(); if (newSettings == null) { ConsoleLogger.showError("Could not load configuration. Aborting."); - server.shutdown(); + getServer().shutdown(); return; } ConsoleLogger.setLoggingOptions(newSettings.getProperty(SecuritySettings.USE_LOGGING), @@ -233,7 +225,7 @@ public class AuthMe extends JavaPlugin { // Old settings manager if (!loadSettings()) { - server.shutdown(); + getServer().shutdown(); setEnabled(false); return; } @@ -249,23 +241,40 @@ public class AuthMe extends JavaPlugin { stopOrUnload(); return; } - - bukkitService = new BukkitService(this); - pluginHooks = new PluginHooks(server.getPluginManager()); - MigrationService.changePlainTextToSha256(newSettings, database, new SHA256()); - passwordSecurity = new PasswordSecurity(getDataSource(), newSettings, Bukkit.getPluginManager()); - // Initialize spawn loader - spawnLoader = new SpawnLoader(getDataFolder(), newSettings, pluginHooks); - permsMan = initializePermissionsManager(); - antiBot = new AntiBot(newSettings, messages, permsMan, bukkitService); - ValidationService validationService = new ValidationService(newSettings, database, permsMan); - commandHandler = initializeCommandHandler(permsMan, messages, passwordSecurity, newSettings, - pluginHooks, spawnLoader, antiBot, validationService, bukkitService); + + AuthMeServiceInitializer initializer = new AuthMeServiceInitializer("fr.xephi.authme"); + // Register elements of the Bukkit / JavaPlugin environment + initializer.register(AuthMe.class, this); + initializer.register(Server.class, getServer()); + initializer.register(PluginManager.class, getServer().getPluginManager()); + initializer.register(BukkitScheduler.class, getServer().getScheduler()); + initializer.provide(DataFolder.class, getDataFolder()); + + // Register elements we instantiate manually + initializer.register(newSettings); + initializer.register(messages); + initializer.register(DataSource.class, database); + initializer.provide(BaseCommands.class, CommandInitializer.buildCommands()); + + // Some statically injected things + initializer.register(PlayerCache.class, PlayerCache.getInstance()); + initializer.register(LimboCache.class, LimboCache.getInstance()); + + permsMan = initializer.get(PermissionsManager.class); + bukkitService = initializer.get(BukkitService.class); + pluginHooks = initializer.get(PluginHooks.class); + passwordSecurity = initializer.get(PasswordSecurity.class); + spawnLoader = initializer.get(SpawnLoader.class); + commandHandler = initializer.get(CommandHandler.class); + api = initializer.get(NewAPI.class); + management = initializer.get(Management.class); + dataManager = initializer.get(DataManager.class); + initializer.get(API.class); // Set up Metrics - MetricsStarter.setupMetrics(plugin, newSettings); + MetricsStarter.setupMetrics(this, newSettings); // Set console filter setupConsoleFilter(); @@ -282,22 +291,12 @@ public class AuthMe extends JavaPlugin { // End of Hooks // Do a backup on start - new PerformBackup(plugin, newSettings).doBackup(PerformBackup.BackupCause.START); + new PerformBackup(this, newSettings).doBackup(PerformBackup.BackupCause.START); // Setup the inventory backup playerBackup = new JsonCache(); - // Set the DataManager - dataManager = new DataManager(this, pluginHooks, bukkitService); - - // Set up the new API - setupApi(); - - // Set up the management - ProcessService processService = new ProcessService(newSettings, messages, this, database, - passwordSecurity, pluginHooks, spawnLoader, validationService, bukkitService); - management = new Management(this, processService, database, PlayerCache.getInstance()); // Set up the BungeeCord hook setupBungeeCordHook(newSettings); @@ -306,7 +305,7 @@ public class AuthMe extends JavaPlugin { reloadSupportHook(); // Register event listeners - registerEventListeners(messages, database, management, pluginHooks, spawnLoader, antiBot); + registerEventListeners(initializer); // Start Email recall task if needed scheduleRecallEmailTask(); @@ -370,29 +369,27 @@ public class AuthMe extends JavaPlugin { /** * Register all event listeners. */ - private void registerEventListeners(Messages messages, DataSource dataSource, Management management, - PluginHooks pluginHooks, SpawnLoader spawnLoader, AntiBot antiBot) { + private void registerEventListeners(AuthMeServiceInitializer initializer) { // Get the plugin manager instance - PluginManager pluginManager = server.getPluginManager(); + PluginManager pluginManager = getServer().getPluginManager(); // Register event listeners - pluginManager.registerEvents(new AuthMePlayerListener( - this, newSettings, messages, dataSource, antiBot, management, bukkitService), this); - pluginManager.registerEvents(new AuthMeBlockListener(), this); - pluginManager.registerEvents(new AuthMeEntityListener(), this); - pluginManager.registerEvents(new AuthMeServerListener(this, messages, pluginHooks, spawnLoader), this); + pluginManager.registerEvents(initializer.get(AuthMePlayerListener.class), this); + pluginManager.registerEvents(initializer.get(AuthMeBlockListener.class), this); + pluginManager.registerEvents(initializer.get(AuthMePlayerListener.class), this); + pluginManager.registerEvents(initializer.get(AuthMeServerListener.class), this); // Try to register 1.6 player listeners try { Class.forName("org.bukkit.event.player.PlayerEditBookEvent"); - pluginManager.registerEvents(new AuthMePlayerListener16(), this); + pluginManager.registerEvents(initializer.get(AuthMePlayerListener16.class), this); } catch (ClassNotFoundException ignore) { } // Try to register 1.8 player listeners try { Class.forName("org.bukkit.event.player.PlayerInteractAtEntityEvent"); - pluginManager.registerEvents(new AuthMePlayerListener18(), this); + pluginManager.registerEvents(initializer.get(AuthMePlayerListener18.class), this); } catch (ClassNotFoundException ignore) { } } @@ -426,30 +423,6 @@ public class AuthMe extends JavaPlugin { } } - private CommandHandler initializeCommandHandler(PermissionsManager permissionsManager, Messages messages, - PasswordSecurity passwordSecurity, NewSetting settings, - PluginHooks pluginHooks, SpawnLoader spawnLoader, AntiBot antiBot, - ValidationService validationService, BukkitService bukkitService) { - HelpProvider helpProvider = new HelpProvider(permissionsManager, settings.getProperty(HELP_HEADER)); - Set baseCommands = CommandInitializer.buildCommands(); - CommandMapper mapper = new CommandMapper(baseCommands, permissionsManager); - CommandService commandService = new CommandService(this, mapper, helpProvider, messages, passwordSecurity, - permissionsManager, settings, pluginHooks, spawnLoader, antiBot, validationService, bukkitService); - return new CommandHandler(commandService); - } - - /** - * Set up the API. This sets up the new and the old API. - */ - @SuppressWarnings("deprecation") - private void setupApi() { - // Set up the API - api = new NewAPI(this); - - // Set up the deprecated API - new API(this); - } - /** * Load the plugin's settings. * @@ -462,7 +435,7 @@ public class AuthMe extends JavaPlugin { } catch (Exception e) { ConsoleLogger.logException("Can't load the configuration file... Something went wrong. " + "To avoid security issues the server will shut down!", e); - server.shutdown(); + getServer().shutdown(); } return false; } @@ -507,14 +480,15 @@ public class AuthMe extends JavaPlugin { // Do backup on stop if enabled if (newSettings != null) { - new PerformBackup(plugin, newSettings).doBackup(PerformBackup.BackupCause.STOP); + new PerformBackup(this, newSettings).doBackup(PerformBackup.BackupCause.STOP); } + final AuthMe pluginInstance = this; new Thread(new Runnable() { @Override public void run() { List pendingTasks = new ArrayList<>(); for (BukkitTask pendingTask : getServer().getScheduler().getPendingTasks()) { - if (pendingTask.getOwner().equals(plugin) && !pendingTask.isSync()) { + if (pendingTask.getOwner().equals(pluginInstance) && !pendingTask.isSync()) { pendingTasks.add(pendingTask.getTaskId()); } } @@ -553,9 +527,9 @@ public class AuthMe extends JavaPlugin { public void stopOrUnload() { if (Settings.isStopEnabled) { ConsoleLogger.showError("THE SERVER IS GOING TO SHUT DOWN AS DEFINED IN THE CONFIGURATION!"); - server.shutdown(); + getServer().shutdown(); } else { - server.getPluginManager().disablePlugin(AuthMe.getInstance()); + getServer().getPluginManager().disablePlugin(AuthMe.getInstance()); } } @@ -598,7 +572,7 @@ public class AuthMe extends JavaPlugin { database = dataSource; if (DataSourceType.SQLITE == dataSourceType) { - server.getScheduler().runTaskAsynchronously(this, new Runnable() { + getServer().getScheduler().runTaskAsynchronously(this, new Runnable() { @Override public void run() { int accounts = database.getAccountsRegistered(); @@ -611,15 +585,6 @@ public class AuthMe extends JavaPlugin { } } - /** - * Set up the permissions manager. - */ - private PermissionsManager initializePermissionsManager() { - PermissionsManager manager = new PermissionsManager(Bukkit.getServer(), getLogger()); - manager.setup(); - return manager; - } - // Set the console filter to remove the passwords private void setLog4JFilter() { Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() { @@ -636,7 +601,7 @@ public class AuthMe extends JavaPlugin { // Check the presence of the ProtocolLib plugin public void checkProtocolLib() { - if (!server.getPluginManager().isPluginEnabled("ProtocolLib")) { + if (!getServer().getPluginManager().isPluginEnabled("ProtocolLib")) { if (newSettings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN)) { ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it..."); Settings.protectInventoryBeforeLogInEnabled = false; @@ -671,7 +636,7 @@ public class AuthMe extends JavaPlugin { // Save Player Data private void savePlayer(Player player) { - if (Utils.isNPC(player) || Utils.isUnrestricted(player)) { + if (safeIsNpc(player) || Utils.isUnrestricted(player)) { return; } String name = player.getName().toLowerCase(); @@ -699,6 +664,10 @@ public class AuthMe extends JavaPlugin { PlayerCache.getInstance().removePlayer(name); } + private boolean safeIsNpc(Player player) { + return pluginHooks != null && pluginHooks.isNpc(player) || player.hasMetadata("NPC"); + } + // Select the player to kick when a vip player joins the server when full public Player generateKickPlayer(Collection collection) { for (Player player : collection) { @@ -715,7 +684,7 @@ public class AuthMe extends JavaPlugin { return; } autoPurging = true; - server.getScheduler().runTaskAsynchronously(this, new Runnable() { + getServer().getScheduler().runTaskAsynchronously(this, new Runnable() { @Override public void run() { ConsoleLogger.info("AutoPurging the Database..."); @@ -772,6 +741,7 @@ public class AuthMe extends JavaPlugin { public String replaceAllInfo(String message, Player player) { String playersOnline = Integer.toString(bukkitService.getOnlinePlayers().size()); String ipAddress = Utils.getPlayerIp(player); + Server server = getServer(); return message .replace("&", "\u00a7") .replace("{PLAYER}", player.getName()) diff --git a/src/main/java/fr/xephi/authme/DataManager.java b/src/main/java/fr/xephi/authme/DataManager.java index 061683fa0..f5751d429 100644 --- a/src/main/java/fr/xephi/authme/DataManager.java +++ b/src/main/java/fr/xephi/authme/DataManager.java @@ -2,32 +2,37 @@ package fr.xephi.authme; import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.PurgeSettings; import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.Utils; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import javax.inject.Inject; import java.io.File; import java.util.ArrayList; import java.util.List; +import static fr.xephi.authme.util.StringUtils.makePath; + /** */ public class DataManager { - private final AuthMe plugin; - private final PluginHooks pluginHooks; - private final BukkitService bukkitService; + @Inject + private Server server; + @Inject + private PluginHooks pluginHooks; + @Inject + private BukkitService bukkitService; + @Inject + private NewSetting settings; + @Inject + private PermissionsManager permissionsManager; - /* - * Constructor. - */ - public DataManager(AuthMe plugin, PluginHooks pluginHooks, BukkitService bukkitService) { - this.plugin = plugin; - this.pluginHooks = pluginHooks; - this.bukkitService = bukkitService; - } + DataManager() { } private List getOfflinePlayers(List names) { List result = new ArrayList<>(); @@ -98,9 +103,8 @@ public class DataManager { public synchronized void purgeDat(List cleared) { int i = 0; - File dataFolder = new File(plugin.getServer().getWorldContainer() - + File.separator + plugin.getSettings().getProperty(PurgeSettings.DEFAULT_WORLD) - + File.separator + "players"); + File dataFolder = new File(server.getWorldContainer(), + makePath(settings.getProperty(PurgeSettings.DEFAULT_WORLD), "players")); List offlinePlayers = getOfflinePlayers(cleared); for (OfflinePlayer player : offlinePlayers) { File playerFile = new File(dataFolder, Utils.getUUIDorName(player) + ".dat"); @@ -142,14 +146,8 @@ public class DataManager { // TODO: What is this method for? Is it correct? // TODO: Make it work with OfflinePlayers group data. public synchronized void purgePermissions(List cleared) { - // Get the permissions manager, and make sure it's valid - PermissionsManager permsMan = plugin.getPermissionsManager(); - if (permsMan == null) { - ConsoleLogger.showError("Unable to access permissions manager instance!"); - return; - } for (String name : cleared) { - permsMan.removeAllGroups(bukkitService.getPlayerExact(name)); + permissionsManager.removeAllGroups(bukkitService.getPlayerExact(name)); } ConsoleLogger.info("AutoPurge: Removed permissions from " + cleared.size() + " player(s)."); } diff --git a/src/main/java/fr/xephi/authme/api/API.java b/src/main/java/fr/xephi/authme/api/API.java index 58960f9d6..26f5ea8f7 100644 --- a/src/main/java/fr/xephi/authme/api/API.java +++ b/src/main/java/fr/xephi/authme/api/API.java @@ -3,6 +3,8 @@ package fr.xephi.authme.api; import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.util.Utils; @@ -12,6 +14,8 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; +import javax.inject.Inject; + /** * Deprecated API of AuthMe. Please use {@link NewAPI} instead. */ @@ -20,7 +24,9 @@ public class API { public static final String newline = System.getProperty("line.separator"); public static AuthMe instance; + private static DataSource dataSource; private static PasswordSecurity passwordSecurity; + private static Management management; /** * Constructor for the deprecated API. @@ -28,9 +34,12 @@ public class API { * @param instance AuthMe */ @Deprecated - public API(AuthMe instance) { + @Inject + API(AuthMe instance, DataSource dataSource, PasswordSecurity passwordSecurity, Management management) { API.instance = instance; - passwordSecurity = instance.getPasswordSecurity(); + API.dataSource = dataSource; + API.passwordSecurity = passwordSecurity; + API.management = management; } /** @@ -109,7 +118,7 @@ public class API { @Deprecated public static boolean isRegistered(String playerName) { String player = playerName.toLowerCase(); - return instance.getDataSource().isAuthAvailable(player); + return dataSource.isAuthAvailable(player); } /** @@ -144,7 +153,7 @@ public class API { .lastLogin(0) .realName(playerName) .build(); - return instance.getDataSource().saveAuth(auth); + return dataSource.saveAuth(auth); } /** @@ -154,7 +163,7 @@ public class API { */ @Deprecated public static void forceLogin(Player player) { - instance.getManagement().performLogin(player, "dontneed", true); + management.performLogin(player, "dontneed", true); } @Deprecated diff --git a/src/main/java/fr/xephi/authme/api/NewAPI.java b/src/main/java/fr/xephi/authme/api/NewAPI.java index 0f5437623..d3a0b7d8c 100644 --- a/src/main/java/fr/xephi/authme/api/NewAPI.java +++ b/src/main/java/fr/xephi/authme/api/NewAPI.java @@ -12,6 +12,8 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.util.Utils; +import javax.inject.Inject; + /** * The current API of AuthMe. Recommended method of retrieving the API object: * @@ -28,6 +30,7 @@ public class NewAPI { * * @param plugin The AuthMe plugin instance */ + @Inject public NewAPI(AuthMe plugin) { this.plugin = plugin; } diff --git a/src/main/java/fr/xephi/authme/command/CommandHandler.java b/src/main/java/fr/xephi/authme/command/CommandHandler.java index 37c1a2a16..5f7b32af5 100644 --- a/src/main/java/fr/xephi/authme/command/CommandHandler.java +++ b/src/main/java/fr/xephi/authme/command/CommandHandler.java @@ -1,14 +1,14 @@ package fr.xephi.authme.command; -import java.util.ArrayList; -import java.util.List; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.command.help.HelpProvider; +import fr.xephi.authme.util.StringUtils; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -import fr.xephi.authme.util.StringUtils; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; /** * The AuthMe command handler, responsible for mapping incoming commands to the correct {@link CommandDescription} @@ -29,6 +29,7 @@ public class CommandHandler { * * @param commandService The CommandService instance */ + @Inject public CommandHandler(CommandService commandService) { this.commandService = commandService; } diff --git a/src/main/java/fr/xephi/authme/command/CommandMapper.java b/src/main/java/fr/xephi/authme/command/CommandMapper.java index e43ee4bb1..18473b281 100644 --- a/src/main/java/fr/xephi/authme/command/CommandMapper.java +++ b/src/main/java/fr/xephi/authme/command/CommandMapper.java @@ -1,11 +1,13 @@ package fr.xephi.authme.command; import fr.xephi.authme.command.executable.HelpCommand; +import fr.xephi.authme.initialization.BaseCommands; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.StringUtils; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -28,7 +30,8 @@ public class CommandMapper { private final Set baseCommands; private final PermissionsManager permissionsManager; - public CommandMapper(Set baseCommands, PermissionsManager permissionsManager) { + @Inject + public CommandMapper(@BaseCommands Set baseCommands, PermissionsManager permissionsManager) { this.baseCommands = baseCommands; this.permissionsManager = permissionsManager; } diff --git a/src/main/java/fr/xephi/authme/command/CommandService.java b/src/main/java/fr/xephi/authme/command/CommandService.java index 5c5abfcc4..d42629449 100644 --- a/src/main/java/fr/xephi/authme/command/CommandService.java +++ b/src/main/java/fr/xephi/authme/command/CommandService.java @@ -19,6 +19,7 @@ import fr.xephi.authme.util.ValidationService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import javax.inject.Inject; import java.util.Collection; import java.util.List; @@ -28,39 +29,30 @@ import java.util.List; */ public class CommandService { - private final AuthMe authMe; - private final Messages messages; - private final HelpProvider helpProvider; - private final CommandMapper commandMapper; - private final PasswordSecurity passwordSecurity; - private final PermissionsManager permissionsManager; - private final NewSetting settings; - private final PluginHooks pluginHooks; - private final SpawnLoader spawnLoader; - private final AntiBot antiBot; - private final ValidationService validationService; - private final BukkitService bukkitService; - - /* - * Constructor. - */ - public CommandService(AuthMe authMe, CommandMapper commandMapper, HelpProvider helpProvider, Messages messages, - PasswordSecurity passwordSecurity, PermissionsManager permissionsManager, NewSetting settings, - PluginHooks pluginHooks, SpawnLoader spawnLoader, AntiBot antiBot, - ValidationService validationService, BukkitService bukkitService) { - this.authMe = authMe; - this.messages = messages; - this.helpProvider = helpProvider; - this.commandMapper = commandMapper; - this.passwordSecurity = passwordSecurity; - this.permissionsManager = permissionsManager; - this.settings = settings; - this.pluginHooks = pluginHooks; - this.spawnLoader = spawnLoader; - this.antiBot = antiBot; - this.validationService = validationService; - this.bukkitService = bukkitService; - } + @Inject + private AuthMe authMe; + @Inject + private Messages messages; + @Inject + private HelpProvider helpProvider; + @Inject + private CommandMapper commandMapper; + @Inject + private PasswordSecurity passwordSecurity; + @Inject + private PermissionsManager permissionsManager; + @Inject + private NewSetting settings; + @Inject + private PluginHooks pluginHooks; + @Inject + private SpawnLoader spawnLoader; + @Inject + private AntiBot antiBot; + @Inject + private ValidationService validationService; + @Inject + private BukkitService bukkitService; /** * Send a message to a player. diff --git a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java b/src/main/java/fr/xephi/authme/command/help/HelpProvider.java index 7bc06f03d..c5a3ec5f8 100644 --- a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java +++ b/src/main/java/fr/xephi/authme/command/help/HelpProvider.java @@ -10,10 +10,13 @@ import fr.xephi.authme.command.FoundCommandResult; import fr.xephi.authme.permission.DefaultPermission; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.util.CollectionUtils; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.ArrayList; import java.util.List; @@ -45,9 +48,10 @@ public class HelpProvider { private final PermissionsManager permissionsManager; private final String helpHeader; - public HelpProvider(PermissionsManager permissionsManager, String helpHeader) { + @Inject + public HelpProvider(PermissionsManager permissionsManager, NewSetting settings) { this.permissionsManager = permissionsManager; - this.helpHeader = helpHeader; + this.helpHeader = settings.getProperty(PluginSettings.HELP_HEADER); } public List printHelp(CommandSender sender, FoundCommandResult result, int options) { diff --git a/src/main/java/fr/xephi/authme/hooks/PluginHooks.java b/src/main/java/fr/xephi/authme/hooks/PluginHooks.java index 3a54709ad..76f8e0347 100644 --- a/src/main/java/fr/xephi/authme/hooks/PluginHooks.java +++ b/src/main/java/fr/xephi/authme/hooks/PluginHooks.java @@ -11,6 +11,7 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; +import javax.inject.Inject; import java.io.File; /** @@ -28,6 +29,7 @@ public class PluginHooks { * * @param pluginManager The server's plugin manager */ + @Inject public PluginHooks(PluginManager pluginManager) { this.pluginManager = pluginManager; tryHookToCombatPlus(); @@ -75,13 +77,23 @@ public class PluginHooks { return null; } + /** + * Checks whether the player is an NPC. + * + * @param player The player to process + * @return True if player is NPC, false otherwise + */ + public boolean isNpc(Player player) { + return player.hasMetadata("NPC") || isNpcInCombatTagPlus(player); + } + /** * Query the CombatTagPlus plugin whether the given player is an NPC. * * @param player The player to verify * @return True if the player is an NPC according to CombatTagPlus, false if not or if the plugin is unavailable */ - public boolean isNpcInCombatTagPlus(Player player) { + private boolean isNpcInCombatTagPlus(Player player) { return combatTagPlus != null && combatTagPlus.getNpcPlayerHelper().isNpc(player); } diff --git a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java new file mode 100644 index 000000000..b751b32ed --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java @@ -0,0 +1,286 @@ +package fr.xephi.authme.initialization; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; + +import javax.annotation.PostConstruct; +import javax.inject.Provider; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Dependency injector of AuthMe: initializes and injects services and tasks. + *

+ * Only constructor and field injection are supported, indicated with the JSR330 + * {@link javax.inject.Inject @Inject} annotation. + *

+ * {@link PostConstruct @PostConstruct} methods are recognized and invoked upon + * instantiation. Note that the parent classes are not scanned for such methods. + */ +public class AuthMeServiceInitializer { + + private final Set ALLOWED_PACKAGES; + private final Map, Object> objects; + + /** + * Constructor. + * + * @param allowedPackages list of allowed packages. Only classes whose package + * starts with any of the given entries will be instantiated + */ + public AuthMeServiceInitializer(String... allowedPackages) { + ALLOWED_PACKAGES = ImmutableSet.copyOf(allowedPackages); + objects = new HashMap<>(); + } + + /** + * Retrieves or instantiates an object of the given type. + * + * @param clazz the class to retrieve the value for + * @param the class' type + * @return object of the class' type + */ + public T get(Class clazz) { + return get(clazz, new HashSet>()); + } + + /** + * Registers an instantiation by its type. + * + * @param object the object to register + * @throws IllegalStateException if an object of the same type has already been registered + */ + public void register(Object object) { + if (object instanceof Type) { + throw new IllegalStateException("You tried to register a Type object: '" + object + + "'. This likely indicates an error. Please use register(Class, T) if really desired."); + } + storeObject(object); + } + + /** + * Register an object with a custom class (supertype). Use this for example to specify a + * concrete implementation of an interface or an abstract class. + * + * @param clazz the class to register the object for + * @param object the object + * @param the class' type + */ + public void register(Class clazz, T object) { + if (objects.containsKey(clazz)) { + throw new IllegalStateException("There is already an object present for " + clazz); + } + Preconditions.checkNotNull(object); + objects.put(clazz, object); + } + + /** + * Associate an annotation with a value. + * + * @param annotation the annotation + * @param value the value + */ + public void provide(Class annotation, Object value) { + if (objects.containsKey(annotation)) { + throw new IllegalStateException("Annotation @" + annotation.getClass().getSimpleName() + + " already registered"); + } + Preconditions.checkNotNull(value); + objects.put(annotation, value); + } + + /** + * Returns an instance of the given class or the value associated with an annotation, + * by retrieving it or by instantiating it if not yet present. + * + * @param clazz the class to retrieve a value for + * @param traversedClasses the list of traversed classes + * @param the class' type + * @return instance or associated value (for annotations) + */ + private T get(Class clazz, Set> traversedClasses) { + if (Annotation.class.isAssignableFrom(clazz)) { + throw new UnsupportedOperationException("Cannot retrieve annotated elements in this way!"); + } else if (objects.containsKey(clazz)) { + return getObject(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. + validatePackage(clazz); + validateInstantiable(clazz); + + traversedClasses = new HashSet<>(traversedClasses); + traversedClasses.add(clazz); + System.out.println(Strings.repeat(" ", traversedClasses.size() * 2) + "- Instantiating " + clazz); + return instantiate(clazz, traversedClasses); + } + + /** + * Instantiates the given class by locating an @Inject constructor and retrieving + * or instantiating its parameters. + * + * @param clazz the class to instantiate + * @param traversedClasses collection of classes already traversed + * @param the class' type + * @return the instantiated object + */ + private T instantiate(Class clazz, Set> traversedClasses) { + Injection injection = firstNotNull(ConstructorInjection.provide(clazz), FieldInjection.provide(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 " + + "the default constructor"); + } + + validateInjectionHasNoCircularDependencies(injection.getDependencies(), traversedClasses); + Object[] dependencies = resolveDependencies(injection, traversedClasses); + T object = injection.instantiateWith(dependencies); + storeObject(object); + executePostConstructMethods(object); + return object; + } + + /** + * Resolves the dependencies for the given constructor, i.e. returns a collection that satisfy + * the constructor's parameter types by retrieving elements or instantiating them where necessary. + * + * @param injection the injection parameters + * @param traversedClasses collection of traversed classes + * @return array with the parameters to use in the constructor + */ + private Object[] resolveDependencies(Injection injection, Set> traversedClasses) { + Class[] dependencies = injection.getDependencies(); + Class[] annotations = injection.getDependencyAnnotations(); + Object[] values = new Object[dependencies.length]; + for (int i = 0; i < dependencies.length; ++i) { + if (annotations[i] != null) { + Object value = objects.get(annotations[i]); + if (value == null) { + throw new IllegalStateException("Value for field with @" + annotations[i].getSimpleName() + + " must be registered beforehand"); + } + values[i] = value; + } else { + values[i] = get(dependencies[i], traversedClasses); + } + } + 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. + * + * @param object the object to store + */ + private void storeObject(Object object) { + if (objects.containsKey(object.getClass())) { + throw new IllegalStateException("There is already an object present for " + object.getClass()); + } + Preconditions.checkNotNull(object); + objects.put(object.getClass(), object); + } + + /** + * Validates that none of the dependencies' types are present in the given collection + * of traversed classes. This prevents circular dependencies. + * + * @param dependencies the dependencies of the class + * @param traversedClasses the collection of traversed classes + */ + private static void validateInjectionHasNoCircularDependencies(Class[] dependencies, + Set> traversedClasses) { + for (Class clazz : dependencies) { + if (traversedClasses.contains(clazz)) { + throw new IllegalStateException("Found cyclic dependency - already traversed '" + clazz + + "' (full traversal list: " + traversedClasses + ")"); + } + } + } + + /** + * Validates the package of a parameter type to ensure that it is part of the allowed packages. + * This ensures that we don't try to instantiate things that are beyond our reach in case some + * external parameter type has not been registered. + * + * @param clazz the class to validate + */ + private void validatePackage(Class clazz) { + if (clazz.getPackage() == null) { + throw new IllegalStateException("Primitive types must be provided explicitly (or use an annotation)."); + } + String packageName = clazz.getPackage().getName(); + for (String allowedPackage : ALLOWED_PACKAGES) { + if (packageName.startsWith(allowedPackage)) { + return; + } + } + throw new IllegalStateException("Class " + clazz + " with package " + packageName + " is outside of the " + + "allowed packages. It must be provided explicitly or the package must be passed to the constructor."); + } + + private static void executePostConstructMethods(Object object) { + for (Method method : object.getClass().getDeclaredMethods()) { + if (method.isAnnotationPresent(PostConstruct.class)) { + if (method.getParameterCount() == 0) { + try { + method.setAccessible(true); + method.invoke(object); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new UnsupportedOperationException(e); + } + } else { + throw new IllegalStateException("@PostConstruct methods must have an empty parameter list. " + + "Found parameters in " + method + " belonging to " + object.getClass()); + } + } + } + } + + private static void validateInstantiable(Class clazz) { + if (clazz.isEnum() || clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) { + throw new IllegalStateException("Class " + clazz.getSimpleName() + " cannot be instantiated"); + } + } + + @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/BaseCommands.java b/src/main/java/fr/xephi/authme/initialization/BaseCommands.java new file mode 100644 index 000000000..156cb947c --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/BaseCommands.java @@ -0,0 +1,14 @@ +package fr.xephi.authme.initialization; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to denote the collection of AuthMe commands. + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface BaseCommands { +} diff --git a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java new file mode 100644 index 000000000..f77ae58cd --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java @@ -0,0 +1,77 @@ +package fr.xephi.authme.initialization; + +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) { + 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 + */ + @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; + } + +} diff --git a/src/main/java/fr/xephi/authme/initialization/DataFolder.java b/src/main/java/fr/xephi/authme/initialization/DataFolder.java new file mode 100644 index 000000000..33b879977 --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/DataFolder.java @@ -0,0 +1,14 @@ +package fr.xephi.authme.initialization; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for specifying the plugin's data folder. + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface DataFolder { +} diff --git a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java new file mode 100644 index 000000000..aafb0bde3 --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java @@ -0,0 +1,112 @@ +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.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 { + fields[i].set(instance, values[i]); + } catch (IllegalAccessException e) { + throw new UnsupportedOperationException(e); + } + } + return instance; + } + + private static Class getFirstNonInjectAnnotation(Field field) { + for (Annotation annotation : field.getAnnotations()) { + if (annotation.annotationType() != Inject.class) { + return annotation.annotationType(); + } + } + return null; + } + + 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 == null ? 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)) { + field.setAccessible(true); + fields.add(field); + } + } + return fields; + } + + 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 new file mode 100644 index 000000000..6e85b4ce5 --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/Injection.java @@ -0,0 +1,36 @@ +package fr.xephi.authme.initialization; + +/** + * Common interface for all injection methods. + * + * @param the type of the concerned object + */ +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/MetricsStarter.java b/src/main/java/fr/xephi/authme/initialization/MetricsStarter.java similarity index 93% rename from src/main/java/fr/xephi/authme/MetricsStarter.java rename to src/main/java/fr/xephi/authme/initialization/MetricsStarter.java index eac9ebaf4..4a163f98a 100644 --- a/src/main/java/fr/xephi/authme/MetricsStarter.java +++ b/src/main/java/fr/xephi/authme/initialization/MetricsStarter.java @@ -1,5 +1,7 @@ -package fr.xephi.authme; +package fr.xephi.authme.initialization; +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.PluginSettings; diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java index a17eacd58..97383c538 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java @@ -18,6 +18,7 @@ import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.Management; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -53,6 +54,7 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerShearEntityEvent; +import javax.inject.Inject; import java.util.concurrent.ConcurrentHashMap; import static fr.xephi.authme.listener.ListenerService.shouldCancelEvent; @@ -67,24 +69,22 @@ public class AuthMePlayerListener implements Listener { public static final ConcurrentHashMap joinMessage = new ConcurrentHashMap<>(); public static final ConcurrentHashMap causeByAuthMe = new ConcurrentHashMap<>(); - private final AuthMe plugin; - private final NewSetting settings; - private final Messages m; - private final DataSource dataSource; - private final AntiBot antiBot; - private final Management management; - private final BukkitService bukkitService; - - public AuthMePlayerListener(AuthMe plugin, NewSetting settings, Messages messages, DataSource dataSource, - AntiBot antiBot, Management management, BukkitService bukkitService) { - this.plugin = plugin; - this.settings = settings; - this.m = messages; - this.dataSource = dataSource; - this.antiBot = antiBot; - this.management = management; - this.bukkitService = bukkitService; - } + @Inject + private AuthMe plugin; + @Inject + private NewSetting settings; + @Inject + private Messages m; + @Inject + private DataSource dataSource; + @Inject + private AntiBot antiBot; + @Inject + private Management management; + @Inject + private BukkitService bukkitService; + @Inject + private SpawnLoader spawnLoader; private void handleChat(AsyncPlayerChatEvent event) { if (settings.getProperty(RestrictionSettings.ALLOW_CHAT)) { @@ -201,7 +201,7 @@ public class AuthMePlayerListener implements Listener { return; } - Location spawn = plugin.getSpawnLocation(player); + Location spawn = spawnLoader.getSpawnLocation(player); if (spawn != null && spawn.getWorld() != null) { if (!player.getWorld().equals(spawn.getWorld())) { player.teleport(spawn); @@ -517,7 +517,7 @@ public class AuthMePlayerListener implements Listener { Player player = event.getPlayer(); String name = player.getName().toLowerCase(); - Location spawn = plugin.getSpawnLocation(player); + Location spawn = spawnLoader.getSpawnLocation(player); if (Settings.isSaveQuitLocationEnabled && dataSource.isAuthAvailable(name)) { PlayerAuth auth = PlayerAuth.builder() .name(name) diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java index ec8326b56..54df067ec 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java @@ -15,21 +15,20 @@ import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.event.server.ServerListPingEvent; +import javax.inject.Inject; + /** */ public class AuthMeServerListener implements Listener { - private final AuthMe plugin; - private final Messages messages; - private final PluginHooks pluginHooks; - private final SpawnLoader spawnLoader; - - public AuthMeServerListener(AuthMe plugin, Messages messages, PluginHooks pluginHooks, SpawnLoader spawnLoader) { - this.plugin = plugin; - this.messages = messages; - this.pluginHooks = pluginHooks; - this.spawnLoader = spawnLoader; - } + @Inject + private AuthMe plugin; + @Inject + private Messages messages; + @Inject + private PluginHooks pluginHooks; + @Inject + private SpawnLoader spawnLoader; @EventHandler(priority = EventPriority.HIGHEST) public void onServerPing(ServerListPingEvent event) { diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeTabCompletePacketAdapter.java b/src/main/java/fr/xephi/authme/listener/AuthMeTabCompletePacketAdapter.java index d82669858..ae6c47bcb 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeTabCompletePacketAdapter.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeTabCompletePacketAdapter.java @@ -11,8 +11,11 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerCache; +import javax.inject.Inject; + public class AuthMeTabCompletePacketAdapter extends PacketAdapter { + @Inject public AuthMeTabCompletePacketAdapter(AuthMe plugin) { super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE); } diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java b/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java index a23ce5c18..d13668d50 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java @@ -19,6 +19,7 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.util.BukkitService; import org.bukkit.entity.Player; +import javax.inject.Inject; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.logging.Level; @@ -27,6 +28,7 @@ public class AuthMeTablistPacketAdapter extends PacketAdapter { private final BukkitService bukkitService; + @Inject public AuthMeTablistPacketAdapter(AuthMe plugin, BukkitService bukkitService) { super(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO); this.bukkitService = bukkitService; diff --git a/src/main/java/fr/xephi/authme/ImageGenerator.java b/src/main/java/fr/xephi/authme/mail/ImageGenerator.java similarity index 88% rename from src/main/java/fr/xephi/authme/ImageGenerator.java rename to src/main/java/fr/xephi/authme/mail/ImageGenerator.java index e529efabd..77d989015 100644 --- a/src/main/java/fr/xephi/authme/ImageGenerator.java +++ b/src/main/java/fr/xephi/authme/mail/ImageGenerator.java @@ -1,6 +1,9 @@ -package fr.xephi.authme; +package fr.xephi.authme.mail; -import java.awt.*; +import java.awt.Color; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics2D; import java.awt.image.BufferedImage; /** diff --git a/src/main/java/fr/xephi/authme/mail/SendMailSSL.java b/src/main/java/fr/xephi/authme/mail/SendMailSSL.java index c80001c34..d83326026 100644 --- a/src/main/java/fr/xephi/authme/mail/SendMailSSL.java +++ b/src/main/java/fr/xephi/authme/mail/SendMailSSL.java @@ -2,7 +2,6 @@ package fr.xephi.authme.mail; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ImageGenerator; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.EmailSettings; diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java index ee27f82cc..7dfcd40c4 100644 --- a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java +++ b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java @@ -2,6 +2,7 @@ package fr.xephi.authme.permission; import de.bananaco.bpermissions.api.ApiLayer; import de.bananaco.bpermissions.api.CalculableType; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.util.CollectionUtils; import net.milkbowl.vault.permission.Permission; @@ -20,11 +21,12 @@ import org.tyrannyofheaven.bukkit.zPermissions.ZPermissionsService; import ru.tehkode.permissions.PermissionUser; import ru.tehkode.permissions.bukkit.PermissionsEx; +import javax.annotation.PostConstruct; +import javax.inject.Inject; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.logging.Logger; /** *

@@ -48,10 +50,7 @@ public class PermissionsManager implements PermissionsService { * Server instance. */ private final Server server; - /** - * Logger instance. - */ - private Logger log; + private final PluginManager pluginManager; /** * Type of permissions system that is currently used. * Null if no permissions system is hooked and/or used. @@ -70,11 +69,11 @@ public class PermissionsManager implements PermissionsService { * Constructor. * * @param server Server instance - * @param log Logger */ - public PermissionsManager(Server server, Logger log) { + @Inject + public PermissionsManager(Server server, PluginManager pluginManager) { this.server = server; - this.log = log; + this.pluginManager = pluginManager; } /** @@ -100,39 +99,37 @@ public class PermissionsManager implements PermissionsService { * * @return The detected permissions system. */ + @PostConstruct public PermissionsSystemType setup() { // Force-unhook from current hooked permissions systems unhook(); - // Define the plugin manager - final PluginManager pluginManager = this.server.getPluginManager(); - // Reset used permissions system type flag permsType = null; // Loop through all the available permissions system types - for(PermissionsSystemType type : PermissionsSystemType.values()) { + for (PermissionsSystemType type : PermissionsSystemType.values()) { // Try to find and hook the current plugin if available, print an error if failed try { // Try to find the plugin for the current permissions system Plugin plugin = pluginManager.getPlugin(type.getPluginName()); // Make sure a plugin with this name was found - if(plugin == null) + if (plugin == null) continue; // Make sure the plugin is enabled before hooking - if(!plugin.isEnabled()) { - this.log.info("Not hooking into " + type.getName() + " because it's disabled!"); + if (!plugin.isEnabled()) { + ConsoleLogger.info("Not hooking into " + type.getName() + " because it's disabled!"); continue; } // Use the proper method to hook this plugin - switch(type) { + switch (type) { case PERMISSIONS_EX: // Get the permissions manager for PermissionsEx and make sure it isn't null - if(PermissionsEx.getPermissionManager() == null) { - this.log.info("Failed to hook into " + type.getName() + "!"); + if (PermissionsEx.getPermissionManager() == null) { + ConsoleLogger.info("Failed to hook into " + type.getName() + "!"); continue; } @@ -146,8 +143,8 @@ public class PermissionsManager implements PermissionsService { case Z_PERMISSIONS: // Set the zPermissions service and make sure it's valid zPermissionsService = Bukkit.getServicesManager().load(ZPermissionsService.class); - if(zPermissionsService == null) { - this.log.info("Failed to hook into " + type.getName() + "!"); + if (zPermissionsService == null) { + ConsoleLogger.info("Failed to hook into " + type.getName() + "!"); continue; } @@ -157,14 +154,14 @@ public class PermissionsManager implements PermissionsService { // Get the permissions provider service RegisteredServiceProvider permissionProvider = this.server.getServicesManager().getRegistration(Permission.class); if (permissionProvider == null) { - this.log.info("Failed to hook into " + type.getName() + "!"); + ConsoleLogger.info("Failed to hook into " + type.getName() + "!"); continue; } // Get the Vault provider and make sure it's valid vaultPerms = permissionProvider.getProvider(); - if(vaultPerms == null) { - this.log.info("Not using " + type.getName() + " because it's disabled!"); + if (vaultPerms == null) { + ConsoleLogger.info("Not using " + type.getName() + " because it's disabled!"); continue; } @@ -177,19 +174,19 @@ public class PermissionsManager implements PermissionsService { this.permsType = type; // Show a success message - this.log.info("Hooked into " + type.getName() + "!"); + ConsoleLogger.info("Hooked into " + type.getName() + "!"); // Return the used permissions system type return type; } catch (Exception ex) { // An error occurred, show a warning message - this.log.info("Error while hooking into " + type.getName() + "!"); + ConsoleLogger.logException("Error while hooking into " + type.getName(), ex); } } // No recognized permissions system found, show a message and return - this.log.info("No supported permissions system found! Permissions are disabled!"); + ConsoleLogger.info("No supported permissions system found! Permissions are disabled!"); return null; } @@ -201,7 +198,7 @@ public class PermissionsManager implements PermissionsService { this.permsType = null; // Print a status message to the console - this.log.info("Unhooked from Permissions!"); + ConsoleLogger.info("Unhooked from Permissions!"); } /** @@ -232,7 +229,7 @@ public class PermissionsManager implements PermissionsService { if (pluginName.equals("PermissionsEx") || pluginName.equals("PermissionsBukkit") || pluginName.equals("bPermissions") || pluginName.equals("GroupManager") || pluginName.equals("zPermissions") || pluginName.equals("Vault")) { - this.log.info(pluginName + " plugin enabled, dynamically updating permissions hooks!"); + ConsoleLogger.info(pluginName + " plugin enabled, dynamically updating permissions hooks!"); setup(); } } @@ -251,7 +248,7 @@ public class PermissionsManager implements PermissionsService { if (pluginName.equals("PermissionsEx") || pluginName.equals("PermissionsBukkit") || pluginName.equals("bPermissions") || pluginName.equals("GroupManager") || pluginName.equals("zPermissions") || pluginName.equals("Vault")) { - this.log.info(pluginName + " plugin disabled, updating hooks!"); + ConsoleLogger.info(pluginName + " plugin disabled, updating hooks!"); setup(); } } diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index 4b279c9a1..04a0f414b 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -3,6 +3,7 @@ package fr.xephi.authme.process; import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.process.email.AsyncAddEmail; import fr.xephi.authme.process.email.AsyncChangeEmail; import fr.xephi.authme.process.join.AsynchronousJoin; @@ -14,23 +15,26 @@ import fr.xephi.authme.process.unregister.AsynchronousUnregister; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitScheduler; +import javax.inject.Inject; + /** */ public class Management { - private final AuthMe plugin; - private final BukkitScheduler sched; - private final ProcessService processService; - private final DataSource dataSource; - private final PlayerCache playerCache; + @Inject + private AuthMe plugin; + @Inject + private BukkitScheduler sched; + @Inject + private ProcessService processService; + @Inject + private DataSource dataSource; + @Inject + private PlayerCache playerCache; + @Inject + private PluginHooks pluginHooks; - public Management(AuthMe plugin, ProcessService processService, DataSource dataSource, PlayerCache playerCache) { - this.plugin = plugin; - this.sched = this.plugin.getServer().getScheduler(); - this.processService = processService; - this.dataSource = dataSource; - this.playerCache = playerCache; - } + Management() { } public void performLogin(final Player player, final String password, final boolean forceLogin) { runTask(new AsynchronousLogin(player, password, forceLogin, plugin, dataSource, processService)); @@ -49,7 +53,7 @@ public class Management { } public void performJoin(final Player player) { - runTask(new AsynchronousJoin(player, plugin, dataSource, playerCache, processService)); + runTask(new AsynchronousJoin(player, plugin, dataSource, playerCache, pluginHooks, processService)); } public void performQuit(final Player player, final boolean isKick) { diff --git a/src/main/java/fr/xephi/authme/process/ProcessService.java b/src/main/java/fr/xephi/authme/process/ProcessService.java index 653be1565..a338cad42 100644 --- a/src/main/java/fr/xephi/authme/process/ProcessService.java +++ b/src/main/java/fr/xephi/authme/process/ProcessService.java @@ -17,6 +17,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.scheduler.BukkitTask; +import javax.inject.Inject; import java.util.Collection; /** @@ -24,29 +25,24 @@ import java.util.Collection; */ public class ProcessService { - private final NewSetting settings; - private final Messages messages; - private final AuthMe authMe; - private final DataSource dataSource; - private final PasswordSecurity passwordSecurity; - private final PluginHooks pluginHooks; - private final SpawnLoader spawnLoader; - private final ValidationService validationService; - private final BukkitService bukkitService; - - public ProcessService(NewSetting settings, Messages messages, AuthMe authMe, DataSource dataSource, - PasswordSecurity passwordSecurity, PluginHooks pluginHooks, SpawnLoader spawnLoader, - ValidationService validationService, BukkitService bukkitService) { - this.settings = settings; - this.messages = messages; - this.authMe = authMe; - this.dataSource = dataSource; - this.passwordSecurity = passwordSecurity; - this.pluginHooks = pluginHooks; - this.spawnLoader = spawnLoader; - this.validationService = validationService; - this.bukkitService = bukkitService; - } + @Inject + private NewSetting settings; + @Inject + private Messages messages; + @Inject + private AuthMe authMe; + @Inject + private DataSource dataSource; + @Inject + private PasswordSecurity passwordSecurity; + @Inject + private PluginHooks pluginHooks; + @Inject + private SpawnLoader spawnLoader; + @Inject + private ValidationService validationService; + @Inject + private BukkitService bukkitService; /** * Retrieve a property's value. diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index 37b0dfbd3..c9ab7159a 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -9,6 +9,7 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.FirstSpawnTeleportEvent; import fr.xephi.authme.events.ProtectInventoryEvent; import fr.xephi.authme.events.SpawnTeleportEvent; +import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.listener.AuthMePlayerListener; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.permission.PlayerStatePermission; @@ -45,18 +46,20 @@ public class AsynchronousJoin implements Process { private final String name; private final ProcessService service; private final PlayerCache playerCache; + private final PluginHooks pluginHooks; private final boolean disableCollisions = MethodUtils .getAccessibleMethod(LivingEntity.class, "setCollidable", new Class[]{}) != null; public AsynchronousJoin(Player player, AuthMe plugin, DataSource database, PlayerCache playerCache, - ProcessService service) { + PluginHooks pluginHooks, ProcessService service) { this.player = player; this.plugin = plugin; this.database = database; this.name = player.getName().toLowerCase(); this.service = service; this.playerCache = playerCache; + this.pluginHooks = pluginHooks; } @Override @@ -190,7 +193,7 @@ public class AsynchronousJoin implements Process { player.setWalkSpeed(0.0f); } player.setNoDamageTicks(registrationTimeout); - if (plugin.getPluginHooks().isEssentialsAvailable() && service.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)) { + if (pluginHooks.isEssentialsAvailable() && service.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)) { player.performCommand("motd"); } if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java index 94a8abeb4..458c99699 100644 --- a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java +++ b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java @@ -8,6 +8,7 @@ import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.plugin.PluginManager; +import javax.inject.Inject; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -22,6 +23,7 @@ public class PasswordSecurity { private final DataSource dataSource; private final PluginManager pluginManager; + @Inject public PasswordSecurity(DataSource dataSource, NewSetting settings, PluginManager pluginManager) { this.settings = settings; this.algorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); diff --git a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java index 59693a98c..a046d813d 100644 --- a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java +++ b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java @@ -4,6 +4,7 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.StringUtils; @@ -14,6 +15,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; +import javax.inject.Inject; import java.io.File; import java.io.IOException; @@ -40,7 +42,8 @@ public class SpawnLoader { * @param settings The setting instance * @param pluginHooks The plugin hooks instance */ - public SpawnLoader(File pluginFolder, NewSetting settings, PluginHooks pluginHooks) { + @Inject + public SpawnLoader(@DataFolder File pluginFolder, NewSetting settings, PluginHooks pluginHooks) { File spawnFile = new File(pluginFolder, "spawn.yml"); // TODO ljacqu 20160312: Check if resource could be copied and handle the case if not FileUtils.copyFileFromResource(spawnFile, "spawn.yml"); diff --git a/src/main/java/fr/xephi/authme/util/BukkitService.java b/src/main/java/fr/xephi/authme/util/BukkitService.java index ea405d807..592caa228 100644 --- a/src/main/java/fr/xephi/authme/util/BukkitService.java +++ b/src/main/java/fr/xephi/authme/util/BukkitService.java @@ -7,6 +7,7 @@ import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; +import javax.inject.Inject; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; @@ -28,6 +29,7 @@ public class BukkitService { private final boolean getOnlinePlayersIsCollection; private Method getOnlinePlayers; + @Inject public BukkitService(AuthMe authMe) { this.authMe = authMe; getOnlinePlayersIsCollection = initializeOnlinePlayersIsCollectionField(); diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index 5850645bd..5d8c08eea 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -165,8 +165,9 @@ public final class Utils { }); } + @Deprecated public static boolean isNPC(Player player) { - return player.hasMetadata("NPC") || plugin.getPluginHooks().isNpcInCombatTagPlus(player); + return plugin.getPluginHooks().isNpc(player); } public static void teleportToSpawn(Player player) { diff --git a/src/main/java/fr/xephi/authme/util/ValidationService.java b/src/main/java/fr/xephi/authme/util/ValidationService.java index 7f8b78b7a..815f493f3 100644 --- a/src/main/java/fr/xephi/authme/util/ValidationService.java +++ b/src/main/java/fr/xephi/authme/util/ValidationService.java @@ -10,6 +10,7 @@ import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.Collection; import java.util.List; @@ -22,6 +23,7 @@ public class ValidationService { private final DataSource dataSource; private final PermissionsManager permissionsManager; + @Inject public ValidationService(NewSetting settings, DataSource dataSource, PermissionsManager permissionsManager) { this.settings = settings; this.dataSource = dataSource; diff --git a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java index 89a35571f..a1ff5e22f 100644 --- a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java @@ -18,10 +18,10 @@ import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.ValidationService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -41,6 +41,7 @@ import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class CommandServiceTest { + @InjectMocks private CommandService commandService; @Mock private AuthMe authMe; @@ -67,12 +68,6 @@ public class CommandServiceTest { @Mock private BukkitService bukkitService; - @Before - public void setUpService() { - commandService = new CommandService(authMe, commandMapper, helpProvider, messages, passwordSecurity, - permissionsManager, settings, pluginHooks, spawnLoader, antiBot, validationService, bukkitService); - } - @Test public void shouldSendMessage() { // given diff --git a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java index 3ec47a4f4..e7d85f146 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java @@ -6,6 +6,8 @@ import fr.xephi.authme.command.FoundResultStatus; import fr.xephi.authme.command.TestCommandsUtil; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerPermission; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.PluginSettings; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.junit.Before; @@ -52,7 +54,9 @@ public class HelpProviderTest { @Before public void setUpHelpProvider() { permissionsManager = mock(PermissionsManager.class); - helpProvider = new HelpProvider(permissionsManager, HELP_HEADER); + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(PluginSettings.HELP_HEADER)).willReturn(HELP_HEADER); + helpProvider = new HelpProvider(permissionsManager, settings); sender = mock(CommandSender.class); } diff --git a/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java b/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java new file mode 100644 index 000000000..613222919 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java @@ -0,0 +1,194 @@ +package fr.xephi.authme.initialization; + +import fr.xephi.authme.initialization.samples.BadFieldInjection; +import fr.xephi.authme.initialization.samples.BetaManager; +import fr.xephi.authme.initialization.samples.CircularClasses; +import fr.xephi.authme.initialization.samples.ClassWithAbstractDependency; +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.InvalidClass; +import fr.xephi.authme.initialization.samples.InvalidPostConstruct; +import fr.xephi.authme.initialization.samples.PostConstructTestClass; +import fr.xephi.authme.initialization.samples.ProvidedClass; +import fr.xephi.authme.initialization.samples.Size; +import org.junit.Before; +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.junit.Assert.assertThat; + +/** + * Test for {@link AuthMeServiceInitializer}. + */ +public class AuthMeServiceInitializerTest { + + private static final String ALLOWED_PACKAGE = "fr.xephi.authme.initialization"; + + private AuthMeServiceInitializer initializer; + + @Before + public void setInitializer() { + initializer = new AuthMeServiceInitializer(ALLOWED_PACKAGE); + initializer.register(new ProvidedClass("")); + } + + @Test + public void shouldInitializeElements() { + // given / when + BetaManager betaManager = initializer.get(BetaManager.class); + + // then + assertThat(betaManager, not(nullValue())); + for (Object o : betaManager.getDependencies()) { + assertThat(o, not(nullValue())); + } + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowForInvalidPackage() { + // given / when / then + initializer.get(InvalidClass.class); + } + + @Test + public void shouldPassValueByAnnotation() { + // given + int size = 12; + long duration = -15482L; + initializer.provide(Size.class, size); + initializer.provide(Duration.class, duration); + + // when + ClassWithAnnotations object = initializer.get(ClassWithAnnotations.class); + + // then + assertThat(object, not(nullValue())); + assertThat(object.getSize(), equalTo(size)); + assertThat(object.getDuration(), equalTo(duration)); + // some sample check to make sure we only have one instance of GammaService + assertThat(object.getGammaService(), equalTo(initializer.get(BetaManager.class).getDependencies()[1])); + } + + @Test(expected = RuntimeException.class) + public void shouldRecognizeCircularReferences() { + // given / when / then + initializer.get(CircularClasses.Circular3.class); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForUnregisteredAnnotation() { + // given + initializer.provide(Size.class, 4523); + + // when / then + initializer.get(ClassWithAnnotations.class); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForFieldInjectionWithNoDefaultConstructor() { + // given / when / then + initializer.get(BadFieldInjection.class); + } + + @Test + public void shouldInjectFieldsWithAnnotationsProperly() { + // given + initializer.provide(Size.class, 2809375); + initializer.provide(Duration.class, 13095L); + + // when + FieldInjectionWithAnnotations result = initializer.get(FieldInjectionWithAnnotations.class); + + // then + assertThat(result.getSize(), equalTo(2809375)); + assertThat(result.getDuration(), equalTo(13095L)); + assertThat(result.getBetaManager(), not(nullValue())); + assertThat(result.getClassWithAnnotations(), not(nullValue())); + assertThat(result.getClassWithAnnotations().getGammaService(), + equalTo(result.getBetaManager().getDependencies()[1])); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForAnnotationAsKey() { + // given / when / then + initializer.get(Size.class); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForSecondRegistration() { + // given / when / then + initializer.register(new ProvidedClass("")); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForSecondAnnotationRegistration() { + // given + initializer.provide(Size.class, 12); + + // when / then + initializer.provide(Size.class, -8); + } + + @Test(expected = NullPointerException.class) + public void shouldThrowForNullValueAssociatedToAnnotation() { + // given / when / then + initializer.provide(Duration.class, null); + } + + @Test(expected = NullPointerException.class) + public void shouldThrowForRegisterWithNull() { + // given / when / then + initializer.register(String.class, null); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForRegisterOfType() { + // given / when / then + // this most likely means that the second argument was forgotten, so throw an error and force + // the API user to use the explicit register(Class.class, String.class) if really, really desired + // (Though for such generic types, an annotation would be a lot better) + initializer.register(String.class); + } + + @Test + public void shouldExecutePostConstructMethod() { + // given + initializer.provide(Size.class, 15123); + + // when + PostConstructTestClass testClass = initializer.get(PostConstructTestClass.class); + + // then + assertThat(testClass.werePostConstructsCalled(), equalTo(true)); + assertThat(testClass.getBetaManager(), not(nullValue())); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForInvalidPostConstructMethod() { + // given / when / then + initializer.get(InvalidPostConstruct.class); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForAbstractNonRegisteredDependency() { + // given / when / then + initializer.get(ClassWithAbstractDependency.class); + } + + @Test + public void shouldInstantiateWithImplementationOfAbstractDependency() { + // given + ClassWithAbstractDependency.ConcreteDependency concrete = new ClassWithAbstractDependency.ConcreteDependency(); + initializer.register(ClassWithAbstractDependency.AbstractDependency.class, concrete); + + // when + ClassWithAbstractDependency cwad = initializer.get(ClassWithAbstractDependency.class); + + // then + assertThat(cwad.getAbstractDependency() == concrete, equalTo(true)); + assertThat(cwad.getAlphaService(), not(nullValue())); + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/AlphaService.java b/src/test/java/fr/xephi/authme/initialization/samples/AlphaService.java new file mode 100644 index 000000000..2a93a43c3 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/AlphaService.java @@ -0,0 +1,20 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Sample - class with dependency to ProvidedClass. + */ +public class AlphaService { + + private ProvidedClass providedClass; + + @Inject + AlphaService(ProvidedClass providedClass) { + this.providedClass = providedClass; + } + + public ProvidedClass getProvidedClass() { + return providedClass; + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/BadFieldInjection.java b/src/test/java/fr/xephi/authme/initialization/samples/BadFieldInjection.java new file mode 100644 index 000000000..9dc415485 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/BadFieldInjection.java @@ -0,0 +1,16 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Sample class with invalid field injection (requires default constructor). + */ +public class BadFieldInjection { + + @Inject + private AlphaService alphaService; + + public BadFieldInjection(BetaManager betaManager) { + throw new IllegalStateException("Should never be called"); + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/BetaManager.java b/src/test/java/fr/xephi/authme/initialization/samples/BetaManager.java new file mode 100644 index 000000000..8c2fe923f --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/BetaManager.java @@ -0,0 +1,20 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Sample - depends on Provided, alpha and gamma. + */ +public class BetaManager { + + @Inject + private ProvidedClass providedClass; + @Inject + private GammaService gammaService; + @Inject + private AlphaService alphaService; + + public Object[] getDependencies() { + return new Object[]{providedClass, gammaService, alphaService}; + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/CircularClasses.java b/src/test/java/fr/xephi/authme/initialization/samples/CircularClasses.java new file mode 100644 index 000000000..e9a1460ea --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/CircularClasses.java @@ -0,0 +1,30 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Classes with circular dependencies. + */ +public class CircularClasses { + + public static final class Circular1 { + @Inject + public Circular1(AlphaService alphaService, Circular3 circular3) { + // -- + } + } + + public static final class Circular2 { + @Inject + public Circular2(Circular1 circular1) { + // -- + } + } + + public static final class Circular3 { + @Inject + public Circular3(Circular2 circular2, BetaManager betaManager) { + // -- + } + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/ClassWithAbstractDependency.java b/src/test/java/fr/xephi/authme/initialization/samples/ClassWithAbstractDependency.java new file mode 100644 index 000000000..25b651b32 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/ClassWithAbstractDependency.java @@ -0,0 +1,32 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Test with an abstract class declared as dependency. + */ +public class ClassWithAbstractDependency { + + private final AlphaService alphaService; + private final AbstractDependency abstractDependency; + + @Inject + public ClassWithAbstractDependency(AlphaService as, AbstractDependency ad) { + this.alphaService = as; + this.abstractDependency = ad; + } + + public AlphaService getAlphaService() { + return alphaService; + } + + public AbstractDependency getAbstractDependency() { + return abstractDependency; + } + + public static abstract class AbstractDependency { + } + + public static final class ConcreteDependency extends AbstractDependency { + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/ClassWithAnnotations.java b/src/test/java/fr/xephi/authme/initialization/samples/ClassWithAnnotations.java new file mode 100644 index 000000000..61dca71b8 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/ClassWithAnnotations.java @@ -0,0 +1,29 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +public class ClassWithAnnotations { + + private int size; + private GammaService gammaService; + private long duration; + + @Inject + ClassWithAnnotations(@Size int size, GammaService gammaService, @Duration long duration) { + this.size = size; + this.gammaService = gammaService; + this.duration = duration; + } + + public int getSize() { + return size; + } + + public GammaService getGammaService() { + return gammaService; + } + + public long getDuration() { + return duration; + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/Duration.java b/src/test/java/fr/xephi/authme/initialization/samples/Duration.java new file mode 100644 index 000000000..e61c6d28b --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/Duration.java @@ -0,0 +1,14 @@ +package fr.xephi.authme.initialization.samples; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Sample annotation. + */ +@Target({ElementType.PARAMETER, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Duration { +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/FieldInjectionWithAnnotations.java b/src/test/java/fr/xephi/authme/initialization/samples/FieldInjectionWithAnnotations.java new file mode 100644 index 000000000..65bdf537d --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/FieldInjectionWithAnnotations.java @@ -0,0 +1,40 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Sample - field injection, including custom annotations. + */ +public class FieldInjectionWithAnnotations { + + @Inject + private BetaManager betaManager; + @Inject + @Size + private int size; + @Duration + @Inject + private long duration; + @Inject + protected ClassWithAnnotations classWithAnnotations; + + FieldInjectionWithAnnotations() { + } + + public BetaManager getBetaManager() { + return betaManager; + } + + public int getSize() { + return size; + } + + public long getDuration() { + return duration; + } + + public ClassWithAnnotations getClassWithAnnotations() { + return classWithAnnotations; + } + +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java b/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java new file mode 100644 index 000000000..8ee970321 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java @@ -0,0 +1,20 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Sample - class dependent on alpha and provided. + */ +public class GammaService { + + private AlphaService alphaService; + + @Inject + GammaService(AlphaService alphaService) { + this.alphaService = alphaService; + } + + public AlphaService getAlphaService() { + return alphaService; + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/InvalidClass.java b/src/test/java/fr/xephi/authme/initialization/samples/InvalidClass.java new file mode 100644 index 000000000..b896af9d6 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/InvalidClass.java @@ -0,0 +1,14 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Sample - invalid class, since Integer parameter type is outside of the allowed package and not annotated. + */ +public class InvalidClass { + + @Inject + public InvalidClass(AlphaService alphaService, Integer i) { + throw new IllegalStateException("Should never be called"); + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java b/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java new file mode 100644 index 000000000..85c7f4c4f --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java @@ -0,0 +1,19 @@ +package fr.xephi.authme.initialization.samples; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +/** + * Class with invalid @PostConstruct method. + */ +public class InvalidPostConstruct { + + @Inject + private AlphaService alphaService; + @Inject + private ProvidedClass providedClass; + + @PostConstruct + public void invalidPostConstr(BetaManager betaManager) { + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/PostConstructTestClass.java b/src/test/java/fr/xephi/authme/initialization/samples/PostConstructTestClass.java new file mode 100644 index 000000000..a4e570324 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/PostConstructTestClass.java @@ -0,0 +1,37 @@ +package fr.xephi.authme.initialization.samples; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +/** + * Sample class for testing the execution of the @PostConstruct method. + */ +public class PostConstructTestClass { + + @Inject + @Size + private int size; + @Inject + private BetaManager betaManager; + private boolean wasPostConstructCalled = false; + private boolean wasSecondPostConstructCalled = false; + + @PostConstruct + protected void setFieldToTrue() { + wasPostConstructCalled = true; + } + + @PostConstruct + public int otherPostConstructMethod() { + wasSecondPostConstructCalled = true; + return 42; + } + + public boolean werePostConstructsCalled() { + return wasPostConstructCalled && wasSecondPostConstructCalled; + } + + public BetaManager getBetaManager() { + return betaManager; + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/ProvidedClass.java b/src/test/java/fr/xephi/authme/initialization/samples/ProvidedClass.java new file mode 100644 index 000000000..7ce81bd30 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/ProvidedClass.java @@ -0,0 +1,18 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Sample - class that is always provided to the initializer beforehand. + */ +public class ProvidedClass { + + @Inject + public ProvidedClass() { + throw new IllegalStateException("Should never be called (tests always provide this class)"); + } + + public ProvidedClass(String manualConstructor) { + } + +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/Size.java b/src/test/java/fr/xephi/authme/initialization/samples/Size.java new file mode 100644 index 000000000..320a7b7c5 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/Size.java @@ -0,0 +1,14 @@ +package fr.xephi.authme.initialization.samples; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Sample annotation. + */ +@Target({ElementType.PARAMETER, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Size { +} diff --git a/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java b/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java index ab935d029..f641b1f01 100644 --- a/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java +++ b/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java @@ -13,9 +13,9 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.ValidationService; import org.bukkit.command.CommandSender; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class ProcessServiceTest { + @InjectMocks private ProcessService processService; @Mock private ValidationService validationService; @@ -51,12 +52,6 @@ public class ProcessServiceTest { @Mock private BukkitService bukkitService; - @Before - public void setUpService() { - processService = new ProcessService(settings, messages, authMe, dataSource, passwordSecurity, - pluginHooks, spawnLoader, validationService, bukkitService); - } - @Test public void shouldGetProperty() { // given From 5963628fa618b1d5b90417a4c59580d24c4edb06 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Wed, 27 Apr 2016 22:59:44 +0200 Subject: [PATCH 2/8] #432 Add field injection to AccountsCommand --- src/main/java/fr/xephi/authme/AuthMe.java | 2 +- .../authme/command/CommandInitializer.java | 73 ++++++++++--------- .../executable/authme/AccountsCommand.java | 18 +++-- .../AuthMeServiceInitializer.java | 13 +++- .../command/CommandInitializerTest.java | 17 ++++- .../authme/AccountsCommandTest.java | 27 +++---- src/tools/commands/CommandPageCreater.java | 5 +- 7 files changed, 97 insertions(+), 58 deletions(-) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 812e88e9f..b69cd2990 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -256,7 +256,7 @@ public class AuthMe extends JavaPlugin { initializer.register(newSettings); initializer.register(messages); initializer.register(DataSource.class, database); - initializer.provide(BaseCommands.class, CommandInitializer.buildCommands()); + initializer.provide(BaseCommands.class, CommandInitializer.buildCommands(initializer)); // Some statically injected things initializer.register(PlayerCache.class, PlayerCache.getInstance()); diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index 6635545fe..14d72f4a4 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -33,6 +33,7 @@ import fr.xephi.authme.command.executable.login.LoginCommand; import fr.xephi.authme.command.executable.logout.LogoutCommand; import fr.xephi.authme.command.executable.register.RegisterCommand; import fr.xephi.authme.command.executable.unregister.UnregisterCommand; +import fr.xephi.authme.initialization.AuthMeServiceInitializer; import fr.xephi.authme.permission.AdminPermission; import fr.xephi.authme.permission.PlayerPermission; @@ -53,13 +54,13 @@ public final class CommandInitializer { // Helper class } - public static Set buildCommands() { + public static Set buildCommands(AuthMeServiceInitializer initializer) { // Register the base AuthMe Reloaded command final CommandDescription AUTHME_BASE = CommandDescription.builder() .labels("authme") .description("Main command") .detailedDescription("The main AuthMeReloaded command. The root for all admin commands.") - .executableCommand(new AuthMeCommand()) + .executableCommand(initializer.newInstance(AuthMeCommand.class)) .build(); // Register the register command @@ -71,7 +72,7 @@ public final class CommandInitializer { .withArgument("player", "Player name", false) .withArgument("password", "Password", false) .permissions(OP_ONLY, AdminPermission.REGISTER) - .executableCommand(new RegisterAdminCommand()) + .executableCommand(initializer.newInstance(RegisterAdminCommand.class)) .build(); // Register the unregister command @@ -82,7 +83,7 @@ public final class CommandInitializer { .detailedDescription("Unregister the specified player.") .withArgument("player", "Player name", false) .permissions(OP_ONLY, AdminPermission.UNREGISTER) - .executableCommand(new UnregisterAdminCommand()) + .executableCommand(initializer.newInstance(UnregisterAdminCommand.class)) .build(); // Register the forcelogin command @@ -93,7 +94,7 @@ public final class CommandInitializer { .detailedDescription("Enforce the specified player to login.") .withArgument("player", "Online player name", true) .permissions(OP_ONLY, AdminPermission.FORCE_LOGIN) - .executableCommand(new ForceLoginCommand()) + .executableCommand(initializer.newInstance(ForceLoginCommand.class)) .build(); // Register the changepassword command @@ -105,7 +106,7 @@ public final class CommandInitializer { .withArgument("player", "Player name", false) .withArgument("pwd", "New password", false) .permissions(OP_ONLY, AdminPermission.CHANGE_PASSWORD) - .executableCommand(new ChangePasswordAdminCommand()) + .executableCommand(initializer.newInstance(ChangePasswordAdminCommand.class)) .build(); // Register the last login command @@ -116,7 +117,7 @@ public final class CommandInitializer { .detailedDescription("View the date of the specified players last login.") .withArgument("player", "Player name", true) .permissions(OP_ONLY, AdminPermission.LAST_LOGIN) - .executableCommand(new LastLoginCommand()) + .executableCommand(initializer.newInstance(LastLoginCommand.class)) .build(); // Register the accounts command @@ -127,7 +128,7 @@ public final class CommandInitializer { .detailedDescription("Display all accounts of a player by his player name or IP.") .withArgument("player", "Player name or IP", true) .permissions(OP_ONLY, AdminPermission.ACCOUNTS) - .executableCommand(new AccountsCommand()) + .executableCommand(initializer.newInstance(AccountsCommand.class)) .build(); // Register the getemail command @@ -138,7 +139,7 @@ public final class CommandInitializer { .detailedDescription("Display the email address of the specified player if set.") .withArgument("player", "Player name", true) .permissions(OP_ONLY, AdminPermission.GET_EMAIL) - .executableCommand(new GetEmailCommand()) + .executableCommand(initializer.newInstance(GetEmailCommand.class)) .build(); // Register the setemail command @@ -150,7 +151,7 @@ public final class CommandInitializer { .withArgument("player", "Player name", false) .withArgument("email", "Player email", false) .permissions(OP_ONLY, AdminPermission.CHANGE_EMAIL) - .executableCommand(new SetEmailCommand()) + .executableCommand(initializer.newInstance(SetEmailCommand.class)) .build(); // Register the getip command @@ -161,7 +162,7 @@ public final class CommandInitializer { .detailedDescription("Get the IP address of the specified online player.") .withArgument("player", "Player name", false) .permissions(OP_ONLY, AdminPermission.GET_IP) - .executableCommand(new GetIpCommand()) + .executableCommand(initializer.newInstance(GetIpCommand.class)) .build(); // Register the spawn command @@ -171,7 +172,7 @@ public final class CommandInitializer { .description("Teleport to spawn") .detailedDescription("Teleport to the spawn.") .permissions(OP_ONLY, AdminPermission.SPAWN) - .executableCommand(new SpawnCommand()) + .executableCommand(initializer.newInstance(SpawnCommand.class)) .build(); // Register the setspawn command @@ -181,7 +182,7 @@ public final class CommandInitializer { .description("Change the spawn") .detailedDescription("Change the player's spawn to your current position.") .permissions(OP_ONLY, AdminPermission.SET_SPAWN) - .executableCommand(new SetSpawnCommand()) + .executableCommand(initializer.newInstance(SetSpawnCommand.class)) .build(); // Register the firstspawn command @@ -191,7 +192,7 @@ public final class CommandInitializer { .description("Teleport to first spawn") .detailedDescription("Teleport to the first spawn.") .permissions(OP_ONLY, AdminPermission.FIRST_SPAWN) - .executableCommand(new FirstSpawnCommand()) + .executableCommand(initializer.newInstance(FirstSpawnCommand.class)) .build(); // Register the setfirstspawn command @@ -201,7 +202,7 @@ public final class CommandInitializer { .description("Change the first spawn") .detailedDescription("Change the first player's spawn to your current position.") .permissions(OP_ONLY, AdminPermission.SET_FIRST_SPAWN) - .executableCommand(new SetFirstSpawnCommand()) + .executableCommand(initializer.newInstance(SetFirstSpawnCommand.class)) .build(); // Register the purge command @@ -212,7 +213,7 @@ public final class CommandInitializer { .detailedDescription("Purge old AuthMeReloaded data longer than the specified amount of days ago.") .withArgument("days", "Number of days", false) .permissions(OP_ONLY, AdminPermission.PURGE) - .executableCommand(new PurgeCommand()) + .executableCommand(initializer.newInstance(PurgeCommand.class)) .build(); // Register the purgelastposition command @@ -224,7 +225,7 @@ public final class CommandInitializer { .detailedDescription("Purge the last know position of the specified player or all of them.") .withArgument("player/*", "Player name or * for all players", false) .permissions(OP_ONLY, AdminPermission.PURGE_LAST_POSITION) - .executableCommand(new PurgeLastPositionCommand()) + .executableCommand(initializer.newInstance(PurgeLastPositionCommand.class)) .build(); // Register the purgebannedplayers command @@ -234,7 +235,7 @@ public final class CommandInitializer { .description("Purge banned players data") .detailedDescription("Purge all AuthMeReloaded data for banned players.") .permissions(OP_ONLY, AdminPermission.PURGE_BANNED_PLAYERS) - .executableCommand(new PurgeBannedPlayersCommand()) + .executableCommand(initializer.newInstance(PurgeBannedPlayersCommand.class)) .build(); // Register the switchantibot command @@ -245,7 +246,7 @@ public final class CommandInitializer { .detailedDescription("Switch or toggle the AntiBot mode to the specified state.") .withArgument("mode", "ON / OFF", true) .permissions(OP_ONLY, AdminPermission.SWITCH_ANTIBOT) - .executableCommand(new SwitchAntiBotCommand()) + .executableCommand(initializer.newInstance(SwitchAntiBotCommand.class)) .build(); // Register the reload command @@ -255,7 +256,7 @@ public final class CommandInitializer { .description("Reload plugin") .detailedDescription("Reload the AuthMeReloaded plugin.") .permissions(OP_ONLY, AdminPermission.RELOAD) - .executableCommand(new ReloadCommand()) + .executableCommand(initializer.newInstance(ReloadCommand.class)) .build(); // Register the version command @@ -265,7 +266,7 @@ public final class CommandInitializer { .description("Version info") .detailedDescription("Show detailed information about the installed AuthMeReloaded version, the " + "developers, contributors, and license.") - .executableCommand(new VersionCommand()) + .executableCommand(initializer.newInstance(VersionCommand.class)) .build(); CommandDescription.builder() @@ -276,7 +277,7 @@ public final class CommandInitializer { .withArgument("job", "Conversion job: xauth / crazylogin / rakamak / " + "royalauth / vauth / sqlitetosql", false) .permissions(OP_ONLY, AdminPermission.CONVERTER) - .executableCommand(new ConverterCommand()) + .executableCommand(initializer.newInstance(ConverterCommand.class)) .build(); // Register the base login command @@ -287,7 +288,7 @@ public final class CommandInitializer { .detailedDescription("Command to log in using AuthMeReloaded.") .withArgument("password", "Login password", false) .permissions(ALLOWED, PlayerPermission.LOGIN) - .executableCommand(new LoginCommand()) + .executableCommand(initializer.newInstance(LoginCommand.class)) .build(); // Register the base logout command @@ -297,7 +298,7 @@ public final class CommandInitializer { .description("Logout command") .detailedDescription("Command to logout using AuthMeReloaded.") .permissions(ALLOWED, PlayerPermission.LOGOUT) - .executableCommand(new LogoutCommand()) + .executableCommand(initializer.newInstance(LogoutCommand.class)) .build(); // Register the base register command @@ -309,7 +310,7 @@ public final class CommandInitializer { .withArgument("password", "Password", true) .withArgument("verifyPassword", "Verify password", true) .permissions(ALLOWED, PlayerPermission.REGISTER) - .executableCommand(new RegisterCommand()) + .executableCommand(initializer.newInstance(RegisterCommand.class)) .build(); // Register the base unregister command @@ -320,7 +321,7 @@ public final class CommandInitializer { .detailedDescription("Command to unregister using AuthMeReloaded.") .withArgument("password", "Password", false) .permissions(ALLOWED, PlayerPermission.UNREGISTER) - .executableCommand(new UnregisterCommand()) + .executableCommand(initializer.newInstance(UnregisterCommand.class)) .build(); // Register the base changepassword command @@ -332,7 +333,7 @@ public final class CommandInitializer { .withArgument("oldPassword", "Old Password", false) .withArgument("newPassword", "New Password.", false) .permissions(ALLOWED, PlayerPermission.CHANGE_PASSWORD) - .executableCommand(new ChangePasswordCommand()) + .executableCommand(initializer.newInstance(ChangePasswordCommand.class)) .build(); // Register the base Email command @@ -341,7 +342,7 @@ public final class CommandInitializer { .labels("email", "mail") .description("Email command") .detailedDescription("The AuthMeReloaded Email command base.") - .executableCommand(new EmailBaseCommand()) + .executableCommand(initializer.newInstance(EmailBaseCommand.class)) .build(); // Register the add command @@ -353,7 +354,7 @@ public final class CommandInitializer { .withArgument("email", "Email address", false) .withArgument("verifyEmail", "Email address verification", false) .permissions(ALLOWED, PlayerPermission.ADD_EMAIL) - .executableCommand(new AddEmailCommand()) + .executableCommand(initializer.newInstance(AddEmailCommand.class)) .build(); // Register the change command @@ -365,7 +366,7 @@ public final class CommandInitializer { .withArgument("oldEmail", "Old email address", false) .withArgument("newEmail", "New email address", false) .permissions(ALLOWED, PlayerPermission.CHANGE_EMAIL) - .executableCommand(new ChangeEmailCommand()) + .executableCommand(initializer.newInstance(ChangeEmailCommand.class)) .build(); // Register the recover command @@ -377,7 +378,7 @@ public final class CommandInitializer { "a new password.") .withArgument("email", "Email address", false) .permissions(ALLOWED, PlayerPermission.RECOVER_EMAIL) - .executableCommand(new RecoverEmailCommand()) + .executableCommand(initializer.newInstance(RecoverEmailCommand.class)) .build(); // Register the base captcha command @@ -388,7 +389,7 @@ public final class CommandInitializer { .detailedDescription("Captcha command for AuthMeReloaded.") .withArgument("captcha", "The Captcha", false) .permissions(ALLOWED, PlayerPermission.CAPTCHA) - .executableCommand(new CaptchaCommand()) + .executableCommand(initializer.newInstance(CaptchaCommand.class)) .build(); Set baseCommands = ImmutableSet.of( @@ -401,7 +402,7 @@ public final class CommandInitializer { EMAIL_BASE, CAPTCHA_BASE); - setHelpOnAllBases(baseCommands); + setHelpOnAllBases(baseCommands, initializer); return baseCommands; } @@ -409,9 +410,11 @@ public final class CommandInitializer { * Set the help command on all base commands, e.g. to register /authme help or /register help. * * @param commands The list of base commands to register a help child command on + * @param initializer The service initializer */ - private static void setHelpOnAllBases(Collection commands) { - final HelpCommand helpCommandExecutable = new HelpCommand(); + private static void setHelpOnAllBases(Collection commands, + AuthMeServiceInitializer initializer) { + final HelpCommand helpCommandExecutable = initializer.newInstance(HelpCommand.class); final List helpCommandLabels = Arrays.asList("help", "hlp", "h", "sos", "?"); for (CommandDescription base : commands) { diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java index d3698cfdf..ad5bf9b2f 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java @@ -3,10 +3,13 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.output.Messages; import fr.xephi.authme.util.StringUtils; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.List; /** @@ -14,6 +17,11 @@ import java.util.List; */ public class AccountsCommand implements ExecutableCommand { + @Inject + private DataSource dataSource; + @Inject + private Messages messages; + @Override public void executeCommand(final CommandSender sender, List arguments, final CommandService commandService) { @@ -24,15 +32,15 @@ public class AccountsCommand implements ExecutableCommand { commandService.runTaskAsynchronously(new Runnable() { @Override public void run() { - PlayerAuth auth = commandService.getDataSource().getAuth(playerName.toLowerCase()); + PlayerAuth auth = dataSource.getAuth(playerName.toLowerCase()); if (auth == null) { - commandService.send(sender, MessageKey.UNKNOWN_USER); + messages.send(sender, MessageKey.UNKNOWN_USER); return; } - List accountList = commandService.getDataSource().getAllAuthsByIp(auth.getIp()); + List accountList = dataSource.getAllAuthsByIp(auth.getIp()); if (accountList.isEmpty()) { - commandService.send(sender, MessageKey.USER_NOT_REGISTERED); + messages.send(sender, MessageKey.USER_NOT_REGISTERED); } else if (accountList.size() == 1) { sender.sendMessage("[AuthMe] " + playerName + " is a single account player"); } else { @@ -44,7 +52,7 @@ public class AccountsCommand implements ExecutableCommand { commandService.runTaskAsynchronously(new Runnable() { @Override public void run() { - List accountList = commandService.getDataSource().getAllAuthsByIp(playerName); + List accountList = dataSource.getAllAuthsByIp(playerName); if (accountList.isEmpty()) { sender.sendMessage("[AuthMe] This IP does not exist in the database."); } else if (accountList.size() == 1) { diff --git a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java index b751b32ed..e5dddd04c 100644 --- a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java +++ b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java @@ -97,6 +97,18 @@ public class AuthMeServiceInitializer { objects.put(annotation, value); } + /** + * Creates a new instance of the given class and does not keep track of it afterwards, + * unlike {@link #get(Class)} (singleton scope). + * + * @param clazz the class to instantiate + * @param the class' type + * @return new instance of class T + */ + public T newInstance(Class clazz) { + return instantiate(clazz, new HashSet>()); + } + /** * Returns an instance of the given class or the value associated with an annotation, * by retrieving it or by instantiating it if not yet present. @@ -120,7 +132,6 @@ public class AuthMeServiceInitializer { traversedClasses = new HashSet<>(traversedClasses); traversedClasses.add(clazz); - System.out.println(Strings.repeat(" ", traversedClasses.size() * 2) + "- Instantiating " + clazz); return instantiate(clazz, traversedClasses); } diff --git a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java index 6e5418d5e..7c62377ff 100644 --- a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java @@ -1,10 +1,13 @@ package fr.xephi.authme.command; +import fr.xephi.authme.initialization.AuthMeServiceInitializer; import fr.xephi.authme.permission.AdminPermission; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.util.StringUtils; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Collection; @@ -21,6 +24,9 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Test for {@link CommandInitializer} to guarantee the integrity of the defined commands. @@ -37,7 +43,16 @@ public class CommandInitializerTest { @BeforeClass public static void initializeCommandManager() { - commands = CommandInitializer.buildCommands(); + AuthMeServiceInitializer initializer = mock(AuthMeServiceInitializer.class); + when(initializer.newInstance(any(Class.class))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + Class clazz = (Class) invocation.getArguments()[0]; + return mock(clazz); + } + }); + + commands = CommandInitializer.buildCommands(initializer); } @Test diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java index 5a837280d..951246589 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java @@ -4,10 +4,14 @@ import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.output.Messages; import org.bukkit.command.CommandSender; -import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -22,26 +26,23 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; /** * Test for {@link AccountsCommand}. */ +@RunWith(MockitoJUnitRunner.class) public class AccountsCommandTest { + @InjectMocks private AccountsCommand command; + @Mock private CommandSender sender; + @Mock private CommandService service; + @Mock private DataSource dataSource; - - @Before - public void setUpMocks() { - command = new AccountsCommand(); - sender = mock(CommandSender.class); - dataSource = mock(DataSource.class); - service = mock(CommandService.class); - when(service.getDataSource()).thenReturn(dataSource); - } + @Mock + private Messages messages; @Test public void shouldGetAccountsOfCurrentUser() { @@ -72,7 +73,7 @@ public class AccountsCommandTest { runInnerRunnable(service); // then - verify(service).send(sender, MessageKey.UNKNOWN_USER); + verify(messages).send(sender, MessageKey.UNKNOWN_USER); verify(sender, never()).sendMessage(anyString()); } @@ -88,7 +89,7 @@ public class AccountsCommandTest { runInnerRunnable(service); // then - verify(service).send(sender, MessageKey.USER_NOT_REGISTERED); + verify(messages).send(sender, MessageKey.USER_NOT_REGISTERED); verify(sender, never()).sendMessage(anyString()); } diff --git a/src/tools/commands/CommandPageCreater.java b/src/tools/commands/CommandPageCreater.java index 8d92dbfbd..3369b906f 100644 --- a/src/tools/commands/CommandPageCreater.java +++ b/src/tools/commands/CommandPageCreater.java @@ -2,7 +2,6 @@ package commands; import fr.xephi.authme.command.CommandArgumentDescription; import fr.xephi.authme.command.CommandDescription; -import fr.xephi.authme.command.CommandInitializer; import fr.xephi.authme.command.CommandPermissions; import fr.xephi.authme.command.CommandUtils; import fr.xephi.authme.permission.PermissionNode; @@ -13,6 +12,7 @@ import utils.ToolTask; import utils.ToolsConstants; import java.util.Collection; +import java.util.HashSet; import java.util.Scanner; import java.util.Set; @@ -27,7 +27,8 @@ public class CommandPageCreater implements ToolTask { @Override public void execute(Scanner scanner) { - final Set baseCommands = CommandInitializer.buildCommands(); + // TODO ljacqu 20160427: Fix initialization of commands + final Set baseCommands = new HashSet<>();//CommandInitializer.buildCommands(); NestedTagValue commandTags = new NestedTagValue(); addCommandsInfo(commandTags, baseCommands); From ee08eb9efb839d45943d34e0647f9cbea6125ceb Mon Sep 17 00:00:00 2001 From: ljacqu Date: Wed, 27 Apr 2016 23:15:32 +0200 Subject: [PATCH 3/8] Replace Java 1.8 method with 1.7 --- .../xephi/authme/initialization/AuthMeServiceInitializer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java index e5dddd04c..dff7a76f6 100644 --- a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java +++ b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java @@ -1,7 +1,6 @@ package fr.xephi.authme.initialization; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import javax.annotation.PostConstruct; @@ -263,7 +262,7 @@ public class AuthMeServiceInitializer { private static void executePostConstructMethods(Object object) { for (Method method : object.getClass().getDeclaredMethods()) { if (method.isAnnotationPresent(PostConstruct.class)) { - if (method.getParameterCount() == 0) { + if (method.getParameterTypes().length == 0) { try { method.setAccessible(true); method.invoke(object); From 2c491803d3f79f1ae8b86fbe5655977d595b89e0 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Fri, 29 Apr 2016 23:49:03 +0200 Subject: [PATCH 4/8] Injector - disallow static PostConstruct methods, add more tests --- .../AuthMeServiceInitializer.java | 39 ++++---------- .../AuthMeServiceInitializerTest.java | 46 +++++++++++++++- .../initialization/FieldInjectionTest.java | 54 +++++++++++++++++++ .../initialization/samples/AlphaService.java | 10 ++++ .../initialization/samples/GammaService.java | 2 +- .../samples/InvalidPostConstruct.java | 35 +++++++++--- 6 files changed, 148 insertions(+), 38 deletions(-) create mode 100644 src/test/java/fr/xephi/authme/initialization/FieldInjectionTest.java diff --git a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java index dff7a76f6..560596b7d 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 613222919..54789ef02 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 000000000..5f0b45c20 --- /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 2a93a43c3..21dd3b32e 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 8ee970321..9f9dd2297 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 85c7f4c4f..c80a0c6cd 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"); + } } } From 908399e2716a5041932f70993a8005a1553f8630 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 30 Apr 2016 10:44:32 +0200 Subject: [PATCH 5/8] #432 Injector - prevent static field injection, add more tests --- src/main/java/fr/xephi/authme/AuthMe.java | 4 +- .../AuthMeServiceInitializer.java | 15 ---- .../authme/initialization/BaseCommands.java | 2 +- .../initialization/ConstructorInjection.java | 11 ++- .../authme/initialization/DataFolder.java | 2 +- .../authme/initialization/FieldInjection.java | 33 +++++--- .../AuthMeServiceInitializerTest.java | 22 +++-- .../ConstructorInjectionTest.java | 81 +++++++++++++++++++ .../initialization/FieldInjectionTest.java | 61 ++++++++++++++ .../samples/CircularClasses.java | 2 +- .../initialization/samples/GammaService.java | 2 +- .../samples/InvalidPostConstruct.java | 2 +- .../samples/InvalidStaticFieldInjection.java | 17 ++++ .../samples/PostConstructTestClass.java | 2 +- 14 files changed, 211 insertions(+), 45 deletions(-) create mode 100644 src/test/java/fr/xephi/authme/initialization/ConstructorInjectionTest.java create mode 100644 src/test/java/fr/xephi/authme/initialization/samples/InvalidStaticFieldInjection.java diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index b69cd2990..b240edae3 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -253,8 +253,8 @@ public class AuthMe extends JavaPlugin { initializer.provide(DataFolder.class, getDataFolder()); // Register elements we instantiate manually - initializer.register(newSettings); - initializer.register(messages); + initializer.register(NewSetting.class, newSettings); + initializer.register(Messages.class, messages); initializer.register(DataSource.class, database); initializer.provide(BaseCommands.class, CommandInitializer.buildCommands(initializer)); diff --git a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java index 560596b7d..576ea2277 100644 --- a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java +++ b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java @@ -9,7 +9,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.Type; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -51,20 +50,6 @@ public class AuthMeServiceInitializer { return get(clazz, new HashSet>()); } - /** - * Registers an instantiation by its type. - * - * @param object the object to register - * @throws IllegalStateException if an object of the same type has already been registered - */ - public void register(Object object) { - if (object instanceof Type) { - throw new IllegalStateException("You tried to register a Type object: '" + object - + "'. This likely indicates an error. Please use register(Class, T) if really desired."); - } - storeObject(object); - } - /** * Register an object with a custom class (supertype). Use this for example to specify a * concrete implementation of an interface or an abstract class. diff --git a/src/main/java/fr/xephi/authme/initialization/BaseCommands.java b/src/main/java/fr/xephi/authme/initialization/BaseCommands.java index 156cb947c..8ff263abd 100644 --- a/src/main/java/fr/xephi/authme/initialization/BaseCommands.java +++ b/src/main/java/fr/xephi/authme/initialization/BaseCommands.java @@ -8,7 +8,7 @@ import java.lang.annotation.Target; /** * Annotation to denote the collection of AuthMe commands. */ -@Target(ElementType.PARAMETER) +@Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface BaseCommands { } diff --git a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java index f77ae58cd..e80ea128c 100644 --- a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java +++ b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java @@ -1,5 +1,7 @@ package fr.xephi.authme.initialization; +import com.google.common.base.Preconditions; + import javax.inject.Inject; import javax.inject.Provider; import java.lang.annotation.Annotation; @@ -36,6 +38,7 @@ class ConstructorInjection implements Injection { @Override public T instantiateWith(Object... values) { + validateNoNullValues(values); try { return constructor.newInstance(values); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { @@ -60,7 +63,7 @@ class ConstructorInjection implements Injection { * * @param clazz the class to process * @param the class' type - * @return injection constructor for the class + * @return injection constructor for the class, null if not applicable */ @SuppressWarnings("unchecked") private static Constructor getInjectionConstructor(Class clazz) { @@ -74,4 +77,10 @@ class ConstructorInjection implements Injection { 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/DataFolder.java b/src/main/java/fr/xephi/authme/initialization/DataFolder.java index 33b879977..0288f45a7 100644 --- a/src/main/java/fr/xephi/authme/initialization/DataFolder.java +++ b/src/main/java/fr/xephi/authme/initialization/DataFolder.java @@ -8,7 +8,7 @@ import java.lang.annotation.Target; /** * Annotation for specifying the plugin's data folder. */ -@Target(ElementType.PARAMETER) +@Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface DataFolder { } diff --git a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java index aafb0bde3..1e7973e23 100644 --- a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java +++ b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java @@ -8,6 +8,7 @@ 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; @@ -57,6 +58,7 @@ class FieldInjection implements Injection { 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); @@ -65,15 +67,15 @@ class FieldInjection implements Injection { return instance; } - private static Class getFirstNonInjectAnnotation(Field field) { - for (Annotation annotation : field.getAnnotations()) { - if (annotation.annotationType() != Inject.class) { - return annotation.annotationType(); - } - } - return null; - } - + /** + * 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 + */ public static Provider> provide(final Class clazz) { return new Provider>() { @Override @@ -92,6 +94,10 @@ class FieldInjection implements Injection { 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); } @@ -99,6 +105,15 @@ class FieldInjection implements Injection { return fields; } + private static Class getFirstNonInjectAnnotation(Field field) { + for (Annotation annotation : field.getAnnotations()) { + if (annotation.annotationType() != Inject.class) { + return annotation.annotationType(); + } + } + return null; + } + private static Constructor getDefaultConstructor(Class clazz) { try { Constructor defaultConstructor = clazz.getDeclaredConstructor(); diff --git a/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java b/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java index 54789ef02..0541c7049 100644 --- a/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java +++ b/src/test/java/fr/xephi/authme/initialization/AuthMeServiceInitializerTest.java @@ -10,6 +10,7 @@ import fr.xephi.authme.initialization.samples.Duration; import fr.xephi.authme.initialization.samples.FieldInjectionWithAnnotations; import fr.xephi.authme.initialization.samples.InvalidClass; import fr.xephi.authme.initialization.samples.InvalidPostConstruct; +import fr.xephi.authme.initialization.samples.InvalidStaticFieldInjection; import fr.xephi.authme.initialization.samples.PostConstructTestClass; import fr.xephi.authme.initialization.samples.ProvidedClass; import fr.xephi.authme.initialization.samples.Size; @@ -34,7 +35,7 @@ public class AuthMeServiceInitializerTest { @Before public void setInitializer() { initializer = new AuthMeServiceInitializer(ALLOWED_PACKAGE); - initializer.register(new ProvidedClass("")); + initializer.register(ProvidedClass.class, new ProvidedClass("")); } @Test @@ -128,7 +129,7 @@ public class AuthMeServiceInitializerTest { @Test(expected = RuntimeException.class) public void shouldThrowForSecondRegistration() { // given / when / then - initializer.register(new ProvidedClass("")); + initializer.register(ProvidedClass.class, new ProvidedClass("")); } @Test(expected = RuntimeException.class) @@ -152,15 +153,6 @@ public class AuthMeServiceInitializerTest { initializer.register(String.class, null); } - @Test(expected = RuntimeException.class) - public void shouldThrowForRegisterOfType() { - // given / when / then - // this most likely means that the second argument was forgotten, so throw an error and force - // the API user to use the explicit register(Class.class, String.class) if really, really desired - // (Though for such generic types, an annotation would be a lot better) - initializer.register(String.class); - } - @Test public void shouldExecutePostConstructMethod() { // given @@ -215,7 +207,7 @@ public class AuthMeServiceInitializerTest { @Test(expected = RuntimeException.class) public void shouldThrowForAlreadyRegisteredClass() { // given - initializer.register(new BetaManager()); + initializer.register(BetaManager.class, new BetaManager()); // when / then initializer.register(BetaManager.class, new BetaManager()); @@ -233,4 +225,10 @@ public class AuthMeServiceInitializerTest { assertThat(singletonScoped, not(sameInstance(requestScoped))); } + @Test(expected = RuntimeException.class) + public void shouldThrowForStaticFieldInjection() { + // given / when / then + initializer.newInstance(InvalidStaticFieldInjection.class); + } + } diff --git a/src/test/java/fr/xephi/authme/initialization/ConstructorInjectionTest.java b/src/test/java/fr/xephi/authme/initialization/ConstructorInjectionTest.java new file mode 100644 index 000000000..dd21a5903 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/ConstructorInjectionTest.java @@ -0,0 +1,81 @@ +package fr.xephi.authme.initialization; + +import fr.xephi.authme.initialization.samples.AlphaService; +import fr.xephi.authme.initialization.samples.ClassWithAnnotations; +import fr.xephi.authme.initialization.samples.Duration; +import fr.xephi.authme.initialization.samples.GammaService; +import fr.xephi.authme.initialization.samples.InvalidClass; +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.equalTo; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link ConstructorInjection}. + */ +public class ConstructorInjectionTest { + + @Test + public void shouldReturnDependencies() { + // given + Injection injection = ConstructorInjection.provide(ClassWithAnnotations.class).get(); + + // when + Class[] dependencies = injection.getDependencies(); + Class[] annotations = injection.getDependencyAnnotations(); + + // then + assertThat(dependencies, arrayContaining(int.class, GammaService.class, long.class)); + assertThat(annotations, arrayContaining((Class) Size.class, null, Duration.class)); + } + + @Test + public void shouldInstantiate() { + // given + GammaService gammaService = new GammaService( + AlphaService.newInstance(new ProvidedClass(""))); + Injection injection = ConstructorInjection.provide(ClassWithAnnotations.class).get(); + + // when + ClassWithAnnotations instance = injection.instantiateWith(-112, gammaService, 19L); + + // then + assertThat(instance, not(nullValue())); + assertThat(instance.getSize(), equalTo(-112)); + assertThat(instance.getGammaService(), equalTo(gammaService)); + assertThat(instance.getDuration(), equalTo(19L)); + } + + @Test(expected = NullPointerException.class) + public void shouldThrowForNullValue() { + // given + Injection injection = ConstructorInjection.provide(ClassWithAnnotations.class).get(); + + // when / then + injection.instantiateWith(-112, null, 12L); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowUponInstantiationError() { + // given + AlphaService alphaService = AlphaService.newInstance(new ProvidedClass("")); + Injection injection = ConstructorInjection.provide(InvalidClass.class).get(); + + // when + injection.instantiateWith(alphaService, 5); + } + + @Test + public void shouldReturnNullForNoConstructorInjection() { + // given / when + Injection injection = ConstructorInjection.provide(FieldInjection.class).get(); + + // then + assertThat(injection, nullValue()); + } +} diff --git a/src/test/java/fr/xephi/authme/initialization/FieldInjectionTest.java b/src/test/java/fr/xephi/authme/initialization/FieldInjectionTest.java index 5f0b45c20..9d2294ae7 100644 --- a/src/test/java/fr/xephi/authme/initialization/FieldInjectionTest.java +++ b/src/test/java/fr/xephi/authme/initialization/FieldInjectionTest.java @@ -1,15 +1,19 @@ 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.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.InvalidStaticFieldInjection; import fr.xephi.authme.initialization.samples.ProvidedClass; import fr.xephi.authme.initialization.samples.Size; import org.junit.Test; +import javax.inject.Inject; + import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; @@ -51,4 +55,61 @@ public class FieldInjectionTest { assertThat(betaManager.getDependencies(), arrayContaining(providedClass, gammaService, alphaService)); } + @Test + public void shouldProvideNullForImpossibleFieldInjection() { + // given / when + FieldInjection injection = FieldInjection.provide(BadFieldInjection.class).get(); + + // then + assertThat(injection, nullValue()); + } + + @Test(expected = RuntimeException.class) + public void shouldForwardExceptionDuringInstantiation() { + // given + FieldInjection injection = FieldInjection.provide(ThrowingConstructor.class).get(); + + // when / when + injection.instantiateWith(new ProvidedClass("")); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForInvalidFieldValue() { + // given + ProvidedClass providedClass = new ProvidedClass(""); + AlphaService alphaService = AlphaService.newInstance(providedClass); + GammaService gammaService = new GammaService(alphaService); + FieldInjection injection = FieldInjection.provide(BetaManager.class).get(); + + // when / then + // Correct order is provided, gamma, alpha + injection.instantiateWith(providedClass, alphaService, gammaService); + } + + @Test(expected = NullPointerException.class) + public void shouldThrowForNullValue() { + // given + ProvidedClass providedClass = new ProvidedClass(""); + AlphaService alphaService = AlphaService.newInstance(providedClass); + FieldInjection injection = FieldInjection.provide(BetaManager.class).get(); + + // when / then + // Correct order is provided, gamma, alpha + injection.instantiateWith(providedClass, null, alphaService); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowForStaticFieldInjection() { + // given / when / then + FieldInjection.provide(InvalidStaticFieldInjection.class).get(); + } + + private static class ThrowingConstructor { + @Inject + private ProvidedClass providedClass; + + public ThrowingConstructor() { + throw new UnsupportedOperationException("Exception in constructor"); + } + } } diff --git a/src/test/java/fr/xephi/authme/initialization/samples/CircularClasses.java b/src/test/java/fr/xephi/authme/initialization/samples/CircularClasses.java index e9a1460ea..dc2313e35 100644 --- a/src/test/java/fr/xephi/authme/initialization/samples/CircularClasses.java +++ b/src/test/java/fr/xephi/authme/initialization/samples/CircularClasses.java @@ -5,7 +5,7 @@ import javax.inject.Inject; /** * Classes with circular dependencies. */ -public class CircularClasses { +public abstract class CircularClasses { public static final class Circular1 { @Inject 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 9f9dd2297..9d6f9bfac 100644 --- a/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java +++ b/src/test/java/fr/xephi/authme/initialization/samples/GammaService.java @@ -3,7 +3,7 @@ package fr.xephi.authme.initialization.samples; import javax.inject.Inject; /** - * Sample - class dependent on alpha and provided. + * Sample - class dependent on alpha service. */ public class GammaService { 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 c80a0c6cd..e97c4f984 100644 --- a/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java +++ b/src/test/java/fr/xephi/authme/initialization/samples/InvalidPostConstruct.java @@ -6,7 +6,7 @@ import javax.inject.Inject; /** * Class with invalid @PostConstruct method. */ -public class InvalidPostConstruct { +public abstract class InvalidPostConstruct { public static final class WithParams { @Inject diff --git a/src/test/java/fr/xephi/authme/initialization/samples/InvalidStaticFieldInjection.java b/src/test/java/fr/xephi/authme/initialization/samples/InvalidStaticFieldInjection.java new file mode 100644 index 000000000..199b2f4a9 --- /dev/null +++ b/src/test/java/fr/xephi/authme/initialization/samples/InvalidStaticFieldInjection.java @@ -0,0 +1,17 @@ +package fr.xephi.authme.initialization.samples; + +import javax.inject.Inject; + +/** + * Sample class - attempted field injection on a static member. + */ +public class InvalidStaticFieldInjection { + + @Inject + private ProvidedClass providedClass; + @Inject + protected static AlphaService alphaService; + + InvalidStaticFieldInjection() { } + +} diff --git a/src/test/java/fr/xephi/authme/initialization/samples/PostConstructTestClass.java b/src/test/java/fr/xephi/authme/initialization/samples/PostConstructTestClass.java index a4e570324..0bec84ee3 100644 --- a/src/test/java/fr/xephi/authme/initialization/samples/PostConstructTestClass.java +++ b/src/test/java/fr/xephi/authme/initialization/samples/PostConstructTestClass.java @@ -4,7 +4,7 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; /** - * Sample class for testing the execution of the @PostConstruct method. + * Sample class for testing the execution of @PostConstruct methods. */ public class PostConstructTestClass { From 3c6415a6a45d426ef66b30ec22fe3fa434c0f42c Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 30 Apr 2016 12:17:18 +0200 Subject: [PATCH 6/8] #432 Use injector instantiate hash algorithms --- .../AuthMeServiceInitializer.java | 1 + .../authme/security/PasswordSecurity.java | 67 +++++++------------ .../xephi/authme/security/crypts/BCRYPT.java | 4 +- .../authme/security/crypts/SALTED2MD5.java | 5 +- .../HashAlgorithmIntegrationTest.java | 13 ++-- .../xephi/authme/security/HashUtilsTest.java | 2 +- .../authme/security/PasswordSecurityTest.java | 50 ++++++++------ 7 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java index 576ea2277..c2241c11d 100644 --- a/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java +++ b/src/main/java/fr/xephi/authme/initialization/AuthMeServiceInitializer.java @@ -37,6 +37,7 @@ public class AuthMeServiceInitializer { public AuthMeServiceInitializer(String... allowedPackages) { ALLOWED_PACKAGES = ImmutableSet.copyOf(allowedPackages); objects = new HashMap<>(); + objects.put(getClass(), this); } /** diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java index 458c99699..4a525531b 100644 --- a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java +++ b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java @@ -2,34 +2,43 @@ package fr.xephi.authme.security; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.PasswordEncryptionEvent; +import fr.xephi.authme.initialization.AuthMeServiceInitializer; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.plugin.PluginManager; +import javax.annotation.PostConstruct; import javax.inject.Inject; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; /** * Manager class for password-related operations. */ public class PasswordSecurity { - private final NewSetting settings; - private HashAlgorithm algorithm; - private boolean supportOldAlgorithm; - private final DataSource dataSource; - private final PluginManager pluginManager; + @Inject + private NewSetting settings; @Inject - public PasswordSecurity(DataSource dataSource, NewSetting settings, PluginManager pluginManager) { - this.settings = settings; + private DataSource dataSource; + + @Inject + private PluginManager pluginManager; + + @Inject + private AuthMeServiceInitializer initializer; + + private HashAlgorithm algorithm; + private boolean supportOldAlgorithm; + + /** + * Load or reload the configuration. + */ + @PostConstruct + public void reload() { this.algorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); this.supportOldAlgorithm = settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH); - this.dataSource = dataSource; - this.pluginManager = pluginManager; } /** @@ -75,14 +84,6 @@ public class PasswordSecurity { || supportOldAlgorithm && compareWithAllEncryptionMethods(password, hashedPassword, playerLowerCase); } - /** - * Reload the configuration. - */ - public void reload() { - this.algorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); - this.supportOldAlgorithm = settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH); - } - /** * Compare the given hash with all available encryption methods to support * the migration to a new encryption method. Upon a successful match, the password @@ -97,7 +98,7 @@ public class PasswordSecurity { private boolean compareWithAllEncryptionMethods(String password, HashedPassword hashedPassword, String playerName) { for (HashAlgorithm algorithm : HashAlgorithm.values()) { if (!HashAlgorithm.CUSTOM.equals(algorithm)) { - EncryptionMethod method = initializeEncryptionMethod(algorithm, settings); + EncryptionMethod method = initializeEncryptionMethod(algorithm); if (methodMatches(method, password, hashedPassword, playerName)) { hashPasswordForNewAlgorithm(password, playerName); return true; @@ -135,7 +136,7 @@ public class PasswordSecurity { * @return The encryption method */ private EncryptionMethod initializeEncryptionMethodWithEvent(HashAlgorithm algorithm, String playerName) { - EncryptionMethod method = initializeEncryptionMethod(algorithm, settings); + EncryptionMethod method = initializeEncryptionMethod(algorithm); PasswordEncryptionEvent event = new PasswordEncryptionEvent(method, playerName); pluginManager.callEvent(event); return event.getMethod(); @@ -145,30 +146,14 @@ public class PasswordSecurity { * Initialize the encryption method associated with the given hash algorithm. * * @param algorithm The algorithm to retrieve the encryption method for - * @param settings The settings instance to pass to the constructor if required * * @return The associated encryption method, or null if CUSTOM / deprecated */ - public static EncryptionMethod initializeEncryptionMethod(HashAlgorithm algorithm, - NewSetting settings) { - try { - if (HashAlgorithm.CUSTOM.equals(algorithm) || HashAlgorithm.PLAINTEXT.equals(algorithm)) { - return null; - } - Constructor constructor = algorithm.getClazz().getConstructors()[0]; - Class[] parameters = constructor.getParameterTypes(); - if (parameters.length == 0) { - return (EncryptionMethod) constructor.newInstance(); - } else if (parameters.length == 1 && parameters[0] == NewSetting.class) { - return (EncryptionMethod) constructor.newInstance(settings); - } else { - throw new UnsupportedOperationException("Did not find default constructor or constructor with settings " - + "parameter in class " + algorithm.getClazz().getSimpleName()); - } - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new UnsupportedOperationException("Constructor for '" + algorithm.getClazz().getSimpleName() - + "' could not be invoked. (Is there no default constructor?)", e); + public EncryptionMethod initializeEncryptionMethod(HashAlgorithm algorithm) { + if (HashAlgorithm.CUSTOM.equals(algorithm) || HashAlgorithm.PLAINTEXT.equals(algorithm)) { + return null; } + return initializer.newInstance(algorithm.getClazz()); } private void hashPasswordForNewAlgorithm(String password, String playerName) { diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java index a114adc09..67d8c794e 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java +++ b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java @@ -9,13 +9,15 @@ import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.util.StringUtils; +import javax.inject.Inject; @Recommendation(Usage.RECOMMENDED) // provided the salt length is >= 8 -@HasSalt(value = SaltType.TEXT) // length depends on Settings.bCryptLog2Rounds +@HasSalt(value = SaltType.TEXT) // length depends on the bcryptLog2Rounds setting public class BCRYPT implements EncryptionMethod { private final int bCryptLog2Rounds; + @Inject public BCRYPT(NewSetting settings) { this.bCryptLog2Rounds = settings.getProperty(HooksSettings.BCRYPT_LOG2_ROUND); } diff --git a/src/main/java/fr/xephi/authme/security/crypts/SALTED2MD5.java b/src/main/java/fr/xephi/authme/security/crypts/SALTED2MD5.java index 27066f7a2..bf67432b8 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/SALTED2MD5.java +++ b/src/main/java/fr/xephi/authme/security/crypts/SALTED2MD5.java @@ -8,14 +8,17 @@ import fr.xephi.authme.security.crypts.description.Usage; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.SecuritySettings; +import javax.inject.Inject; + import static fr.xephi.authme.security.HashUtils.md5; @Recommendation(Usage.ACCEPTABLE) // presuming that length is something sensible (>= 8) -@HasSalt(value = SaltType.TEXT) // length defined by Settings.saltLength +@HasSalt(value = SaltType.TEXT) // length defined by the doubleMd5SaltLength setting public class SALTED2MD5 extends SeparateSaltMethod { private final int saltLength; + @Inject public SALTED2MD5(NewSetting settings) { saltLength = settings.getProperty(SecuritySettings.DOUBLE_MD5_SALT_LENGTH); } diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java index 976a7a445..30940c345 100644 --- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java @@ -1,5 +1,6 @@ package fr.xephi.authme.security; +import fr.xephi.authme.initialization.AuthMeServiceInitializer; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.NewSetting; @@ -13,8 +14,6 @@ import java.util.HashSet; import java.util.Set; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.BDDMockito.given; @@ -25,13 +24,15 @@ import static org.mockito.Mockito.mock; */ public class HashAlgorithmIntegrationTest { - private static NewSetting settings; + private static AuthMeServiceInitializer initializer; @BeforeClass public static void setUpWrapper() { - settings = mock(NewSetting.class); + NewSetting settings = mock(NewSetting.class); given(settings.getProperty(HooksSettings.BCRYPT_LOG2_ROUND)).willReturn(8); given(settings.getProperty(SecuritySettings.DOUBLE_MD5_SALT_LENGTH)).willReturn(16); + initializer = new AuthMeServiceInitializer(); + initializer.register(NewSetting.class, settings); } @Test @@ -55,9 +56,7 @@ public class HashAlgorithmIntegrationTest { // given / when / then for (HashAlgorithm algorithm : HashAlgorithm.values()) { if (!HashAlgorithm.CUSTOM.equals(algorithm) && !HashAlgorithm.PLAINTEXT.equals(algorithm)) { - EncryptionMethod method = PasswordSecurity.initializeEncryptionMethod(algorithm, settings); - assertThat("Encryption method for algorithm '" + algorithm + "' is not null", - method, not(nullValue())); + EncryptionMethod method = initializer.newInstance(algorithm.getClazz()); HashedPassword hashedPassword = method.computeHash("pwd", "name"); assertThat("Salt should not be null if method.hasSeparateSalt(), and vice versa. Method: '" + method + "'", StringUtils.isEmpty(hashedPassword.getSalt()), equalTo(!method.hasSeparateSalt())); diff --git a/src/test/java/fr/xephi/authme/security/HashUtilsTest.java b/src/test/java/fr/xephi/authme/security/HashUtilsTest.java index e515fdbba..c7ef1ccd2 100644 --- a/src/test/java/fr/xephi/authme/security/HashUtilsTest.java +++ b/src/test/java/fr/xephi/authme/security/HashUtilsTest.java @@ -18,7 +18,7 @@ public class HashUtilsTest { /** * List of passwords whose hash is provided to the class to test against. */ - public static final String[] GIVEN_PASSWORDS = {"", "password", "PassWord1", "&^%te$t?Pw@_"}; + private static final String[] GIVEN_PASSWORDS = {"", "password", "PassWord1", "&^%te$t?Pw@_"}; @Test public void shouldHashMd5() { diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java index 470b8a2d5..f3d577dc9 100644 --- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java +++ b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java @@ -4,6 +4,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.AuthMeServiceInitializer; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.JOOMLA; @@ -30,7 +31,6 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -41,12 +41,20 @@ import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class PasswordSecurityTest { + private AuthMeServiceInitializer initializer; + + @Mock + private NewSetting settings; + @Mock private PluginManager pluginManager; + @Mock private DataSource dataSource; + @Mock private EncryptionMethod method; + private Class caughtClassInEvent; @BeforeClass @@ -71,6 +79,10 @@ public class PasswordSecurityTest { return null; } }).when(pluginManager).callEvent(any(Event.class)); + initializer = new AuthMeServiceInitializer(new String[]{}); + initializer.register(NewSetting.class, settings); + initializer.register(DataSource.class, dataSource); + initializer.register(PluginManager.class, pluginManager); } @Test @@ -84,8 +96,8 @@ public class PasswordSecurityTest { given(dataSource.getPassword(playerName)).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(true); - PasswordSecurity security = - new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.BCRYPT, false), pluginManager); + initSettings(HashAlgorithm.BCRYPT, false); + PasswordSecurity security = initializer.newInstance(PasswordSecurity.class); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -107,8 +119,8 @@ public class PasswordSecurityTest { given(dataSource.getPassword(playerName)).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false); - PasswordSecurity security = - new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.CUSTOM, false), pluginManager); + initSettings(HashAlgorithm.CUSTOM, false); + PasswordSecurity security = initializer.newInstance(PasswordSecurity.class); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -127,8 +139,8 @@ public class PasswordSecurityTest { String clearTextPass = "tables"; given(dataSource.getPassword(playerName)).willReturn(null); - PasswordSecurity security = - new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.MD5, false), pluginManager); + initSettings(HashAlgorithm.MD5, false); + PasswordSecurity security = initializer.newInstance(PasswordSecurity.class); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -155,8 +167,8 @@ public class PasswordSecurityTest { given(dataSource.getPassword(argThat(equalToIgnoringCase(playerName)))).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false); given(method.computeHash(clearTextPass, playerLowerCase)).willReturn(newPassword); - PasswordSecurity security = - new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.MD5, true), pluginManager); + initSettings(HashAlgorithm.MD5, true); + PasswordSecurity security = initializer.newInstance(PasswordSecurity.class); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -180,8 +192,8 @@ public class PasswordSecurityTest { String clearTextPass = "someInvalidPassword"; given(dataSource.getPassword(playerName)).willReturn(password); given(method.comparePassword(clearTextPass, password, playerName)).willReturn(false); - PasswordSecurity security = - new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.MD5, true), pluginManager); + initSettings(HashAlgorithm.MD5, true); + PasswordSecurity security = initializer.newInstance(PasswordSecurity.class); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -199,8 +211,8 @@ public class PasswordSecurityTest { String usernameLowerCase = username.toLowerCase(); HashedPassword hashedPassword = new HashedPassword("$T$est#Hash", "__someSalt__"); given(method.computeHash(password, usernameLowerCase)).willReturn(hashedPassword); - PasswordSecurity security = - new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.JOOMLA, true), pluginManager); + initSettings(HashAlgorithm.JOOMLA, true); + PasswordSecurity security = initializer.newInstance(PasswordSecurity.class); // when HashedPassword result = security.computeHash(password, username); @@ -222,8 +234,8 @@ public class PasswordSecurityTest { HashedPassword hashedPassword = new HashedPassword("~T!est#Hash"); given(method.computeHash(password, username)).willReturn(hashedPassword); given(method.hasSeparateSalt()).willReturn(true); - PasswordSecurity security = - new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.XAUTH, false), pluginManager); + initSettings(HashAlgorithm.XAUTH, false); + PasswordSecurity security = initializer.newInstance(PasswordSecurity.class); // when boolean result = security.comparePassword(password, hashedPassword, username); @@ -238,8 +250,8 @@ public class PasswordSecurityTest { @Test public void shouldReloadSettings() { // given - NewSetting settings = mockSettings(HashAlgorithm.BCRYPT, false); - PasswordSecurity passwordSecurity = new PasswordSecurity(dataSource, settings, pluginManager); + initSettings(HashAlgorithm.BCRYPT, false); + PasswordSecurity passwordSecurity = initializer.newInstance(PasswordSecurity.class); given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.MD5); given(settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)).willReturn(true); @@ -253,13 +265,11 @@ public class PasswordSecurityTest { equalTo((Object) Boolean.TRUE)); } - private static NewSetting mockSettings(HashAlgorithm algorithm, boolean supportOldPassword) { - NewSetting settings = mock(NewSetting.class); + private void initSettings(HashAlgorithm algorithm, boolean supportOldPassword) { given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(algorithm); given(settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)).willReturn(supportOldPassword); given(settings.getProperty(HooksSettings.BCRYPT_LOG2_ROUND)).willReturn(8); given(settings.getProperty(SecuritySettings.DOUBLE_MD5_SALT_LENGTH)).willReturn(16); - return settings; } } From 9af596327a88ddb5241b27664754eea5658b1a05 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 2 May 2016 18:52:34 +0200 Subject: [PATCH 7/8] #432 Inject in commands: DataSource / AntiBot / PasswordSecurity / PlayerCache - Inject the services instead of passing them through the command service --- src/main/java/fr/xephi/authme/AuthMe.java | 2 +- .../xephi/authme/command/CommandService.java | 31 ----------- .../authme/ChangePasswordAdminCommand.java | 20 +++++-- .../executable/authme/GetEmailCommand.java | 7 ++- .../executable/authme/LastLoginCommand.java | 7 ++- .../authme/PurgeBannedPlayersCommand.java | 9 +++- .../executable/authme/PurgeCommand.java | 7 ++- .../authme/PurgeLastPositionCommand.java | 13 +++-- .../authme/RegisterAdminCommand.java | 18 +++++-- .../executable/authme/SetEmailCommand.java | 12 +++-- .../authme/SwitchAntiBotCommand.java | 5 +- .../authme/UnregisterAdminCommand.java | 14 +++-- .../changepassword/ChangePasswordCommand.java | 5 +- .../executable/email/RecoverEmailCommand.java | 18 +++++-- .../authme/command/CommandServiceTest.java | 26 --------- .../ChangePasswordAdminCommandTest.java | 54 ++++++------------- .../authme/GetEmailCommandTest.java | 31 ++++++----- .../authme/LastLoginCommandTest.java | 40 ++++++-------- .../authme/PurgeLastPositionCommandTest.java | 44 ++++++--------- .../authme/RegisterAdminCommandTest.java | 31 ++++------- .../authme/SwitchAntiBotCommandTest.java | 33 +++++------- .../ChangePasswordCommandTest.java | 24 +++++---- .../EncryptionMethodInfoGatherer.java | 3 +- 23 files changed, 211 insertions(+), 243 deletions(-) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index b240edae3..626ed2a6a 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -256,11 +256,11 @@ public class AuthMe extends JavaPlugin { initializer.register(NewSetting.class, newSettings); initializer.register(Messages.class, messages); initializer.register(DataSource.class, database); - initializer.provide(BaseCommands.class, CommandInitializer.buildCommands(initializer)); // Some statically injected things initializer.register(PlayerCache.class, PlayerCache.getInstance()); initializer.register(LimboCache.class, LimboCache.getInstance()); + initializer.provide(BaseCommands.class, CommandInitializer.buildCommands(initializer)); permsMan = initializer.get(PermissionsManager.class); bukkitService = initializer.get(BukkitService.class); diff --git a/src/main/java/fr/xephi/authme/command/CommandService.java b/src/main/java/fr/xephi/authme/command/CommandService.java index d42629449..95ddb5639 100644 --- a/src/main/java/fr/xephi/authme/command/CommandService.java +++ b/src/main/java/fr/xephi/authme/command/CommandService.java @@ -1,10 +1,7 @@ package fr.xephi.authme.command; -import fr.xephi.authme.AntiBot; import fr.xephi.authme.AuthMe; -import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.help.HelpProvider; -import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; @@ -48,8 +45,6 @@ public class CommandService { @Inject private SpawnLoader spawnLoader; @Inject - private AntiBot antiBot; - @Inject private ValidationService validationService; @Inject private BukkitService bukkitService; @@ -95,15 +90,6 @@ public class CommandService { authMe.getServer().getScheduler().runTaskAsynchronously(authMe, task); } - /** - * Return the AuthMe data source. - * - * @return The used data source - */ - public DataSource getDataSource() { - return authMe.getDataSource(); - } - /** * Return the AuthMe instance for further manipulation. Use only if other methods from * the command service cannot be used. @@ -114,15 +100,6 @@ public class CommandService { return authMe; } - /** - * Return the PasswordSecurity instance. - * - * @return The password security instance - */ - public PasswordSecurity getPasswordSecurity() { - return passwordSecurity; - } - /** * Output the help for a given command. * @@ -185,10 +162,6 @@ public class CommandService { return settings; } - public PlayerCache getPlayerCache() { - return PlayerCache.getInstance(); - } - public PluginHooks getPluginHooks() { return pluginHooks; } @@ -197,10 +170,6 @@ public class CommandService { return spawnLoader; } - public AntiBot getAntiBot() { - return antiBot; - } - /** * Verifies whether a password is valid according to the plugin settings. * diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java index 867443edb..f81ac07f7 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java @@ -2,13 +2,16 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.List; /** @@ -16,6 +19,15 @@ import java.util.List; */ public class ChangePasswordAdminCommand implements ExecutableCommand { + @Inject + private PasswordSecurity passwordSecurity; + + @Inject + private PlayerCache playerCache; + + @Inject + private DataSource dataSource; + @Override public void executeCommand(final CommandSender sender, List arguments, final CommandService commandService) { @@ -36,10 +48,9 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { @Override public void run() { - DataSource dataSource = commandService.getDataSource(); PlayerAuth auth = null; - if (commandService.getPlayerCache().isAuthenticated(playerNameLowerCase)) { - auth = commandService.getPlayerCache().getAuth(playerNameLowerCase); + if (playerCache.isAuthenticated(playerNameLowerCase)) { + auth = playerCache.getAuth(playerNameLowerCase); } else if (dataSource.isAuthAvailable(playerNameLowerCase)) { auth = dataSource.getAuth(playerNameLowerCase); } @@ -48,8 +59,7 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { return; } - HashedPassword hashedPassword = commandService.getPasswordSecurity() - .computeHash(playerPass, playerNameLowerCase); + HashedPassword hashedPassword = passwordSecurity.computeHash(playerPass, playerNameLowerCase); auth.setPassword(hashedPassword); if (!dataSource.updatePassword(auth)) { diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java index 53a3051c6..ede9a424c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java @@ -3,9 +3,11 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.List; /** @@ -13,11 +15,14 @@ import java.util.List; */ public class GetEmailCommand implements ExecutableCommand { + @Inject + private DataSource dataSource; + @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0); - PlayerAuth auth = commandService.getDataSource().getAuth(playerName); + PlayerAuth auth = dataSource.getAuth(playerName); if (auth == null) { commandService.send(sender, MessageKey.UNKNOWN_USER); } else { diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java index 05fdab2b6..4f0135a2d 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java @@ -3,9 +3,11 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.Date; import java.util.List; @@ -14,12 +16,15 @@ import java.util.List; */ public class LastLoginCommand implements ExecutableCommand { + @Inject + private DataSource dataSource; + @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { // Get the player String playerName = (arguments.size() >= 1) ? arguments.get(0) : sender.getName(); - PlayerAuth auth = commandService.getDataSource().getAuth(playerName); + PlayerAuth auth = dataSource.getAuth(playerName); if (auth == null) { commandService.send(sender, MessageKey.USER_NOT_REGISTERED); return; diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java index 0ace9424b..995350279 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java @@ -3,10 +3,12 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.AuthMe; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.settings.properties.PurgeSettings; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.ArrayList; import java.util.List; @@ -16,6 +18,9 @@ import java.util.List; */ public class PurgeBannedPlayersCommand implements ExecutableCommand { + @Inject + private DataSource dataSource; + @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { // AuthMe plugin instance @@ -23,12 +28,12 @@ public class PurgeBannedPlayersCommand implements ExecutableCommand { // Get the list of banned players List bannedPlayers = new ArrayList<>(); - for (OfflinePlayer offlinePlayer : plugin.getServer().getBannedPlayers()) { + for (OfflinePlayer offlinePlayer : commandService.getBukkitService().getBannedPlayers()) { bannedPlayers.add(offlinePlayer.getName().toLowerCase()); } // Purge the banned players - commandService.getDataSource().purgeBanned(bannedPlayers); + dataSource.purgeBanned(bannedPlayers); if (commandService.getProperty(PurgeSettings.REMOVE_ESSENTIALS_FILES) && commandService.getPluginHooks().isEssentialsAvailable()) plugin.dataManager.purgeEssentials(bannedPlayers); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java index 43e1daa53..51ca6df11 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java @@ -3,10 +3,12 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.AuthMe; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.settings.properties.PurgeSettings; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.Calendar; import java.util.List; @@ -18,6 +20,9 @@ public class PurgeCommand implements ExecutableCommand { private static final int MINIMUM_LAST_SEEN_DAYS = 30; + @Inject + private DataSource dataSource; + @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { // Get the days parameter @@ -45,7 +50,7 @@ public class PurgeCommand implements ExecutableCommand { long until = calendar.getTimeInMillis(); // Purge the data, get the purged values - List purged = commandService.getDataSource().autoPurgeDatabase(until); + List purged = dataSource.autoPurgeDatabase(until); // Show a status message sender.sendMessage(ChatColor.GOLD + "Deleted " + purged.size() + " user accounts"); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java index 118f8460c..7c88df86a 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java @@ -3,9 +3,11 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.List; /** @@ -13,26 +15,29 @@ import java.util.List; */ public class PurgeLastPositionCommand implements ExecutableCommand { + @Inject + private DataSource dataSource; + @Override public void executeCommand(final CommandSender sender, List arguments, CommandService commandService) { String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0); if ("*".equals(playerName)) { - for (PlayerAuth auth : commandService.getDataSource().getAllAuths()) { + for (PlayerAuth auth : dataSource.getAllAuths()) { resetLastPosition(auth); - commandService.getDataSource().updateQuitLoc(auth); + dataSource.updateQuitLoc(auth); } sender.sendMessage("All players last position locations are now reset"); } else { // Get the user auth and make sure the user exists - PlayerAuth auth = commandService.getDataSource().getAuth(playerName); + PlayerAuth auth = dataSource.getAuth(playerName); if (auth == null) { commandService.send(sender, MessageKey.UNKNOWN_USER); return; } resetLastPosition(auth); - commandService.getDataSource().updateQuitLoc(auth); + dataSource.updateQuitLoc(auth); sender.sendMessage(playerName + "'s last position location is now reset"); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java index 2caf7a3cf..d61ea002c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java @@ -4,11 +4,14 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import javax.inject.Inject; import java.util.List; /** @@ -16,6 +19,12 @@ import java.util.List; */ public class RegisterAdminCommand implements ExecutableCommand { + @Inject + private PasswordSecurity passwordSecurity; + + @Inject + private DataSource dataSource; + @Override public void executeCommand(final CommandSender sender, List arguments, final CommandService commandService) { @@ -35,23 +44,22 @@ public class RegisterAdminCommand implements ExecutableCommand { @Override public void run() { - if (commandService.getDataSource().isAuthAvailable(playerNameLowerCase)) { + if (dataSource.isAuthAvailable(playerNameLowerCase)) { commandService.send(sender, MessageKey.NAME_ALREADY_REGISTERED); return; } - HashedPassword hashedPassword = commandService.getPasswordSecurity() - .computeHash(playerPass, playerNameLowerCase); + HashedPassword hashedPassword = passwordSecurity.computeHash(playerPass, playerNameLowerCase); PlayerAuth auth = PlayerAuth.builder() .name(playerNameLowerCase) .realName(playerName) .password(hashedPassword) .build(); - if (!commandService.getDataSource().saveAuth(auth)) { + if (!dataSource.saveAuth(auth)) { commandService.send(sender, MessageKey.ERROR); return; } - commandService.getDataSource().setUnlogged(playerNameLowerCase); + dataSource.setUnlogged(playerNameLowerCase); commandService.send(sender, MessageKey.REGISTER_SUCCESS); ConsoleLogger.info(sender.getName() + " registered " + playerName); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java index 0e7bcf35c..989c72104 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java @@ -8,6 +8,7 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.List; /** @@ -15,6 +16,12 @@ import java.util.List; */ public class SetEmailCommand implements ExecutableCommand { + @Inject + private DataSource dataSource; + + @Inject + private PlayerCache playerCache; + @Override public void executeCommand(final CommandSender sender, List arguments, final CommandService commandService) { @@ -32,7 +39,6 @@ public class SetEmailCommand implements ExecutableCommand { @Override public void run() { // Validate the user - DataSource dataSource = commandService.getDataSource(); PlayerAuth auth = dataSource.getAuth(playerName); if (auth == null) { commandService.send(sender, MessageKey.UNKNOWN_USER); @@ -50,8 +56,8 @@ public class SetEmailCommand implements ExecutableCommand { } // Update the player cache - if (PlayerCache.getInstance().getAuth(playerName) != null) { - PlayerCache.getInstance().updatePlayer(auth); + if (playerCache.getAuth(playerName) != null) { + playerCache.updatePlayer(auth); } // Show a status message diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java index 3c36c1690..e996e6f08 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java @@ -8,6 +8,7 @@ import fr.xephi.authme.command.help.HelpProvider; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import javax.inject.Inject; import java.util.Arrays; import java.util.List; @@ -16,9 +17,11 @@ import java.util.List; */ public class SwitchAntiBotCommand implements ExecutableCommand { + @Inject + private AntiBot antiBot; + @Override public void executeCommand(final CommandSender sender, List arguments, CommandService commandService) { - AntiBot antiBot = commandService.getAntiBot(); if (arguments.isEmpty()) { sender.sendMessage("[AuthMe] AntiBot status: " + antiBot.getAntiBotStatus().name()); return; diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java index 5c8196ddf..c69f9affe 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java @@ -6,6 +6,7 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -19,6 +20,7 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitTask; +import javax.inject.Inject; import java.util.List; import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND; @@ -28,6 +30,12 @@ import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND; */ public class UnregisterAdminCommand implements ExecutableCommand { + @Inject + private DataSource dataSource; + + @Inject + private PlayerCache playerCache; + @Override public void executeCommand(final CommandSender sender, List arguments, CommandService commandService) { // Get the player name @@ -35,20 +43,20 @@ public class UnregisterAdminCommand implements ExecutableCommand { String playerNameLowerCase = playerName.toLowerCase(); // Make sure the user is valid - if (!commandService.getDataSource().isAuthAvailable(playerNameLowerCase)) { + if (!dataSource.isAuthAvailable(playerNameLowerCase)) { commandService.send(sender, MessageKey.UNKNOWN_USER); return; } // Remove the player - if (!commandService.getDataSource().removeAuth(playerNameLowerCase)) { + if (!dataSource.removeAuth(playerNameLowerCase)) { commandService.send(sender, MessageKey.ERROR); return; } // Unregister the player Player target = commandService.getPlayer(playerNameLowerCase); - PlayerCache.getInstance().removePlayer(playerNameLowerCase); + playerCache.removePlayer(playerNameLowerCase); Utils.setGroup(target, Utils.GroupType.UNREGISTERED); if (target != null && target.isOnline()) { if (commandService.getProperty(RegistrationSettings.FORCE)) { diff --git a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java b/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java index 7d378ca88..67df2d867 100644 --- a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java @@ -8,6 +8,7 @@ import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.task.ChangePasswordTask; import org.bukkit.entity.Player; +import javax.inject.Inject; import java.util.List; /** @@ -15,13 +16,15 @@ import java.util.List; */ public class ChangePasswordCommand extends PlayerCommand { + @Inject + private PlayerCache playerCache; + @Override public void runCommand(Player player, List arguments, CommandService commandService) { String oldPassword = arguments.get(0); String newPassword = arguments.get(1); String name = player.getName().toLowerCase(); - final PlayerCache playerCache = commandService.getPlayerCache(); if (!playerCache.isAuthenticated(name)) { commandService.send(player, MessageKey.NOT_LOGGED_IN); return; diff --git a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java index f4a319a5f..56fb5e5c6 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java @@ -8,16 +8,27 @@ import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.RandomString; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.util.StringUtils; import org.bukkit.entity.Player; +import javax.inject.Inject; import java.util.List; public class RecoverEmailCommand extends PlayerCommand { + @Inject + private PasswordSecurity passwordSecurity; + + @Inject + private DataSource dataSource; + + @Inject + private PlayerCache playerCache; + @Override public void runCommand(Player player, List arguments, CommandService commandService) { final String playerMail = arguments.get(0); @@ -29,7 +40,6 @@ public class RecoverEmailCommand extends PlayerCommand { commandService.send(player, MessageKey.ERROR); return; } - DataSource dataSource = commandService.getDataSource(); if (dataSource.isAuthAvailable(playerName)) { if (PlayerCache.getInstance().isAuthenticated(playerName)) { commandService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); @@ -37,10 +47,10 @@ public class RecoverEmailCommand extends PlayerCommand { } String thePass = RandomString.generate(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)); - HashedPassword hashNew = commandService.getPasswordSecurity().computeHash(thePass, playerName); + HashedPassword hashNew = passwordSecurity.computeHash(thePass, playerName); PlayerAuth auth; - if (PlayerCache.getInstance().isAuthenticated(playerName)) { - auth = PlayerCache.getInstance().getAuth(playerName); + if (playerCache.isAuthenticated(playerName)) { + auth = playerCache.getAuth(playerName); } else if (dataSource.isAuthAvailable(playerName)) { auth = dataSource.getAuth(playerName); } else { diff --git a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java index a1ff5e22f..76685fce1 100644 --- a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java @@ -1,9 +1,7 @@ package fr.xephi.authme.command; -import fr.xephi.authme.AntiBot; import fr.xephi.authme.AuthMe; import fr.xephi.authme.command.help.HelpProvider; -import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; @@ -62,8 +60,6 @@ public class CommandServiceTest { @Mock private SpawnLoader spawnLoader; @Mock - private AntiBot antiBot; - @Mock private ValidationService validationService; @Mock private BukkitService bukkitService; @@ -108,28 +104,6 @@ public class CommandServiceTest { verify(commandMapper).mapPartsToCommand(sender, commandParts); } - @Test - public void shouldGetDataSource() { - // given - DataSource dataSource = mock(DataSource.class); - given(authMe.getDataSource()).willReturn(dataSource); - - // when - DataSource result = commandService.getDataSource(); - - // then - assertThat(result, equalTo(dataSource)); - } - - @Test - public void shouldGetPasswordSecurity() { - // given/when - PasswordSecurity passwordSecurity = commandService.getPasswordSecurity(); - - // then - assertThat(passwordSecurity, equalTo(this.passwordSecurity)); - } - @Test public void shouldOutputHelp() { // given diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java index b4f42886e..163bee151 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java @@ -4,7 +4,6 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.security.PasswordSecurity; @@ -13,6 +12,7 @@ import org.bukkit.command.CommandSender; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -24,6 +24,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; /** * Test for {@link ChangePasswordAdminCommand}. @@ -31,9 +32,21 @@ import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class ChangePasswordAdminCommandTest { + @InjectMocks + private ChangePasswordAdminCommand command; + @Mock private CommandService service; + @Mock + private PasswordSecurity passwordSecurity; + + @Mock + private DataSource dataSource; + + @Mock + private PlayerCache playerCache; + @BeforeClass public static void setUpLogger() { TestHelper.setupLogger(); @@ -42,7 +55,6 @@ public class ChangePasswordAdminCommandTest { @Test public void shouldRejectInvalidPassword() { // given - ExecutableCommand command = new ChangePasswordAdminCommand(); CommandSender sender = mock(CommandSender.class); given(service.validatePassword("Bobby", "bobby")).willReturn(MessageKey.PASSWORD_IS_USERNAME_ERROR); @@ -52,23 +64,16 @@ public class ChangePasswordAdminCommandTest { // then verify(service).validatePassword("Bobby", "bobby"); verify(service).send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR); - verify(service, never()).getDataSource(); + verifyZeroInteractions(dataSource); } @Test public void shouldRejectCommandForUnknownUser() { // given - ExecutableCommand command = new ChangePasswordAdminCommand(); CommandSender sender = mock(CommandSender.class); String player = "player"; - - PlayerCache playerCache = mock(PlayerCache.class); given(playerCache.isAuthenticated(player)).willReturn(false); - given(service.getPlayerCache()).willReturn(playerCache); - - DataSource dataSource = mock(DataSource.class); given(dataSource.getAuth(player)).willReturn(null); - given(service.getDataSource()).willReturn(dataSource); // when command.executeCommand(sender, Arrays.asList(player, "password"), service); @@ -82,26 +87,17 @@ public class ChangePasswordAdminCommandTest { @Test public void shouldUpdatePasswordOfLoggedInUser() { // given - ExecutableCommand command = new ChangePasswordAdminCommand(); CommandSender sender = mock(CommandSender.class); - String player = "my_user12"; String password = "passPass"; PlayerAuth auth = mock(PlayerAuth.class); - PlayerCache playerCache = mock(PlayerCache.class); given(playerCache.isAuthenticated(player)).willReturn(true); given(playerCache.getAuth(player)).willReturn(auth); - given(service.getPlayerCache()).willReturn(playerCache); - PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); HashedPassword hashedPassword = mock(HashedPassword.class); given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); - given(service.getPasswordSecurity()).willReturn(passwordSecurity); - - DataSource dataSource = mock(DataSource.class); given(dataSource.updatePassword(auth)).willReturn(true); - given(service.getDataSource()).willReturn(dataSource); // when command.executeCommand(sender, Arrays.asList(player, password), service); @@ -118,27 +114,17 @@ public class ChangePasswordAdminCommandTest { @Test public void shouldUpdatePasswordOfOfflineUser() { // given - ExecutableCommand command = new ChangePasswordAdminCommand(); CommandSender sender = mock(CommandSender.class); - String player = "my_user12"; String password = "passPass"; PlayerAuth auth = mock(PlayerAuth.class); - - PlayerCache playerCache = mock(PlayerCache.class); given(playerCache.isAuthenticated(player)).willReturn(false); - given(service.getPlayerCache()).willReturn(playerCache); - - DataSource dataSource = mock(DataSource.class); given(dataSource.isAuthAvailable(player)).willReturn(true); given(dataSource.getAuth(player)).willReturn(auth); given(dataSource.updatePassword(auth)).willReturn(true); - given(service.getDataSource()).willReturn(dataSource); - PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); HashedPassword hashedPassword = mock(HashedPassword.class); given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); - given(service.getPasswordSecurity()).willReturn(passwordSecurity); // when command.executeCommand(sender, Arrays.asList(player, password), service); @@ -155,26 +141,16 @@ public class ChangePasswordAdminCommandTest { @Test public void shouldReportWhenSaveFailed() { // given - ExecutableCommand command = new ChangePasswordAdminCommand(); CommandSender sender = mock(CommandSender.class); - String player = "my_user12"; String password = "passPass"; PlayerAuth auth = mock(PlayerAuth.class); - - PlayerCache playerCache = mock(PlayerCache.class); given(playerCache.isAuthenticated(player)).willReturn(true); given(playerCache.getAuth(player)).willReturn(auth); - given(service.getPlayerCache()).willReturn(playerCache); - PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); HashedPassword hashedPassword = mock(HashedPassword.class); given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); - given(service.getPasswordSecurity()).willReturn(passwordSecurity); - - DataSource dataSource = mock(DataSource.class); given(dataSource.updatePassword(auth)).willReturn(false); - given(service.getDataSource()).willReturn(dataSource); // when command.executeCommand(sender, Arrays.asList(player, password), service); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java index 6d2320377..f03ff4267 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java @@ -2,11 +2,14 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import org.bukkit.command.CommandSender; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import java.util.Collections; @@ -19,19 +22,26 @@ import static org.mockito.Mockito.verify; /** * Test for {@link GetEmailCommand}. */ +@RunWith(MockitoJUnitRunner.class) public class GetEmailCommandTest { + @InjectMocks + private GetEmailCommand command; + + @Mock + private DataSource dataSource; + + @Mock + private CommandSender sender; + + @Mock + private CommandService service; + @Test public void shouldReportUnknownUser() { // given String user = "myTestUser"; - DataSource dataSource = mock(DataSource.class); given(dataSource.getAuth(user)).willReturn(null); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - CommandSender sender = mock(CommandSender.class); - ExecutableCommand command = new GetEmailCommand(); // when command.executeCommand(sender, Collections.singletonList(user), service); @@ -47,14 +57,7 @@ public class GetEmailCommandTest { String email = "user.email@example.org"; PlayerAuth auth = mock(PlayerAuth.class); given(auth.getEmail()).willReturn(email); - - DataSource dataSource = mock(DataSource.class); given(dataSource.getAuth(user)).willReturn(auth); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - CommandSender sender = mock(CommandSender.class); - ExecutableCommand command = new GetEmailCommand(); // when command.executeCommand(sender, Collections.singletonList(user), service); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java index d1e58e92a..04ffabef1 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java @@ -2,12 +2,15 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import org.bukkit.command.CommandSender; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import java.util.Collections; import java.util.Date; @@ -23,8 +26,21 @@ import static org.mockito.Mockito.verify; /** * Test for {@link LastLoginCommand}. */ +@RunWith(MockitoJUnitRunner.class) public class LastLoginCommandTest { + @InjectMocks + private LastLoginCommand command; + + @Mock + private DataSource dataSource; + + @Mock + private CommandService service; + + @Mock + private CommandSender sender; + private static final long HOUR_IN_MSEC = 3600 * 1000; private static final long DAY_IN_MSEC = 24 * HOUR_IN_MSEC; @@ -32,15 +48,8 @@ public class LastLoginCommandTest { public void shouldRejectNonExistentUser() { // given String player = "tester"; - DataSource dataSource = mock(DataSource.class); given(dataSource.getAuth(player)).willReturn(null); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - CommandSender sender = mock(CommandSender.class); - ExecutableCommand command = new LastLoginCommand(); - // when command.executeCommand(sender, Collections.singletonList(player), service); @@ -58,14 +67,7 @@ public class LastLoginCommandTest { PlayerAuth auth = mock(PlayerAuth.class); given(auth.getLastLogin()).willReturn(lastLogin); given(auth.getIp()).willReturn("123.45.66.77"); - - DataSource dataSource = mock(DataSource.class); given(dataSource.getAuth(player)).willReturn(auth); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - CommandSender sender = mock(CommandSender.class); - ExecutableCommand command = new LastLoginCommand(); // when command.executeCommand(sender, Collections.singletonList(player), service); @@ -85,7 +87,6 @@ public class LastLoginCommandTest { public void shouldDisplayLastLoginOfCommandSender() { // given String name = "CommandSender"; - CommandSender sender = mock(CommandSender.class); given(sender.getName()).willReturn(name); long lastLogin = System.currentTimeMillis() - @@ -93,14 +94,7 @@ public class LastLoginCommandTest { PlayerAuth auth = mock(PlayerAuth.class); given(auth.getLastLogin()).willReturn(lastLogin); given(auth.getIp()).willReturn("123.45.66.77"); - - DataSource dataSource = mock(DataSource.class); given(dataSource.getAuth(name)).willReturn(auth); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - - ExecutableCommand command = new LastLoginCommand(); // when command.executeCommand(sender, Collections.emptyList(), service); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java index bcce0342e..2c80a19d9 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java @@ -2,11 +2,14 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import org.bukkit.command.CommandSender; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -20,21 +23,27 @@ import static org.mockito.Mockito.verify; /** * Test for {@link PurgeLastPositionCommand}. */ +@RunWith(MockitoJUnitRunner.class) public class PurgeLastPositionCommandTest { + @InjectMocks + private PurgeLastPositionCommand command; + + @Mock + private DataSource dataSource; + + @Mock + private CommandService service; + + @Mock + private CommandSender sender; + @Test public void shouldPurgeLastPosOfUser() { // given String player = "_Bobby"; PlayerAuth auth = mock(PlayerAuth.class); - - DataSource dataSource = mock(DataSource.class); given(dataSource.getAuth(player)).willReturn(auth); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - CommandSender sender = mock(CommandSender.class); - ExecutableCommand command = new PurgeLastPositionCommand(); // when command.executeCommand(sender, Collections.singletonList(player), service); @@ -51,14 +60,8 @@ public class PurgeLastPositionCommandTest { String player = "_Bobby"; CommandSender sender = mock(CommandSender.class); given(sender.getName()).willReturn(player); - PlayerAuth auth = mock(PlayerAuth.class); - DataSource dataSource = mock(DataSource.class); given(dataSource.getAuth(player)).willReturn(auth); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - ExecutableCommand command = new PurgeLastPositionCommand(); // when command.executeCommand(sender, Collections.emptyList(), service); @@ -72,12 +75,6 @@ public class PurgeLastPositionCommandTest { @Test public void shouldHandleNonExistentUser() { // given - DataSource dataSource = mock(DataSource.class); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - ExecutableCommand command = new PurgeLastPositionCommand(); - CommandSender sender = mock(CommandSender.class); String name = "invalidPlayer"; // when @@ -94,14 +91,7 @@ public class PurgeLastPositionCommandTest { PlayerAuth auth1 = mock(PlayerAuth.class); PlayerAuth auth2 = mock(PlayerAuth.class); PlayerAuth auth3 = mock(PlayerAuth.class); - - DataSource dataSource = mock(DataSource.class); given(dataSource.getAllAuths()).willReturn(Arrays.asList(auth1, auth2, auth3)); - CommandService service = mock(CommandService.class); - given(service.getDataSource()).willReturn(dataSource); - - ExecutableCommand command = new PurgeLastPositionCommand(); - CommandSender sender = mock(CommandSender.class); // when command.executeCommand(sender, Collections.singletonList("*"), service); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java index 66b7d6c44..3ce93fcff 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java @@ -3,7 +3,6 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.TestHelper; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.security.PasswordSecurity; @@ -14,6 +13,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -35,11 +35,21 @@ import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class RegisterAdminCommandTest { + @InjectMocks + private RegisterAdminCommand command; + + @Mock + private PasswordSecurity passwordSecurity; + @Mock private CommandSender sender; + @Mock private CommandService commandService; + @Mock + private DataSource dataSource; + @BeforeClass public static void setUpLogger() { TestHelper.setupLogger(); @@ -51,7 +61,6 @@ public class RegisterAdminCommandTest { String user = "tester"; String password = "myPassword"; given(commandService.validatePassword(password, user)).willReturn(MessageKey.INVALID_PASSWORD_LENGTH); - ExecutableCommand command = new RegisterAdminCommand(); // when command.executeCommand(sender, Arrays.asList(user, password), commandService); @@ -68,10 +77,7 @@ public class RegisterAdminCommandTest { String user = "my_name55"; String password = "@some-pass@"; given(commandService.validatePassword(password, user)).willReturn(null); - DataSource dataSource = mock(DataSource.class); given(dataSource.isAuthAvailable(user)).willReturn(true); - given(commandService.getDataSource()).willReturn(dataSource); - ExecutableCommand command = new RegisterAdminCommand(); // when command.executeCommand(sender, Arrays.asList(user, password), commandService); @@ -89,15 +95,10 @@ public class RegisterAdminCommandTest { String user = "test-test"; String password = "afdjhfkt"; given(commandService.validatePassword(password, user)).willReturn(null); - DataSource dataSource = mock(DataSource.class); given(dataSource.isAuthAvailable(user)).willReturn(false); given(dataSource.saveAuth(any(PlayerAuth.class))).willReturn(false); - given(commandService.getDataSource()).willReturn(dataSource); - PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); HashedPassword hashedPassword = new HashedPassword("235sdf4w5udsgf"); given(passwordSecurity.computeHash(password, user)).willReturn(hashedPassword); - given(commandService.getPasswordSecurity()).willReturn(passwordSecurity); - ExecutableCommand command = new RegisterAdminCommand(); // when command.executeCommand(sender, Arrays.asList(user, password), commandService); @@ -117,16 +118,11 @@ public class RegisterAdminCommandTest { String user = "someone"; String password = "Al1O3P49S5%"; given(commandService.validatePassword(password, user)).willReturn(null); - DataSource dataSource = mock(DataSource.class); given(dataSource.isAuthAvailable(user)).willReturn(false); given(dataSource.saveAuth(any(PlayerAuth.class))).willReturn(true); - given(commandService.getDataSource()).willReturn(dataSource); - PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); HashedPassword hashedPassword = new HashedPassword("$aea2345EW235dfsa@#R%987048"); given(passwordSecurity.computeHash(password, user)).willReturn(hashedPassword); - given(commandService.getPasswordSecurity()).willReturn(passwordSecurity); given(commandService.getPlayer(user)).willReturn(null); - ExecutableCommand command = new RegisterAdminCommand(); // when command.executeCommand(sender, Arrays.asList(user, password), commandService); @@ -147,17 +143,12 @@ public class RegisterAdminCommandTest { String user = "someone"; String password = "Al1O3P49S5%"; given(commandService.validatePassword(password, user)).willReturn(null); - DataSource dataSource = mock(DataSource.class); given(dataSource.isAuthAvailable(user)).willReturn(false); given(dataSource.saveAuth(any(PlayerAuth.class))).willReturn(true); - given(commandService.getDataSource()).willReturn(dataSource); - PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); HashedPassword hashedPassword = new HashedPassword("$aea2345EW235dfsa@#R%987048"); given(passwordSecurity.computeHash(password, user)).willReturn(hashedPassword); - given(commandService.getPasswordSecurity()).willReturn(passwordSecurity); Player player = mock(Player.class); given(commandService.getPlayer(user)).willReturn(player); - ExecutableCommand command = new RegisterAdminCommand(); // when command.executeCommand(sender, Arrays.asList(user, password), commandService); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java index 3f9767f92..f8945a1c3 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java @@ -2,11 +2,14 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.AntiBot; import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.FoundCommandResult; import fr.xephi.authme.command.help.HelpProvider; import org.bukkit.command.CommandSender; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import java.util.Collections; @@ -22,17 +25,23 @@ import static org.mockito.Mockito.verify; /** * Test for {@link SwitchAntiBotCommand}. */ +@RunWith(MockitoJUnitRunner.class) public class SwitchAntiBotCommandTest { + @InjectMocks + private SwitchAntiBotCommand command; + + @Mock + private AntiBot antiBot; + + @Mock + private CommandService service; + @Test public void shouldReturnAntiBotState() { // given - AntiBot antiBot = mock(AntiBot.class); given(antiBot.getAntiBotStatus()).willReturn(AntiBot.AntiBotStatus.ACTIVE); - CommandService service = mock(CommandService.class); - given(service.getAntiBot()).willReturn(antiBot); CommandSender sender = mock(CommandSender.class); - ExecutableCommand command = new SwitchAntiBotCommand(); // when command.executeCommand(sender, Collections.emptyList(), service); @@ -44,11 +53,7 @@ public class SwitchAntiBotCommandTest { @Test public void shouldActivateAntiBot() { // given - AntiBot antiBot = mock(AntiBot.class); - CommandService service = mock(CommandService.class); - given(service.getAntiBot()).willReturn(antiBot); CommandSender sender = mock(CommandSender.class); - ExecutableCommand command = new SwitchAntiBotCommand(); // when command.executeCommand(sender, Collections.singletonList("on"), service); @@ -61,11 +66,7 @@ public class SwitchAntiBotCommandTest { @Test public void shouldDeactivateAntiBot() { // given - AntiBot antiBot = mock(AntiBot.class); - CommandService service = mock(CommandService.class); - given(service.getAntiBot()).willReturn(antiBot); CommandSender sender = mock(CommandSender.class); - ExecutableCommand command = new SwitchAntiBotCommand(); // when command.executeCommand(sender, Collections.singletonList("Off"), service); @@ -79,15 +80,9 @@ public class SwitchAntiBotCommandTest { public void shouldShowHelpForUnknownState() { // given CommandSender sender = mock(CommandSender.class); - - AntiBot antiBot = mock(AntiBot.class); FoundCommandResult foundCommandResult = mock(FoundCommandResult.class); - CommandService service = mock(CommandService.class); - given(service.getAntiBot()).willReturn(antiBot); given(service.mapPartsToCommand(sender, asList("authme", "antibot"))).willReturn(foundCommandResult); - ExecutableCommand command = new SwitchAntiBotCommand(); - // when command.executeCommand(sender, Collections.singletonList("wrong"), service); diff --git a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java index bc8e0876a..1dc4dbb11 100644 --- a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java @@ -12,7 +12,11 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import java.util.ArrayList; import java.util.Arrays; @@ -33,26 +37,31 @@ import static org.mockito.Mockito.when; /** * Test for {@link ChangePasswordCommand}. */ +@RunWith(MockitoJUnitRunner.class) public class ChangePasswordCommandTest { + @InjectMocks + private ChangePasswordCommand command; + + @Mock + private PlayerCache playerCache; + + @Mock private CommandService commandService; @Before - public void setUpMocks() { - commandService = mock(CommandService.class); - + public void setSettings() { when(commandService.getProperty(SecuritySettings.MIN_PASSWORD_LENGTH)).thenReturn(2); when(commandService.getProperty(SecuritySettings.MAX_PASSWORD_LENGTH)).thenReturn(50); // Only allow passwords with alphanumerical characters for the test when(commandService.getProperty(RestrictionSettings.ALLOWED_PASSWORD_REGEX)).thenReturn("[a-zA-Z0-9]+"); - when(commandService.getProperty(SecuritySettings.UNSAFE_PASSWORDS)).thenReturn(Collections. emptyList()); + when(commandService.getProperty(SecuritySettings.UNSAFE_PASSWORDS)).thenReturn(Collections.emptyList()); } @Test public void shouldRejectNonPlayerSender() { // given CommandSender sender = mock(BlockCommandSender.class); - ChangePasswordCommand command = new ChangePasswordCommand(); // when command.executeCommand(sender, new ArrayList(), commandService); @@ -65,7 +74,6 @@ public class ChangePasswordCommandTest { public void shouldRejectNotLoggedInPlayer() { // given CommandSender sender = initPlayerWithName("name", false); - ChangePasswordCommand command = new ChangePasswordCommand(); // when command.executeCommand(sender, Arrays.asList("pass", "pass"), commandService); @@ -78,7 +86,6 @@ public class ChangePasswordCommandTest { public void shouldRejectInvalidPassword() { // given CommandSender sender = initPlayerWithName("abc12", true); - ChangePasswordCommand command = new ChangePasswordCommand(); String password = "newPW"; given(commandService.validatePassword(password, "abc12")).willReturn(MessageKey.INVALID_PASSWORD_LENGTH); @@ -94,7 +101,6 @@ public class ChangePasswordCommandTest { public void shouldForwardTheDataForValidPassword() { // given CommandSender sender = initPlayerWithName("parker", true); - ChangePasswordCommand command = new ChangePasswordCommand(); // when command.executeCommand(sender, Arrays.asList("abc123", "abc123"), commandService); @@ -112,9 +118,7 @@ public class ChangePasswordCommandTest { private Player initPlayerWithName(String name, boolean loggedIn) { Player player = mock(Player.class); when(player.getName()).thenReturn(name); - PlayerCache playerCache = mock(PlayerCache.class); when(playerCache.isAuthenticated(name)).thenReturn(loggedIn); - when(commandService.getPlayerCache()).thenReturn(playerCache); return player; } diff --git a/src/tools/hashmethods/EncryptionMethodInfoGatherer.java b/src/tools/hashmethods/EncryptionMethodInfoGatherer.java index 4ade88ce6..d56e370d0 100644 --- a/src/tools/hashmethods/EncryptionMethodInfoGatherer.java +++ b/src/tools/hashmethods/EncryptionMethodInfoGatherer.java @@ -1,7 +1,6 @@ package hashmethods; import fr.xephi.authme.security.HashAlgorithm; -import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HexSaltedMethod; import fr.xephi.authme.security.crypts.description.AsciiRestricted; @@ -55,7 +54,7 @@ public class EncryptionMethodInfoGatherer { private static MethodDescription createDescription(HashAlgorithm algorithm) { Class clazz = algorithm.getClazz(); - EncryptionMethod method = PasswordSecurity.initializeEncryptionMethod(algorithm, settings); + EncryptionMethod method = null; // TODO ljacqu PasswordSecurity.initializeEncryptionMethod(algorithm, settings); if (method == null) { throw new NullPointerException("Method for '" + algorithm + "' is null"); } From 491dc06de4184234f17d0d072fbc5c92209e77ff Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 2 May 2016 19:57:54 +0200 Subject: [PATCH 8/8] Fix RegisterAdminCommand test verifying that online player is kicked --- .../authme/RegisterAdminCommand.java | 7 +++---- .../authme/RegisterAdminCommandTest.java | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java index d57581ff5..5ddd0b951 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java @@ -63,13 +63,12 @@ public class RegisterAdminCommand implements ExecutableCommand { commandService.send(sender, MessageKey.REGISTER_SUCCESS); ConsoleLogger.info(sender.getName() + " registered " + playerName); - Player player = commandService.getPlayer(playerName); + final Player player = commandService.getPlayer(playerName); if (player != null) { - final Player p = player; - p.getServer().getScheduler().scheduleSyncDelayedTask(commandService.getAuthMe(), new Runnable() { + commandService.getBukkitService().scheduleSyncDelayedTask(new Runnable() { @Override public void run() { - p.kickPlayer("An admin just registered you, please log in again"); + player.kickPlayer("An admin just registered you, please log in again"); } }); } diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java index 89fc3da61..47f4de1bf 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java @@ -7,7 +7,9 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.util.BukkitService; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -18,10 +20,13 @@ import org.mockito.runners.MockitoJUnitRunner; import java.util.Arrays; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -143,10 +148,15 @@ public class RegisterAdminCommandTest { given(dataSource.saveAuth(any(PlayerAuth.class))).willReturn(true); HashedPassword hashedPassword = new HashedPassword("$aea2345EW235dfsa@#R%987048"); given(passwordSecurity.computeHash(password, user)).willReturn(hashedPassword); + Player player = mock(Player.class); + given(commandService.getPlayer(user)).willReturn(player); + BukkitService bukkitService = mock(BukkitService.class); + given(commandService.getBukkitService()).willReturn(bukkitService); // when command.executeCommand(sender, Arrays.asList(user, password), commandService); TestHelper.runInnerRunnable(commandService); + runSyncDelayedTask(bukkitService); // then verify(commandService).validatePassword(password, user); @@ -155,6 +165,7 @@ public class RegisterAdminCommandTest { verify(dataSource).saveAuth(captor.capture()); assertAuthHasInfo(captor.getValue(), user, hashedPassword); verify(dataSource).setUnlogged(user); + verify(player).kickPlayer(argThat(containsString("please log in again"))); } private void assertAuthHasInfo(PlayerAuth auth, String name, HashedPassword hashedPassword) { @@ -162,4 +173,11 @@ public class RegisterAdminCommandTest { assertThat(auth.getNickname(), equalTo(name.toLowerCase())); assertThat(auth.getPassword(), equalTo(hashedPassword)); } + + private static void runSyncDelayedTask(BukkitService bukkitService) { + ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); + verify(bukkitService).scheduleSyncDelayedTask(captor.capture()); + Runnable runnable = captor.getValue(); + runnable.run(); + } }