From 471b4496c947156058da50268e9108604a86984e Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 18:14:25 -0700 Subject: [PATCH] Add task management and improve threaded code. --- .../worldguard/bukkit/WorldGuardPlugin.java | 77 ++++- .../bukkit/commands/AsyncCommandHelper.java | 147 ++++++++ .../bukkit/commands/CommandUtils.java | 149 +------- .../commands/FutureProgressListener.java | 55 +++ .../commands/MessageFutureCallback.java | 103 ++++++ .../bukkit/commands/RegionCommands.java | 317 +++++++----------- .../bukkit/commands/RegionMemberCommands.java | 57 ++-- .../bukkit/commands/WorldGuardCommands.java | 74 +++- .../database/mysql/UserToIdCache.java | 181 ---------- .../internal/util/sql/StatementUtils.java | 55 +++ .../protection/GlobalRegionManager.java | 2 +- .../protection/databases/CSVDatabase.java | 24 +- .../InvalidTableFormatException.java | 39 --- .../protection/databases/MySQLDatabase.java | 16 +- .../databases/ProtectionDatabase.java | 10 +- .../protection/databases/RegionDBUtil.java | 51 +-- .../protection/databases/YAMLDatabase.java | 77 +++-- .../AbstractDatabaseMigrator.java | 124 +++---- .../DatabaseMigrator.java | 62 ++-- .../MigrationException.java | 80 ++--- .../{migrators => migrator}/MigratorKey.java | 84 ++--- .../MySQLToYAMLMigrator.java | 194 +++++------ .../YAMLToMySQLMigrator.java | 172 +++++----- .../databases}/mysql/AbstractJob.java | 2 +- .../databases}/mysql/MySQLDatabaseImpl.java | 7 +- .../databases}/mysql/RegionLoader.java | 8 +- .../databases/mysql/RegionWriter.java} | 30 +- .../databases/mysql/UserRowCache.java | 184 ++++++++++ .../managers/FlatRegionManager.java | 16 +- .../managers/PRTreeRegionManager.java | 29 +- .../protection/managers/RegionManager.java | 1 + .../protection/regions/ProtectedRegion.java | 32 +- .../com/sk89q/worldguard/util/RegionUtil.java | 4 +- 33 files changed, 1344 insertions(+), 1119 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java delete mode 100644 src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/InvalidTableFormatException.java rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/AbstractDatabaseMigrator.java (95%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/DatabaseMigrator.java (91%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/MigrationException.java (92%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/MigratorKey.java (92%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/MySQLToYAMLMigrator.java (95%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/YAMLToMySQLMigrator.java (95%) rename src/main/java/com/sk89q/worldguard/{internal/protection/database => protection/databases}/mysql/AbstractJob.java (97%) rename src/main/java/com/sk89q/worldguard/{internal/protection/database => protection/databases}/mysql/MySQLDatabaseImpl.java (97%) rename src/main/java/com/sk89q/worldguard/{internal/protection/database => protection/databases}/mysql/RegionLoader.java (98%) rename src/main/java/com/sk89q/worldguard/{internal/protection/database/mysql/RegionCommitter.java => protection/databases/mysql/RegionWriter.java} (96%) create mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index deeae98d..a7213061 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -30,6 +30,9 @@ import com.sk89q.minecraft.util.commands.SimpleInjector; import com.sk89q.minecraft.util.commands.WrappedCommandException; import com.sk89q.odeum.concurrency.EvenMoreExecutors; +import com.sk89q.odeum.task.SimpleSupervisor; +import com.sk89q.odeum.task.Supervisor; +import com.sk89q.odeum.task.Task; import com.sk89q.squirrelid.cache.HashMapCache; import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.squirrelid.cache.SQLiteCache; @@ -49,6 +52,7 @@ import com.sk89q.worldguard.internal.listener.ChestProtectionListener; import com.sk89q.worldguard.internal.listener.RegionProtectionListener; import com.sk89q.worldguard.protection.GlobalRegionManager; +import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; import org.bukkit.ChatColor; @@ -64,6 +68,7 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import javax.annotation.Nullable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -74,6 +79,9 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; import java.util.jar.JarFile; import java.util.logging.Level; import java.util.zip.ZipEntry; @@ -88,8 +96,8 @@ public class WorldGuardPlugin extends JavaPlugin { private final GlobalRegionManager globalRegionManager; private final ConfigurationManager configuration; private FlagStateManager flagStateManager; - private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator( - EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, 20)); + private final Supervisor supervisor = new SimpleSupervisor(); + private ListeningExecutorService executorService; private ProfileService profileService; private ProfileCache profileCache; @@ -124,6 +132,7 @@ public static WorldGuardPlugin inst() { @Override @SuppressWarnings("deprecation") public void onEnable() { + executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20)); // Set the proper command injector commands.setInjector(new SimpleInjector(this)); @@ -226,14 +235,33 @@ public void run() { @Override public void onDisable() { + executorService.shutdownNow(); + + try { + getLogger().log(Level.INFO, "Shutting down executor and waiting for any pending tasks..."); + + List> tasks = supervisor.getTasks(); + if (!tasks.isEmpty()) { + StringBuilder builder = new StringBuilder("Known tasks:"); + for (Task task : tasks) { + builder.append("\n"); + builder.append(task.getName()); + } + getLogger().log(Level.INFO, builder.toString()); + } + + executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + globalRegionManager.unload(); configuration.unload(); this.getServer().getScheduler().cancelTasks(this); } @Override - public boolean onCommand(CommandSender sender, Command cmd, String label, - String[] args) { + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { try { commands.execute(cmd.getName(), args, sender, sender); } catch (CommandPermissionsException e) { @@ -244,12 +272,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, sender.sendMessage(ChatColor.RED + e.getMessage()); sender.sendMessage(ChatColor.RED + e.getUsage()); } catch (WrappedCommandException e) { - if (e.getCause() instanceof NumberFormatException) { - sender.sendMessage(ChatColor.RED + "Number expected, string received instead."); - } else { - sender.sendMessage(ChatColor.RED + "An error has occurred. See console."); - e.printStackTrace(); - } + sender.sendMessage(ChatColor.RED + convertThrowable(e.getCause())); } catch (CommandException e) { sender.sendMessage(ChatColor.RED + e.getMessage()); } @@ -257,6 +280,31 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, return true; } + /** + * Convert the throwable into a somewhat friendly message. + * + * @param throwable the throwable + * @return a message + */ + public String convertThrowable(@Nullable Throwable throwable) { + if (throwable instanceof NumberFormatException) { + return "Number expected, string received instead."; + } else if (throwable instanceof RejectedExecutionException) { + return "There are currently too many tasks queued to add yours. Use /wg running to list queued and running tasks."; + } else if (throwable instanceof CancellationException) { + return "Task was cancelled"; + } else if (throwable instanceof InterruptedException) { + return "Task was interrupted"; + } else if (throwable instanceof UnresolvedNamesException) { + return throwable.getMessage(); + } else if (throwable instanceof Exception) { + getLogger().log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable); + return "Unexpected error occurred: " + ((Exception) throwable).getMessage(); + } else { + return "Unknown error"; + } + } + /** * Get the GlobalRegionManager. * @@ -295,6 +343,15 @@ public ConfigurationManager getGlobalStateManager() { return configuration; } + /** + * Get the supervisor. + * + * @return the supervisor + */ + public Supervisor getSupervisor() { + return supervisor; + } + /** * Get the global executor service for internal usage (please use your * own executor service). diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java new file mode 100644 index 00000000..184e9306 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java @@ -0,0 +1,147 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.sk89q.odeum.task.FutureForwardingTask; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.World; +import org.bukkit.command.CommandSender; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +class AsyncCommandHelper { + + private final ListenableFuture future; + private final WorldGuardPlugin plugin; + private final CommandSender sender; + @Nullable + private Object[] formatArgs; + + private AsyncCommandHelper(ListenableFuture future, WorldGuardPlugin plugin, CommandSender sender) { + checkNotNull(future); + checkNotNull(plugin); + checkNotNull(sender); + + this.future = future; + this.plugin = plugin; + this.sender = sender; + } + + public AsyncCommandHelper formatUsing(Object... args) { + this.formatArgs = args; + return this; + } + + private String format(String message) { + if (formatArgs != null) { + return String.format(message, formatArgs); + } else { + return message; + } + } + + public AsyncCommandHelper registerWithSupervisor(String description) { + plugin.getSupervisor().monitor( + FutureForwardingTask.create( + future, format(description), sender)); + return this; + } + + public AsyncCommandHelper sendMessageAfterDelay(String message) { + FutureProgressListener.addProgressListener(future, sender, format(message)); + return this; + } + + public AsyncCommandHelper thenRespondWith(String success, String failure) { + // Send a response message + Futures.addCallback( + future, + new MessageFutureCallback.Builder(plugin, sender) + .onSuccess(format(success)) + .onFailure(format(failure)) + .build()); + return this; + } + + public AsyncCommandHelper thenTellErrorsOnly(String failure) { + // Send a response message + Futures.addCallback( + future, + new MessageFutureCallback.Builder(plugin, sender) + .onFailure(format(failure)) + .build()); + return this; + } + + public AsyncCommandHelper forRegionDataLoad(World world, boolean silent) { + checkNotNull(world); + + formatUsing(world.getName()); + registerWithSupervisor("Loading region data for '%s'"); + if (silent) { + thenTellErrorsOnly("Failed to load regions '%s'"); + } else { + sendMessageAfterDelay("(Please wait... loading the region data for '%s')"); + thenRespondWith( + "Loaded region data for '%s'", + "Failed to load regions '%s'"); + } + + return this; + } + + public AsyncCommandHelper forRegionDataSave(World world, boolean silent) { + checkNotNull(world); + + formatUsing(world.getName()); + registerWithSupervisor("Saving region data for '%s'"); + if (silent) { + thenTellErrorsOnly("Failed to save regions '%s'"); + } else { + sendMessageAfterDelay("(Please wait... saving the region data for '%s')"); + thenRespondWith( + "Saved region data for '%s'", + "Failed to load regions '%s'"); + } + + return this; + } + + public AsyncCommandHelper thenSaveRegionData(RegionManager manager, World world) { + checkNotNull(manager); + checkNotNull(world); + + ListenableFuture future = manager.save(true); + + AsyncCommandHelper.wrap(future, plugin, sender).forRegionDataSave(world, true); + + return this; + } + + public static AsyncCommandHelper wrap(ListenableFuture future, WorldGuardPlugin plugin, CommandSender sender) { + return new AsyncCommandHelper(future, plugin, sender); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java index 9fb2e002..1d0f7080 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java @@ -20,85 +20,39 @@ package com.sk89q.worldguard.bukkit.commands; import com.google.common.base.Function; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; -import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException; -import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.ChatColor; -import org.bukkit.World; +import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; import javax.annotation.Nullable; -import java.util.Timer; -import java.util.concurrent.CancellationException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static com.google.common.base.Preconditions.checkNotNull; /** * Command-related utility methods. */ final class CommandUtils { - private static final Logger log = Logger.getLogger(CommandUtils.class.getCanonicalName()); - private static final Timer timer = new Timer(); - private static final int MESSAGE_DELAY = 1000; - private CommandUtils() { } /** - * Add a message that gets shown after a delay if the future has not - * completed. + * Get the name of the given owner object. * - * @param future the future - * @param sender the sender to send a message to - * @param message the message to send (will be grey) + * @param owner the owner object + * @return a name */ - static void progressCallback(ListenableFuture future, CommandSender sender, String message) { - checkNotNull(future); - checkNotNull(sender); - checkNotNull(message); - - final MessageTimerTask task = new MessageTimerTask(sender, ChatColor.GRAY + message); - timer.schedule(task, MESSAGE_DELAY); - future.addListener(new Runnable() { - @Override - public void run() { - task.cancel(); - } - }, MoreExecutors.sameThreadExecutor()); - } - - /** - * Create a callback to print a message to the user. - * - * @param sender the sender - * @param successMessage a success message or {@code null} to print nothing - * @param errorMessage an error message - * @param ignored type - * @return a callback - */ - static FutureCallback messageCallback(final CommandSender sender, @Nullable final String successMessage, final String errorMessage) { - checkNotNull(sender); - checkNotNull(errorMessage); - - return new FutureCallback() { - @Override - public void onSuccess(@Nullable T o) { - if (successMessage != null) { - sender.sendMessage(ChatColor.YELLOW + successMessage); - } - } - - @Override - public void onFailure(@Nullable Throwable throwable) { - sender.sendMessage(ChatColor.RED + errorMessage + ": " + convertThrowable(throwable)); - } - }; + static String getOwnerName(@Nullable Object owner) { + if (owner == null) { + return "?"; + } else if (owner instanceof Player) { + return ((Player) owner).getName(); + } else if (owner instanceof ConsoleCommandSender) { + return "*CONSOLE*"; + } else if (owner instanceof BlockCommandSender) { + return ((BlockCommandSender) owner).getBlock().getLocation().toString(); + } else { + return "?"; + } } /** @@ -118,71 +72,4 @@ public Object apply(@Nullable String s) { }; } - /** - * Return a function to add a prefix and suffix to the input string. - * - * @param prefix the prefix - * @param suffix the suffix - * @return a function - */ - static Function messageAppender(final String prefix, final String suffix) { - return new Function() { - @Override - public String apply(@Nullable String s) { - return prefix + s + suffix; - } - }; - } - - /** - * Create a callback to save a region manager on success. - * - * @param sender the sender - * @param manager the region manager - * @param world the world - * @param silent true to not print a success message - * @param an ignored type - * @return a callback - */ - static FutureCallback saveRegionsCallback(final CommandSender sender, final RegionManager manager, final World world, final boolean silent) { - checkNotNull(sender); - checkNotNull(manager); - checkNotNull(world); - - return new FutureCallback() { - @Override - public void onSuccess(@Nullable T o) { - ListenableFuture future = manager.save(true); - String successMessage = silent ? null : "Successfully saved the region data for '" + world.getName() + "'."; - String failureMessage = "Failed to save the region data for '" + world.getName() + "'"; - Futures.addCallback(future, messageCallback(sender, successMessage, failureMessage)); - } - - @Override - public void onFailure(@Nullable Throwable throwable) { - } - }; - } - - /** - * Convert the throwable into a somewhat friendly message. - * - * @param throwable the throwable - * @return a message - */ - private static String convertThrowable(@Nullable Throwable throwable) { - if (throwable instanceof CancellationException) { - return "Task was cancelled"; - } else if (throwable instanceof InterruptedException) { - return "Task was interrupted"; - } else if (throwable instanceof UnresolvedNamesException) { - return throwable.getMessage(); - } else if (throwable instanceof Exception) { - log.log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable); - return "Unexpected error occurred: " + ((Exception) throwable).getMessage(); - } else { - return "Unknown error"; - } - } - } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java new file mode 100644 index 00000000..549aaa05 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java @@ -0,0 +1,55 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.Timer; + +import static com.google.common.base.Preconditions.checkNotNull; + +class FutureProgressListener implements Runnable { + + private static final Timer timer = new Timer(); + private static final int MESSAGE_DELAY = 1000; + + private final MessageTimerTask task; + + FutureProgressListener(CommandSender sender, String message) { + checkNotNull(sender); + checkNotNull(message); + + task = new MessageTimerTask(sender, ChatColor.GRAY + message); + timer.schedule(task, MESSAGE_DELAY); + } + + @Override + public void run() { + task.cancel(); + } + + public static void addProgressListener(ListenableFuture future, CommandSender sender, String message) { + future.addListener(new FutureProgressListener(sender, message), MoreExecutors.sameThreadExecutor()); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java new file mode 100644 index 00000000..cb25126a --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java @@ -0,0 +1,103 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.google.common.util.concurrent.FutureCallback; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +class MessageFutureCallback implements FutureCallback { + + private final WorldGuardPlugin plugin; + private final CommandSender sender; + @Nullable + private final String success; + @Nullable + private final String failure; + + private MessageFutureCallback(WorldGuardPlugin plugin, CommandSender sender, @Nullable String success, @Nullable String failure) { + this.plugin = plugin; + this.sender = sender; + this.success = success; + this.failure = failure; + } + + @Override + public void onSuccess(@Nullable V v) { + if (success != null) { + sender.sendMessage(ChatColor.YELLOW + success); + } + } + + @Override + public void onFailure(@Nullable Throwable throwable) { + String failure = this.failure != null ? this.failure : "An error occurred"; + sender.sendMessage(ChatColor.RED + failure + ": " + plugin.convertThrowable(throwable)); + } + + static class Builder { + private final WorldGuardPlugin plugin; + private final CommandSender sender; + @Nullable + private String success; + @Nullable + private String failure; + + Builder(WorldGuardPlugin plugin, CommandSender sender) { + checkNotNull(plugin); + checkNotNull(sender); + + this.plugin = plugin; + this.sender = sender; + } + + public Builder onSuccess(@Nullable String message) { + this.success = message; + return this; + } + + public Builder onFailure(@Nullable String message) { + this.failure = message; + return this; + } + + public MessageFutureCallback build() { + return new MessageFutureCallback(plugin, sender, success, failure); + } + } + + public static MessageFutureCallback createRegionLoadCallback(WorldGuardPlugin plugin, CommandSender sender) { + return new Builder(plugin, sender) + .onSuccess("Successfully load the region data.") + .build(); + } + + public static MessageFutureCallback createRegionSaveCallback(WorldGuardPlugin plugin, CommandSender sender) { + return new Builder(plugin, sender) + .onSuccess("Successfully saved the region data.") + .build(); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java index 15b519d9..4bf89246 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java @@ -19,7 +19,6 @@ package com.sk89q.worldguard.bukkit.commands; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.sk89q.minecraft.util.commands.Command; @@ -40,9 +39,9 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.databases.RegionDBUtil; -import com.sk89q.worldguard.protection.databases.migrators.AbstractDatabaseMigrator; -import com.sk89q.worldguard.protection.databases.migrators.MigrationException; -import com.sk89q.worldguard.protection.databases.migrators.MigratorKey; +import com.sk89q.worldguard.protection.databases.migrator.AbstractDatabaseMigrator; +import com.sk89q.worldguard.protection.databases.migrator.MigrationException; +import com.sk89q.worldguard.protection.databases.migrator.MigratorKey; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; @@ -54,6 +53,7 @@ import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.command.CommandSender; @@ -65,8 +65,6 @@ import java.util.Date; import java.util.List; import java.util.Map; -import java.util.concurrent.RejectedExecutionException; -import java.util.logging.Level; /** * Implements the /region commands for WorldGuard. @@ -91,7 +89,18 @@ public RegionCommands(WorldGuardPlugin plugin) { private static RegionPermissionModel getPermissionModel(CommandSender sender) { return new RegionPermissionModel(WorldGuardPlugin.inst(), sender); } - + + /** + * Save the regions asynchronously and alert the sender if any errors + * occur during save. + * + * @param manager the manager to save + * @param sender the sender + */ + private void saveRegionsSilently(RegionManager manager, World world, CommandSender sender) { + AsyncCommandHelper.wrap(Futures.immediateFuture(null), plugin, sender).thenSaveRegionData(manager, world); + } + /** * Gets the world from the given flag, or falling back to the the current player * if the sender is a player, otherwise reporting an error. @@ -102,8 +111,7 @@ private static RegionPermissionModel getPermissionModel(CommandSender sender) { * @return a world * @throws CommandException on error */ - private static World getWorld(CommandContext args, CommandSender sender, char flag) - throws CommandException { + private static World getWorld(CommandContext args, CommandSender sender, char flag) throws CommandException { if (args.hasFlag(flag)) { return WorldGuardPlugin.inst().matchWorld(sender, args.getFlag(flag)); } else { @@ -150,9 +158,7 @@ private static String validateRegionId(String id, boolean allowGlobal) * @param allowGlobal true to allow selecting __global__ * @throws CommandException thrown if no region is found by the given name */ - private static ProtectedRegion findExistingRegion( - RegionManager regionManager, String id, boolean allowGlobal) - throws CommandException { + private static ProtectedRegion findExistingRegion(RegionManager regionManager, String id, boolean allowGlobal) throws CommandException { // Validate the id validateRegionId(id, allowGlobal); @@ -186,8 +192,7 @@ private static ProtectedRegion findExistingRegion( * @return a region * @throws CommandException thrown if no region was found */ - private static ProtectedRegion findRegionStandingIn( - RegionManager regionManager, Player player) throws CommandException { + private static ProtectedRegion findRegionStandingIn(RegionManager regionManager, Player player) throws CommandException { return findRegionStandingIn(regionManager, player, false); } @@ -206,8 +211,7 @@ private static ProtectedRegion findRegionStandingIn( * @return a region * @throws CommandException thrown if no region was found */ - private static ProtectedRegion findRegionStandingIn( - RegionManager regionManager, Player player, boolean allowGlobal) throws CommandException { + private static ProtectedRegion findRegionStandingIn(RegionManager regionManager, Player player, boolean allowGlobal) throws CommandException { ApplicableRegionSet set = regionManager.getApplicableRegions( player.getLocation()); @@ -272,9 +276,7 @@ private static Selection getSelection(Player player) throws CommandException { * @return a new region * @throws CommandException thrown on an error */ - private static ProtectedRegion createRegionFromSelection(Player player, String id) - throws CommandException { - + private static ProtectedRegion createRegionFromSelection(Player player, String id) throws CommandException { Selection selection = getSelection(player); // Detect the type of region from WorldEdit @@ -293,84 +295,6 @@ private static ProtectedRegion createRegionFromSelection(Player player, String i } } - /** - * Save the region database. - * - * @param sender the sender - * @param regionManager the region manager - * @param silent whether to suppress messages sent to the player - * @throws CommandException throw on an error - */ - private ListenableFuture commitChanges(final CommandSender sender, RegionManager regionManager, final World world, final boolean silent) throws CommandException { - ListenableFuture future; - - try { - future = regionManager.save(true); - } catch (RejectedExecutionException e) { - throw new CommandException("There are too many interleaved load and save tasks currently queued. Please try again later."); - } - - if (!silent) { - sender.sendMessage(ChatColor.GRAY + "(A save of the region data has been queued.)"); - } - - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(Object o) { - if (!silent) { - sender.sendMessage(ChatColor.YELLOW + "Successfully saved the region data for the '" + world.getName() + "' world."); - } - } - - @Override - public void onFailure(Throwable throwable) { - sender.sendMessage(ChatColor.RED + "Failed to save region data for the '" + world.getName() + "' world: " + throwable.getMessage()); - plugin.getLogger().log(Level.WARNING, "Failed to save region data", throwable); - } - }); - - return future; - } - - /** - * Load the region database. - * - * @param sender the sender - * @param regionManager the region manager - * @param silent whether to suppress messages sent to the player - * @throws CommandException throw on an error - */ - private ListenableFuture reloadChanges(final CommandSender sender, RegionManager regionManager, final World world, final boolean silent) throws CommandException { - ListenableFuture future; - - try { - future = regionManager.load(true); - } catch (RejectedExecutionException e) { - throw new CommandException("There are too many interleaved load and save tasks currently queued. Please try again later."); - } - - if (!silent) { - sender.sendMessage(ChatColor.GRAY + "(A load of the region data has been queued.)"); - } - - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(Object o) { - if (!silent) { - sender.sendMessage(ChatColor.YELLOW + "Successfully loaded the region data for the '" + world.getName() + "' world."); - } - } - - @Override - public void onFailure(Throwable throwable) { - sender.sendMessage(ChatColor.RED + "Failed to load region data for the '" + world.getName() + "' world: " + throwable.getMessage()); - plugin.getLogger().log(Level.WARNING, "Failed to load region data", throwable); - } - }); - - return future; - } - /** * Set a player's selection to a given region. * @@ -422,9 +346,7 @@ private static void setPlayerSelection(Player player, ProtectedRegion region) * @param value the value * @throws InvalidFlagFormat thrown if the value is invalid */ - private static void setFlag(ProtectedRegion region, - Flag flag, CommandSender sender, String value) - throws InvalidFlagFormat { + private static void setFlag(ProtectedRegion region, Flag flag, CommandSender sender, String value) throws InvalidFlagFormat { region.setFlag(flag, flag.parseInput(WorldGuardPlugin.inst(), sender, value)); } @@ -439,8 +361,7 @@ private static void setFlag(ProtectedRegion region, usage = " [ [ []]]", desc = "Defines a region", min = 1) - public void define(CommandContext args, CommandSender sender) - throws CommandException { + public void define(CommandContext args, CommandSender sender) throws CommandException { Player player = plugin.checkPlayer(sender); // Check permissions @@ -466,9 +387,6 @@ public void define(CommandContext args, CommandSender sender) if (args.argsLength() > 1) { region.setOwners(RegionDBUtil.parseDomainString(args.getSlice(1), 1)); } - - regionManager.addRegion(region); - commitChanges(sender, regionManager, player.getWorld(), true); // Save to disk // Issue a warning about height int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); @@ -488,6 +406,12 @@ public void define(CommandContext args, CommandSender sender) // Tell the user sender.sendMessage(ChatColor.YELLOW + "A new region has been made named '" + id + "'."); + + // Add region + regionManager.addRegion(region); + + // Save regions + saveRegionsSilently(regionManager, player.getWorld(), sender); } /** @@ -501,8 +425,7 @@ public void define(CommandContext args, CommandSender sender) usage = "", desc = "Re-defines the shape of a region", min = 1, max = 1) - public void redefine(CommandContext args, CommandSender sender) - throws CommandException { + public void redefine(CommandContext args, CommandSender sender) throws CommandException { Player player = plugin.checkPlayer(sender); World world = player.getWorld(); @@ -532,9 +455,6 @@ public void redefine(CommandContext args, CommandSender sender) // This should not be thrown } - regionManager.addRegion(region); // Replace region - commitChanges(sender, regionManager, player.getWorld(), true); // Save to disk - // Issue a warning about height int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); if (height <= 2) { @@ -543,6 +463,11 @@ public void redefine(CommandContext args, CommandSender sender) } sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area."); + + regionManager.addRegion(region); // Replace region + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -573,8 +498,8 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep String id = validateRegionId(args.getString(0), false); // Can't replace existing regions - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - if (mgr.hasRegion(id)) { + RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld()); + if (regionManager.hasRegion(id)) { throw new CommandException( "That region already exists. Please choose a different name."); } @@ -593,13 +518,13 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep if (!permModel.mayClaimRegionsUnbounded()) { int maxRegionCount = wcfg.getMaxRegionCount(player); if (maxRegionCount >= 0 - && mgr.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { + && regionManager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { throw new CommandException( "You own too many regions, delete one first to claim a new one."); } } - ProtectedRegion existing = mgr.getRegionExact(id); + ProtectedRegion existing = regionManager.getRegionExact(id); // Check for an existing region if (existing != null) { @@ -610,7 +535,7 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep } // We have to check whether this region violates the space of any other reion - ApplicableRegionSet regions = mgr.getApplicableRegions(region); + ApplicableRegionSet regions = regionManager.getApplicableRegions(region); // Check if this region overlaps any other region if (regions.size() > 0) { @@ -634,34 +559,15 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep } } - /*if (plugin.getGlobalConfiguration().getiConomy() != null && wcfg.useiConomy && wcfg.buyOnClaim) { - if (iConomy.getBank().hasAccount(player.getName())) { - Account account = iConomy.getBank().getAccount(player.getName()); - double balance = account.getBalance(); - double regionCosts = region.countBlocks() * wcfg.buyOnClaimPrice; - if (balance >= regionCosts) { - account.subtract(regionCosts); - player.sendMessage(ChatColor.YELLOW + "You have bought that region for " - + iConomy.getBank().format(regionCosts)); - account.save(); - } else { - player.sendMessage(ChatColor.RED + "You have not enough money."); - player.sendMessage(ChatColor.RED + "The region you want to claim costs " - + iConomy.getBank().format(regionCosts)); - player.sendMessage(ChatColor.RED + "You have " + iConomy.getBank().format(balance)); - return; - } - } else { - player.sendMessage(ChatColor.YELLOW + "You have not enough money."); - return; - } - }*/ - region.getOwners().addPlayer(player.getName()); - - mgr.addRegion(region); - commitChanges(sender, mgr, player.getWorld(), true); // Save to disk + sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area."); + + // Replace region + regionManager.addRegion(region); + + // Save regions + saveRegionsSilently(regionManager, player.getWorld(), sender); } /** @@ -733,22 +639,31 @@ public void info(CommandContext args, CommandSender sender) throws CommandExcept throw new CommandPermissionsException(); } - // Print region information - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache()); - ListenableFuture future = Futures.transform( - plugin.getExecutorService().submit(printout), - CommandUtils.messageFunction(sender)); - Futures.addCallback(future, CommandUtils.messageCallback(sender, null, "Failed to retrieve region info")); - - // Let the player also select the region + // Let the player select the region if (args.hasFlag('s')) { // Check permissions if (!permModel.maySelect(existing)) { throw new CommandPermissionsException(); } - + setPlayerSelection(plugin.checkPlayer(sender), existing); } + + // Print region information + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache()); + ListenableFuture future = Futures.transform( + plugin.getExecutorService().submit(printout), + CommandUtils.messageFunction(sender)); + + // If it takes too long... + FutureProgressListener.addProgressListener( + future, sender, "(Please wait... fetching region information...)"); + + // Send a response message + Futures.addCallback(future, + new MessageFutureCallback.Builder(plugin, sender) + .onFailure("Failed to fetch region information") + .build()); } /** @@ -963,8 +878,6 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept } } - commitChanges(sender, regionManager, world, true); // Save to disk - // Print region information RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, null); printout.append(ChatColor.GRAY); @@ -972,6 +885,9 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept printout.appendFlagsList(false); printout.append(")"); printout.send(sender); + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -1002,11 +918,13 @@ public void setPriority(CommandContext args, CommandSender sender) } existing.setPriority(priority); - commitChanges(sender, regionManager, world, true); // Save to disk sender.sendMessage(ChatColor.YELLOW + "Priority of '" + existing.getId() + "' set to " + priority + " (higher numbers override)."); + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -1058,8 +976,6 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE printout.send(sender); return; } - - commitChanges(sender, regionManager, world, true); // Save to disk // Tell the user the current inheritance RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child, null); @@ -1073,6 +989,9 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE printout.append(")"); } printout.send(sender); + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -1101,10 +1020,11 @@ public void remove(CommandContext args, CommandSender sender) throws CommandExce } regionManager.removeRegion(existing.getId()); - commitChanges(sender, regionManager, world, true); // Save to disk - sender.sendMessage(ChatColor.YELLOW - + "Region '" + existing.getId() + "' removed."); + sender.sendMessage(ChatColor.YELLOW + "Region '" + existing.getId() + "' removed."); + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -1132,33 +1052,33 @@ public void load(CommandContext args, final CommandSender sender) throws Command } if (world != null) { - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - if (regionManager == null) { + RegionManager manager = plugin.getGlobalRegionManager().get(world); + + if (manager == null) { throw new CommandException("No region manager exists for world '" + world.getName() + "'."); } - reloadChanges(sender, regionManager, world, false); + + ListenableFuture future = manager.load(true); + + AsyncCommandHelper.wrap(future, plugin, sender) + .forRegionDataLoad(world, false); } else { + // Load regions for all worlds List> futures = new ArrayList>(); - for (World w : plugin.getServer().getWorlds()) { - RegionManager regionManager = plugin.getGlobalRegionManager().get(w); - if (regionManager == null) { - continue; + for (World w : Bukkit.getServer().getWorlds()) { + RegionManager manager = plugin.getGlobalRegionManager().get(w); + if (manager != null) { + futures.add(manager.load(true)); } - futures.add(reloadChanges(sender, regionManager, world, true)); } + ListenableFuture future = Futures.successfulAsList(futures); - Futures.addCallback(Futures.allAsList(futures), new FutureCallback() { - @Override - public void onSuccess(Object o) { - sender.sendMessage(ChatColor.YELLOW + "Successfully loaded region data for all worlds."); - } - - @Override - public void onFailure(Throwable throwable) { - sender.sendMessage(ChatColor.RED + "Failed to load region data for all worlds: " + throwable.getMessage()); - plugin.getLogger().log(Level.WARNING, "Failed to load region data", throwable); - } - }); + AsyncCommandHelper.wrap(future, plugin, sender) + .registerWithSupervisor("Loading regions for all worlds") + .sendMessageAfterDelay("(Please wait... loading region data for all worlds...)") + .thenRespondWith( + "Successfully load the region data for all worlds.", + "Failed to load regions for all worlds"); } } @@ -1187,34 +1107,33 @@ public void save(CommandContext args, final CommandSender sender) throws Command } if (world != null) { - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - if (regionManager == null) { + RegionManager manager = plugin.getGlobalRegionManager().get(world); + + if (manager == null) { throw new CommandException("No region manager exists for world '" + world.getName() + "'."); } - commitChanges(sender, regionManager, world, false); + + ListenableFuture future = manager.save(true); + + AsyncCommandHelper.wrap(future, plugin, sender) + .forRegionDataSave(world, false); } else { - sender.sendMessage(ChatColor.YELLOW + "Saving all region databases... This might take a bit."); + // Save for all worlds List> futures = new ArrayList>(); - for (World w : plugin.getServer().getWorlds()) { - RegionManager regionManager = plugin.getGlobalRegionManager().get(w); - if (regionManager == null) { - continue; + for (World w : Bukkit.getServer().getWorlds()) { + RegionManager manager = plugin.getGlobalRegionManager().get(w); + if (manager != null) { + futures.add(manager.save(true)); } - futures.add(commitChanges(sender, regionManager, world, true)); } + ListenableFuture future = Futures.successfulAsList(futures); - Futures.addCallback(Futures.allAsList(futures), new FutureCallback() { - @Override - public void onSuccess(Object o) { - sender.sendMessage(ChatColor.YELLOW + "Successfully saved region data for all worlds."); - } - - @Override - public void onFailure(Throwable throwable) { - sender.sendMessage(ChatColor.RED + "Failed to save region data for all worlds: " + throwable.getMessage()); - plugin.getLogger().log(Level.WARNING, "Failed to save region data", throwable); - } - }); + AsyncCommandHelper.wrap(future, plugin, sender) + .registerWithSupervisor("Saving regions for all worlds") + .sendMessageAfterDelay("(Please wait... saving region data for all worlds...)") + .thenRespondWith( + "Successfully saved the region data for all worlds.", + "Failed to save regions for all worlds"); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java index e981d2a8..a817a05b 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java @@ -36,9 +36,6 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import static com.google.common.util.concurrent.Futures.addCallback; -import static com.sk89q.worldguard.bukkit.commands.CommandUtils.*; - // @TODO: A lot of code duplication here! Need to fix. public class RegionMemberCommands { @@ -74,8 +71,8 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE String id = args.getString(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = mgr.getRegion(id); + RegionManager manager = plugin.getGlobalRegionManager().get(world); + ProtectedRegion region = manager.getRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -103,9 +100,12 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE plugin.getExecutorService().submit(resolver), resolver.createAddAllFunction(region.getMembers())); - progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); - addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to add members")); - addCallback(future, saveRegionsCallback(sender, mgr, world, true)); + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Adding members to the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with new members.", "Failed to add new members") + .thenSaveRegionData(manager, world); } @Command(aliases = {"addowner", "addowner", "ao"}, @@ -133,8 +133,8 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx String id = args.getString(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = mgr.getRegion(id); + RegionManager manager = plugin.getGlobalRegionManager().get(world); + ProtectedRegion region = manager.getRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -148,7 +148,7 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx if (flag != null && flag && owners != null && owners.size() == 0) { if (!plugin.hasPermission(player, "worldguard.region.unlimited")) { int maxRegionCount = plugin.getGlobalStateManager().get(world).getMaxRegionCount(player); - if (maxRegionCount >= 0 && mgr.getRegionCountOfPlayer(localPlayer) + if (maxRegionCount >= 0 && manager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { throw new CommandException("You already own the maximum allowed amount of regions."); } @@ -175,9 +175,12 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx plugin.getExecutorService().submit(resolver), resolver.createAddAllFunction(region.getOwners())); - progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); - addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to add owners")); - addCallback(future, saveRegionsCallback(sender, mgr, world, true)); + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Adding owners to the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners") + .thenSaveRegionData(manager, world); } @Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"}, @@ -205,8 +208,8 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma String id = args.getString(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = mgr.getRegion(id); + RegionManager manager = plugin.getGlobalRegionManager().get(world); + ProtectedRegion region = manager.getRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -246,9 +249,12 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma resolver.createRemoveAllFunction(region.getMembers())); } - progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); - addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to remove members")); - addCallback(future, saveRegionsCallback(sender, mgr, world, true)); + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Removing members from the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members") + .thenSaveRegionData(manager, world); } @Command(aliases = {"removeowner", "remowner", "ro"}, @@ -277,8 +283,8 @@ public void removeOwner(CommandContext args, String id = args.getString(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = mgr.getRegion(id); + RegionManager manager = plugin.getGlobalRegionManager().get(world); + ProtectedRegion region = manager.getRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -318,8 +324,11 @@ public void removeOwner(CommandContext args, resolver.createRemoveAllFunction(region.getOwners())); } - progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); - addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to remove owners")); - addCallback(future, saveRegionsCallback(sender, mgr, world, true)); + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Removing owners from the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners") + .thenSaveRegionData(manager, world); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java index 9e7fab21..41690de1 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java @@ -19,24 +19,27 @@ package com.sk89q.worldguard.bukkit.commands; -import java.io.File; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.odeum.task.Task; +import com.sk89q.odeum.task.TaskStateComparator; import com.sk89q.worldguard.bukkit.LoggerToChatHandler; import com.sk89q.worldguard.bukkit.ReportWriter; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.util.PastebinPoster; import com.sk89q.worldguard.util.PastebinPoster.PasteCallback; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; public class WorldGuardCommands { private final WorldGuardPlugin plugin; @@ -56,6 +59,11 @@ public void version(CommandContext args, CommandSender sender) throws CommandExc @Command(aliases = {"reload"}, desc = "Reload WorldGuard configuration", max = 0) @CommandPermissions({"worldguard.reload"}) public void reload(CommandContext args, CommandSender sender) throws CommandException { + // TODO: This is subject to a race condition, but at least other commands are not being processed concurrently + List> tasks = plugin.getSupervisor().getTasks(); + if (!tasks.isEmpty()) { + throw new CommandException("There are currently pending tasks. Use /wg running and /wg cancelall to monitor and cancel these tasks first."); + } LoggerToChatHandler handler = null; Logger minecraftLogger = null; @@ -135,4 +143,52 @@ public void flushStates(CommandContext args, CommandSender sender) throws Comman } } + @Command(aliases = {"cancelall"}, flags = "f", desc = "Cancel all running tasks", max = 0) + @CommandPermissions("worldguard.cancel.all") + public void cancelAllTasks(CommandContext args, CommandSender sender) throws CommandException { + List> tasks = plugin.getSupervisor().getTasks(); + + boolean force = args.hasFlag('f'); + if (force && !plugin.hasPermission(sender, "worldguard.cancel.force")) { + throw new CommandException("You are not permitted to force cancel."); + } + + if (!tasks.isEmpty()) { + for (Task task : tasks) { + task.cancel(force); + sender.sendMessage(ChatColor.YELLOW + "Cancelled: " + ChatColor.WHITE + task.getName()); + } + } else { + sender.sendMessage(ChatColor.YELLOW + "There are currently no running tasks."); + } + } + + @Command(aliases = {"running", "queue"}, desc = "List running tasks", max = 0) + @CommandPermissions("worldguard.running") + public void listRunningTasks(CommandContext args, CommandSender sender) throws CommandException { + List> tasks = plugin.getSupervisor().getTasks(); + + if (!tasks.isEmpty()) { + Collections.sort(tasks, new TaskStateComparator()); + StringBuilder builder = new StringBuilder(); + builder.append(ChatColor.GRAY); + builder.append("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"); + builder.append(" Running tasks "); + builder.append("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"); + builder.append("\n").append(ChatColor.GRAY).append("Note: Some 'running' tasks may be waiting to be start."); + for (Task task : tasks) { + builder.append("\n"); + builder.append(ChatColor.BLUE).append("(").append(task.getState().name()).append(") "); + builder.append(ChatColor.YELLOW); + builder.append(CommandUtils.getOwnerName(task.getOwner())); + builder.append(": "); + builder.append(ChatColor.WHITE); + builder.append(task.getName()); + } + sender.sendMessage(builder.toString()); + } else { + sender.sendMessage(ChatColor.YELLOW + "There are currently no running tasks."); + } + } + } diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java deleted file mode 100644 index cf0d95a5..00000000 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.internal.protection.database.mysql; - -import com.google.common.collect.Lists; -import com.sk89q.worldguard.protection.databases.RegionDBUtil; -import com.sk89q.worldguard.util.io.Closer; - -import javax.annotation.Nullable; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static com.google.common.base.Preconditions.checkNotNull; - -abstract class UserToIdCache extends AbstractJob { - - private static final int MAX_NUMBER_PER_QUERY = 100; - - private final Map cache = new HashMap(); - private final String fieldName; - - protected UserToIdCache(MySQLDatabaseImpl database, Connection conn, String fieldName) { - super(database, conn); - this.fieldName = fieldName; - } - - protected abstract String fromType(V o); - - protected abstract V toType(String o); - - protected abstract V toKey(V object); - - @Nullable - public Integer find(V object) { - return cache.get(object); - } - - public void fetch(Collection entries) throws SQLException { - checkNotNull(entries); - - // Get a list of missing entries - List fetchList = new ArrayList(); - for (V entry : entries) { - if (!cache.containsKey(toKey(entry))) { - fetchList.add(entry); - } - } - - if (fetchList.isEmpty()) { - return; // Nothing to do - } - - // Search for entries - for (List partition : Lists.partition(fetchList, MAX_NUMBER_PER_QUERY)) { - Closer closer = Closer.create(); - try { - PreparedStatement statement = closer.register(conn.prepareStatement( - String.format( - "SELECT `user`.`id`, `user`.`" + fieldName + "` " + - "FROM `" + config.sqlTablePrefix + "user` AS `user` " + - "WHERE `" + fieldName + "` IN (%s)", - RegionDBUtil.preparePlaceHolders(partition.size())))); - - String[] values = new String[partition.size()]; - int i = 0; - for (V entry : partition) { - values[i] = fromType(entry); - i++; - } - - RegionDBUtil.setValues(statement, values); - ResultSet results = closer.register(statement.executeQuery()); - while (results.next()) { - cache.put(toKey(toType(results.getString(fieldName))), results.getInt("id")); - } - } finally { - closer.closeQuietly(); - } - } - - List missing = new ArrayList(); - for (V entry : fetchList) { - if (!cache.containsKey(toKey(entry))) { - missing.add(entry); - } - } - - // Insert entries that are missing - if (!missing.isEmpty()) { - Closer closer = Closer.create(); - try { - PreparedStatement statement = closer.register(conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)", - Statement.RETURN_GENERATED_KEYS)); - - for (V entry : missing) { - statement.setString(1, fromType(entry)); - statement.execute(); - - ResultSet generatedKeys = statement.getGeneratedKeys(); - if (generatedKeys.first()) { - cache.put(toKey(entry), generatedKeys.getInt(1)); - } else { - logger.warning("Could not get the database ID for user " + entry); - } - } - } finally { - closer.closeQuietly(); - } - } - } - - static class NameToIdCache extends UserToIdCache { - protected NameToIdCache(MySQLDatabaseImpl database, Connection conn) { - super(database, conn, "name"); - } - - @Override - protected String fromType(String o) { - return o; - } - - @Override - protected String toType(String o) { - return o; - } - - @Override - protected String toKey(String object) { - return object.toLowerCase(); - } - } - - static class UUIDToIdCache extends UserToIdCache { - protected UUIDToIdCache(MySQLDatabaseImpl database, Connection conn) { - super(database, conn, "uuid"); - } - - @Override - protected String fromType(UUID o) { - return o.toString(); - } - - @Override - protected UUID toType(String o) { - return UUID.fromString(o); - } - - @Override - protected UUID toKey(UUID object) { - return object; - } - } - -} diff --git a/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java b/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java new file mode 100644 index 00000000..6f3eea39 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java @@ -0,0 +1,55 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.util.sql; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class StatementUtils { + /** + * Creates a comma separated list of PreparedStatement place holders + * + * @param length The number of wildcards to create + * @return A string with {@code length} wildcards for usage in a PreparedStatement + */ + public static String preparePlaceHolders(int length) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length;) { + builder.append("?"); + if (++i < length) { + builder.append(","); + } + } + return builder.toString(); + } + + /** + * Adds all of the parsed values to the PreparedStatement + * + * @param preparedStatement The preparedStanement to add to + * @param values The values to set + * @throws SQLException see {@link PreparedStatement#setString(int, String)} + */ + public static void setValues(PreparedStatement preparedStatement, String... values) throws SQLException { + for (int i = 0; i < values.length; i++) { + preparedStatement.setString(i + 1, values[i]); + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index dd23de6a..cf3c4859 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -25,9 +25,9 @@ import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.databases.MySQLDatabase; +import com.sk89q.worldguard.protection.databases.YAMLDatabase; import com.sk89q.worldguard.protection.databases.ProtectionDatabase; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java index 9e9fd196..75a72be7 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java @@ -19,18 +19,7 @@ package com.sk89q.worldguard.protection.databases; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import au.com.bytecode.opencsv.CSVReader; - import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.Vector; import com.sk89q.worldguard.domains.DefaultDomain; @@ -42,11 +31,22 @@ import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; import com.sk89q.worldguard.util.ArrayReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Represents a protected area database that uses CSV files. * - * @author sk89q + * @deprecated no longer maintained - use {@link YAMLDatabase} */ +@Deprecated public class CSVDatabase extends AbstractProtectionDatabase { private static final Map legacyFlagCodes = new HashMap(); diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/InvalidTableFormatException.java b/src/main/java/com/sk89q/worldguard/protection/databases/InvalidTableFormatException.java deleted file mode 100644 index 7b6e53d2..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/InvalidTableFormatException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases; - -import com.sk89q.worldguard.util.FatalConfigurationLoadingException; - -public class InvalidTableFormatException extends FatalConfigurationLoadingException { - private static final long serialVersionUID = 1L; - - protected String updateFile; - - public InvalidTableFormatException(String updateFile) { - super(); - - this.updateFile = updateFile; - } - - public String toString() { - return "You need to update your database to the latest version.\n" + - "\t\tPlease see " + this.updateFile; - } -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java index e237a22e..aff2c284 100755 --- a/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java @@ -19,14 +19,26 @@ package com.sk89q.worldguard.protection.databases; - +import com.google.common.util.concurrent.ListeningExecutorService; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.internal.protection.database.mysql.MySQLDatabaseImpl; +import com.sk89q.worldguard.protection.databases.mysql.MySQLDatabaseImpl; import java.util.logging.Logger; +/** + * A store that persists regions in a MySQL database. + */ public class MySQLDatabase extends MySQLDatabaseImpl { + /** + * Create a new instance. + * + * @param executor the executor to perform loads and saves in + * @param config the configuration + * @param worldName the world name + * @param logger a logger + * @throws ProtectionDatabaseException thrown on error + */ public MySQLDatabase(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException { super(config, worldName, logger); } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java index 5339d805..88ade282 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java @@ -42,7 +42,7 @@ public interface ProtectionDatabase { /** * Load the list of regions into a region manager. * - *

This call will block and it must be run from the main server thread.

+ *

This call will block.

* * @param manager The manager to load regions into * @throws ProtectionDatabaseException when an error occurs @@ -57,8 +57,6 @@ public interface ProtectionDatabase { *

{@code async} is merely a suggestion and it may be ignored by * implementations if it is not supported.

* - *

This method must be run from the main server thread.

- * * @param manager The manager to load regions into * @param async true to attempt to save the data asynchronously if it is supported */ @@ -74,7 +72,7 @@ public interface ProtectionDatabase { /** * Save the list of regions from a region manager. * - *

This call will block and it must be run from the main server thread.

+ *

This call will block.

* * @param manager The manager to load regions into * @throws ProtectionDatabaseException when an error occurs @@ -89,11 +87,9 @@ public interface ProtectionDatabase { *

{@code async} is merely a suggestion and it may be ignored by * implementations if it is not supported.

* - *

This method must be run from the main server thread.

- * * @param manager The manager to load regions into * @param async true to attempt to save the data asynchronously if it is supported - * @throws ProtectionDatabaseException when an error occurs + * @throws RejectedExecutionException on rejection */ public ListenableFuture save(RegionManager manager, boolean async) throws RejectedExecutionException; diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java index 9d587e9b..158d6567 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java @@ -20,18 +20,18 @@ package com.sk89q.worldguard.protection.databases; import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.databases.util.DomainInputResolver; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Various utility functions for parsing region databases. - * - * @author sk89q + * + * @deprecated use {@link DomainInputResolver} */ -public class RegionDBUtil { +@Deprecated +public final class RegionDBUtil { private static Pattern groupPattern = Pattern.compile("(?i)^[G]:(.+)$"); @@ -45,9 +45,10 @@ private RegionDBUtil() { * @param domain The domain to add to * @param split The {@link String[]} containing names to add to {@code domain} * @param startIndex The beginning index in the array + * @deprecated use {@link DomainInputResolver} */ - public static void addToDomain(DefaultDomain domain, String[] split, - int startIndex) { + @Deprecated + public static void addToDomain(DefaultDomain domain, String[] split, int startIndex) { for (int i = startIndex; i < split.length; i++) { String s = split[i]; Matcher m = groupPattern.matcher(s); @@ -65,9 +66,10 @@ public static void addToDomain(DefaultDomain domain, String[] split, * @param domain The domain to remove from * @param split The {@link String[]} containing names to remove from {@code domain} * @param startIndex The beginning index in the array + * @deprecated use {@link DomainInputResolver} */ - public static void removeFromDomain(DefaultDomain domain, String[] split, - int startIndex) { + @Deprecated + public static void removeFromDomain(DefaultDomain domain, String[] split, int startIndex) { for (int i = startIndex; i < split.length; i++) { String s = split[i]; Matcher m = groupPattern.matcher(s); @@ -85,7 +87,9 @@ public static void removeFromDomain(DefaultDomain domain, String[] split, * @param split The array of names to add * @param startIndex The beginning index in the array * @return The resulting DefaultDomain + * @deprecated use {@link DomainInputResolver} */ + @Deprecated public static DefaultDomain parseDomainString(String[] split, int startIndex) { DefaultDomain domain = new DefaultDomain(); @@ -101,34 +105,5 @@ public static DefaultDomain parseDomainString(String[] split, int startIndex) { return domain; } - - /** - * Creates a comma separated list of PreparedStatement place holders - * - * @param length The number of wildcards to create - * @return A string with {@code length} wildcards for usage in a PreparedStatement - */ - public static String preparePlaceHolders(int length) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < length;) { - builder.append("?"); - if (++i < length) { - builder.append(","); - } - } - return builder.toString(); - } - /** - * Adds all of the parsed values to the PreparedStatement - * - * @param preparedStatement The preparedStanement to add to - * @param values The values to set - * @throws SQLException see {@link PreparedStatement#setString(int, String)} - */ - public static void setValues(PreparedStatement preparedStatement, String... values) throws SQLException { - for (int i = 0; i < values.length; i++) { - preparedStatement.setString(i + 1, values[i]); - } - } } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java index db944ffd..52bc7ffb 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java @@ -52,6 +52,9 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * A store that persists regions in a YAML-encoded file. + */ public class YAMLDatabase extends AbstractAsynchronousDatabase { /** @@ -62,7 +65,15 @@ public class YAMLDatabase extends AbstractAsynchronousDatabase { private Map regions; private final File file; private final Logger logger; - + + /** + * Create a new instance. + * + * @param file the file + * @param logger a logger + * @throws ProtectionDatabaseException + * @throws FileNotFoundException + */ public YAMLDatabase(File file, Logger logger) throws ProtectionDatabaseException, FileNotFoundException { this.logger = logger; this.file = file; @@ -89,9 +100,9 @@ public void performLoad() throws ProtectionDatabaseException { } catch (IOException e) { throw new ProtectionDatabaseException(e); } - + Map regionData = config.getNodes("regions"); - + // No regions are even configured if (regionData == null) { this.regions = new HashMap(); @@ -108,14 +119,14 @@ public void performLoad() throws ProtectionDatabaseException { Map regions = new HashMap(); Map parentSets = new LinkedHashMap(); - + for (Map.Entry entry : regionData.entrySet()) { String id = entry.getKey().toLowerCase().replace(".", ""); YAMLNode node = entry.getValue(); String type = node.getString("type"); ProtectedRegion region; - + try { if (type == null) { logger.warning("Undefined region type for region '" + id + "'!\n" + @@ -139,14 +150,14 @@ public void performLoad() throws ProtectionDatabaseException { "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + "\n"); continue; } - + Integer priority = checkNonNull(node.getInt("priority")); region.setPriority(priority); setFlags(region, node.getNode("flags")); region.setOwners(parseDomain(node.getNode("owners"))); region.setMembers(parseDomain(node.getNode("members"))); regions.put(id, region); - + String parentId = node.getString("parent"); if (parentId != null) { parentSets.put(region, parentId); @@ -154,11 +165,11 @@ public void performLoad() throws ProtectionDatabaseException { } catch (NullPointerException e) { logger.log(Level.WARNING, "Unexpected NullPointerException encountered during parsing for the region '" + id + "'!\n" + - "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + - "\n\nNote: This region will disappear as a result!", e); + "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + + "\n\nNote: This region will disappear as a result!", e); } } - + // Relink parents for (Map.Entry entry : parentSets.entrySet()) { ProtectedRegion parent = regions.get(entry.getValue()); @@ -172,23 +183,23 @@ public void performLoad() throws ProtectionDatabaseException { logger.warning("Unknown region parent: " + entry.getValue()); } } - + this.regions = regions; } - + private V checkNonNull(V val) throws NullPointerException { if (val == null) { throw new NullPointerException(); } - + return val; } - + private void setFlags(ProtectedRegion region, YAMLNode flagsData) { if (flagsData == null) { return; } - + // @TODO: Make this better for (Flag flag : DefaultFlag.getFlags()) { if (flag == null) { @@ -201,16 +212,16 @@ private void setFlags(ProtectedRegion region, YAMLNode flagsData) { if (o != null) { setFlag(region, flag, o); } - + if (flag.getRegionGroupFlag() != null) { - Object o2 = flagsData.getProperty(flag.getRegionGroupFlag().getName()); + Object o2 = flagsData.getProperty(flag.getRegionGroupFlag().getName()); if (o2 != null) { setFlag(region, flag.getRegionGroupFlag(), o2); } } } } - + private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) { T val = flag.unmarshal(rawValue); if (val == null) { @@ -220,13 +231,13 @@ private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) } region.setFlag(flag, val); } - + @SuppressWarnings("deprecation") private DefaultDomain parseDomain(YAMLNode node) { if (node == null) { return new DefaultDomain(); } - + DefaultDomain domain = new DefaultDomain(); for (String name : node.getStringList("players", null)) { @@ -240,11 +251,11 @@ private DefaultDomain parseDomain(YAMLNode node) { logger.log(Level.WARNING, "Failed to parse UUID '" + stringId +"'", e); } } - + for (String name : node.getStringList("groups", null)) { domain.addGroup(name); } - + return domain; } @@ -307,18 +318,18 @@ protected void performSave() { "#"); config.save(); } - + private Map getFlagData(ProtectedRegion region) { Map flagData = new HashMap(); - + for (Map.Entry, Object> entry : region.getFlags().entrySet()) { Flag flag = entry.getKey(); addMarshalledFlag(flagData, flag, entry.getValue()); } - + return flagData; } - + @SuppressWarnings("unchecked") private void addMarshalledFlag(Map flagData, Flag flag, Object val) { if (val == null) { @@ -327,7 +338,7 @@ private void addMarshalledFlag(Map flagData, Flag flag, O flagData.put(flag.getName(), flag.marshal((V) val)); } - + @SuppressWarnings("deprecation") private Map getDomainData(DefaultDomain domain) { Map domainData = new HashMap(); @@ -335,21 +346,21 @@ private Map getDomainData(DefaultDomain domain) { setDomainData(domainData, "players", domain.getPlayers()); setDomainData(domainData, "unique-ids", domain.getUniqueIds()); setDomainData(domainData, "groups", domain.getGroups()); - + return domainData; } - + private void setDomainData(Map domainData, String key, Set domain) { if (domain.isEmpty()) { return; } - + List list = new ArrayList(); - + for (Object str : domain) { list.add(String.valueOf(str)); } - + domainData.put(key, list); } @@ -384,5 +395,5 @@ private static String dumpAsYaml(Object object) { return ""; } } - + } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/AbstractDatabaseMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/AbstractDatabaseMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java index 549fdaea..abfd57a6 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/AbstractDatabaseMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java @@ -1,62 +1,62 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -public abstract class AbstractDatabaseMigrator implements DatabaseMigrator { - - private static HashMap> migrators = - new HashMap>(); - - public static Map> getMigrators() { - if (!migrators.isEmpty()) return migrators; - - AbstractDatabaseMigrator.migrators.put(new MigratorKey("mysql", "yaml"), MySQLToYAMLMigrator.class); - AbstractDatabaseMigrator.migrators.put(new MigratorKey("yaml", "mysql"), YAMLToMySQLMigrator.class); - - return migrators; - } - - protected abstract Set getWorldsFromOld() throws MigrationException; - - protected abstract Map getRegionsForWorldFromOld(String world) throws MigrationException; - - protected abstract ProtectionDatabase getNewWorldStorage(String world) throws MigrationException; - - public void migrate() throws MigrationException { - for (String world : this.getWorldsFromOld()) { - ProtectionDatabase database = this.getNewWorldStorage(world); - database.setRegions(this.getRegionsForWorldFromOld(world)); - - try { - database.save(); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.sk89q.worldguard.protection.databases.ProtectionDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +public abstract class AbstractDatabaseMigrator implements DatabaseMigrator { + + private static HashMap> migrators = + new HashMap>(); + + public static Map> getMigrators() { + if (!migrators.isEmpty()) return migrators; + + AbstractDatabaseMigrator.migrators.put(new MigratorKey("mysql", "yaml"), MySQLToYAMLMigrator.class); + AbstractDatabaseMigrator.migrators.put(new MigratorKey("yaml", "mysql"), YAMLToMySQLMigrator.class); + + return migrators; + } + + protected abstract Set getWorldsFromOld() throws MigrationException; + + protected abstract Map getRegionsForWorldFromOld(String world) throws MigrationException; + + protected abstract ProtectionDatabase getNewWorldStorage(String world) throws MigrationException; + + public void migrate() throws MigrationException { + for (String world : this.getWorldsFromOld()) { + ProtectionDatabase database = this.getNewWorldStorage(world); + database.setRegions(this.getRegionsForWorldFromOld(world)); + + try { + database.save(); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/DatabaseMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java similarity index 91% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/DatabaseMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java index 838170d0..56623766 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/DatabaseMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java @@ -1,31 +1,31 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -/** - * Represents a method of converting from one database to another. - * - * @author Nicholas Steicke - */ -public interface DatabaseMigrator { - - public void migrate() throws MigrationException; -} - +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +/** + * Represents a method of converting from one database to another. + * + * @author Nicholas Steicke + */ +public interface DatabaseMigrator { + + public void migrate() throws MigrationException; +} + diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigrationException.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java similarity index 92% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigrationException.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java index 9ce01b43..84aef164 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigrationException.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java @@ -1,40 +1,40 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -public class MigrationException extends Exception { - private static final long serialVersionUID = 1L; - - public MigrationException() { - super(); - } - - public MigrationException(String message) { - super(message); - } - - public MigrationException(String message, Throwable cause) { - super(message, cause); - } - - public MigrationException(Throwable cause) { - super(cause); - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +public class MigrationException extends Exception { + private static final long serialVersionUID = 1L; + + public MigrationException() { + super(); + } + + public MigrationException(String message) { + super(message); + } + + public MigrationException(String message, Throwable cause) { + super(message, cause); + } + + public MigrationException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigratorKey.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java similarity index 92% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigratorKey.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java index 5fe0e8bf..da85cdc4 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigratorKey.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java @@ -1,43 +1,43 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -public class MigratorKey { - public final String from; - public final String to; - - public MigratorKey(String from, String to) { - this.from = from; - this.to = to; - } - - public boolean equals(Object o) { - MigratorKey other = (MigratorKey) o; - - return other.from.equals(this.from) && other.to.equals(this.to); - } - - public int hashCode() { - int hash = 17; - hash = hash * 31 + this.from.hashCode(); - hash = hash * 31 + this.to.hashCode(); - return hash; - } +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +public class MigratorKey { + public final String from; + public final String to; + + public MigratorKey(String from, String to) { + this.from = from; + this.to = to; + } + + public boolean equals(Object o) { + MigratorKey other = (MigratorKey) o; + + return other.from.equals(this.from) && other.to.equals(this.to); + } + + public int hashCode() { + int hash = 17; + hash = hash * 31 + this.from.hashCode(); + hash = hash * 31 + this.to.hashCode(); + return hash; + } } \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MySQLToYAMLMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/MySQLToYAMLMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java index 370e9e32..bdfafa5a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MySQLToYAMLMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java @@ -1,97 +1,97 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.databases.MySQLDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.io.File; -import java.io.FileNotFoundException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class MySQLToYAMLMigrator extends AbstractDatabaseMigrator { - - private WorldGuardPlugin plugin; - private Set worlds; - - public MySQLToYAMLMigrator(WorldGuardPlugin plugin) throws MigrationException { - this.plugin = plugin; - this.worlds = new HashSet(); - - ConfigurationManager config = plugin.getGlobalStateManager(); - - try { - Connection conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword); - - ResultSet worlds = conn.prepareStatement("SELECT `name` FROM `world`;").executeQuery(); - - while(worlds.next()) { - this.worlds.add(worlds.getString(1)); - } - - conn.close(); - } catch (SQLException e) { - throw new MigrationException(e); - } - } - - @Override - protected Set getWorldsFromOld() { - return this.worlds; - } - - @Override - protected Map getRegionsForWorldFromOld(String world) throws MigrationException { - ProtectionDatabase oldDatabase; - try { - oldDatabase = new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); - oldDatabase.load(); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - - return oldDatabase.getRegions(); - } - - @Override - protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { - try { - File file = new File(plugin.getDataFolder(), - "worlds" + File.separator + world + File.separator + "regions.yml"); - - return new YAMLDatabase(file, plugin.getLogger()); - } catch (FileNotFoundException e) { - throw new MigrationException(e); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.databases.MySQLDatabase; +import com.sk89q.worldguard.protection.databases.YAMLDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.File; +import java.io.FileNotFoundException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class MySQLToYAMLMigrator extends AbstractDatabaseMigrator { + + private WorldGuardPlugin plugin; + private Set worlds; + + public MySQLToYAMLMigrator(WorldGuardPlugin plugin) throws MigrationException { + this.plugin = plugin; + this.worlds = new HashSet(); + + ConfigurationManager config = plugin.getGlobalStateManager(); + + try { + Connection conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword); + + ResultSet worlds = conn.prepareStatement("SELECT `name` FROM `world`;").executeQuery(); + + while(worlds.next()) { + this.worlds.add(worlds.getString(1)); + } + + conn.close(); + } catch (SQLException e) { + throw new MigrationException(e); + } + } + + @Override + protected Set getWorldsFromOld() { + return this.worlds; + } + + @Override + protected Map getRegionsForWorldFromOld(String world) throws MigrationException { + ProtectionDatabase oldDatabase; + try { + oldDatabase = new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); + oldDatabase.load(); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + + return oldDatabase.getRegions(); + } + + @Override + protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { + try { + File file = new File(plugin.getDataFolder(), + "worlds" + File.separator + world + File.separator + "regions.yml"); + + return new YAMLDatabase(file, plugin.getLogger()); + } catch (FileNotFoundException e) { + throw new MigrationException(e); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/YAMLToMySQLMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/YAMLToMySQLMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java index 8b6dc950..2a1d879f 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/YAMLToMySQLMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java @@ -1,86 +1,86 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.databases.MySQLDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class YAMLToMySQLMigrator extends AbstractDatabaseMigrator { - - private WorldGuardPlugin plugin; - private HashMap regionYamlFiles; - - public YAMLToMySQLMigrator(WorldGuardPlugin plugin) { - this.plugin = plugin; - - this.regionYamlFiles = new HashMap(); - - File files[] = new File(plugin.getDataFolder(), "worlds" + File.separator).listFiles(); - for (File item : files) { - if (item.isDirectory()) { - for (File subItem : item.listFiles()) { - if (subItem.getName().equals("regions.yml")) { - this.regionYamlFiles.put(item.getName(), subItem); - } - } - } - } - } - - @Override - protected Set getWorldsFromOld() { - return this.regionYamlFiles.keySet(); - } - - @Override - protected Map getRegionsForWorldFromOld(String world) throws MigrationException { - ProtectionDatabase oldDatabase; - try { - oldDatabase = new YAMLDatabase(this.regionYamlFiles.get(world), plugin.getLogger()); - oldDatabase.load(); - } catch (FileNotFoundException e) { - throw new MigrationException(e); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - - return oldDatabase.getRegions(); - } - - @Override - protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { - try { - return new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } - -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.databases.MySQLDatabase; +import com.sk89q.worldguard.protection.databases.YAMLDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class YAMLToMySQLMigrator extends AbstractDatabaseMigrator { + + private WorldGuardPlugin plugin; + private HashMap regionYamlFiles; + + public YAMLToMySQLMigrator(WorldGuardPlugin plugin) { + this.plugin = plugin; + + this.regionYamlFiles = new HashMap(); + + File files[] = new File(plugin.getDataFolder(), "worlds" + File.separator).listFiles(); + for (File item : files) { + if (item.isDirectory()) { + for (File subItem : item.listFiles()) { + if (subItem.getName().equals("regions.yml")) { + this.regionYamlFiles.put(item.getName(), subItem); + } + } + } + } + } + + @Override + protected Set getWorldsFromOld() { + return this.regionYamlFiles.keySet(); + } + + @Override + protected Map getRegionsForWorldFromOld(String world) throws MigrationException { + ProtectionDatabase oldDatabase; + try { + oldDatabase = new YAMLDatabase(this.regionYamlFiles.get(world), plugin.getLogger()); + oldDatabase.load(); + } catch (FileNotFoundException e) { + throw new MigrationException(e); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + + return oldDatabase.getRegions(); + } + + @Override + protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { + try { + return new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java rename to src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java index 44e6e3ec..944a3b7b 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.protection.database.mysql; +package com.sk89q.worldguard.protection.databases.mysql; import com.sk89q.worldguard.bukkit.ConfigurationManager; import org.yaml.snakeyaml.error.YAMLException; diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java rename to src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java index a095db61..7c592a17 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java @@ -17,8 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.protection.database.mysql; +package com.sk89q.worldguard.protection.databases.mysql; +import com.google.common.util.concurrent.ListeningExecutorService; import com.jolbox.bonecp.BoneCP; import com.jolbox.bonecp.BoneCPConfig; import com.sk89q.worldguard.bukkit.ConfigurationManager; @@ -270,7 +271,7 @@ protected void performLoad() throws ProtectionDatabaseException { Connection connection = null; try { connection = getConnection(); - setRegions(new RegionLoader(this, connection).call()); + setRegions(new RegionLoader(this, connection).load()); } catch (SQLException e) { throw new ProtectionDatabaseException("Failed to load regions database", e); } finally { @@ -288,7 +289,7 @@ protected void performSave() throws ProtectionDatabaseException { Connection connection = null; try { connection = getConnection(); - new RegionCommitter(this, connection, getRegions()).call(); + new RegionWriter(this, connection, getRegions()).save(); } catch (SQLException e) { throw new ProtectionDatabaseException("Failed to save regions database", e); } finally { diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java similarity index 98% rename from src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java rename to src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java index 648290e5..e4793837 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.protection.database.mysql; +package com.sk89q.worldguard.protection.databases.mysql; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector2D; @@ -40,9 +40,8 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.concurrent.Callable; -class RegionLoader extends AbstractJob implements Callable> { +class RegionLoader extends AbstractJob { /* ========= Everything below is a nightmare. ========= @@ -60,8 +59,7 @@ class RegionLoader extends AbstractJob implements Callable call() { + public Map load() { parentSets = new HashMap(); // We load the cuboid regions first, as this is likely to be the diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java rename to src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java index ff3bbbd5..c3294638 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java @@ -17,15 +17,13 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.protection.database.mysql; +package com.sk89q.worldguard.protection.databases.mysql; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector2D; import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.internal.protection.database.mysql.UserToIdCache.NameToIdCache; -import com.sk89q.worldguard.internal.protection.database.mysql.UserToIdCache.UUIDToIdCache; +import com.sk89q.worldguard.internal.util.sql.StatementUtils; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.RegionDBUtil; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; @@ -43,33 +41,33 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.concurrent.Callable; import java.util.logging.Level; import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldguard.protection.databases.mysql.UserRowCache.NameRowCache; +import static com.sk89q.worldguard.protection.databases.mysql.UserRowCache.UUIDRowCache; -class RegionCommitter extends AbstractJob implements Callable { +class RegionWriter extends AbstractJob { /* ========= Everything below is a nightmare. ========= */ private final Map regions; - private final NameToIdCache usernameCache; - private final UUIDToIdCache uuidCache; + private final NameRowCache usernameCache; + private final UUIDRowCache uuidCache; private final int worldId; - RegionCommitter(MySQLDatabaseImpl database, Connection conn, Map regions) { + RegionWriter(MySQLDatabaseImpl database, Connection conn, Map regions) { super(database, conn); checkNotNull(regions); this.regions = regions; this.worldId = database.getWorldId(); - usernameCache = new NameToIdCache(database, conn); - uuidCache = new UUIDToIdCache(database, conn); + usernameCache = new NameRowCache(database, conn); + uuidCache = new UUIDRowCache(database, conn); } - @Override - public Object call() throws SQLException, ProtectionDatabaseException { + public void save() throws SQLException, ProtectionDatabaseException { /* * As we don't get notified on the creation/removal of regions: @@ -183,8 +181,6 @@ public Object call() throws SQLException, ProtectionDatabaseException { closeQuietly(removeRegion); } } - - return null; } /* @@ -207,11 +203,11 @@ private Map getGroupIds(String... groupnames) { "`group`.`name` " + "FROM `" + config.sqlTablePrefix + "group` AS `group` " + "WHERE `name` IN (%s)", - RegionDBUtil.preparePlaceHolders(groupnames.length) + StatementUtils.preparePlaceHolders(groupnames.length) ) ); - RegionDBUtil.setValues(findGroupsStatement, groupnames); + StatementUtils.setValues(findGroupsStatement, groupnames); findGroupsResults = findGroupsStatement.executeQuery(); diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java new file mode 100644 index 00000000..0ae2ebd7 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java @@ -0,0 +1,184 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.mysql; + +import com.google.common.collect.Lists; +import com.sk89q.worldguard.internal.util.sql.StatementUtils; +import com.sk89q.worldguard.util.io.Closer; + +import javax.annotation.Nullable; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.google.common.base.Preconditions.checkNotNull; + +abstract class UserRowCache extends AbstractJob { + + private static final int MAX_NUMBER_PER_QUERY = 100; + private static final Object LOCK = new Object(); + + private final Map cache = new HashMap(); + private final String fieldName; + + protected UserRowCache(MySQLDatabaseImpl database, Connection conn, String fieldName) { + super(database, conn); + this.fieldName = fieldName; + } + + protected abstract String fromType(V o); + + protected abstract V toType(String o); + + protected abstract V toKey(V object); + + @Nullable + public Integer find(V object) { + return cache.get(object); + } + + public void fetch(Collection entries) throws SQLException { + synchronized (LOCK) { // Lock across all cache instances + checkNotNull(entries); + + // Get a list of missing entries + List fetchList = new ArrayList(); + for (V entry : entries) { + if (!cache.containsKey(toKey(entry))) { + fetchList.add(entry); + } + } + + if (fetchList.isEmpty()) { + return; // Nothing to do + } + + // Search for entries + for (List partition : Lists.partition(fetchList, MAX_NUMBER_PER_QUERY)) { + Closer closer = Closer.create(); + try { + PreparedStatement statement = closer.register(conn.prepareStatement( + String.format( + "SELECT `user`.`id`, `user`.`" + fieldName + "` " + + "FROM `" + config.sqlTablePrefix + "user` AS `user` " + + "WHERE `" + fieldName + "` IN (%s)", + StatementUtils.preparePlaceHolders(partition.size())))); + + String[] values = new String[partition.size()]; + int i = 0; + for (V entry : partition) { + values[i] = fromType(entry); + i++; + } + + StatementUtils.setValues(statement, values); + ResultSet results = closer.register(statement.executeQuery()); + while (results.next()) { + cache.put(toKey(toType(results.getString(fieldName))), results.getInt("id")); + } + } finally { + closer.closeQuietly(); + } + } + + List missing = new ArrayList(); + for (V entry : fetchList) { + if (!cache.containsKey(toKey(entry))) { + missing.add(entry); + } + } + + // Insert entries that are missing + if (!missing.isEmpty()) { + Closer closer = Closer.create(); + try { + PreparedStatement statement = closer.register(conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)", + Statement.RETURN_GENERATED_KEYS)); + + for (V entry : missing) { + statement.setString(1, fromType(entry)); + statement.execute(); + + ResultSet generatedKeys = statement.getGeneratedKeys(); + if (generatedKeys.first()) { + cache.put(toKey(entry), generatedKeys.getInt(1)); + } else { + logger.warning("Could not get the database ID for user " + entry); + } + } + } finally { + closer.closeQuietly(); + } + } + } + } + + static class NameRowCache extends UserRowCache { + protected NameRowCache(MySQLDatabaseImpl database, Connection conn) { + super(database, conn, "name"); + } + + @Override + protected String fromType(String o) { + return o; + } + + @Override + protected String toType(String o) { + return o; + } + + @Override + protected String toKey(String object) { + return object.toLowerCase(); + } + } + + static class UUIDRowCache extends UserRowCache { + protected UUIDRowCache(MySQLDatabaseImpl database, Connection conn) { + super(database, conn, "uuid"); + } + + @Override + protected String fromType(UUID o) { + return o.toString(); + } + + @Override + protected UUID toType(String o) { + return UUID.fromString(o); + } + + @Override + protected UUID toKey(UUID object) { + return object; + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java index 7cdc8926..aa929763 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java @@ -26,21 +26,24 @@ import com.sk89q.worldguard.protection.databases.ProtectionDatabase; import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * A very simple implementation of the region manager that uses a flat list * and iterates through the list to identify applicable regions. This method * is not very efficient. - * - * @author sk89q */ public class FlatRegionManager extends RegionManager { /** * List of protected regions. */ - private Map regions; + private ConcurrentMap regions = new ConcurrentHashMap(); /** * Construct the manager. @@ -49,7 +52,6 @@ public class FlatRegionManager extends RegionManager { */ public FlatRegionManager(ProtectionDatabase regionLoader) { super(regionLoader); - regions = new TreeMap(); } @Override @@ -59,7 +61,7 @@ public Map getRegions() { @Override public void setRegions(Map regions) { - this.regions = new TreeMap(regions); + this.regions = new ConcurrentHashMap(regions); } @Override @@ -185,7 +187,7 @@ public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer pl intersectRegions = new ArrayList(); } - return intersectRegions.size() > 0; + return !intersectRegions.isEmpty(); } @Override diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java index a9bc74ab..487e5e29 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java @@ -30,7 +30,12 @@ import org.khelekore.prtree.PRTree; import org.khelekore.prtree.SimpleMBR; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; public class PRTreeRegionManager extends RegionManager { @@ -55,7 +60,7 @@ public Map getRegions() { @Override public void setRegions(Map regions) { - Map newRegions = new TreeMap(regions); + ConcurrentMap newRegions = new ConcurrentHashMap(regions); PRTree tree = new PRTree(converter, BRANCH_FACTOR); tree.load(newRegions.values()); this.data = new RegionsContainer(newRegions, tree); @@ -65,8 +70,9 @@ public void setRegions(Map regions) { public void addRegion(ProtectedRegion region) { RegionsContainer data = this.data; data.regions.put(region.getId().toLowerCase(), region); - data.tree = new PRTree(converter, BRANCH_FACTOR); - data.tree.load(data.regions.values()); + PRTree tree = new PRTree(converter, BRANCH_FACTOR); + tree.load(data.regions.values()); + this.data = new RegionsContainer(data.regions, tree); } @Override @@ -95,8 +101,9 @@ public void removeRegion(String id) { } } - data.tree = new PRTree(converter, BRANCH_FACTOR); - data.tree.load(data.regions.values()); + PRTree tree = new PRTree(converter, BRANCH_FACTOR); + tree.load(data.regions.values()); + this.data = new RegionsContainer(data.regions, tree); } @Override @@ -197,7 +204,7 @@ public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer pl intersectRegions = new ArrayList(); } - return intersectRegions.size() > 0; + return !intersectRegions.isEmpty(); } @Override @@ -219,15 +226,15 @@ public int getRegionCountOfPlayer(LocalPlayer player) { } private class RegionsContainer { - private final Map regions; - private PRTree tree; + private final ConcurrentMap regions; + private final PRTree tree; private RegionsContainer() { - regions = new TreeMap(); + regions = new ConcurrentHashMap(); tree = new PRTree(converter, BRANCH_FACTOR); } - private RegionsContainer(Map regions, PRTree tree) { + private RegionsContainer(ConcurrentMap regions, PRTree tree) { this.regions = regions; this.tree = tree; } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java index 62a3ec23..51ad8698 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java @@ -60,6 +60,7 @@ public RegionManager(ProtectionDatabase loader) { public void load() throws ProtectionDatabaseException { loader.load(this); } + /** * Load the list of regions. If the regions do not load properly, then * the existing list should be used (as stored previously). diff --git a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java index ff69b01b..e3355391 100644 --- a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java +++ b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java @@ -35,8 +35,6 @@ /** * Represents a region of any shape and size that can be protected. - * - * @author sk89q */ public abstract class ProtectedRegion implements Comparable { @@ -45,34 +43,11 @@ public abstract class ProtectedRegion implements Comparable { private static final Pattern idPattern = Pattern.compile("^[A-Za-z0-9_,'\\-\\+/]{1,}$"); - /** - * Holds the region's ID. - */ private String id; - - /** - * Priority. - */ private int priority = 0; - - /** - * Holds the curParent. - */ private ProtectedRegion parent; - - /** - * List of owners. - */ private DefaultDomain owners = new DefaultDomain(); - - /** - * List of members. - */ private DefaultDomain members = new DefaultDomain(); - - /** - * List of flags. - */ private Map, Object> flags = new ConcurrentHashMap, Object>(); /** @@ -457,8 +432,8 @@ public boolean contains(int x, int y, int z) { /** * Check to see if any of the 2D points are inside this region. * - * @param pts - * @return + * @param pts a list positions + * @return true if contained */ public boolean containsAny(List pts) { for (BlockVector2D pt : pts) { @@ -477,6 +452,7 @@ public boolean containsAny(List pts) { * * @param other The region to compare to */ + @Override public int compareTo(ProtectedRegion other) { if (priority > other.priority) { return -1; @@ -598,6 +574,6 @@ public boolean equals(Object obj) { * situation. */ public static class CircularInheritanceException extends Exception { - private static final long serialVersionUID = 7479613488496776022L; } + } diff --git a/src/main/java/com/sk89q/worldguard/util/RegionUtil.java b/src/main/java/com/sk89q/worldguard/util/RegionUtil.java index 3dc0426e..332963bc 100644 --- a/src/main/java/com/sk89q/worldguard/util/RegionUtil.java +++ b/src/main/java/com/sk89q/worldguard/util/RegionUtil.java @@ -24,11 +24,9 @@ /** * Various utility functions for regions. - * - * @author sk89q */ @Deprecated -public class RegionUtil { +public final class RegionUtil { private RegionUtil() { }