From e892d6056409d9b5b7b8f49feb132eef34c74176 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 23 Oct 2016 02:50:48 +0200 Subject: [PATCH] Added event exception handler --- pom.xml | 29 ++-- .../core/EventExceptionHandler.java | 154 ++++++++++++++++++ .../java/buttondevteam/core/MainPlugin.java | 4 +- 3 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 src/main/java/buttondevteam/core/EventExceptionHandler.java diff --git a/pom.xml b/pom.xml index 2e4a993..095ae1a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,9 @@ 4.0.0 - buttondevteam + com.github.TBMCPlugins.ButtonCore ButtonCore - 0.0.1-SNAPSHOT + master-SNAPSHOT ButtonCore ButtonCore @@ -15,16 +15,16 @@ **/*.java - - src/main/resources - - *.properties - *.yml - *.csv - *.txt - - true - + + src/main/resources + + *.properties + *.yml + *.csv + *.txt + + true + ButtonCore @@ -99,6 +99,11 @@ Towny master-SNAPSHOT + + com.github.TBMCPlugins + DiscordPlugin + master-SNAPSHOT + TBMCPlugins diff --git a/src/main/java/buttondevteam/core/EventExceptionHandler.java b/src/main/java/buttondevteam/core/EventExceptionHandler.java new file mode 100644 index 0000000..2c83a23 --- /dev/null +++ b/src/main/java/buttondevteam/core/EventExceptionHandler.java @@ -0,0 +1,154 @@ +package buttondevteam.core; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import org.bukkit.plugin.IllegalPluginAccessException; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.RegisteredListener; + +import com.google.common.collect.Lists; + +public abstract class EventExceptionHandler { // https://gist.github.com/aadnk/5430459 + // For wrapping a registered listener + private static class ExceptionRegisteredListener extends RegisteredListener { + /** + * Represents an event executor that does nothing. This is not really necessary in the current + * implementation of CraftBukkit, but we will take no chances. + */ + private static EventExecutor NULL_EXECUTOR = new EventExecutor() { + @Override + public void execute(Listener listener, Event event) throws EventException { + // Do nothing + } + }; + + private final RegisteredListener delegate; + private final EventExceptionHandler handler; + + public ExceptionRegisteredListener(RegisteredListener delegate, EventExceptionHandler handler) { + super(delegate.getListener(), NULL_EXECUTOR, delegate.getPriority(), + delegate.getPlugin(), delegate.isIgnoringCancelled()); + this.delegate = delegate; + this.handler = handler; + } + + @Override + public void callEvent(Event event) throws EventException { + try { + delegate.callEvent(event); + } catch (EventException e) { + if (!handler.handle(e.getCause(), event)) { + throw e; + } + } catch (Throwable e) { + if (!handler.handle(e, event)) { + doThrow(e); + } + } + } + + // WARNING: HORRIBLE, HORRIBLE HACK to get around checked exceptions + private static void doThrow(Throwable e) { + ExceptionRegisteredListener. doThrowInner(e); + } + + @SuppressWarnings("unchecked") + private static void doThrowInner(Throwable e) throws E { + throw (E) e; + } + } + + /** + * Register Bukkit event handlers with a given exception handler. + * @param listener - a class of event handlers. + * @param plugin - the current plugin. + * @param handler - exception handler. + */ + public static void registerEvents(Listener listener, Plugin plugin, EventExceptionHandler handler) { + Validate.notNull(plugin, "Plugin cannot be NULL."); + + registerEvents(plugin.getServer().getPluginManager(), listener, plugin, handler); + } + + /** + * Register Bukkit event handlers with a given exception handler. + * @param manager - the current plugin manager. + * @param listener - a class of event handlers. + * @param plugin - the current plugin. + * @param handler - exception handler. + */ + public static void registerEvents(PluginManager manager, Listener listener, Plugin plugin, EventExceptionHandler handler) { + Validate.notNull(manager, "Manager cannot be NULL."); + Validate.notNull(listener, "Listener cannot be NULL."); + Validate.notNull(plugin, "Plugin cannot be NULL."); + Validate.notNull(handler, "Handler cannot be NULL."); + + if (!plugin.isEnabled()) { + throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled"); + } + + // Create normal listeners + for (Map.Entry, Set> entry : + plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()) { + + // Wrap these listeners in our exception handler + getHandlerList(entry.getKey()).registerAll(wrapAll(entry.getValue(), handler)); + } + } + + /** + * Wrap every listener in the given collection around an exception handler. + * @param listeners - the listeners to wrap. + * @param handler - the exception handler to add. + * @return The wrapped listeners. + */ + private static Collection wrapAll(Collection listeners, EventExceptionHandler handler) { + List output = Lists.newArrayList(); + + for (RegisteredListener listener : listeners) { + output.add(new ExceptionRegisteredListener(listener, handler)); + } + return output; + } + + /** + * Retrieve the handler list associated with the given class. + * @param clazz - given event class. + * @return Associated handler list. + */ + private static HandlerList getHandlerList(Class clazz) { + // Class must have Event as its superclass + while (clazz.getSuperclass() != null && Event.class.isAssignableFrom(clazz.getSuperclass())) { + try { + Method method = clazz.getDeclaredMethod("getHandlerList"); + method.setAccessible(true); + return (HandlerList) method.invoke(null); + } catch (NoSuchMethodException e) { + // Keep on searching + clazz = clazz.getSuperclass().asSubclass(Event.class); + } catch (Exception e) { + throw new IllegalPluginAccessException(e.getMessage()); + } + } + throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName()); + } + + /** + * Handle a given exception. + * @param ex - the exception to handle. + * @param event - the event that was being handled. + * @return TRUE to indicate that the exception has been handled, FALSE to rethrow it. + */ + public abstract boolean handle(Throwable ex, Event event); +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/core/MainPlugin.java b/src/main/java/buttondevteam/core/MainPlugin.java index c143f45..11c2007 100644 --- a/src/main/java/buttondevteam/core/MainPlugin.java +++ b/src/main/java/buttondevteam/core/MainPlugin.java @@ -3,6 +3,7 @@ package buttondevteam.core; import java.util.logging.Logger; import org.bukkit.Bukkit; +import org.bukkit.event.Event; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.java.JavaPlugin; @@ -19,7 +20,8 @@ public class MainPlugin extends JavaPlugin { logger = getLogger(); logger.info(pdfFile.getName() + " has been Enabled (V." + pdfFile.getVersion() + ")."); - Bukkit.getPluginManager().registerEvents(new PlayerListener(), this); + EventExceptionHandler.registerEvents(new PlayerListener(), this, new EventExceptionDiscordSender() { + }); } }