From a2fe62e3ff935523eca60b059eb147f66c2bb132 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Mon, 20 Jan 2020 17:40:37 +0100 Subject: [PATCH 1/8] Start working on thread safety of on join checks --- .../xephi/authme/annotation/MightBeAsync.java | 11 ++++ .../authme/annotation/ShouldBeAsync.java | 11 ++++ .../java/fr/xephi/authme/annotation/Sync.java | 11 ++++ .../xephi/authme/listener/OnJoinVerifier.java | 10 +++ .../xephi/authme/listener/PlayerListener.java | 9 ++- .../xephi/authme/service/AntiBotService.java | 66 +++++++++---------- .../fr/xephi/authme/util/AtomicCounter.java | 34 ++++++++++ 7 files changed, 117 insertions(+), 35 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/annotation/MightBeAsync.java create mode 100644 src/main/java/fr/xephi/authme/annotation/ShouldBeAsync.java create mode 100644 src/main/java/fr/xephi/authme/annotation/Sync.java create mode 100644 src/main/java/fr/xephi/authme/util/AtomicCounter.java diff --git a/src/main/java/fr/xephi/authme/annotation/MightBeAsync.java b/src/main/java/fr/xephi/authme/annotation/MightBeAsync.java new file mode 100644 index 000000000..fdb72aa11 --- /dev/null +++ b/src/main/java/fr/xephi/authme/annotation/MightBeAsync.java @@ -0,0 +1,11 @@ +package fr.xephi.authme.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +public @interface MightBeAsync { +} diff --git a/src/main/java/fr/xephi/authme/annotation/ShouldBeAsync.java b/src/main/java/fr/xephi/authme/annotation/ShouldBeAsync.java new file mode 100644 index 000000000..f69afe6d4 --- /dev/null +++ b/src/main/java/fr/xephi/authme/annotation/ShouldBeAsync.java @@ -0,0 +1,11 @@ +package fr.xephi.authme.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ShouldBeAsync { +} diff --git a/src/main/java/fr/xephi/authme/annotation/Sync.java b/src/main/java/fr/xephi/authme/annotation/Sync.java new file mode 100644 index 000000000..f0aa18585 --- /dev/null +++ b/src/main/java/fr/xephi/authme/annotation/Sync.java @@ -0,0 +1,11 @@ +package fr.xephi.authme.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Sync { +} diff --git a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java index ce61b8f2b..8c3a10eff 100644 --- a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java +++ b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java @@ -1,6 +1,8 @@ package fr.xephi.authme.listener; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.ShouldBeAsync; +import fr.xephi.authme.annotation.Sync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.Reloadable; @@ -71,6 +73,7 @@ public void reload() { * @param isAuthAvailable whether or not the player is registered * @throws FailedVerificationException if the verification fails */ + @ShouldBeAsync public void checkAntibot(String name, boolean isAuthAvailable) throws FailedVerificationException { if (isAuthAvailable || permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)) { return; @@ -87,6 +90,7 @@ public void checkAntibot(String name, boolean isAuthAvailable) throws FailedVeri * @param isAuthAvailable whether or not the player is registered * @throws FailedVerificationException if the verification fails */ + @ShouldBeAsync public void checkKickNonRegistered(boolean isAuthAvailable) throws FailedVerificationException { if (!isAuthAvailable && settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)) { throw new FailedVerificationException(MessageKey.MUST_REGISTER_MESSAGE); @@ -99,6 +103,7 @@ public void checkKickNonRegistered(boolean isAuthAvailable) throws FailedVerific * @param name the name to verify * @throws FailedVerificationException if the verification fails */ + @ShouldBeAsync public void checkIsValidName(String name) throws FailedVerificationException { if (name.length() > settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH) || name.length() < settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)) { @@ -119,6 +124,7 @@ public void checkIsValidName(String name) throws FailedVerificationException { * @return true if the player's connection should be refused (i.e. the event does not need to be processed * further), false if the player is not refused */ + @Sync public boolean refusePlayerForFullServer(PlayerLoginEvent event) { final Player player = event.getPlayer(); if (event.getResult() != PlayerLoginEvent.Result.KICK_FULL) { @@ -155,6 +161,7 @@ public boolean refusePlayerForFullServer(PlayerLoginEvent event) { * @param auth the auth object associated with the player * @throws FailedVerificationException if the verification fails */ + @ShouldBeAsync public void checkNameCasing(String connectingName, PlayerAuth auth) throws FailedVerificationException { if (auth != null && settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)) { String realName = auth.getRealName(); // might be null or "Player" @@ -175,6 +182,7 @@ public void checkNameCasing(String connectingName, PlayerAuth auth) throws Faile * @param isAuthAvailable whether or not the user is registered * @throws FailedVerificationException if the verification fails */ + @ShouldBeAsync public void checkPlayerCountry(String name, String address, boolean isAuthAvailable) throws FailedVerificationException { if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)) @@ -192,6 +200,7 @@ public void checkPlayerCountry(String name, String address, * @param name the player name to check * @throws FailedVerificationException if the verification fails */ + @Sync public void checkSingleSession(String name) throws FailedVerificationException { if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) { return; @@ -210,6 +219,7 @@ public void checkSingleSession(String name) throws FailedVerificationException { * * @return the player to kick, or null if none applicable */ + @Sync private Player generateKickPlayer(Collection onlinePlayers) { for (Player player : onlinePlayers) { if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) { diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 34cf62a6f..a96f6263e 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -112,7 +112,6 @@ public void onAsyncPlayerPreLoginEventLowest(AsyncPlayerPreLoginEvent event) { // Non-blocking checks try { - onJoinVerifier.checkSingleSession(name); onJoinVerifier.checkIsValidName(name); } catch (FailedVerificationException e) { event.setKickMessage(messages.retrieveSingle(name, e.getReason(), e.getArgs())); @@ -161,6 +160,14 @@ public void onPlayerLogin(PlayerLoginEvent event) { final Player player = event.getPlayer(); final String name = player.getName(); + try { + onJoinVerifier.checkSingleSession(name); + } catch (FailedVerificationException e) { + event.setKickMessage(messages.retrieveSingle(name, e.getReason(), e.getArgs())); + event.setResult(PlayerLoginEvent.Result.KICK_OTHER); + return; + } + if (validationService.isUnrestricted(name)) { return; } diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java index b65400992..e3a1ab089 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -1,5 +1,8 @@ package fr.xephi.authme.service; +import fr.xephi.authme.annotation.MightBeAsync; +import fr.xephi.authme.annotation.ShouldBeAsync; +import fr.xephi.authme.annotation.Sync; import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; @@ -7,11 +10,10 @@ import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; +import fr.xephi.authme.util.AtomicCounter; import org.bukkit.scheduler.BukkitTask; import javax.inject.Inject; -import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.concurrent.CopyOnWriteArrayList; import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; @@ -29,14 +31,11 @@ public class AntiBotService implements SettingsDependent { private final CopyOnWriteArrayList antibotKicked = new CopyOnWriteArrayList<>(); // Settings private int duration; - private int sensibility; - private int interval; // Service status private AntiBotStatus antiBotStatus; private boolean startup; private BukkitTask disableTask; - private Instant lastFlaggedJoin; - private int flagged = 0; + private AtomicCounter flaggedCounter; @Inject AntiBotService(Settings settings, Messages messages, PermissionsManager permissionsManager, @@ -47,7 +46,6 @@ public class AntiBotService implements SettingsDependent { this.bukkitService = bukkitService; // Initial status disableTask = null; - flagged = 0; antiBotStatus = AntiBotStatus.DISABLED; startup = true; // Load settings and start if required @@ -58,8 +56,9 @@ public class AntiBotService implements SettingsDependent { public void reload(Settings settings) { // Load settings duration = settings.getProperty(ProtectionSettings.ANTIBOT_DURATION); - sensibility = settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY); - interval = settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL); + int sensibility = settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY); + int interval = settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL) * 1000; + flaggedCounter = new AtomicCounter(sensibility, interval); // Stop existing protection stopProtection(); @@ -83,24 +82,32 @@ public void reload(Settings settings) { } } - private void startProtection() { - // Disable existing antibot session - stopProtection(); - // Enable the new session - antiBotStatus = AntiBotStatus.ACTIVE; - - // Inform admins - bukkitService.getOnlinePlayers().stream() - .filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) - .forEach(player -> messages.send(player, MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE)); - + /** + * Transitions the anti bot service to an active status. + */ + @MightBeAsync + private synchronized void startProtection() { + if (antiBotStatus == AntiBotStatus.ACTIVE) { + return; // Already activating/active + } + if (disableTask != null) { + disableTask.cancel(); + } // Schedule auto-disable disableTask = bukkitService.runTaskLater(this::stopProtection, duration * TICKS_PER_MINUTE); + antiBotStatus = AntiBotStatus.ACTIVE; + bukkitService.scheduleSyncDelayedTask(() -> { + // Inform admins + bukkitService.getOnlinePlayers().stream() + .filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) + .forEach(player -> messages.send(player, MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE)); + }); } /** * Transitions the anti bot service from active status back to listening. */ + @Sync private void stopProtection() { if (antiBotStatus != AntiBotStatus.ACTIVE) { return; @@ -108,7 +115,7 @@ private void stopProtection() { // Change status antiBotStatus = AntiBotStatus.LISTENING; - flagged = 0; + flaggedCounter.reset(); antibotKicked.clear(); // Cancel auto-disable task @@ -151,24 +158,14 @@ public void overrideAntiBotStatus(boolean started) { * * @return if the player should be kicked */ + @ShouldBeAsync public boolean shouldKick() { if (antiBotStatus == AntiBotStatus.DISABLED) { return false; } else if (antiBotStatus == AntiBotStatus.ACTIVE) { return true; } - - if (lastFlaggedJoin == null) { - lastFlaggedJoin = Instant.now(); - } - if (ChronoUnit.SECONDS.between(lastFlaggedJoin, Instant.now()) <= interval) { - flagged++; - } else { - // reset to 1 because this player is also count as not registered - flagged = 1; - lastFlaggedJoin = null; - } - if (flagged > sensibility) { + if (flaggedCounter.handle()) { startProtection(); return true; } @@ -180,9 +177,9 @@ public boolean shouldKick() { * when antibot is deactivated. * * @param name the name to check - * * @return true if the given name has been kicked because of Antibot */ + @MightBeAsync public boolean wasPlayerKicked(String name) { return antibotKicked.contains(name.toLowerCase()); } @@ -193,6 +190,7 @@ public boolean wasPlayerKicked(String name) { * * @param name the name to add */ + @MightBeAsync public void addPlayerKick(String name) { antibotKicked.addIfAbsent(name.toLowerCase()); } diff --git a/src/main/java/fr/xephi/authme/util/AtomicCounter.java b/src/main/java/fr/xephi/authme/util/AtomicCounter.java new file mode 100644 index 000000000..a5b929e65 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/AtomicCounter.java @@ -0,0 +1,34 @@ +package fr.xephi.authme.util; + +public class AtomicCounter { + private final int threshold; + private final int interval; + private int count; + private long lastInsert; + + public AtomicCounter(int threshold, int interval) { + this.threshold = threshold; + this.interval = interval; + reset(); + } + + public synchronized void reset() { + count = 0; + lastInsert = 0; + } + + public synchronized boolean handle() { + long now = System.currentTimeMillis(); + if (now - lastInsert > interval) { + count = 1; + } else { + count++; + } + if (count > threshold) { + reset(); + return true; + } + lastInsert = now; + return false; + } +} From e1f893afe38883fbcecb4b40deeecb1cee817cee Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 21 Jan 2020 11:27:28 +0100 Subject: [PATCH 2/8] Fix some unit testing --- .../fr/xephi/authme/service/AntiBotService.java | 6 +++--- ...cCounter.java => AtomicIntervalCounter.java} | 4 ++-- .../authme/listener/PlayerListenerTest.java | 17 ++++++++++------- 3 files changed, 15 insertions(+), 12 deletions(-) rename src/main/java/fr/xephi/authme/util/{AtomicCounter.java => AtomicIntervalCounter.java} (87%) diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java index e3a1ab089..14e1fd51e 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -10,7 +10,7 @@ import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; -import fr.xephi.authme.util.AtomicCounter; +import fr.xephi.authme.util.AtomicIntervalCounter; import org.bukkit.scheduler.BukkitTask; import javax.inject.Inject; @@ -35,7 +35,7 @@ public class AntiBotService implements SettingsDependent { private AntiBotStatus antiBotStatus; private boolean startup; private BukkitTask disableTask; - private AtomicCounter flaggedCounter; + private AtomicIntervalCounter flaggedCounter; @Inject AntiBotService(Settings settings, Messages messages, PermissionsManager permissionsManager, @@ -58,7 +58,7 @@ public void reload(Settings settings) { duration = settings.getProperty(ProtectionSettings.ANTIBOT_DURATION); int sensibility = settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY); int interval = settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL) * 1000; - flaggedCounter = new AtomicCounter(sensibility, interval); + flaggedCounter = new AtomicIntervalCounter(sensibility, interval); // Stop existing protection stopProtection(); diff --git a/src/main/java/fr/xephi/authme/util/AtomicCounter.java b/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java similarity index 87% rename from src/main/java/fr/xephi/authme/util/AtomicCounter.java rename to src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java index a5b929e65..b7abcf850 100644 --- a/src/main/java/fr/xephi/authme/util/AtomicCounter.java +++ b/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java @@ -1,12 +1,12 @@ package fr.xephi.authme.util; -public class AtomicCounter { +public class AtomicIntervalCounter { private final int threshold; private final int interval; private int count; private long lastInsert; - public AtomicCounter(int threshold, int interval) { + public AtomicIntervalCounter(int threshold, int interval) { this.threshold = threshold; this.interval = interval; reset(); diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index e72d48be6..04f45ab1c 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -646,7 +646,7 @@ public void shouldHandlePlayerJoining() { } @Test - public void shouldNotInterfereWithUnrestrictedUser() { + public void shouldNotInterfereWithUnrestrictedUser() throws FailedVerificationException { // given String name = "Player01"; Player player = mockPlayerWithName(name); @@ -658,12 +658,13 @@ public void shouldNotInterfereWithUnrestrictedUser() { // then verify(validationService).isUnrestricted(name); + verify(onJoinVerifier).checkSingleSession(name); verifyNoModifyingCalls(event); - verifyNoInteractions(onJoinVerifier); + verifyNoMoreInteractions(onJoinVerifier); } @Test - public void shouldStopHandlingForFullServer() { + public void shouldStopHandlingForFullServer() throws FailedVerificationException { // given String name = "someone"; Player player = mockPlayerWithName(name); @@ -676,12 +677,14 @@ public void shouldStopHandlingForFullServer() { // then verify(validationService).isUnrestricted(name); - verify(onJoinVerifier, only()).refusePlayerForFullServer(event); + verify(onJoinVerifier).checkSingleSession(name); + verify(onJoinVerifier).refusePlayerForFullServer(event); + verifyNoMoreInteractions(onJoinVerifier); verifyNoModifyingCalls(event); } @Test - public void shouldStopHandlingEventForBadResult() { + public void shouldStopHandlingEventForBadResult() throws FailedVerificationException { // given String name = "someone"; Player player = mockPlayerWithName(name); @@ -696,7 +699,8 @@ public void shouldStopHandlingEventForBadResult() { // then verify(validationService).isUnrestricted(name); - verify(onJoinVerifier, only()).refusePlayerForFullServer(event); + verify(onJoinVerifier).checkSingleSession(name); + verify(onJoinVerifier).refusePlayerForFullServer(event); verifyNoModifyingCalls(event); } @@ -715,7 +719,6 @@ public void shouldPerformAllJoinVerificationsSuccessfullyPreLoginLowest() throws // then verify(validationService).isUnrestricted(name); - verify(onJoinVerifier).checkSingleSession(name); verify(onJoinVerifier).checkIsValidName(name); verifyNoInteractions(dataSource); verifyNoModifyingCalls(preLoginEvent); From 3697a34fe0cd06d018157cff7577f7db95984a83 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 28 Jan 2020 00:45:36 +0100 Subject: [PATCH 3/8] Add async/sync catchers --- .../fr/xephi/authme/ThreadSafetyUtils.java | 25 ++++++++ .../java/fr/xephi/authme/annotation/Sync.java | 11 ---- .../executable/authme/AccountsCommand.java | 54 ++++++++-------- .../executable/authme/ConverterCommand.java | 15 ++--- .../executable/authme/PurgePlayerCommand.java | 5 +- .../datasource/AbstractSqlDataSource.java | 42 +++++++++++++ .../authme/datasource/CacheDataSource.java | 61 +++++++++++++++++++ .../xephi/authme/datasource/DataSource.java | 33 ++++++++++ .../datasource/converter/Converter.java | 3 + .../authme/initialization/OnStartupTasks.java | 14 +++-- .../authme/listener/ListenerService.java | 3 + .../xephi/authme/listener/OnJoinVerifier.java | 16 +++-- .../xephi/authme/listener/PlayerListener.java | 6 ++ .../fr/xephi/authme/mail/EmailService.java | 14 +++++ .../fr/xephi/authme/message/Messages.java | 9 +++ .../xephi/authme/service/AntiBotService.java | 6 +- .../xephi/authme/service/BukkitService.java | 2 + .../xephi/authme/service/CommonService.java | 4 ++ .../fr/xephi/authme/service/GeoIpService.java | 4 ++ .../service/PasswordRecoveryService.java | 8 +++ .../authme/service/ValidationService.java | 2 + .../authme/task/purge/PurgeExecutor.java | 5 ++ .../xephi/authme/task/purge/PurgeService.java | 6 +- .../fr/xephi/authme/task/purge/PurgeTask.java | 4 ++ .../fr/xephi/authme/util/PlayerUtils.java | 3 + 25 files changed, 290 insertions(+), 65 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/ThreadSafetyUtils.java delete mode 100644 src/main/java/fr/xephi/authme/annotation/Sync.java diff --git a/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java b/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java new file mode 100644 index 000000000..43fa36fad --- /dev/null +++ b/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java @@ -0,0 +1,25 @@ +package fr.xephi.authme; + +import org.bukkit.Bukkit; + +public final class ThreadSafetyUtils { + + private ThreadSafetyUtils() { + } + + public static void requireSync() { + if (Bukkit.isPrimaryThread()) { + return; + } + System.err.println("Async call to sync method detected!"); + new Throwable().printStackTrace(); + } + + public static void shouldBeAsync() { + if (!Bukkit.isPrimaryThread()) { + return; + } + System.err.println("Sync call to async method detected!"); + new Throwable().printStackTrace(); + } +} diff --git a/src/main/java/fr/xephi/authme/annotation/Sync.java b/src/main/java/fr/xephi/authme/annotation/Sync.java deleted file mode 100644 index f0aa18585..000000000 --- a/src/main/java/fr/xephi/authme/annotation/Sync.java +++ /dev/null @@ -1,11 +0,0 @@ -package fr.xephi.authme.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Sync { -} 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 9f24592f3..2a87802eb 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 @@ -32,40 +32,34 @@ public void executeCommand(final CommandSender sender, List arguments) { // Assumption: a player name cannot contain '.' if (playerName.contains(".")) { - bukkitService.runTaskAsynchronously(new Runnable() { - @Override - public void run() { - List accountList = dataSource.getAllAuthsByIp(playerName); - if (accountList.isEmpty()) { - sender.sendMessage("[AuthMe] This IP does not exist in the database."); - } else if (accountList.size() == 1) { - sender.sendMessage("[AuthMe] " + playerName + " is a single account player"); - } else { - outputAccountsList(sender, playerName, accountList); - } + bukkitService.runTaskAsynchronously(() -> { + List accountList = dataSource.getAllAuthsByIp(playerName); + if (accountList.isEmpty()) { + sender.sendMessage("[AuthMe] This IP does not exist in the database."); + } else if (accountList.size() == 1) { + sender.sendMessage("[AuthMe] " + playerName + " is a single account player"); + } else { + outputAccountsList(sender, playerName, accountList); } }); } else { - bukkitService.runTaskAsynchronously(new Runnable() { - @Override - public void run() { - PlayerAuth auth = dataSource.getAuth(playerName.toLowerCase()); - if (auth == null) { - commonService.send(sender, MessageKey.UNKNOWN_USER); - return; - } else if (auth.getLastIp() == null) { - sender.sendMessage("No known last IP address for player"); - return; - } + bukkitService.runTaskAsynchronously(() -> { + PlayerAuth auth = dataSource.getAuth(playerName.toLowerCase()); + if (auth == null) { + commonService.send(sender, MessageKey.UNKNOWN_USER); + return; + } else if (auth.getLastIp() == null) { + sender.sendMessage("No known last IP address for player"); + return; + } - List accountList = dataSource.getAllAuthsByIp(auth.getLastIp()); - if (accountList.isEmpty()) { - commonService.send(sender, MessageKey.UNKNOWN_USER); - } else if (accountList.size() == 1) { - sender.sendMessage("[AuthMe] " + playerName + " is a single account player"); - } else { - outputAccountsList(sender, playerName, accountList); - } + List accountList = dataSource.getAllAuthsByIp(auth.getLastIp()); + if (accountList.isEmpty()) { + commonService.send(sender, MessageKey.UNKNOWN_USER); + } else if (accountList.size() == 1) { + sender.sendMessage("[AuthMe] " + playerName + " is a single account player"); + } else { + outputAccountsList(sender, playerName, accountList); } }); } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java index b95eda17d..fd0a73513 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java @@ -55,15 +55,12 @@ public void executeCommand(CommandSender sender, List arguments) { final Converter converter = converterFactory.newInstance(converterClass); // Run the convert job - bukkitService.runTaskAsynchronously(new Runnable() { - @Override - public void run() { - try { - converter.execute(sender); - } catch (Exception e) { - commonService.send(sender, MessageKey.ERROR); - logger.logException("Error during conversion:", e); - } + bukkitService.runTaskAsynchronously(() -> { + try { + converter.execute(sender); + } catch (Exception e) { + commonService.send(sender, MessageKey.ERROR); + logger.logException("Error during conversion:", e); } }); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java index 0905458a8..435792c49 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java @@ -1,5 +1,7 @@ package fr.xephi.authme.command.executable.authme; +import fr.xephi.authme.annotation.MightBeAsync; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.service.BukkitService; @@ -29,10 +31,11 @@ public class PurgePlayerCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments) { String option = arguments.size() > 1 ? arguments.get(1) : null; - bukkitService.runTaskOptionallyAsync( + bukkitService.runTaskAsynchronously( () -> executeCommand(sender, arguments.get(0), option)); } + @ShouldBeAsync private void executeCommand(CommandSender sender, String name, String option) { if ("force".equals(option) || !dataSource.isAuthAvailable(name)) { OfflinePlayer offlinePlayer = bukkitService.getOfflinePlayer(name); diff --git a/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java b/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java index 067851684..1928d1b6d 100644 --- a/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java @@ -4,6 +4,8 @@ import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import ch.jalu.datasourcecolumns.data.DataSourceValues; import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler; @@ -27,7 +29,9 @@ public abstract class AbstractSqlDataSource implements DataSource { protected AuthMeColumnsHandler columnsHandler; @Override + @ShouldBeAsync public boolean isAuthAvailable(String user) { + ThreadSafetyUtils.shouldBeAsync(); try { return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists(); } catch (SQLException e) { @@ -37,7 +41,9 @@ public boolean isAuthAvailable(String user) { } @Override + @ShouldBeAsync public HashedPassword getPassword(String user) { + ThreadSafetyUtils.shouldBeAsync(); try { DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); if (values.rowExists()) { @@ -50,7 +56,9 @@ public HashedPassword getPassword(String user) { } @Override + @ShouldBeAsync public boolean saveAuth(PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); return columnsHandler.insert(auth, AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT, AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP, @@ -58,7 +66,9 @@ public boolean saveAuth(PlayerAuth auth) { } @Override + @ShouldBeAsync public boolean hasSession(String user) { + ThreadSafetyUtils.shouldBeAsync(); try { DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION); return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); @@ -69,31 +79,41 @@ public boolean hasSession(String user) { } @Override + @ShouldBeAsync public boolean updateSession(PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME); } @Override + @ShouldBeAsync public boolean updatePassword(PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); return updatePassword(auth.getNickname(), auth.getPassword()); } @Override + @ShouldBeAsync public boolean updatePassword(String user, HashedPassword password) { + ThreadSafetyUtils.shouldBeAsync(); return columnsHandler.update(user, with(AuthMeColumns.PASSWORD, password.getHash()) .and(AuthMeColumns.SALT, password.getSalt()).build()); } @Override + @ShouldBeAsync public boolean updateQuitLoc(PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z, AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH); } @Override + @ShouldBeAsync public List getAllAuthsByIp(String ip) { + ThreadSafetyUtils.shouldBeAsync(); try { return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME); } catch (SQLException e) { @@ -103,17 +123,23 @@ public List getAllAuthsByIp(String ip) { } @Override + @ShouldBeAsync public int countAuthsByEmail(String email) { + ThreadSafetyUtils.shouldBeAsync(); return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email)); } @Override + @ShouldBeAsync public boolean updateEmail(PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.EMAIL); } @Override + @ShouldBeAsync public boolean isLogged(String user) { + ThreadSafetyUtils.shouldBeAsync(); try { DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.IS_LOGGED); return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); @@ -124,42 +150,58 @@ public boolean isLogged(String user) { } @Override + @ShouldBeAsync public void setLogged(String user) { + ThreadSafetyUtils.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 1); } @Override + @ShouldBeAsync public void setUnlogged(String user) { + ThreadSafetyUtils.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 0); } @Override + @ShouldBeAsync public void grantSession(String user) { + ThreadSafetyUtils.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1); } @Override + @ShouldBeAsync public void revokeSession(String user) { + ThreadSafetyUtils.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0); } @Override + @ShouldBeAsync public void purgeLogged() { + ThreadSafetyUtils.shouldBeAsync(); columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0); } @Override + @ShouldBeAsync public int getAccountsRegistered() { + ThreadSafetyUtils.shouldBeAsync(); return columnsHandler.count(new AlwaysTruePredicate<>()); } @Override + @ShouldBeAsync public boolean updateRealName(String user, String realName) { + ThreadSafetyUtils.shouldBeAsync(); return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName); } @Override + @ShouldBeAsync public DataSourceValue getEmail(String user) { + ThreadSafetyUtils.shouldBeAsync(); try { return columnsHandler.retrieve(user, AuthMeColumns.EMAIL); } catch (SQLException e) { diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index e1418dcaf..979baa0f7 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -10,6 +10,8 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -80,12 +82,16 @@ public boolean isCached() { } @Override + @ShouldBeAsync public boolean isAuthAvailable(String user) { + ThreadSafetyUtils.shouldBeAsync(); return getAuth(user) != null; } @Override + @ShouldBeAsync public HashedPassword getPassword(String user) { + ThreadSafetyUtils.shouldBeAsync(); user = user.toLowerCase(); Optional pAuthOpt = cachedAuths.getIfPresent(user); if (pAuthOpt != null && pAuthOpt.isPresent()) { @@ -95,13 +101,17 @@ public HashedPassword getPassword(String user) { } @Override + @ShouldBeAsync public PlayerAuth getAuth(String user) { + ThreadSafetyUtils.shouldBeAsync(); user = user.toLowerCase(); return cachedAuths.getUnchecked(user).orElse(null); } @Override + @ShouldBeAsync public boolean saveAuth(PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); boolean result = source.saveAuth(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -110,7 +120,9 @@ public boolean saveAuth(PlayerAuth auth) { } @Override + @ShouldBeAsync public boolean updatePassword(PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); boolean result = source.updatePassword(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -119,7 +131,9 @@ public boolean updatePassword(PlayerAuth auth) { } @Override + @ShouldBeAsync public boolean updatePassword(String user, HashedPassword password) { + ThreadSafetyUtils.shouldBeAsync(); user = user.toLowerCase(); boolean result = source.updatePassword(user, password); if (result) { @@ -129,7 +143,9 @@ public boolean updatePassword(String user, HashedPassword password) { } @Override + @ShouldBeAsync public boolean updateSession(PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); boolean result = source.updateSession(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -138,7 +154,9 @@ public boolean updateSession(PlayerAuth auth) { } @Override + @ShouldBeAsync public boolean updateQuitLoc(final PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); boolean result = source.updateQuitLoc(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -147,12 +165,16 @@ public boolean updateQuitLoc(final PlayerAuth auth) { } @Override + @ShouldBeAsync public Set getRecordsToPurge(long until) { + ThreadSafetyUtils.shouldBeAsync(); return source.getRecordsToPurge(until); } @Override + @ShouldBeAsync public boolean removeAuth(String name) { + ThreadSafetyUtils.shouldBeAsync(); name = name.toLowerCase(); boolean result = source.removeAuth(name); if (result) { @@ -174,7 +196,9 @@ public void closeConnection() { } @Override + @ShouldBeAsync public boolean updateEmail(final PlayerAuth auth) { + ThreadSafetyUtils.shouldBeAsync(); boolean result = source.updateEmail(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -183,69 +207,94 @@ public boolean updateEmail(final PlayerAuth auth) { } @Override + @ShouldBeAsync public List getAllAuthsByIp(String ip) { + ThreadSafetyUtils.shouldBeAsync(); return source.getAllAuthsByIp(ip); } @Override + @ShouldBeAsync public int countAuthsByEmail(String email) { + ThreadSafetyUtils.shouldBeAsync(); return source.countAuthsByEmail(email); } @Override + @ShouldBeAsync public void purgeRecords(Collection banned) { + ThreadSafetyUtils.shouldBeAsync(); source.purgeRecords(banned); cachedAuths.invalidateAll(banned); } @Override public DataSourceType getType() { + ThreadSafetyUtils.shouldBeAsync(); return source.getType(); } @Override + @ShouldBeAsync public boolean isLogged(String user) { + ThreadSafetyUtils.shouldBeAsync(); return source.isLogged(user); } @Override + @ShouldBeAsync public void setLogged(final String user) { + ThreadSafetyUtils.shouldBeAsync(); source.setLogged(user.toLowerCase()); } @Override + @ShouldBeAsync public void setUnlogged(final String user) { + ThreadSafetyUtils.shouldBeAsync(); source.setUnlogged(user.toLowerCase()); } @Override + @ShouldBeAsync public boolean hasSession(final String user) { + ThreadSafetyUtils.shouldBeAsync(); return source.hasSession(user); } @Override + @ShouldBeAsync public void grantSession(final String user) { + ThreadSafetyUtils.shouldBeAsync(); source.grantSession(user); } @Override + @ShouldBeAsync public void revokeSession(final String user) { + ThreadSafetyUtils.shouldBeAsync(); source.revokeSession(user); } @Override + @ShouldBeAsync public void purgeLogged() { + ThreadSafetyUtils.shouldBeAsync(); source.purgeLogged(); cachedAuths.invalidateAll(); } @Override + @ShouldBeAsync public int getAccountsRegistered() { + ThreadSafetyUtils.shouldBeAsync(); return source.getAccountsRegistered(); } @Override + @ShouldBeAsync public boolean updateRealName(String user, String realName) { + ThreadSafetyUtils.shouldBeAsync(); boolean result = source.updateRealName(user, realName); if (result) { cachedAuths.refresh(user); @@ -254,19 +303,25 @@ public boolean updateRealName(String user, String realName) { } @Override + @ShouldBeAsync public DataSourceValue getEmail(String user) { + ThreadSafetyUtils.shouldBeAsync(); return cachedAuths.getUnchecked(user) .map(auth -> DataSourceValueImpl.of(auth.getEmail())) .orElse(DataSourceValueImpl.unknownRow()); } @Override + @ShouldBeAsync public List getAllAuths() { + ThreadSafetyUtils.shouldBeAsync(); return source.getAllAuths(); } @Override + @ShouldBeAsync public List getLoggedPlayersWithEmptyMail() { + ThreadSafetyUtils.shouldBeAsync(); return playerCache.getCache().values().stream() .filter(auth -> Utils.isEmailEmpty(auth.getEmail())) .map(PlayerAuth::getRealName) @@ -274,12 +329,16 @@ public List getLoggedPlayersWithEmptyMail() { } @Override + @ShouldBeAsync public List getRecentlyLoggedInPlayers() { + ThreadSafetyUtils.shouldBeAsync(); return source.getRecentlyLoggedInPlayers(); } @Override + @ShouldBeAsync public boolean setTotpKey(String user, String totpKey) { + ThreadSafetyUtils.shouldBeAsync(); boolean result = source.setTotpKey(user, totpKey); if (result) { cachedAuths.refresh(user); @@ -293,7 +352,9 @@ public void invalidateCache(String playerName) { } @Override + @ShouldBeAsync public void refreshCache(String playerName) { + ThreadSafetyUtils.shouldBeAsync(); if (cachedAuths.getIfPresent(playerName) != null) { cachedAuths.refresh(playerName); } diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index 3152eb17e..26078353d 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -1,6 +1,8 @@ package fr.xephi.authme.datasource; import ch.jalu.datasourcecolumns.data.DataSourceValue; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.security.crypts.HashedPassword; @@ -29,6 +31,7 @@ default boolean isCached() { * @param user The username to look up * @return True if there is a record, false otherwise */ + @ShouldBeAsync boolean isAuthAvailable(String user); /** @@ -37,6 +40,7 @@ default boolean isCached() { * @param user The user whose password should be retrieve * @return The password hash of the player */ + @ShouldBeAsync HashedPassword getPassword(String user); /** @@ -45,6 +49,7 @@ default boolean isCached() { * @param user The user to retrieve * @return The PlayerAuth object for the given username */ + @ShouldBeAsync PlayerAuth getAuth(String user); /** @@ -53,6 +58,7 @@ default boolean isCached() { * @param auth The new PlayerAuth to persist * @return True upon success, false upon failure */ + @ShouldBeAsync boolean saveAuth(PlayerAuth auth); /** @@ -61,6 +67,7 @@ default boolean isCached() { * @param auth The PlayerAuth object to update in the database * @return True upon success, false upon failure */ + @ShouldBeAsync boolean updateSession(PlayerAuth auth); /** @@ -69,6 +76,7 @@ default boolean isCached() { * @param auth The PlayerAuth whose password should be updated * @return True upon success, false upon failure */ + @ShouldBeAsync boolean updatePassword(PlayerAuth auth); /** @@ -78,6 +86,7 @@ default boolean isCached() { * @param password The new password * @return True upon success, false upon failure */ + @ShouldBeAsync boolean updatePassword(String user, HashedPassword password); /** @@ -86,6 +95,7 @@ default boolean isCached() { * @param until The minimum last login * @return The account names selected to purge */ + @ShouldBeAsync Set getRecordsToPurge(long until); /** @@ -93,6 +103,7 @@ default boolean isCached() { * * @param toPurge The players to purge */ + @ShouldBeAsync void purgeRecords(Collection toPurge); /** @@ -101,6 +112,7 @@ default boolean isCached() { * @param user The user to remove * @return True upon success, false upon failure */ + @ShouldBeAsync boolean removeAuth(String user); /** @@ -109,6 +121,7 @@ default boolean isCached() { * @param auth The entry whose quit location should be updated * @return True upon success, false upon failure */ + @ShouldBeAsync boolean updateQuitLoc(PlayerAuth auth); /** @@ -117,6 +130,7 @@ default boolean isCached() { * @param ip The IP address to look up * @return Usernames associated with the given IP address */ + @ShouldBeAsync List getAllAuthsByIp(String ip); /** @@ -125,6 +139,7 @@ default boolean isCached() { * @param email The email address to look up * @return Number of accounts using the given email address */ + @ShouldBeAsync int countAuthsByEmail(String email); /** @@ -133,6 +148,7 @@ default boolean isCached() { * @param auth The PlayerAuth whose email should be updated * @return True upon success, false upon failure */ + @ShouldBeAsync boolean updateEmail(PlayerAuth auth); /** @@ -153,6 +169,7 @@ default boolean isCached() { * @param user The name of the player to verify * @return True if logged in, false otherwise */ + @ShouldBeAsync boolean isLogged(String user); /** @@ -160,6 +177,7 @@ default boolean isCached() { * * @param user The name of the player to change */ + @ShouldBeAsync void setLogged(String user); /** @@ -167,6 +185,7 @@ default boolean isCached() { * * @param user The name of the player to change */ + @ShouldBeAsync void setUnlogged(String user); /** @@ -176,6 +195,7 @@ default boolean isCached() { * @param user The name of the player to verify * @return True if the user has a valid session, false otherwise */ + @ShouldBeAsync boolean hasSession(String user); /** @@ -183,6 +203,7 @@ default boolean isCached() { * * @param user The name of the player to change */ + @ShouldBeAsync void grantSession(String user); /** @@ -190,11 +211,13 @@ default boolean isCached() { * * @param user The name of the player to change */ + @ShouldBeAsync void revokeSession(String user); /** * Set all players who are marked as logged in as NOT logged in. */ + @ShouldBeAsync void purgeLogged(); /** @@ -202,6 +225,7 @@ default boolean isCached() { * * @return logged in players with no email */ + @ShouldBeAsync List getLoggedPlayersWithEmptyMail(); /** @@ -209,6 +233,7 @@ default boolean isCached() { * * @return Total number of accounts */ + @ShouldBeAsync int getAccountsRegistered(); /** @@ -218,6 +243,7 @@ default boolean isCached() { * @param realName The real name of the user (proper casing) * @return True upon success, false upon failure */ + @ShouldBeAsync boolean updateRealName(String user, String realName); /** @@ -226,6 +252,7 @@ default boolean isCached() { * @param user the user to retrieve an email for * @return the email saved for the user, or null if user or email is not present */ + @ShouldBeAsync DataSourceValue getEmail(String user); /** @@ -233,6 +260,7 @@ default boolean isCached() { * * @return List of all players */ + @ShouldBeAsync List getAllAuths(); /** @@ -240,6 +268,7 @@ default boolean isCached() { * * @return the 10 last players who last logged in */ + @ShouldBeAsync List getRecentlyLoggedInPlayers(); /** @@ -249,6 +278,7 @@ default boolean isCached() { * @param totpKey the totp key to set * @return True upon success, false upon failure */ + @ShouldBeAsync boolean setTotpKey(String user, String totpKey); /** @@ -257,6 +287,7 @@ default boolean isCached() { * @param user the name of the player to modify * @return True upon success, false upon failure */ + @ShouldBeAsync default boolean removeTotpKey(String user) { return setTotpKey(user, null); } @@ -280,7 +311,9 @@ default void invalidateCache(String playerName) { * * @param playerName the player name */ + @ShouldBeAsync default void refreshCache(String playerName) { + ThreadSafetyUtils.shouldBeAsync(); } } diff --git a/src/main/java/fr/xephi/authme/datasource/converter/Converter.java b/src/main/java/fr/xephi/authme/datasource/converter/Converter.java index 7549bb481..ca065b87a 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/Converter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/Converter.java @@ -1,5 +1,6 @@ package fr.xephi.authme.datasource.converter; +import fr.xephi.authme.annotation.ShouldBeAsync; import org.bukkit.command.CommandSender; /** @@ -9,8 +10,10 @@ public interface Converter { /** * Execute the conversion. + * TODO: apply the ShouldBeAsync annotation to all the converters * * @param sender the sender who initialized the conversion */ + @ShouldBeAsync void execute(CommandSender sender); } diff --git a/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java b/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java index 1645ae651..e1108156e 100644 --- a/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java +++ b/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java @@ -19,6 +19,7 @@ import org.bukkit.entity.Player; import javax.inject.Inject; +import java.util.List; import java.util.logging.Logger; import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; @@ -94,12 +95,15 @@ public void scheduleRecallEmailTask() { return; } bukkitService.runTaskTimerAsynchronously(() -> { - for (String playerWithoutMail : dataSource.getLoggedPlayersWithEmptyMail()) { - Player player = bukkitService.getPlayerExact(playerWithoutMail); - if (player != null) { - messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); + List loggedPlayersWithEmptyMail = dataSource.getLoggedPlayersWithEmptyMail(); + bukkitService.runTask(() -> { + for (String playerWithoutMail : loggedPlayersWithEmptyMail) { + Player player = bukkitService.getPlayerExact(playerWithoutMail); + if (player != null) { + messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); + } } - } + }); }, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL)); } } diff --git a/src/main/java/fr/xephi/authme/listener/ListenerService.java b/src/main/java/fr/xephi/authme/listener/ListenerService.java index d283f3e4e..45033d1e7 100644 --- a/src/main/java/fr/xephi/authme/listener/ListenerService.java +++ b/src/main/java/fr/xephi/authme/listener/ListenerService.java @@ -1,5 +1,6 @@ package fr.xephi.authme.listener; +import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.SettingsDependent; @@ -76,6 +77,7 @@ public boolean shouldCancelEvent(PlayerEvent event) { * @param player the player to verify * @return true if the associated event should be canceled, false otherwise */ + @MightBeAsync public boolean shouldCancelEvent(Player player) { return player != null && !checkAuth(player.getName()) && !PlayerUtils.isNpc(player); } @@ -92,6 +94,7 @@ public void reload(Settings settings) { * @param name the name of the player to verify * @return true if the player may play, false otherwise */ + @MightBeAsync private boolean checkAuth(String name) { if (validationService.isUnrestricted(name) || playerCache.isAuthenticated(name)) { return true; diff --git a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java index 8c3a10eff..5ecb86b1b 100644 --- a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java +++ b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java @@ -1,8 +1,9 @@ package fr.xephi.authme.listener; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; -import fr.xephi.authme.annotation.Sync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.Reloadable; @@ -75,6 +76,7 @@ public void reload() { */ @ShouldBeAsync public void checkAntibot(String name, boolean isAuthAvailable) throws FailedVerificationException { + ThreadSafetyUtils.shouldBeAsync(); if (isAuthAvailable || permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)) { return; } @@ -90,7 +92,7 @@ public void checkAntibot(String name, boolean isAuthAvailable) throws FailedVeri * @param isAuthAvailable whether or not the player is registered * @throws FailedVerificationException if the verification fails */ - @ShouldBeAsync + @MightBeAsync public void checkKickNonRegistered(boolean isAuthAvailable) throws FailedVerificationException { if (!isAuthAvailable && settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)) { throw new FailedVerificationException(MessageKey.MUST_REGISTER_MESSAGE); @@ -103,7 +105,7 @@ public void checkKickNonRegistered(boolean isAuthAvailable) throws FailedVerific * @param name the name to verify * @throws FailedVerificationException if the verification fails */ - @ShouldBeAsync + @MightBeAsync public void checkIsValidName(String name) throws FailedVerificationException { if (name.length() > settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH) || name.length() < settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)) { @@ -124,8 +126,8 @@ public void checkIsValidName(String name) throws FailedVerificationException { * @return true if the player's connection should be refused (i.e. the event does not need to be processed * further), false if the player is not refused */ - @Sync public boolean refusePlayerForFullServer(PlayerLoginEvent event) { + ThreadSafetyUtils.requireSync(); final Player player = event.getPlayer(); if (event.getResult() != PlayerLoginEvent.Result.KICK_FULL) { // Server is not full, no need to do anything @@ -163,6 +165,7 @@ public boolean refusePlayerForFullServer(PlayerLoginEvent event) { */ @ShouldBeAsync public void checkNameCasing(String connectingName, PlayerAuth auth) throws FailedVerificationException { + ThreadSafetyUtils.shouldBeAsync(); if (auth != null && settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)) { String realName = auth.getRealName(); // might be null or "Player" @@ -185,6 +188,7 @@ public void checkNameCasing(String connectingName, PlayerAuth auth) throws Faile @ShouldBeAsync public void checkPlayerCountry(String name, String address, boolean isAuthAvailable) throws FailedVerificationException { + ThreadSafetyUtils.shouldBeAsync(); if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)) && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION) && !permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_COUNTRY_CHECK) @@ -200,8 +204,8 @@ public void checkPlayerCountry(String name, String address, * @param name the player name to check * @throws FailedVerificationException if the verification fails */ - @Sync public void checkSingleSession(String name) throws FailedVerificationException { + ThreadSafetyUtils.requireSync(); if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) { return; } @@ -219,8 +223,8 @@ public void checkSingleSession(String name) throws FailedVerificationException { * * @return the player to kick, or null if none applicable */ - @Sync private Player generateKickPlayer(Collection onlinePlayers) { + ThreadSafetyUtils.requireSync(); for (Player player : onlinePlayers) { if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) { return player; diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index a96f6263e..1a41c5236 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -1,5 +1,7 @@ package fr.xephi.authme.listener; +import fr.xephi.authme.annotation.MightBeAsync; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.QuickCommandsProtectionManager; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; @@ -93,6 +95,7 @@ public class PlayerListener implements Listener { // Lowest priority to apply fast protection checks @EventHandler(priority = EventPriority.LOWEST) + @ShouldBeAsync public void onAsyncPlayerPreLoginEventLowest(AsyncPlayerPreLoginEvent event) { if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { return; @@ -127,6 +130,7 @@ public void onAsyncPlayerPreLoginEventLowest(AsyncPlayerPreLoginEvent event) { // the permission handler, we don't need to call permissionsManager.loadUserData() @EventHandler(priority = EventPriority.HIGHEST) + @ShouldBeAsync public void onAsyncPlayerPreLoginEventHighest(AsyncPlayerPreLoginEvent event) { if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { return; @@ -264,6 +268,7 @@ public void onPlayerKick(PlayerKickEvent event) { * Chat/command events */ + @MightBeAsync private void removeUnauthorizedRecipients(AsyncPlayerChatEvent event) { if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) { event.getRecipients().removeIf(listenerService::shouldCancelEvent); @@ -274,6 +279,7 @@ private void removeUnauthorizedRecipients(AsyncPlayerChatEvent event) { } @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + @MightBeAsync public void onPlayerChat(AsyncPlayerChatEvent event) { if (settings.getProperty(RestrictionSettings.ALLOW_CHAT)) { return; diff --git a/src/main/java/fr/xephi/authme/mail/EmailService.java b/src/main/java/fr/xephi/authme/mail/EmailService.java index 6aef04337..fe2452ec0 100644 --- a/src/main/java/fr/xephi/authme/mail/EmailService.java +++ b/src/main/java/fr/xephi/authme/mail/EmailService.java @@ -1,6 +1,9 @@ package fr.xephi.authme.mail; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.MightBeAsync; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.settings.Settings; @@ -49,7 +52,9 @@ public boolean hasAllInformation() { * @param newPass the new password * @return true if email could be sent, false otherwise */ + @ShouldBeAsync public boolean sendPasswordMail(String name, String mailAddress, String newPass) { + ThreadSafetyUtils.shouldBeAsync(); if (!hasAllInformation()) { logger.warning("Cannot perform email registration: not all email settings are complete"); return false; @@ -89,7 +94,9 @@ public boolean sendPasswordMail(String name, String mailAddress, String newPass) * @param code the verification code * @return true if email could be sent, false otherwise */ + @ShouldBeAsync public boolean sendVerificationMail(String name, String mailAddress, String code) { + ThreadSafetyUtils.shouldBeAsync(); if (!hasAllInformation()) { logger.warning("Cannot send verification email: not all email settings are complete"); return false; @@ -116,7 +123,9 @@ public boolean sendVerificationMail(String name, String mailAddress, String code * @param code the recovery code * @return true if email could be sent, false otherwise */ + @ShouldBeAsync public boolean sendRecoveryCode(String name, String email, String code) { + ThreadSafetyUtils.shouldBeAsync(); HtmlEmail htmlEmail; try { htmlEmail = sendMailSsl.initializeMail(email); @@ -130,6 +139,7 @@ public boolean sendRecoveryCode(String name, String email, String code) { return sendMailSsl.sendEmail(message, htmlEmail); } + @MightBeAsync private File generatePasswordImage(String name, String newPass) throws IOException { ImageGenerator gen = new ImageGenerator(newPass); File file = new File(dataFolder, name + "_new_pass.jpg"); @@ -137,6 +147,7 @@ private File generatePasswordImage(String name, String newPass) throws IOExcepti return file; } + @MightBeAsync private static String embedImageIntoEmailContent(File image, HtmlEmail email, String content) throws EmailException { DataSource source = new FileDataSource(image); @@ -144,6 +155,7 @@ private static String embedImageIntoEmailContent(File image, HtmlEmail email, St return content.replace("", ""); } + @MightBeAsync private String replaceTagsForPasswordMail(String mailText, String name, String newPass) { return mailText .replace("", name) @@ -151,6 +163,7 @@ private String replaceTagsForPasswordMail(String mailText, String name, String n .replace("", newPass); } + @MightBeAsync private String replaceTagsForVerificationEmail(String mailText, String name, String code, int minutesValid) { return mailText .replace("", name) @@ -159,6 +172,7 @@ private String replaceTagsForVerificationEmail(String mailText, String name, Str .replace("", String.valueOf(minutesValid)); } + @MightBeAsync private String replaceTagsForRecoveryCodeMail(String mailText, String name, String code, int hoursValid) { return mailText .replace("", name) diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java index 2305de332..90a926a7a 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/src/main/java/fr/xephi/authme/message/Messages.java @@ -2,6 +2,7 @@ import com.google.common.collect.ImmutableMap; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.util.expiring.Duration; @@ -57,6 +58,7 @@ public class Messages { * @param sender The entity to send the message to * @param key The key of the message to send */ + @MightBeAsync public void send(CommandSender sender, MessageKey key) { String[] lines = retrieve(key, sender); for (String line : lines) { @@ -73,6 +75,7 @@ public void send(CommandSender sender, MessageKey key) { * @param key The key of the message to send * @param replacements The replacements to apply for the tags */ + @MightBeAsync public void send(CommandSender sender, MessageKey key, String... replacements) { String message = retrieveSingle(sender, key, replacements); for (String line : message.split("\n")) { @@ -87,6 +90,7 @@ public void send(CommandSender sender, MessageKey key, String... replacements) { * @param sender The entity to send the message to * @return The message split by new lines */ + @MightBeAsync public String[] retrieve(MessageKey key, CommandSender sender) { String message = retrieveMessage(key, sender); if (message.isEmpty()) { @@ -103,6 +107,7 @@ public String[] retrieve(MessageKey key, CommandSender sender) { * @param duration the duration to build a text of * @return text of the duration */ + @MightBeAsync public String formatDuration(Duration duration) { long value = duration.getDuration(); MessageKey timeUnitKey = value == 1 @@ -119,6 +124,7 @@ public String formatDuration(Duration duration) { * @param sender The entity to send the message to * @return The message from the file */ + @MightBeAsync private String retrieveMessage(MessageKey key, CommandSender sender) { String message = messagesFileHandler.getMessage(key.getKey()); String displayName = sender.getName(); @@ -139,6 +145,7 @@ private String retrieveMessage(MessageKey key, CommandSender sender) { * @param name The name of the entity to send the message to * @return The message from the file */ + @MightBeAsync private String retrieveMessage(MessageKey key, String name) { String message = messagesFileHandler.getMessage(key.getKey()); @@ -158,6 +165,7 @@ private String retrieveMessage(MessageKey key, String name) { * @param replacements The replacements to apply for the tags * @return The message from the file with replacements */ + @MightBeAsync public String retrieveSingle(CommandSender sender, MessageKey key, String... replacements) { String message = retrieveMessage(key, sender); String[] tags = key.getTags(); @@ -181,6 +189,7 @@ public String retrieveSingle(CommandSender sender, MessageKey key, String... rep * @param replacements The replacements to apply for the tags * @return The message from the file with replacements */ + @MightBeAsync public String retrieveSingle(String name, MessageKey key, String... replacements) { String message = retrieveMessage(key, name); String[] tags = key.getTags(); diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java index 14e1fd51e..77190aee1 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -1,8 +1,8 @@ package fr.xephi.authme.service; +import fr.xephi.authme.ThreadSafetyUtils; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; -import fr.xephi.authme.annotation.Sync; import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; @@ -107,8 +107,8 @@ private synchronized void startProtection() { /** * Transitions the anti bot service from active status back to listening. */ - @Sync private void stopProtection() { + ThreadSafetyUtils.requireSync(); if (antiBotStatus != AntiBotStatus.ACTIVE) { return; } @@ -144,6 +144,7 @@ public AntiBotStatus getAntiBotStatus() { * @param started the new protection status */ public void overrideAntiBotStatus(boolean started) { + ThreadSafetyUtils.requireSync(); if (antiBotStatus != AntiBotStatus.DISABLED) { if (started) { startProtection(); @@ -160,6 +161,7 @@ public void overrideAntiBotStatus(boolean started) { */ @ShouldBeAsync public boolean shouldKick() { + ThreadSafetyUtils.shouldBeAsync(); if (antiBotStatus == AntiBotStatus.DISABLED) { return false; } else if (antiBotStatus == AntiBotStatus.ACTIVE) { diff --git a/src/main/java/fr/xephi/authme/service/BukkitService.java b/src/main/java/fr/xephi/authme/service/BukkitService.java index a6af581ab..5e693d6f5 100644 --- a/src/main/java/fr/xephi/authme/service/BukkitService.java +++ b/src/main/java/fr/xephi/authme/service/BukkitService.java @@ -2,6 +2,7 @@ import com.google.common.collect.Iterables; import fr.xephi.authme.AuthMe; +import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; @@ -206,6 +207,7 @@ public Player getPlayerExact(String name) { * @param name the name the player to retrieve * @return an offline player */ + @MightBeAsync public OfflinePlayer getOfflinePlayer(String name) { return authMe.getServer().getOfflinePlayer(name); } diff --git a/src/main/java/fr/xephi/authme/service/CommonService.java b/src/main/java/fr/xephi/authme/service/CommonService.java index 92a49267b..01aa67956 100644 --- a/src/main/java/fr/xephi/authme/service/CommonService.java +++ b/src/main/java/fr/xephi/authme/service/CommonService.java @@ -1,6 +1,7 @@ package fr.xephi.authme.service; import ch.jalu.configme.properties.Property; +import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionNode; @@ -45,6 +46,7 @@ public T getProperty(Property property) { * @param sender the command sender * @param key the message key */ + @MightBeAsync public void send(CommandSender sender, MessageKey key) { messages.send(sender, key); } @@ -56,6 +58,7 @@ public void send(CommandSender sender, MessageKey key) { * @param key the message key * @param replacements the replacements to apply to the message */ + @MightBeAsync public void send(CommandSender sender, MessageKey key, String... replacements) { messages.send(sender, key, replacements); } @@ -67,6 +70,7 @@ public void send(CommandSender sender, MessageKey key, String... replacements) { * @param key the key of the message * @return the message */ + @MightBeAsync public String retrieveSingleMessage(CommandSender sender, MessageKey key) { return messages.retrieveSingle(sender, key); } diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index ef4f003f2..b4f900955 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -14,6 +14,8 @@ import com.maxmind.db.model.Country; import com.maxmind.db.model.CountryResponse; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.util.FileUtils; @@ -131,7 +133,9 @@ private synchronized boolean isDataAvailable() { /** * Tries to update the database by downloading a new version from the website. */ + @ShouldBeAsync private void updateDatabase() { + ThreadSafetyUtils.shouldBeAsync(); logger.info("Downloading GEO IP database, because the old database is older than " + UPDATE_INTERVAL_DAYS + " days or doesn't exist"); diff --git a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java b/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java index 20a78d7f4..4799b3406 100644 --- a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java +++ b/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java @@ -1,6 +1,9 @@ package fr.xephi.authme.service; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.MightBeAsync; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.initialization.Reloadable; @@ -67,7 +70,9 @@ private void initEmailCooldownSet() { * @param player The player getting the code. * @param email The email to send the code to. */ + @ShouldBeAsync public void createAndSendRecoveryCode(Player player, String email) { + ThreadSafetyUtils.shouldBeAsync(); if (!checkEmailCooldown(player)) { return; } @@ -89,7 +94,9 @@ public void createAndSendRecoveryCode(Player player, String email) { * @param player The player recovering their password. * @param email The email to send the password to. */ + @ShouldBeAsync public void generateAndSendNewPassword(Player player, String email) { + ThreadSafetyUtils.shouldBeAsync(); if (!checkEmailCooldown(player)) { return; } @@ -140,6 +147,7 @@ public void removeFromSuccessfulRecovery(Player player) { * @param player The player to check. * @return True if the player is not on cooldown. */ + @MightBeAsync private boolean checkEmailCooldown(Player player) { Duration waitDuration = emailCooldown.getExpiration(player.getName().toLowerCase()); if (waitDuration.getDuration() > 0) { diff --git a/src/main/java/fr/xephi/authme/service/ValidationService.java b/src/main/java/fr/xephi/authme/service/ValidationService.java index 183a3b4a6..c5dec2fd0 100644 --- a/src/main/java/fr/xephi/authme/service/ValidationService.java +++ b/src/main/java/fr/xephi/authme/service/ValidationService.java @@ -4,6 +4,7 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -138,6 +139,7 @@ public boolean isCountryAdmitted(String hostAddress) { * @param name the name to verify * @return true if unrestricted, false otherwise */ + @MightBeAsync public boolean isUnrestricted(String name) { return settings.getProperty(RestrictionSettings.UNRESTRICTED_NAMES).contains(name.toLowerCase()); } diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java index 399ff3985..d0ac1b858 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java @@ -1,6 +1,9 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.MightBeAsync; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.permission.PermissionsManager; @@ -53,7 +56,9 @@ public class PurgeExecutor { * @param players the players to purge * @param names names to purge */ + @ShouldBeAsync public void executePurge(Collection players, Collection names) { + ThreadSafetyUtils.shouldBeAsync(); // Purge other data purgeFromAuthMe(names); purgeEssentials(players); diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java index 489262883..d7ac7ef5b 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java @@ -1,6 +1,8 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.permission.PermissionsManager; @@ -99,7 +101,7 @@ public void purgePlayers(CommandSender sender, Set names, OfflinePlayer[ isPurging = true; PurgeTask purgeTask = new PurgeTask(this, permissionsManager, sender, names, players); - bukkitService.runTaskTimer(purgeTask, 0, 1); + bukkitService.runTaskTimerAsynchronously(purgeTask, 0, 1); } /** @@ -117,7 +119,9 @@ void setPurging(boolean purging) { * @param players the players (associated with the names) * @param names the lowercase names */ + @ShouldBeAsync void executePurge(Collection players, Collection names) { + ThreadSafetyUtils.shouldBeAsync(); purgeExecutor.executePurge(players, names); } } diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java index f29d6f71d..cc99e81d8 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java @@ -1,6 +1,8 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; @@ -57,7 +59,9 @@ class PurgeTask extends BukkitRunnable { } @Override + @ShouldBeAsync public void run() { + ThreadSafetyUtils.shouldBeAsync(); if (toPurge.isEmpty()) { //everything was removed finish(); diff --git a/src/main/java/fr/xephi/authme/util/PlayerUtils.java b/src/main/java/fr/xephi/authme/util/PlayerUtils.java index 04216933c..1dec6b19a 100644 --- a/src/main/java/fr/xephi/authme/util/PlayerUtils.java +++ b/src/main/java/fr/xephi/authme/util/PlayerUtils.java @@ -1,5 +1,6 @@ package fr.xephi.authme.util; +import fr.xephi.authme.annotation.MightBeAsync; import org.bukkit.entity.Player; /** @@ -23,10 +24,12 @@ public static String getPlayerIp(Player player) { /** * Returns if the player is an NPC or not. + * TODO: is player metadata thread-safe? * * @param player The player to check * @return True if the player is an NPC, false otherwise */ + @MightBeAsync public static boolean isNpc(Player player) { return player.hasMetadata("NPC"); } From 5acf9c0d94e675ac100ed4d3a014e2b41c40e64e Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 28 Jan 2020 14:36:54 +0100 Subject: [PATCH 4/8] Fix unit test --- src/main/java/fr/xephi/authme/AuthMe.java | 1 + src/main/java/fr/xephi/authme/ThreadSafetyUtils.java | 10 ++++++++-- .../java/fr/xephi/authme/service/AntiBotService.java | 2 +- .../executable/authme/PurgePlayerCommandTest.java | 7 ++++--- .../fr/xephi/authme/service/AntiBotServiceTest.java | 2 ++ .../fr/xephi/authme/task/purge/PurgeServiceTest.java | 2 +- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 045d1ee97..3ccf30aaf 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -116,6 +116,7 @@ public static String getPluginBuildNumber() { */ @Override public void onEnable() { + ThreadSafetyUtils.setEnabled(true); // Load the plugin version data from the plugin description file loadPluginInfo(getDescription().getVersion()); diff --git a/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java b/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java index 43fa36fad..0d092dd13 100644 --- a/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java +++ b/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java @@ -4,11 +4,17 @@ public final class ThreadSafetyUtils { + private static boolean enabled = false; + private ThreadSafetyUtils() { } + public static void setEnabled(boolean enabled) { + ThreadSafetyUtils.enabled = enabled; + } + public static void requireSync() { - if (Bukkit.isPrimaryThread()) { + if (!enabled || Bukkit.isPrimaryThread()) { return; } System.err.println("Async call to sync method detected!"); @@ -16,7 +22,7 @@ public static void requireSync() { } public static void shouldBeAsync() { - if (!Bukkit.isPrimaryThread()) { + if (!enabled || !Bukkit.isPrimaryThread()) { return; } System.err.println("Sync call to async method detected!"); diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java index 77190aee1..3e1ace6cb 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -96,7 +96,7 @@ private synchronized void startProtection() { // Schedule auto-disable disableTask = bukkitService.runTaskLater(this::stopProtection, duration * TICKS_PER_MINUTE); antiBotStatus = AntiBotStatus.ACTIVE; - bukkitService.scheduleSyncDelayedTask(() -> { + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { // Inform admins bukkitService.getOnlinePlayers().stream() .filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java index 370ba1294..846c4cabb 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java @@ -11,6 +11,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToRunTaskAsynchronously; import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToRunTaskOptionallyAsync; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -45,7 +46,7 @@ public void shouldNotExecutePurgeForRegisteredPlayer() { String name = "Bobby"; given(dataSource.isAuthAvailable(name)).willReturn(true); CommandSender sender = mock(CommandSender.class); - setBukkitServiceToRunTaskOptionallyAsync(bukkitService); + setBukkitServiceToRunTaskAsynchronously(bukkitService); // when command.executeCommand(sender, singletonList(name)); @@ -63,7 +64,7 @@ public void shouldExecutePurge() { OfflinePlayer player = mock(OfflinePlayer.class); given(bukkitService.getOfflinePlayer(name)).willReturn(player); CommandSender sender = mock(CommandSender.class); - setBukkitServiceToRunTaskOptionallyAsync(bukkitService); + setBukkitServiceToRunTaskAsynchronously(bukkitService); // when command.executeCommand(sender, singletonList(name)); @@ -80,7 +81,7 @@ public void shouldExecutePurgeOfRegisteredPlayer() { OfflinePlayer player = mock(OfflinePlayer.class); given(bukkitService.getOfflinePlayer(name)).willReturn(player); CommandSender sender = mock(CommandSender.class); - setBukkitServiceToRunTaskOptionallyAsync(bukkitService); + setBukkitServiceToRunTaskAsynchronously(bukkitService); // when command.executeCommand(sender, asList(name, "force")); diff --git a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java b/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java index 8a41671a3..643792547 100644 --- a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java @@ -20,6 +20,7 @@ import java.util.List; import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncDelayedTaskWithDelay; +import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -160,6 +161,7 @@ public void shouldInformPlayersOnActivation() { given(bukkitService.getOnlinePlayers()).willReturn(players); given(permissionsManager.hasPermission(players.get(0), AdminPermission.ANTIBOT_MESSAGES)).willReturn(false); given(permissionsManager.hasPermission(players.get(1), AdminPermission.ANTIBOT_MESSAGES)).willReturn(true); + setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); // when antiBotService.overrideAntiBotStatus(true); diff --git a/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java b/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java index 8b0864e59..8823f1aa1 100644 --- a/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java +++ b/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java @@ -189,7 +189,7 @@ private void assertCorrectPurgeTimestamp(long timestamp, int configuredDays) { private void verifyScheduledPurgeTask(UUID senderUuid, Set names) { ArgumentCaptor captor = ArgumentCaptor.forClass(PurgeTask.class); - verify(bukkitService).runTaskTimer(captor.capture(), eq(0L), eq(1L)); + verify(bukkitService).runTaskTimerAsynchronously(captor.capture(), eq(0L), eq(1L)); PurgeTask task = captor.getValue(); Object senderInTask = ReflectionTestUtils.getFieldValue(PurgeTask.class, task, "sender"); From 81d240f4a4b9e91a10e32f9ee6f5bfd4324ebe4a Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 28 Jan 2020 15:08:47 +0100 Subject: [PATCH 5/8] Cleanup --- src/main/java/fr/xephi/authme/AuthMe.java | 3 +- ...readSafetyUtils.java => ThreadSafety.java} | 6 +- .../datasource/AbstractSqlDataSource.java | 42 ++++++------- .../authme/datasource/CacheDataSource.java | 62 +++++++++---------- .../xephi/authme/datasource/DataSource.java | 4 +- .../xephi/authme/listener/OnJoinVerifier.java | 14 ++--- .../fr/xephi/authme/mail/EmailService.java | 8 +-- .../xephi/authme/service/AntiBotService.java | 8 +-- .../fr/xephi/authme/service/GeoIpService.java | 4 +- .../service/PasswordRecoveryService.java | 6 +- .../authme/task/purge/PurgeExecutor.java | 5 +- .../xephi/authme/task/purge/PurgeService.java | 4 +- .../fr/xephi/authme/task/purge/PurgeTask.java | 4 +- 13 files changed, 85 insertions(+), 85 deletions(-) rename src/main/java/fr/xephi/authme/{ThreadSafetyUtils.java => ThreadSafety.java} (84%) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 3ccf30aaf..8bef3823f 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -116,7 +116,6 @@ public static String getPluginBuildNumber() { */ @Override public void onEnable() { - ThreadSafetyUtils.setEnabled(true); // Load the plugin version data from the plugin description file loadPluginInfo(getDescription().getVersion()); @@ -172,6 +171,8 @@ public void onEnable() { // Successful message logger.info("AuthMe " + getPluginVersion() + " build n." + getPluginBuildNumber() + " successfully enabled!"); + // Start catching wrong sync/async calls + ThreadSafety.setEnabled(true); // Purge on start if enabled PurgeService purgeService = injector.getSingleton(PurgeService.class); diff --git a/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java b/src/main/java/fr/xephi/authme/ThreadSafety.java similarity index 84% rename from src/main/java/fr/xephi/authme/ThreadSafetyUtils.java rename to src/main/java/fr/xephi/authme/ThreadSafety.java index 0d092dd13..386a89b95 100644 --- a/src/main/java/fr/xephi/authme/ThreadSafetyUtils.java +++ b/src/main/java/fr/xephi/authme/ThreadSafety.java @@ -2,15 +2,15 @@ import org.bukkit.Bukkit; -public final class ThreadSafetyUtils { +public final class ThreadSafety { private static boolean enabled = false; - private ThreadSafetyUtils() { + private ThreadSafety() { } public static void setEnabled(boolean enabled) { - ThreadSafetyUtils.enabled = enabled; + ThreadSafety.enabled = enabled; } public static void requireSync() { diff --git a/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java b/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java index 1928d1b6d..b79831984 100644 --- a/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java @@ -4,7 +4,7 @@ import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import ch.jalu.datasourcecolumns.data.DataSourceValues; import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; @@ -31,7 +31,7 @@ public abstract class AbstractSqlDataSource implements DataSource { @Override @ShouldBeAsync public boolean isAuthAvailable(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); try { return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists(); } catch (SQLException e) { @@ -43,7 +43,7 @@ public boolean isAuthAvailable(String user) { @Override @ShouldBeAsync public HashedPassword getPassword(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); try { DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); if (values.rowExists()) { @@ -58,7 +58,7 @@ public HashedPassword getPassword(String user) { @Override @ShouldBeAsync public boolean saveAuth(PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return columnsHandler.insert(auth, AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT, AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP, @@ -68,7 +68,7 @@ public boolean saveAuth(PlayerAuth auth) { @Override @ShouldBeAsync public boolean hasSession(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); try { DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION); return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); @@ -81,21 +81,21 @@ public boolean hasSession(String user) { @Override @ShouldBeAsync public boolean updateSession(PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME); } @Override @ShouldBeAsync public boolean updatePassword(PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return updatePassword(auth.getNickname(), auth.getPassword()); } @Override @ShouldBeAsync public boolean updatePassword(String user, HashedPassword password) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return columnsHandler.update(user, with(AuthMeColumns.PASSWORD, password.getHash()) .and(AuthMeColumns.SALT, password.getSalt()).build()); @@ -104,7 +104,7 @@ public boolean updatePassword(String user, HashedPassword password) { @Override @ShouldBeAsync public boolean updateQuitLoc(PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z, AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH); @@ -113,7 +113,7 @@ public boolean updateQuitLoc(PlayerAuth auth) { @Override @ShouldBeAsync public List getAllAuthsByIp(String ip) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); try { return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME); } catch (SQLException e) { @@ -125,21 +125,21 @@ public List getAllAuthsByIp(String ip) { @Override @ShouldBeAsync public int countAuthsByEmail(String email) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email)); } @Override @ShouldBeAsync public boolean updateEmail(PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.EMAIL); } @Override @ShouldBeAsync public boolean isLogged(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); try { DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.IS_LOGGED); return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); @@ -152,56 +152,56 @@ public boolean isLogged(String user) { @Override @ShouldBeAsync public void setLogged(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 1); } @Override @ShouldBeAsync public void setUnlogged(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 0); } @Override @ShouldBeAsync public void grantSession(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1); } @Override @ShouldBeAsync public void revokeSession(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0); } @Override @ShouldBeAsync public void purgeLogged() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0); } @Override @ShouldBeAsync public int getAccountsRegistered() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return columnsHandler.count(new AlwaysTruePredicate<>()); } @Override @ShouldBeAsync public boolean updateRealName(String user, String realName) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName); } @Override @ShouldBeAsync public DataSourceValue getEmail(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); try { return columnsHandler.retrieve(user, AuthMeColumns.EMAIL); } catch (SQLException e) { diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 979baa0f7..3c377dc61 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -10,7 +10,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; @@ -84,14 +84,14 @@ public boolean isCached() { @Override @ShouldBeAsync public boolean isAuthAvailable(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return getAuth(user) != null; } @Override @ShouldBeAsync public HashedPassword getPassword(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); user = user.toLowerCase(); Optional pAuthOpt = cachedAuths.getIfPresent(user); if (pAuthOpt != null && pAuthOpt.isPresent()) { @@ -103,7 +103,7 @@ public HashedPassword getPassword(String user) { @Override @ShouldBeAsync public PlayerAuth getAuth(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); user = user.toLowerCase(); return cachedAuths.getUnchecked(user).orElse(null); } @@ -111,7 +111,7 @@ public PlayerAuth getAuth(String user) { @Override @ShouldBeAsync public boolean saveAuth(PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); boolean result = source.saveAuth(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -122,7 +122,7 @@ public boolean saveAuth(PlayerAuth auth) { @Override @ShouldBeAsync public boolean updatePassword(PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); boolean result = source.updatePassword(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -133,7 +133,7 @@ public boolean updatePassword(PlayerAuth auth) { @Override @ShouldBeAsync public boolean updatePassword(String user, HashedPassword password) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); user = user.toLowerCase(); boolean result = source.updatePassword(user, password); if (result) { @@ -145,7 +145,7 @@ public boolean updatePassword(String user, HashedPassword password) { @Override @ShouldBeAsync public boolean updateSession(PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); boolean result = source.updateSession(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -156,7 +156,7 @@ public boolean updateSession(PlayerAuth auth) { @Override @ShouldBeAsync public boolean updateQuitLoc(final PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); boolean result = source.updateQuitLoc(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -167,14 +167,14 @@ public boolean updateQuitLoc(final PlayerAuth auth) { @Override @ShouldBeAsync public Set getRecordsToPurge(long until) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.getRecordsToPurge(until); } @Override @ShouldBeAsync public boolean removeAuth(String name) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); name = name.toLowerCase(); boolean result = source.removeAuth(name); if (result) { @@ -198,7 +198,7 @@ public void closeConnection() { @Override @ShouldBeAsync public boolean updateEmail(final PlayerAuth auth) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); boolean result = source.updateEmail(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -209,77 +209,77 @@ public boolean updateEmail(final PlayerAuth auth) { @Override @ShouldBeAsync public List getAllAuthsByIp(String ip) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.getAllAuthsByIp(ip); } @Override @ShouldBeAsync public int countAuthsByEmail(String email) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.countAuthsByEmail(email); } @Override @ShouldBeAsync public void purgeRecords(Collection banned) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); source.purgeRecords(banned); cachedAuths.invalidateAll(banned); } @Override public DataSourceType getType() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.getType(); } @Override @ShouldBeAsync public boolean isLogged(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.isLogged(user); } @Override @ShouldBeAsync public void setLogged(final String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); source.setLogged(user.toLowerCase()); } @Override @ShouldBeAsync public void setUnlogged(final String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); source.setUnlogged(user.toLowerCase()); } @Override @ShouldBeAsync public boolean hasSession(final String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.hasSession(user); } @Override @ShouldBeAsync public void grantSession(final String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); source.grantSession(user); } @Override @ShouldBeAsync public void revokeSession(final String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); source.revokeSession(user); } @Override @ShouldBeAsync public void purgeLogged() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); source.purgeLogged(); cachedAuths.invalidateAll(); } @@ -287,14 +287,14 @@ public void purgeLogged() { @Override @ShouldBeAsync public int getAccountsRegistered() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.getAccountsRegistered(); } @Override @ShouldBeAsync public boolean updateRealName(String user, String realName) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); boolean result = source.updateRealName(user, realName); if (result) { cachedAuths.refresh(user); @@ -305,7 +305,7 @@ public boolean updateRealName(String user, String realName) { @Override @ShouldBeAsync public DataSourceValue getEmail(String user) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return cachedAuths.getUnchecked(user) .map(auth -> DataSourceValueImpl.of(auth.getEmail())) .orElse(DataSourceValueImpl.unknownRow()); @@ -314,14 +314,14 @@ public DataSourceValue getEmail(String user) { @Override @ShouldBeAsync public List getAllAuths() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.getAllAuths(); } @Override @ShouldBeAsync public List getLoggedPlayersWithEmptyMail() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return playerCache.getCache().values().stream() .filter(auth -> Utils.isEmailEmpty(auth.getEmail())) .map(PlayerAuth::getRealName) @@ -331,14 +331,14 @@ public List getLoggedPlayersWithEmptyMail() { @Override @ShouldBeAsync public List getRecentlyLoggedInPlayers() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); return source.getRecentlyLoggedInPlayers(); } @Override @ShouldBeAsync public boolean setTotpKey(String user, String totpKey) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); boolean result = source.setTotpKey(user, totpKey); if (result) { cachedAuths.refresh(user); @@ -354,7 +354,7 @@ public void invalidateCache(String playerName) { @Override @ShouldBeAsync public void refreshCache(String playerName) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (cachedAuths.getIfPresent(playerName) != null) { cachedAuths.refresh(playerName); } diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index 26078353d..d699f7588 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -1,7 +1,7 @@ package fr.xephi.authme.datasource; import ch.jalu.datasourcecolumns.data.DataSourceValue; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.initialization.Reloadable; @@ -313,7 +313,7 @@ default void invalidateCache(String playerName) { */ @ShouldBeAsync default void refreshCache(String playerName) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); } } diff --git a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java index 5ecb86b1b..8444e518e 100644 --- a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java +++ b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java @@ -1,7 +1,7 @@ package fr.xephi.authme.listener; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; @@ -76,7 +76,7 @@ public void reload() { */ @ShouldBeAsync public void checkAntibot(String name, boolean isAuthAvailable) throws FailedVerificationException { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (isAuthAvailable || permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)) { return; } @@ -127,7 +127,7 @@ public void checkIsValidName(String name) throws FailedVerificationException { * further), false if the player is not refused */ public boolean refusePlayerForFullServer(PlayerLoginEvent event) { - ThreadSafetyUtils.requireSync(); + ThreadSafety.requireSync(); final Player player = event.getPlayer(); if (event.getResult() != PlayerLoginEvent.Result.KICK_FULL) { // Server is not full, no need to do anything @@ -165,7 +165,7 @@ public boolean refusePlayerForFullServer(PlayerLoginEvent event) { */ @ShouldBeAsync public void checkNameCasing(String connectingName, PlayerAuth auth) throws FailedVerificationException { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (auth != null && settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)) { String realName = auth.getRealName(); // might be null or "Player" @@ -188,7 +188,7 @@ public void checkNameCasing(String connectingName, PlayerAuth auth) throws Faile @ShouldBeAsync public void checkPlayerCountry(String name, String address, boolean isAuthAvailable) throws FailedVerificationException { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)) && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION) && !permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_COUNTRY_CHECK) @@ -205,7 +205,7 @@ public void checkPlayerCountry(String name, String address, * @throws FailedVerificationException if the verification fails */ public void checkSingleSession(String name) throws FailedVerificationException { - ThreadSafetyUtils.requireSync(); + ThreadSafety.requireSync(); if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) { return; } @@ -224,7 +224,7 @@ public void checkSingleSession(String name) throws FailedVerificationException { * @return the player to kick, or null if none applicable */ private Player generateKickPlayer(Collection onlinePlayers) { - ThreadSafetyUtils.requireSync(); + ThreadSafety.requireSync(); for (Player player : onlinePlayers) { if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) { return player; diff --git a/src/main/java/fr/xephi/authme/mail/EmailService.java b/src/main/java/fr/xephi/authme/mail/EmailService.java index fe2452ec0..f25ad9549 100644 --- a/src/main/java/fr/xephi/authme/mail/EmailService.java +++ b/src/main/java/fr/xephi/authme/mail/EmailService.java @@ -1,7 +1,7 @@ package fr.xephi.authme.mail; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.initialization.DataFolder; @@ -54,7 +54,7 @@ public boolean hasAllInformation() { */ @ShouldBeAsync public boolean sendPasswordMail(String name, String mailAddress, String newPass) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (!hasAllInformation()) { logger.warning("Cannot perform email registration: not all email settings are complete"); return false; @@ -96,7 +96,7 @@ public boolean sendPasswordMail(String name, String mailAddress, String newPass) */ @ShouldBeAsync public boolean sendVerificationMail(String name, String mailAddress, String code) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (!hasAllInformation()) { logger.warning("Cannot send verification email: not all email settings are complete"); return false; @@ -125,7 +125,7 @@ public boolean sendVerificationMail(String name, String mailAddress, String code */ @ShouldBeAsync public boolean sendRecoveryCode(String name, String email, String code) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); HtmlEmail htmlEmail; try { htmlEmail = sendMailSsl.initializeMail(email); diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java index 3e1ace6cb..5c8fe89dc 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -1,6 +1,6 @@ package fr.xephi.authme.service; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.initialization.SettingsDependent; @@ -108,7 +108,7 @@ private synchronized void startProtection() { * Transitions the anti bot service from active status back to listening. */ private void stopProtection() { - ThreadSafetyUtils.requireSync(); + ThreadSafety.requireSync(); if (antiBotStatus != AntiBotStatus.ACTIVE) { return; } @@ -144,7 +144,7 @@ public AntiBotStatus getAntiBotStatus() { * @param started the new protection status */ public void overrideAntiBotStatus(boolean started) { - ThreadSafetyUtils.requireSync(); + ThreadSafety.requireSync(); if (antiBotStatus != AntiBotStatus.DISABLED) { if (started) { startProtection(); @@ -161,7 +161,7 @@ public void overrideAntiBotStatus(boolean started) { */ @ShouldBeAsync public boolean shouldKick() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (antiBotStatus == AntiBotStatus.DISABLED) { return false; } else if (antiBotStatus == AntiBotStatus.ACTIVE) { diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index 59a62d8b5..1d0e9c7bb 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -11,7 +11,7 @@ import com.maxmind.db.model.Country; import com.maxmind.db.model.CountryResponse; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -132,7 +132,7 @@ private synchronized boolean isDataAvailable() { */ @ShouldBeAsync private void updateDatabase() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); logger.info("Downloading GEO IP database, because the old database is older than " + UPDATE_INTERVAL_DAYS + " days or doesn't exist"); diff --git a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java b/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java index 4799b3406..d8726f5e5 100644 --- a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java +++ b/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java @@ -1,7 +1,7 @@ package fr.xephi.authme.service; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; @@ -72,7 +72,7 @@ private void initEmailCooldownSet() { */ @ShouldBeAsync public void createAndSendRecoveryCode(Player player, String email) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (!checkEmailCooldown(player)) { return; } @@ -96,7 +96,7 @@ public void createAndSendRecoveryCode(Player player, String email) { */ @ShouldBeAsync public void generateAndSendNewPassword(Player player, String email) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (!checkEmailCooldown(player)) { return; } diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java index d0ac1b858..2e795f991 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java @@ -1,8 +1,7 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafetyUtils; -import fr.xephi.authme.annotation.MightBeAsync; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -58,7 +57,7 @@ public class PurgeExecutor { */ @ShouldBeAsync public void executePurge(Collection players, Collection names) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); // Purge other data purgeFromAuthMe(names); purgeEssentials(players); diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java index d7ac7ef5b..3372acfab 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java @@ -1,7 +1,7 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -121,7 +121,7 @@ void setPurging(boolean purging) { */ @ShouldBeAsync void executePurge(Collection players, Collection names) { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); purgeExecutor.executePurge(players, names); } } diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java index cc99e81d8..46942c515 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java @@ -1,7 +1,7 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafetyUtils; +import fr.xephi.authme.ThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.permission.PermissionsManager; @@ -61,7 +61,7 @@ class PurgeTask extends BukkitRunnable { @Override @ShouldBeAsync public void run() { - ThreadSafetyUtils.shouldBeAsync(); + ThreadSafety.shouldBeAsync(); if (toPurge.isEmpty()) { //everything was removed finish(); From 3a00e290bb5c2b616d564f5cb6a256018aab605d Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Tue, 28 Jan 2020 15:09:49 +0100 Subject: [PATCH 6/8] Fix SyncPlayerLogin calling a db query on the main thread --- .../xephi/authme/process/login/ProcessSyncPlayerLogin.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java index eb2aad817..a54a8e5b0 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -1,6 +1,7 @@ package fr.xephi.authme.process.login; import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.limbo.LimboPlayer; import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.datasource.DataSource; @@ -40,7 +41,7 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { private TeleportationService teleportationService; @Inject - private DataSource dataSource; + private PlayerCache playerCache; @Inject private CommandManager commandManager; @@ -88,7 +89,7 @@ public void processPlayerLogin(Player player, boolean isFirstLogin, List restoreInventory(player); } - final PlayerAuth auth = dataSource.getAuth(name); + final PlayerAuth auth = playerCache.getAuth(name); teleportationService.teleportOnLogin(player, auth, limbo); // We can now display the join message (if delayed) From c95443fda7d2ef81493ef0dc672cbf0c16592787 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sun, 2 Feb 2020 19:05:12 +0100 Subject: [PATCH 7/8] Cleanup --- src/main/java/fr/xephi/authme/AuthMe.java | 5 +- .../xephi/authme/annotation/MightBeAsync.java | 2 +- .../authme/annotation/ShouldBeAsync.java | 2 +- .../executable/authme/ConverterCommand.java | 16 ++--- .../executable/authme/PurgePlayerCommand.java | 1 - .../datasource/AbstractSqlDataSource.java | 42 ++++++------- .../authme/datasource/CacheDataSource.java | 62 +++++++++---------- .../xephi/authme/datasource/DataSource.java | 4 +- .../converter/AbstractConverter.java | 25 ++++++++ .../AbstractDataSourceConverter.java | 6 +- .../datasource/converter/Converter.java | 19 ------ .../converter/CrazyLoginConverter.java | 6 +- .../converter/LoginSecurityConverter.java | 6 +- .../converter/RakamakConverter.java | 8 ++- .../converter/RoyalAuthConverter.java | 6 +- .../datasource/converter/VAuthConverter.java | 6 +- .../datasource/converter/XAuthConverter.java | 6 +- .../xephi/authme/listener/OnJoinVerifier.java | 14 ++--- .../fr/xephi/authme/mail/EmailService.java | 8 +-- .../process/login/ProcessSyncPlayerLogin.java | 1 - .../xephi/authme/service/AntiBotService.java | 8 +-- .../fr/xephi/authme/service/GeoIpService.java | 4 +- .../service/PasswordRecoveryService.java | 6 +- .../authme/task/purge/PurgeExecutor.java | 4 +- .../xephi/authme/task/purge/PurgeService.java | 4 +- .../fr/xephi/authme/task/purge/PurgeTask.java | 4 +- .../authme/util/AtomicIntervalCounter.java | 19 ++++++ .../BukkitThreadSafety.java} | 22 +++++-- .../fr/xephi/authme/util/PlayerUtils.java | 1 - .../authme/ConverterCommandTest.java | 18 +++--- .../AbstractDataSourceConverterTest.java | 10 +-- .../converter/CrazyLoginConverterTest.java | 4 +- .../tools/dependencygraph/DrawDependency.java | 4 +- 33 files changed, 203 insertions(+), 150 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/datasource/converter/AbstractConverter.java delete mode 100644 src/main/java/fr/xephi/authme/datasource/converter/Converter.java rename src/main/java/fr/xephi/authme/{ThreadSafety.java => util/BukkitThreadSafety.java} (50%) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 8bef3823f..2547920af 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -31,6 +31,7 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.CleanupTask; import fr.xephi.authme.task.purge.PurgeService; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.util.ExceptionUtils; import org.bukkit.Server; import org.bukkit.command.Command; @@ -172,7 +173,9 @@ public void onEnable() { // Successful message logger.info("AuthMe " + getPluginVersion() + " build n." + getPluginBuildNumber() + " successfully enabled!"); // Start catching wrong sync/async calls - ThreadSafety.setEnabled(true); + if(System.getProperty("authme.disableThreadSafetyChecks") == null) { + BukkitThreadSafety.setEnabled(true); + } // Purge on start if enabled PurgeService purgeService = injector.getSingleton(PurgeService.class); diff --git a/src/main/java/fr/xephi/authme/annotation/MightBeAsync.java b/src/main/java/fr/xephi/authme/annotation/MightBeAsync.java index fdb72aa11..be796a4f0 100644 --- a/src/main/java/fr/xephi/authme/annotation/MightBeAsync.java +++ b/src/main/java/fr/xephi/authme/annotation/MightBeAsync.java @@ -6,6 +6,6 @@ import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) -@Retention(RetentionPolicy.RUNTIME) +@Retention(RetentionPolicy.SOURCE) public @interface MightBeAsync { } diff --git a/src/main/java/fr/xephi/authme/annotation/ShouldBeAsync.java b/src/main/java/fr/xephi/authme/annotation/ShouldBeAsync.java index f69afe6d4..c00297ebb 100644 --- a/src/main/java/fr/xephi/authme/annotation/ShouldBeAsync.java +++ b/src/main/java/fr/xephi/authme/annotation/ShouldBeAsync.java @@ -6,6 +6,6 @@ import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) -@Retention(RetentionPolicy.RUNTIME) +@Retention(RetentionPolicy.SOURCE) public @interface ShouldBeAsync { } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java index fd0a73513..e2e2c8d6d 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java @@ -5,7 +5,7 @@ import com.google.common.collect.ImmutableSortedMap; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.ExecutableCommand; -import fr.xephi.authme.datasource.converter.Converter; +import fr.xephi.authme.datasource.converter.AbstractConverter; import fr.xephi.authme.datasource.converter.CrazyLoginConverter; import fr.xephi.authme.datasource.converter.LoginSecurityConverter; import fr.xephi.authme.datasource.converter.MySqlToSqlite; @@ -30,7 +30,7 @@ public class ConverterCommand implements ExecutableCommand { @VisibleForTesting - static final Map> CONVERTERS = getConverters(); + static final Map> CONVERTERS = getConverters(); private final ConsoleLogger logger = ConsoleLoggerFactory.get(ConverterCommand.class); @@ -41,18 +41,18 @@ public class ConverterCommand implements ExecutableCommand { private BukkitService bukkitService; @Inject - private Factory converterFactory; + private Factory converterFactory; @Override public void executeCommand(CommandSender sender, List arguments) { - Class converterClass = getConverterClassFromArgs(arguments); + Class converterClass = getConverterClassFromArgs(arguments); if (converterClass == null) { sender.sendMessage("Converters: " + String.join(", ", CONVERTERS.keySet())); return; } // Get the proper converter instance - final Converter converter = converterFactory.newInstance(converterClass); + final AbstractConverter converter = converterFactory.newInstance(converterClass); // Run the convert job bukkitService.runTaskAsynchronously(() -> { @@ -68,7 +68,7 @@ public void executeCommand(CommandSender sender, List arguments) { sender.sendMessage("[AuthMe] Successfully started " + arguments.get(0)); } - private static Class getConverterClassFromArgs(List arguments) { + private static Class getConverterClassFromArgs(List arguments) { return arguments.isEmpty() ? null : CONVERTERS.get(arguments.get(0).toLowerCase()); @@ -79,8 +79,8 @@ private static Class getConverterClassFromArgs(List * * @return map with all available converters */ - private static Map> getConverters() { - return ImmutableSortedMap.>naturalOrder() + private static Map> getConverters() { + return ImmutableSortedMap.>naturalOrder() .put("xauth", XAuthConverter.class) .put("crazylogin", CrazyLoginConverter.class) .put("rakamak", RakamakConverter.class) diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java index 435792c49..5fb8ddc0a 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java @@ -1,6 +1,5 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; diff --git a/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java b/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java index b79831984..c38833309 100644 --- a/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java @@ -4,7 +4,7 @@ import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import ch.jalu.datasourcecolumns.data.DataSourceValues; import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.columnshandler.AuthMeColumns; @@ -31,7 +31,7 @@ public abstract class AbstractSqlDataSource implements DataSource { @Override @ShouldBeAsync public boolean isAuthAvailable(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); try { return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists(); } catch (SQLException e) { @@ -43,7 +43,7 @@ public boolean isAuthAvailable(String user) { @Override @ShouldBeAsync public HashedPassword getPassword(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); try { DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT); if (values.rowExists()) { @@ -58,7 +58,7 @@ public HashedPassword getPassword(String user) { @Override @ShouldBeAsync public boolean saveAuth(PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return columnsHandler.insert(auth, AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT, AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP, @@ -68,7 +68,7 @@ public boolean saveAuth(PlayerAuth auth) { @Override @ShouldBeAsync public boolean hasSession(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); try { DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION); return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); @@ -81,21 +81,21 @@ public boolean hasSession(String user) { @Override @ShouldBeAsync public boolean updateSession(PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME); } @Override @ShouldBeAsync public boolean updatePassword(PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return updatePassword(auth.getNickname(), auth.getPassword()); } @Override @ShouldBeAsync public boolean updatePassword(String user, HashedPassword password) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return columnsHandler.update(user, with(AuthMeColumns.PASSWORD, password.getHash()) .and(AuthMeColumns.SALT, password.getSalt()).build()); @@ -104,7 +104,7 @@ public boolean updatePassword(String user, HashedPassword password) { @Override @ShouldBeAsync public boolean updateQuitLoc(PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z, AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH); @@ -113,7 +113,7 @@ public boolean updateQuitLoc(PlayerAuth auth) { @Override @ShouldBeAsync public List getAllAuthsByIp(String ip) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); try { return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME); } catch (SQLException e) { @@ -125,21 +125,21 @@ public List getAllAuthsByIp(String ip) { @Override @ShouldBeAsync public int countAuthsByEmail(String email) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email)); } @Override @ShouldBeAsync public boolean updateEmail(PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return columnsHandler.update(auth, AuthMeColumns.EMAIL); } @Override @ShouldBeAsync public boolean isLogged(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); try { DataSourceValue result = columnsHandler.retrieve(user, AuthMeColumns.IS_LOGGED); return result.rowExists() && Integer.valueOf(1).equals(result.getValue()); @@ -152,56 +152,56 @@ public boolean isLogged(String user) { @Override @ShouldBeAsync public void setLogged(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 1); } @Override @ShouldBeAsync public void setUnlogged(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 0); } @Override @ShouldBeAsync public void grantSession(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1); } @Override @ShouldBeAsync public void revokeSession(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0); } @Override @ShouldBeAsync public void purgeLogged() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0); } @Override @ShouldBeAsync public int getAccountsRegistered() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return columnsHandler.count(new AlwaysTruePredicate<>()); } @Override @ShouldBeAsync public boolean updateRealName(String user, String realName) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName); } @Override @ShouldBeAsync public DataSourceValue getEmail(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); try { return columnsHandler.retrieve(user, AuthMeColumns.EMAIL); } catch (SQLException e) { diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 3c377dc61..eb1117f10 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -10,7 +10,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; @@ -84,14 +84,14 @@ public boolean isCached() { @Override @ShouldBeAsync public boolean isAuthAvailable(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return getAuth(user) != null; } @Override @ShouldBeAsync public HashedPassword getPassword(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); user = user.toLowerCase(); Optional pAuthOpt = cachedAuths.getIfPresent(user); if (pAuthOpt != null && pAuthOpt.isPresent()) { @@ -103,7 +103,7 @@ public HashedPassword getPassword(String user) { @Override @ShouldBeAsync public PlayerAuth getAuth(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); user = user.toLowerCase(); return cachedAuths.getUnchecked(user).orElse(null); } @@ -111,7 +111,7 @@ public PlayerAuth getAuth(String user) { @Override @ShouldBeAsync public boolean saveAuth(PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); boolean result = source.saveAuth(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -122,7 +122,7 @@ public boolean saveAuth(PlayerAuth auth) { @Override @ShouldBeAsync public boolean updatePassword(PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); boolean result = source.updatePassword(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -133,7 +133,7 @@ public boolean updatePassword(PlayerAuth auth) { @Override @ShouldBeAsync public boolean updatePassword(String user, HashedPassword password) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); user = user.toLowerCase(); boolean result = source.updatePassword(user, password); if (result) { @@ -145,7 +145,7 @@ public boolean updatePassword(String user, HashedPassword password) { @Override @ShouldBeAsync public boolean updateSession(PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); boolean result = source.updateSession(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -156,7 +156,7 @@ public boolean updateSession(PlayerAuth auth) { @Override @ShouldBeAsync public boolean updateQuitLoc(final PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); boolean result = source.updateQuitLoc(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -167,14 +167,14 @@ public boolean updateQuitLoc(final PlayerAuth auth) { @Override @ShouldBeAsync public Set getRecordsToPurge(long until) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.getRecordsToPurge(until); } @Override @ShouldBeAsync public boolean removeAuth(String name) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); name = name.toLowerCase(); boolean result = source.removeAuth(name); if (result) { @@ -198,7 +198,7 @@ public void closeConnection() { @Override @ShouldBeAsync public boolean updateEmail(final PlayerAuth auth) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); boolean result = source.updateEmail(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -209,77 +209,77 @@ public boolean updateEmail(final PlayerAuth auth) { @Override @ShouldBeAsync public List getAllAuthsByIp(String ip) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.getAllAuthsByIp(ip); } @Override @ShouldBeAsync public int countAuthsByEmail(String email) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.countAuthsByEmail(email); } @Override @ShouldBeAsync public void purgeRecords(Collection banned) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); source.purgeRecords(banned); cachedAuths.invalidateAll(banned); } @Override public DataSourceType getType() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.getType(); } @Override @ShouldBeAsync public boolean isLogged(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.isLogged(user); } @Override @ShouldBeAsync public void setLogged(final String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); source.setLogged(user.toLowerCase()); } @Override @ShouldBeAsync public void setUnlogged(final String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); source.setUnlogged(user.toLowerCase()); } @Override @ShouldBeAsync public boolean hasSession(final String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.hasSession(user); } @Override @ShouldBeAsync public void grantSession(final String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); source.grantSession(user); } @Override @ShouldBeAsync public void revokeSession(final String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); source.revokeSession(user); } @Override @ShouldBeAsync public void purgeLogged() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); source.purgeLogged(); cachedAuths.invalidateAll(); } @@ -287,14 +287,14 @@ public void purgeLogged() { @Override @ShouldBeAsync public int getAccountsRegistered() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.getAccountsRegistered(); } @Override @ShouldBeAsync public boolean updateRealName(String user, String realName) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); boolean result = source.updateRealName(user, realName); if (result) { cachedAuths.refresh(user); @@ -305,7 +305,7 @@ public boolean updateRealName(String user, String realName) { @Override @ShouldBeAsync public DataSourceValue getEmail(String user) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return cachedAuths.getUnchecked(user) .map(auth -> DataSourceValueImpl.of(auth.getEmail())) .orElse(DataSourceValueImpl.unknownRow()); @@ -314,14 +314,14 @@ public DataSourceValue getEmail(String user) { @Override @ShouldBeAsync public List getAllAuths() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.getAllAuths(); } @Override @ShouldBeAsync public List getLoggedPlayersWithEmptyMail() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return playerCache.getCache().values().stream() .filter(auth -> Utils.isEmailEmpty(auth.getEmail())) .map(PlayerAuth::getRealName) @@ -331,14 +331,14 @@ public List getLoggedPlayersWithEmptyMail() { @Override @ShouldBeAsync public List getRecentlyLoggedInPlayers() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); return source.getRecentlyLoggedInPlayers(); } @Override @ShouldBeAsync public boolean setTotpKey(String user, String totpKey) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); boolean result = source.setTotpKey(user, totpKey); if (result) { cachedAuths.refresh(user); @@ -354,7 +354,7 @@ public void invalidateCache(String playerName) { @Override @ShouldBeAsync public void refreshCache(String playerName) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (cachedAuths.getIfPresent(playerName) != null) { cachedAuths.refresh(playerName); } diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index d699f7588..d84672b87 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -1,7 +1,7 @@ package fr.xephi.authme.datasource; import ch.jalu.datasourcecolumns.data.DataSourceValue; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.initialization.Reloadable; @@ -313,7 +313,7 @@ default void invalidateCache(String playerName) { */ @ShouldBeAsync default void refreshCache(String playerName) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); } } diff --git a/src/main/java/fr/xephi/authme/datasource/converter/AbstractConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/AbstractConverter.java new file mode 100644 index 000000000..15407e6d1 --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/converter/AbstractConverter.java @@ -0,0 +1,25 @@ +package fr.xephi.authme.datasource.converter; + +import fr.xephi.authme.annotation.ShouldBeAsync; +import fr.xephi.authme.util.BukkitThreadSafety; +import org.bukkit.command.CommandSender; + +/** + * Abstract class for AuthMe converters. + */ +public abstract class AbstractConverter { + + /** + * Execute the conversion. + * + * @param sender the sender who initialized the conversion + */ + @ShouldBeAsync + public void execute(CommandSender sender) { + BukkitThreadSafety.shouldBeAsync(); + executeInternal(sender); + } + + @ShouldBeAsync + protected abstract void executeInternal(CommandSender sender); +} diff --git a/src/main/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverter.java index fdced2fa4..5f9605f3f 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverter.java @@ -1,6 +1,7 @@ package fr.xephi.authme.datasource.converter; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSourceType; @@ -17,7 +18,7 @@ * * @param the source type to convert from */ -public abstract class AbstractDataSourceConverter implements Converter { +public abstract class AbstractDataSourceConverter extends AbstractConverter { private final ConsoleLogger logger = ConsoleLoggerFactory.get(MySqlToSqlite.class); @@ -40,7 +41,8 @@ public AbstractDataSourceConverter(DataSource destination, DataSourceType destin // Implementation note: Because of ForceFlatToSqlite it is possible that the CommandSender is null, // which is never the case when a converter is launched from the /authme converter command. @Override - public void execute(CommandSender sender) { + @ShouldBeAsync + public void executeInternal(CommandSender sender) { if (destinationType != destination.getType()) { if (sender != null) { sender.sendMessage("Please configure your connection to " diff --git a/src/main/java/fr/xephi/authme/datasource/converter/Converter.java b/src/main/java/fr/xephi/authme/datasource/converter/Converter.java deleted file mode 100644 index ca065b87a..000000000 --- a/src/main/java/fr/xephi/authme/datasource/converter/Converter.java +++ /dev/null @@ -1,19 +0,0 @@ -package fr.xephi.authme.datasource.converter; - -import fr.xephi.authme.annotation.ShouldBeAsync; -import org.bukkit.command.CommandSender; - -/** - * Interface for AuthMe converters. - */ -public interface Converter { - - /** - * Execute the conversion. - * TODO: apply the ShouldBeAsync annotation to all the converters - * - * @param sender the sender who initialized the conversion - */ - @ShouldBeAsync - void execute(CommandSender sender); -} diff --git a/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java index b1c9613e2..f3ef35dcc 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java @@ -1,6 +1,7 @@ package fr.xephi.authme.datasource.converter; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; @@ -18,7 +19,7 @@ /** * Converter for CrazyLogin to AuthMe. */ -public class CrazyLoginConverter implements Converter { +public class CrazyLoginConverter extends AbstractConverter { private final ConsoleLogger logger = ConsoleLoggerFactory.get(CrazyLoginConverter.class); @@ -34,7 +35,8 @@ public class CrazyLoginConverter implements Converter { } @Override - public void execute(CommandSender sender) { + @ShouldBeAsync + public void executeInternal(CommandSender sender) { String fileName = settings.getProperty(ConverterSettings.CRAZYLOGIN_FILE_NAME); File source = new File(dataFolder, fileName); if (!source.exists()) { diff --git a/src/main/java/fr/xephi/authme/datasource/converter/LoginSecurityConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/LoginSecurityConverter.java index 5f74ec1e8..f6c0ad202 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/LoginSecurityConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/LoginSecurityConverter.java @@ -2,6 +2,7 @@ import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; @@ -30,7 +31,7 @@ /** * Converts data from LoginSecurity to AuthMe. */ -public class LoginSecurityConverter implements Converter { +public class LoginSecurityConverter extends AbstractConverter { private final ConsoleLogger logger = ConsoleLoggerFactory.get(LoginSecurityConverter.class); private final File dataFolder; @@ -55,7 +56,8 @@ public class LoginSecurityConverter implements Converter { } @Override - public void execute(CommandSender sender) { + @ShouldBeAsync + public void executeInternal(CommandSender sender) { try (Connection connection = createConnectionOrInformSender(sender)) { if (connection != null) { performConversion(sender, connection); diff --git a/src/main/java/fr/xephi/authme/datasource/converter/RakamakConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/RakamakConverter.java index b69fe688d..5490a1b7e 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/RakamakConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/RakamakConverter.java @@ -1,6 +1,7 @@ package fr.xephi.authme.datasource.converter; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; @@ -24,7 +25,7 @@ /** * @author Xephi59 */ -public class RakamakConverter implements Converter { +public class RakamakConverter extends AbstractConverter { private final ConsoleLogger logger = ConsoleLoggerFactory.get(RakamakConverter.class); private final DataSource database; @@ -41,9 +42,10 @@ public class RakamakConverter implements Converter { this.passwordSecurity = passwordSecurity; } - @Override //TODO ljacqu 20151229: Restructure this into smaller portions - public void execute(CommandSender sender) { + @Override + @ShouldBeAsync + public void executeInternal(CommandSender sender) { boolean useIp = settings.getProperty(ConverterSettings.RAKAMAK_USE_IP); String fileName = settings.getProperty(ConverterSettings.RAKAMAK_FILE_NAME); String ipFileName = settings.getProperty(ConverterSettings.RAKAMAK_IP_FILE_NAME); diff --git a/src/main/java/fr/xephi/authme/datasource/converter/RoyalAuthConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/RoyalAuthConverter.java index 7c45aebfa..2f234ed9e 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/RoyalAuthConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/RoyalAuthConverter.java @@ -2,6 +2,7 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -15,7 +16,7 @@ import static fr.xephi.authme.util.FileUtils.makePath; -public class RoyalAuthConverter implements Converter { +public class RoyalAuthConverter extends AbstractConverter { private static final String LAST_LOGIN_PATH = "timestamps.quit"; private static final String PASSWORD_PATH = "login.password"; @@ -32,7 +33,8 @@ public class RoyalAuthConverter implements Converter { } @Override - public void execute(CommandSender sender) { + @ShouldBeAsync + public void executeInternal(CommandSender sender) { for (OfflinePlayer player : plugin.getServer().getOfflinePlayers()) { try { String name = player.getName().toLowerCase(); diff --git a/src/main/java/fr/xephi/authme/datasource/converter/VAuthConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/VAuthConverter.java index e9c706077..248f167d2 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/VAuthConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/VAuthConverter.java @@ -1,6 +1,7 @@ package fr.xephi.authme.datasource.converter; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; @@ -17,7 +18,7 @@ import static fr.xephi.authme.util.FileUtils.makePath; -public class VAuthConverter implements Converter { +public class VAuthConverter extends AbstractConverter { private final ConsoleLogger logger = ConsoleLoggerFactory.get(VAuthConverter.class); private final DataSource dataSource; @@ -30,7 +31,8 @@ public class VAuthConverter implements Converter { } @Override - public void execute(CommandSender sender) { + @ShouldBeAsync + public void executeInternal(CommandSender sender) { try (Scanner scanner = new Scanner(vAuthPasswordsFile)) { while (scanner.hasNextLine()) { String line = scanner.nextLine(); diff --git a/src/main/java/fr/xephi/authme/datasource/converter/XAuthConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/XAuthConverter.java index 928566583..ec8f06f2b 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/XAuthConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/XAuthConverter.java @@ -3,6 +3,7 @@ import de.luricos.bukkit.xAuth.database.DatabaseTables; import de.luricos.bukkit.xAuth.utils.xAuthLog; import de.luricos.bukkit.xAuth.xAuth; +import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; @@ -21,7 +22,7 @@ import static fr.xephi.authme.util.FileUtils.makePath; -public class XAuthConverter implements Converter { +public class XAuthConverter extends AbstractConverter { @Inject @DataFolder @@ -35,7 +36,8 @@ public class XAuthConverter implements Converter { } @Override - public void execute(CommandSender sender) { + @ShouldBeAsync + public void executeInternal(CommandSender sender) { try { Class.forName("de.luricos.bukkit.xAuth.xAuth"); convert(sender); diff --git a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java index 8444e518e..b2e2f79f2 100644 --- a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java +++ b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java @@ -1,7 +1,7 @@ package fr.xephi.authme.listener; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.data.auth.PlayerAuth; @@ -76,7 +76,7 @@ public void reload() { */ @ShouldBeAsync public void checkAntibot(String name, boolean isAuthAvailable) throws FailedVerificationException { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (isAuthAvailable || permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)) { return; } @@ -127,7 +127,7 @@ public void checkIsValidName(String name) throws FailedVerificationException { * further), false if the player is not refused */ public boolean refusePlayerForFullServer(PlayerLoginEvent event) { - ThreadSafety.requireSync(); + BukkitThreadSafety.requireSync(); final Player player = event.getPlayer(); if (event.getResult() != PlayerLoginEvent.Result.KICK_FULL) { // Server is not full, no need to do anything @@ -165,7 +165,7 @@ public boolean refusePlayerForFullServer(PlayerLoginEvent event) { */ @ShouldBeAsync public void checkNameCasing(String connectingName, PlayerAuth auth) throws FailedVerificationException { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (auth != null && settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)) { String realName = auth.getRealName(); // might be null or "Player" @@ -188,7 +188,7 @@ public void checkNameCasing(String connectingName, PlayerAuth auth) throws Faile @ShouldBeAsync public void checkPlayerCountry(String name, String address, boolean isAuthAvailable) throws FailedVerificationException { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)) && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION) && !permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_COUNTRY_CHECK) @@ -205,7 +205,7 @@ public void checkPlayerCountry(String name, String address, * @throws FailedVerificationException if the verification fails */ public void checkSingleSession(String name) throws FailedVerificationException { - ThreadSafety.requireSync(); + BukkitThreadSafety.requireSync(); if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) { return; } @@ -224,7 +224,7 @@ public void checkSingleSession(String name) throws FailedVerificationException { * @return the player to kick, or null if none applicable */ private Player generateKickPlayer(Collection onlinePlayers) { - ThreadSafety.requireSync(); + BukkitThreadSafety.requireSync(); for (Player player : onlinePlayers) { if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) { return player; diff --git a/src/main/java/fr/xephi/authme/mail/EmailService.java b/src/main/java/fr/xephi/authme/mail/EmailService.java index f25ad9549..d6954a627 100644 --- a/src/main/java/fr/xephi/authme/mail/EmailService.java +++ b/src/main/java/fr/xephi/authme/mail/EmailService.java @@ -1,7 +1,7 @@ package fr.xephi.authme.mail; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.initialization.DataFolder; @@ -54,7 +54,7 @@ public boolean hasAllInformation() { */ @ShouldBeAsync public boolean sendPasswordMail(String name, String mailAddress, String newPass) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (!hasAllInformation()) { logger.warning("Cannot perform email registration: not all email settings are complete"); return false; @@ -96,7 +96,7 @@ public boolean sendPasswordMail(String name, String mailAddress, String newPass) */ @ShouldBeAsync public boolean sendVerificationMail(String name, String mailAddress, String code) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (!hasAllInformation()) { logger.warning("Cannot send verification email: not all email settings are complete"); return false; @@ -125,7 +125,7 @@ public boolean sendVerificationMail(String name, String mailAddress, String code */ @ShouldBeAsync public boolean sendRecoveryCode(String name, String email, String code) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); HtmlEmail htmlEmail; try { htmlEmail = sendMailSsl.initializeMail(email); diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java index a54a8e5b0..8501e85cb 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -4,7 +4,6 @@ import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.limbo.LimboPlayer; import fr.xephi.authme.data.limbo.LimboService; -import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.LoginEvent; import fr.xephi.authme.events.RestoreInventoryEvent; import fr.xephi.authme.permission.PermissionsManager; diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java index 5c8fe89dc..c2bd62190 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -1,6 +1,6 @@ package fr.xephi.authme.service; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.initialization.SettingsDependent; @@ -108,7 +108,7 @@ private synchronized void startProtection() { * Transitions the anti bot service from active status back to listening. */ private void stopProtection() { - ThreadSafety.requireSync(); + BukkitThreadSafety.requireSync(); if (antiBotStatus != AntiBotStatus.ACTIVE) { return; } @@ -144,7 +144,7 @@ public AntiBotStatus getAntiBotStatus() { * @param started the new protection status */ public void overrideAntiBotStatus(boolean started) { - ThreadSafety.requireSync(); + BukkitThreadSafety.requireSync(); if (antiBotStatus != AntiBotStatus.DISABLED) { if (started) { startProtection(); @@ -161,7 +161,7 @@ public void overrideAntiBotStatus(boolean started) { */ @ShouldBeAsync public boolean shouldKick() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (antiBotStatus == AntiBotStatus.DISABLED) { return false; } else if (antiBotStatus == AntiBotStatus.ACTIVE) { diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index 1d0e9c7bb..bb433a795 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -11,7 +11,7 @@ import com.maxmind.db.model.Country; import com.maxmind.db.model.CountryResponse; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -132,7 +132,7 @@ private synchronized boolean isDataAvailable() { */ @ShouldBeAsync private void updateDatabase() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); logger.info("Downloading GEO IP database, because the old database is older than " + UPDATE_INTERVAL_DAYS + " days or doesn't exist"); diff --git a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java b/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java index d8726f5e5..1935dff67 100644 --- a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java +++ b/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java @@ -1,7 +1,7 @@ package fr.xephi.authme.service; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.MightBeAsync; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; @@ -72,7 +72,7 @@ private void initEmailCooldownSet() { */ @ShouldBeAsync public void createAndSendRecoveryCode(Player player, String email) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (!checkEmailCooldown(player)) { return; } @@ -96,7 +96,7 @@ public void createAndSendRecoveryCode(Player player, String email) { */ @ShouldBeAsync public void generateAndSendNewPassword(Player player, String email) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (!checkEmailCooldown(player)) { return; } diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java index 2e795f991..81e6e12cf 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java @@ -1,7 +1,7 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -57,7 +57,7 @@ public class PurgeExecutor { */ @ShouldBeAsync public void executePurge(Collection players, Collection names) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); // Purge other data purgeFromAuthMe(names); purgeEssentials(players); diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java index 3372acfab..abeb10ce7 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java @@ -1,7 +1,7 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.ConsoleLoggerFactory; @@ -121,7 +121,7 @@ void setPurging(boolean purging) { */ @ShouldBeAsync void executePurge(Collection players, Collection names) { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); purgeExecutor.executePurge(players, names); } } diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java index 46942c515..c3a5d648e 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java @@ -1,7 +1,7 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.ThreadSafety; +import fr.xephi.authme.util.BukkitThreadSafety; import fr.xephi.authme.annotation.ShouldBeAsync; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.permission.PermissionsManager; @@ -61,7 +61,7 @@ class PurgeTask extends BukkitRunnable { @Override @ShouldBeAsync public void run() { - ThreadSafety.shouldBeAsync(); + BukkitThreadSafety.shouldBeAsync(); if (toPurge.isEmpty()) { //everything was removed finish(); diff --git a/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java b/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java index b7abcf850..2a50e660a 100644 --- a/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java +++ b/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java @@ -1,22 +1,41 @@ package fr.xephi.authme.util; +/** + * A thread-safe interval counter, allows to detect if an event happens more than 'threshold' times + * in the given 'interval'. + */ public class AtomicIntervalCounter { private final int threshold; private final int interval; private int count; private long lastInsert; + /** + * Constructs a new counter. + * + * @param threshold the threshold value of the counter. + * @param interval the counter interval in milliseconds. + */ public AtomicIntervalCounter(int threshold, int interval) { this.threshold = threshold; this.interval = interval; reset(); } + /** + * Resets the counter count. + */ public synchronized void reset() { count = 0; lastInsert = 0; } + /** + * Increments the counter and returns true if the current count has reached the threshold value + * in the given interval, this will also reset the count value. + * + * @return true if the count has reached the threshold value. + */ public synchronized boolean handle() { long now = System.currentTimeMillis(); if (now - lastInsert > interval) { diff --git a/src/main/java/fr/xephi/authme/ThreadSafety.java b/src/main/java/fr/xephi/authme/util/BukkitThreadSafety.java similarity index 50% rename from src/main/java/fr/xephi/authme/ThreadSafety.java rename to src/main/java/fr/xephi/authme/util/BukkitThreadSafety.java index 386a89b95..af36f618e 100644 --- a/src/main/java/fr/xephi/authme/ThreadSafety.java +++ b/src/main/java/fr/xephi/authme/util/BukkitThreadSafety.java @@ -1,18 +1,29 @@ -package fr.xephi.authme; +package fr.xephi.authme.util; import org.bukkit.Bukkit; -public final class ThreadSafety { +/** + * Utility class that provides static methods to ensure that methods are called from the right thread. + */ +public final class BukkitThreadSafety { private static boolean enabled = false; - private ThreadSafety() { + private BukkitThreadSafety() { } + /** + * Enables/disables the bukkit thread-safety warnings. + * + * @param enabled true if the warnings should be enabled, false otherwise. + */ public static void setEnabled(boolean enabled) { - ThreadSafety.enabled = enabled; + BukkitThreadSafety.enabled = enabled; } + /** + * Prints a warning if called by an async thread (not the main server thread). + */ public static void requireSync() { if (!enabled || Bukkit.isPrimaryThread()) { return; @@ -21,6 +32,9 @@ public static void requireSync() { new Throwable().printStackTrace(); } + /** + * Prints a warning if called by the main server thread. + */ public static void shouldBeAsync() { if (!enabled || !Bukkit.isPrimaryThread()) { return; diff --git a/src/main/java/fr/xephi/authme/util/PlayerUtils.java b/src/main/java/fr/xephi/authme/util/PlayerUtils.java index 1dec6b19a..f67078589 100644 --- a/src/main/java/fr/xephi/authme/util/PlayerUtils.java +++ b/src/main/java/fr/xephi/authme/util/PlayerUtils.java @@ -24,7 +24,6 @@ public static String getPlayerIp(Player player) { /** * Returns if the player is an NPC or not. - * TODO: is player metadata thread-safe? * * @param player The player to check * @return True if the player is an NPC, false otherwise diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java index 43c1c9067..ae56accee 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java @@ -2,7 +2,7 @@ import ch.jalu.injector.factory.Factory; import fr.xephi.authme.TestHelper; -import fr.xephi.authme.datasource.converter.Converter; +import fr.xephi.authme.datasource.converter.AbstractConverter; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; @@ -49,7 +49,7 @@ public class ConverterCommandTest { private BukkitService bukkitService; @Mock - private Factory converterFactory; + private Factory converterFactory; @BeforeClass public static void initLogger() { @@ -87,10 +87,10 @@ public void shouldHandleCommandWithNoArgs() { @Test public void shouldHaveUniqueClassForEachConverter() { // given - Set> classes = new HashSet<>(); + Set> classes = new HashSet<>(); // when / then - for (Map.Entry> entry : ConverterCommand.CONVERTERS.entrySet()) { + for (Map.Entry> entry : ConverterCommand.CONVERTERS.entrySet()) { assertThat("Name is not null or empty", StringUtils.isEmpty(entry.getKey()), equalTo(false)); @@ -103,8 +103,8 @@ public void shouldHaveUniqueClassForEachConverter() { public void shouldLaunchConverterForAllTypes() { // given String converterName = "rakamak"; - Class converterClass = ConverterCommand.CONVERTERS.get(converterName); - Converter converter = createMockReturnedByInjector(converterClass); + Class converterClass = ConverterCommand.CONVERTERS.get(converterName); + AbstractConverter converter = createMockReturnedByInjector(converterClass); CommandSender sender = mock(CommandSender.class); setBukkitServiceToRunTaskAsynchronously(bukkitService); @@ -122,8 +122,8 @@ public void shouldLaunchConverterForAllTypes() { public void shouldCatchExceptionInConverterAndInformSender() { // given String converterName = "vauth"; - Class converterClass = ConverterCommand.CONVERTERS.get(converterName); - Converter converter = createMockReturnedByInjector(converterClass); + Class converterClass = ConverterCommand.CONVERTERS.get(converterName); + AbstractConverter converter = createMockReturnedByInjector(converterClass); doThrow(IllegalStateException.class).when(converter).execute(any(CommandSender.class)); CommandSender sender = mock(CommandSender.class); setBukkitServiceToRunTaskAsynchronously(bukkitService); @@ -139,7 +139,7 @@ public void shouldCatchExceptionInConverterAndInformSender() { verify(commonService).send(sender, MessageKey.ERROR); } - private T createMockReturnedByInjector(Class clazz) { + private T createMockReturnedByInjector(Class clazz) { T converter = mock(clazz); given(converterFactory.newInstance(clazz)).willReturn(converter); return converter; diff --git a/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java b/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java index 5dd032444..54dff95fd 100644 --- a/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java +++ b/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java @@ -42,11 +42,11 @@ public void shouldThrowForDestinationTypeMismatch() { given(destination.getType()).willReturn(DataSourceType.MYSQL); DataSourceType destinationType = DataSourceType.SQLITE; DataSource source = mock(DataSource.class); - Converter converter = new DataSourceConverterTestImpl<>(source, destination, destinationType); + AbstractConverter converter = new DataSourceConverterTestImpl<>(source, destination, destinationType); CommandSender sender = mock(CommandSender.class); // when - converter.execute(sender); + converter.executeInternal(sender); // then verify(sender).sendMessage(argThat(containsString("Please configure your connection to SQLITE"))); @@ -67,7 +67,7 @@ public void shouldHandleSourceThrowingException() { CommandSender sender = mock(CommandSender.class); // when - converter.execute(sender); + converter.executeInternal(sender); // then verify(sender).sendMessage("The data source to convert from could not be initialized"); @@ -88,11 +88,11 @@ public void shouldConvertAndSkipExistingPlayers() { given(source.getAllAuths()).willReturn(auths); given(destination.isAuthAvailable(auths.get(0).getNickname())).willReturn(true); - Converter converter = new DataSourceConverterTestImpl<>(source, destination, destinationType); + AbstractConverter converter = new DataSourceConverterTestImpl<>(source, destination, destinationType); CommandSender sender = mock(CommandSender.class); // when - converter.execute(sender); + converter.executeInternal(sender); // then verify(destination).getType(); diff --git a/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java b/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java index 8819772de..77840d604 100644 --- a/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java +++ b/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java @@ -59,7 +59,7 @@ public void shouldImportUsers() { CommandSender sender = mock(CommandSender.class); // when - crazyLoginConverter.execute(sender); + crazyLoginConverter.executeInternal(sender); // then ArgumentCaptor authCaptor = ArgumentCaptor.forClass(PlayerAuth.class); @@ -78,7 +78,7 @@ public void shouldStopForNonExistentFile() { CommandSender sender = mock(CommandSender.class); // when - crazyLoginConverter.execute(sender); + crazyLoginConverter.executeInternal(sender); // then verifyNoInteractions(dataSource); diff --git a/src/test/java/tools/dependencygraph/DrawDependency.java b/src/test/java/tools/dependencygraph/DrawDependency.java index 40a3b6b23..c25be95fc 100644 --- a/src/test/java/tools/dependencygraph/DrawDependency.java +++ b/src/test/java/tools/dependencygraph/DrawDependency.java @@ -14,7 +14,7 @@ import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.executable.authme.debug.DebugCommand; import fr.xephi.authme.data.limbo.persistence.LimboPersistence; -import fr.xephi.authme.datasource.converter.Converter; +import fr.xephi.authme.datasource.converter.AbstractConverter; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.SynchronousProcess; @@ -204,7 +204,7 @@ private static List> buildSuperTypesList() { LimboPersistence.class.getPackage().getName() + ".LimboPersistenceHandler"); return ImmutableList.of(ExecutableCommand.class, SynchronousProcess.class, AsynchronousProcess.class, - EncryptionMethod.class, Converter.class, Listener.class, RegistrationExecutor.class, debugSectionClass, + EncryptionMethod.class, AbstractConverter.class, Listener.class, RegistrationExecutor.class, debugSectionClass, limboPersistenceClass); } catch (ClassNotFoundException e) { throw new IllegalStateException(e); From 12ba0f955e842b9d3a38c79fe9b10b54da8519a7 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Sun, 2 Feb 2020 19:08:07 +0100 Subject: [PATCH 8/8] Fix code style --- src/main/java/fr/xephi/authme/AuthMe.java | 2 +- src/test/java/tools/dependencygraph/DrawDependency.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 2547920af..d5ebc38b1 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -173,7 +173,7 @@ public void onEnable() { // Successful message logger.info("AuthMe " + getPluginVersion() + " build n." + getPluginBuildNumber() + " successfully enabled!"); // Start catching wrong sync/async calls - if(System.getProperty("authme.disableThreadSafetyChecks") == null) { + if (System.getProperty("authme.disableThreadSafetyChecks") == null) { BukkitThreadSafety.setEnabled(true); } diff --git a/src/test/java/tools/dependencygraph/DrawDependency.java b/src/test/java/tools/dependencygraph/DrawDependency.java index c25be95fc..0f6311e3a 100644 --- a/src/test/java/tools/dependencygraph/DrawDependency.java +++ b/src/test/java/tools/dependencygraph/DrawDependency.java @@ -204,8 +204,8 @@ private static List> buildSuperTypesList() { LimboPersistence.class.getPackage().getName() + ".LimboPersistenceHandler"); return ImmutableList.of(ExecutableCommand.class, SynchronousProcess.class, AsynchronousProcess.class, - EncryptionMethod.class, AbstractConverter.class, Listener.class, RegistrationExecutor.class, debugSectionClass, - limboPersistenceClass); + EncryptionMethod.class, AbstractConverter.class, Listener.class, RegistrationExecutor.class, + debugSectionClass, limboPersistenceClass); } catch (ClassNotFoundException e) { throw new IllegalStateException(e); }