From 19bf0a56a0ee703d978da8e5f84999d1f5b20602 Mon Sep 17 00:00:00 2001 From: Joseph Hirschfeld Date: Sat, 20 Feb 2016 20:07:46 -0500 Subject: [PATCH] Add exception reporting event diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java index 12d9232..f35bc09 100644 --- a/src/main/java/org/bukkit/command/SimpleCommandMap.java +++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java @@ -17,6 +17,9 @@ import org.bukkit.Server; import org.bukkit.command.defaults.*; import org.bukkit.entity.Player; import org.bukkit.util.StringUtil; +import org.github.paperspigot.event.ServerExceptionEvent; +import org.github.paperspigot.exception.ServerCommandException; +import org.github.paperspigot.exception.ServerTabCompleteException; public class SimpleCommandMap implements CommandMap { private static final Pattern PATTERN_ON_SPACE = Pattern.compile(" ", Pattern.LITERAL); @@ -147,7 +150,9 @@ public class SimpleCommandMap implements CommandMap { throw ex; } catch (Throwable ex) { target.timings.stopTiming(); // Spigot - throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex); + String msg = "Unhandled exception executing '" + commandLine + "' in " + target; + server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerCommandException(ex, target, sender, args))); // Paper + throw new CommandException(msg, ex); } // return true as command was handled @@ -224,7 +229,9 @@ public class SimpleCommandMap implements CommandMap { } catch (CommandException ex) { throw ex; } catch (Throwable ex) { - throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex); + String msg = "Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target; + server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerTabCompleteException(msg, ex, target, sender, args))); // Paper + throw new CommandException(msg, ex); } } // PaperSpigot end diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java index 1325b03..ce9839e 100644 --- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java +++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java @@ -33,6 +33,9 @@ import org.bukkit.permissions.PermissionDefault; import org.bukkit.util.FileUtil; import com.google.common.collect.ImmutableSet; +import org.github.paperspigot.event.ServerExceptionEvent; +import org.github.paperspigot.exception.ServerEventException; +import org.github.paperspigot.exception.ServerPluginEnableDisableException; /** * Handles all plugin management from the Server @@ -403,7 +406,8 @@ public final class SimplePluginManager implements PluginManager { try { plugin.getPluginLoader().enablePlugin(plugin); } catch (Throwable ex) { - server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + handlePluginException("Error occurred (in the plugin loader) while enabling " + + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); } HandlerList.bakeAll(); @@ -422,36 +426,48 @@ public final class SimplePluginManager implements PluginManager { try { plugin.getPluginLoader().disablePlugin(plugin); } catch (Throwable ex) { - server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + handlePluginException("Error occurred (in the plugin loader) while disabling " + + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper } try { server.getScheduler().cancelTasks(plugin); } catch (Throwable ex) { - server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while cancelling tasks for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for " + + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper } try { server.getServicesManager().unregisterAll(plugin); } catch (Throwable ex) { - server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering services for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + handlePluginException("Error occurred (in the plugin loader) while unregistering services for " + + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper } try { HandlerList.unregisterAll(plugin); } catch (Throwable ex) { - server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering events for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + handlePluginException("Error occurred (in the plugin loader) while unregistering events for " + + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper } try { server.getMessenger().unregisterIncomingPluginChannel(plugin); server.getMessenger().unregisterOutgoingPluginChannel(plugin); } catch(Throwable ex) { - server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering plugin channels for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + handlePluginException("Error occurred (in the plugin loader) while unregistering plugin channels for " + + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper } } } + // Paper start + private void handlePluginException(String msg, Throwable ex, Plugin plugin) { + server.getLogger().log(Level.SEVERE, msg, ex); + callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin))); + } + // Paper end + public void clearPlugins() { synchronized (this) { disablePlugins(); @@ -513,7 +529,13 @@ public final class SimplePluginManager implements PluginManager { )); } } catch (Throwable ex) { - server.getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(), ex); + // Paper start - error reporting + String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(); + server.getLogger().log(Level.SEVERE, msg, ex); + if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop + callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event))); + } + // Paper end } } } diff --git a/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java b/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java index 4c171e8..9ef6525 100644 --- a/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java +++ b/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java @@ -8,6 +8,8 @@ import java.util.Map; import java.util.Set; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; +import org.github.paperspigot.event.ServerExceptionEvent; +import org.github.paperspigot.exception.ServerPluginMessageException; /** * Standard implementation to {@link Messenger} @@ -427,7 +429,13 @@ public class StandardMessenger implements Messenger { registration.getListener().onPluginMessageReceived( channel, source, message ); } catch ( Throwable t ) { - org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.WARNING, "Could not pass incoming plugin message to " + registration.getPlugin(), t ); + // Paper start + String msg = "Could not pass incoming plugin message to " + registration.getPlugin(); + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.WARNING, msg, t ); + source.getServer().getPluginManager().callEvent(new ServerExceptionEvent( + new ServerPluginMessageException(msg, t, registration.getPlugin(), source, channel, message) + )); + // Paper end } // Spigot End } diff --git a/src/main/java/org/github/paperspigot/event/ServerExceptionEvent.java b/src/main/java/org/github/paperspigot/event/ServerExceptionEvent.java new file mode 100644 index 0000000..317e9d2 --- /dev/null +++ b/src/main/java/org/github/paperspigot/event/ServerExceptionEvent.java @@ -0,0 +1,36 @@ +package org.github.paperspigot.event; + +import com.google.common.base.Preconditions; +import org.apache.commons.lang.Validate; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.github.paperspigot.exception.ServerException; + +/** + * Called whenever an exception is thrown in a recoverable section of the server. + */ +public class ServerExceptionEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + private ServerException exception; + + public ServerExceptionEvent (ServerException exception) { + this.exception = Preconditions.checkNotNull(exception, "exception"); + } + + /** + * Gets the wrapped exception that was thrown. + * @return Exception thrown + */ + public ServerException getException() { + return exception; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerCommandException.java b/src/main/java/org/github/paperspigot/exception/ServerCommandException.java new file mode 100644 index 0000000..aae647a --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerCommandException.java @@ -0,0 +1,64 @@ +package org.github.paperspigot.exception; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Thrown when a command throws an exception + */ +public class ServerCommandException extends ServerException { + + private final Command command; + private final CommandSender commandSender; + private final String[] arguments; + + public ServerCommandException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) { + super(message, cause); + this.commandSender = checkNotNull(commandSender, "commandSender"); + this.arguments = checkNotNull(arguments, "arguments"); + this.command = checkNotNull(command, "command"); + } + + public ServerCommandException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) { + super(cause); + this.commandSender = checkNotNull(commandSender, "commandSender"); + this.arguments = checkNotNull(arguments, "arguments"); + this.command = checkNotNull(command, "command"); + } + + protected ServerCommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) { + super(message, cause, enableSuppression, writableStackTrace); + this.commandSender = checkNotNull(commandSender, "commandSender"); + this.arguments = checkNotNull(arguments, "arguments"); + this.command = checkNotNull(command, "command"); + } + + /** + * Gets the command which threw the exception + * + * @return exception throwing command + */ + public Command getCommand() { + return command; + } + + /** + * Gets the command sender which executed the command request + * + * @return command sender of exception thrown command request + */ + public CommandSender getCommandSender() { + return commandSender; + } + + /** + * Gets the arguments which threw the exception for the command + * + * @return arguments of exception thrown command request + */ + public String[] getArguments() { + return arguments; + } +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerEventException.java b/src/main/java/org/github/paperspigot/exception/ServerEventException.java new file mode 100644 index 0000000..d99cab8 --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerEventException.java @@ -0,0 +1,52 @@ +package org.github.paperspigot.exception; + +import org.bukkit.event.Event; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; + +import static com.google.common.base.Preconditions.*; + +/** + * Exception thrown when a server event listener throws an exception + */ +public class ServerEventException extends ServerPluginException { + + private final Listener listener; + private final Event event; + + public ServerEventException(String message, Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) { + super(message, cause, responsiblePlugin); + this.listener = checkNotNull(listener, "listener"); + this.event = checkNotNull(event, "event"); + } + + public ServerEventException(Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) { + super(cause, responsiblePlugin); + this.listener = checkNotNull(listener, "listener"); + this.event = checkNotNull(event, "event"); + } + + protected ServerEventException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Listener listener, Event event) { + super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin); + this.listener = checkNotNull(listener, "listener"); + this.event = checkNotNull(event, "event"); + } + + /** + * Gets the listener which threw the exception + * + * @return event listener + */ + public Listener getListener() { + return listener; + } + + /** + * Gets the event which caused the exception + * + * @return event + */ + public Event getEvent() { + return event; + } +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerException.java b/src/main/java/org/github/paperspigot/exception/ServerException.java new file mode 100644 index 0000000..f7aad05 --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerException.java @@ -0,0 +1,23 @@ +package org.github.paperspigot.exception; + +/** + * Wrapper exception for all exceptions that are thrown by the server. + */ +public class ServerException extends Exception { + + public ServerException(String message) { + super(message); + } + + public ServerException(String message, Throwable cause) { + super(message, cause); + } + + public ServerException(Throwable cause) { + super(cause); + } + + protected ServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerInternalException.java b/src/main/java/org/github/paperspigot/exception/ServerInternalException.java new file mode 100644 index 0000000..17ad4cb --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerInternalException.java @@ -0,0 +1,35 @@ +package org.github.paperspigot.exception; + +import org.bukkit.Bukkit; +import org.bukkit.entity.ThrownExpBottle; +import org.github.paperspigot.event.ServerExceptionEvent; + +/** + * Thrown when the internal server throws a recoverable exception. + */ +public class ServerInternalException extends ServerException { + + public ServerInternalException(String message) { + super(message); + } + + public ServerInternalException(String message, Throwable cause) { + super(message, cause); + } + + public ServerInternalException(Throwable cause) { + super(cause); + } + + protected ServerInternalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public static void reportInternalException(Throwable cause) { + try { + Bukkit.getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(cause)));; + } catch (Throwable t) { + t.printStackTrace(); // Don't want to rethrow! + } + } +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerPluginEnableDisableException.java b/src/main/java/org/github/paperspigot/exception/ServerPluginEnableDisableException.java new file mode 100644 index 0000000..8c938d0 --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerPluginEnableDisableException.java @@ -0,0 +1,20 @@ +package org.github.paperspigot.exception; + +import org.bukkit.plugin.Plugin; + +/** + * Thrown whenever there is an exception with any enabling or disabling of plugins. + */ +public class ServerPluginEnableDisableException extends ServerPluginException { + public ServerPluginEnableDisableException(String message, Throwable cause, Plugin responsiblePlugin) { + super(message, cause, responsiblePlugin); + } + + public ServerPluginEnableDisableException(Throwable cause, Plugin responsiblePlugin) { + super(cause, responsiblePlugin); + } + + protected ServerPluginEnableDisableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) { + super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin); + } +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerPluginException.java b/src/main/java/org/github/paperspigot/exception/ServerPluginException.java new file mode 100644 index 0000000..f9241ad --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerPluginException.java @@ -0,0 +1,39 @@ +package org.github.paperspigot.exception; + +import com.google.common.base.Preconditions; +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.Plugin; + +import static com.google.common.base.Preconditions.*; + +/** + * Wrapper exception for all cases to which a plugin can be immediately blamed for + */ +public class ServerPluginException extends ServerException { + public ServerPluginException(String message, Throwable cause, Plugin responsiblePlugin) { + super(message, cause); + this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin"); + } + + public ServerPluginException(Throwable cause, Plugin responsiblePlugin) { + super(cause); + this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin"); + } + + protected ServerPluginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) { + super(message, cause, enableSuppression, writableStackTrace); + this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin"); + } + + private final Plugin responsiblePlugin; + + /** + * Gets the plugin which is directly responsible for the exception being thrown + * + * @return plugin which is responsible for the exception throw + */ + public Plugin getResponsiblePlugin() { + return responsiblePlugin; + } + +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerPluginMessageException.java b/src/main/java/org/github/paperspigot/exception/ServerPluginMessageException.java new file mode 100644 index 0000000..b71303b --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerPluginMessageException.java @@ -0,0 +1,61 @@ +package org.github.paperspigot.exception; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import static com.google.common.base.Preconditions.*; + +/** + * Thrown when an incoming plugin message channel throws an exception + */ +public class ServerPluginMessageException extends ServerPluginException { + + private final Player player; + private final String channel; + private final byte[] data; + + public ServerPluginMessageException(String message, Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) { + super(message, cause, responsiblePlugin); + this.player = checkNotNull(player, "player"); + this.channel = checkNotNull(channel, "channel"); + this.data = checkNotNull(data, "data"); + } + + public ServerPluginMessageException(Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) { + super(cause, responsiblePlugin); + this.player = checkNotNull(player, "player"); + this.channel = checkNotNull(channel, "channel"); + this.data = checkNotNull(data, "data"); + } + + protected ServerPluginMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Player player, String channel, byte[] data) { + super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin); + this.player = checkNotNull(player, "player"); + this.channel = checkNotNull(channel, "channel"); + this.data = checkNotNull(data, "data"); + } + + /** + * Gets the channel to which the error occurred from recieving data from + * @return exception channel + */ + public String getChannel() { + return channel; + } + + /** + * Gets the data to which the error occurred from + * @return exception data + */ + public byte[] getData() { + return data; + } + + /** + * Gets the player which the plugin message causing the exception originated from + * @return exception player + */ + public Player getPlayer() { + return player; + } +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerSchedulerException.java b/src/main/java/org/github/paperspigot/exception/ServerSchedulerException.java new file mode 100644 index 0000000..99757ee --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerSchedulerException.java @@ -0,0 +1,37 @@ +package org.github.paperspigot.exception; + +import org.bukkit.scheduler.BukkitTask; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Thrown when a plugin's scheduler fails with an exception + */ +public class ServerSchedulerException extends ServerPluginException { + + private final BukkitTask task; + + public ServerSchedulerException(String message, Throwable cause, BukkitTask task) { + super(message, cause, task.getOwner()); + this.task = checkNotNull(task, "task"); + } + + public ServerSchedulerException(Throwable cause, BukkitTask task) { + super(cause, task.getOwner()); + this.task = checkNotNull(task, "task"); + } + + protected ServerSchedulerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, BukkitTask task) { + super(message, cause, enableSuppression, writableStackTrace, task.getOwner()); + this.task = checkNotNull(task, "task"); + } + + /** + * Gets the task which threw the exception + * @return exception throwing task + */ + public BukkitTask getTask() { + return task; + } + +} diff --git a/src/main/java/org/github/paperspigot/exception/ServerTabCompleteException.java b/src/main/java/org/github/paperspigot/exception/ServerTabCompleteException.java new file mode 100644 index 0000000..6e1e2ab --- /dev/null +++ b/src/main/java/org/github/paperspigot/exception/ServerTabCompleteException.java @@ -0,0 +1,22 @@ +package org.github.paperspigot.exception; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +/** + * Called when a tab-complete request throws an exception + */ +public class ServerTabCompleteException extends ServerCommandException { + + public ServerTabCompleteException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) { + super(message, cause, command, commandSender, arguments); + } + + public ServerTabCompleteException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) { + super(cause, command, commandSender, arguments); + } + + protected ServerTabCompleteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) { + super(message, cause, enableSuppression, writableStackTrace, command, commandSender, arguments); + } +} -- 2.5.0