From 36af2dbd7af3bc54af7092ac7cba8236fd13bf6a Mon Sep 17 00:00:00 2001 From: Daniil Date: Wed, 25 Jul 2018 22:07:47 +0700 Subject: [PATCH] Preprocessing concept implementation --- .../java/mc/core/events/BaseEventLoop.java | 45 ++++---- .../java/mc/core/events/SimpleEventLoop.java | 19 +++- .../mc/core/events/async/AsyncEventLoop.java | 105 ++++++++++++++++++ .../core/events/async/EventPreprocessor.java | 12 ++ .../ru/core/events/AsyncEventLoopTest.java | 22 ++++ .../events/handlers/AsyncEventHandler.java | 18 +++ 6 files changed, 190 insertions(+), 31 deletions(-) create mode 100644 event-loop/src/main/java/mc/core/events/async/AsyncEventLoop.java create mode 100644 event-loop/src/main/java/mc/core/events/async/EventPreprocessor.java create mode 100644 event-loop/src/test/java/ru/core/events/AsyncEventLoopTest.java create mode 100644 event-loop/src/test/java/ru/core/events/handlers/AsyncEventHandler.java diff --git a/event-loop/src/main/java/mc/core/events/BaseEventLoop.java b/event-loop/src/main/java/mc/core/events/BaseEventLoop.java index 59231d6..9aff373 100644 --- a/event-loop/src/main/java/mc/core/events/BaseEventLoop.java +++ b/event-loop/src/main/java/mc/core/events/BaseEventLoop.java @@ -7,35 +7,28 @@ import java.lang.reflect.Modifier; @Slf4j public abstract class BaseEventLoop implements EventLoop { - @Override - public void addEventHandler(Object object) { - for (Method method : object.getClass().getDeclaredMethods()) { - EventHandler annotation = method.getAnnotation(EventHandler.class); - if (annotation == null) - continue; // We are not interested in methods without @EventHandler annotation - if (!Modifier.isPublic(method.getModifiers())) { - log.error("Unable to register {} as an EventHandler. Method must have a 'private' access modifier.", method.toString()); - continue; + protected boolean notValidEventHandler(EventHandler annotation, Method method) { + if (annotation == null) + return true; - } + if (!Modifier.isPublic(method.getModifiers())) { + log.error("Unable to register {} as an EventHandler. Method must have a 'private' access modifier.", method.toString()); + return true; - if (method.getParameterCount() != 1) { - log.error("Unable to register {} as an EventHandler. Method must have exactly one argument.", method.toString()); - continue; - } - - Class firstParamType = method.getParameterTypes()[0]; - if (!Event.class.isAssignableFrom(firstParamType)) { - log.error("Unable to register {} as an EventHandler. First parameter type must implement 'Event' interface.", method.toString()); - continue; - } - - @SuppressWarnings("unchecked") Class eventType = (Class) firstParamType; - - registerMethod(object, method, annotation, eventType); } - } - protected abstract void registerMethod(Object object, Method method, EventHandler annotation, Class eventType); + if (method.getParameterCount() != 1) { + log.error("Unable to register {} as an EventHandler. Method must have exactly one argument.", method.toString()); + return true; + } + + Class firstParamType = method.getParameterTypes()[0]; + if (!Event.class.isAssignableFrom(firstParamType)) { + log.error("Unable to register {} as an EventHandler. First parameter type must implement 'Event' interface.", method.toString()); + return true; + } + + return false; + } } diff --git a/event-loop/src/main/java/mc/core/events/SimpleEventLoop.java b/event-loop/src/main/java/mc/core/events/SimpleEventLoop.java index 5963a38..7f22112 100644 --- a/event-loop/src/main/java/mc/core/events/SimpleEventLoop.java +++ b/event-loop/src/main/java/mc/core/events/SimpleEventLoop.java @@ -6,7 +6,6 @@ import lombok.extern.slf4j.Slf4j; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.*; @Slf4j @@ -27,10 +26,20 @@ public class SimpleEventLoop extends BaseEventLoop { } } - protected void registerMethod(Object object, Method method, EventHandler annotation, Class eventType) { - List eventHandlers = handlers.computeIfAbsent(eventType, s -> new ArrayList<>()); - eventHandlers.add(new SimpleEventLoop.ExecutorLink(annotation.priority().getValue(), annotation.ignoreCancelled(), method, object)); - eventHandlers.sort(Comparator.comparingInt(o -> o.priority)); + @Override + public void addEventHandler(Object object) { + for (Method method : object.getClass().getDeclaredMethods()) { + EventHandler annotation = method.getAnnotation(EventHandler.class); + + if (notValidEventHandler(annotation, method)) + continue; + + @SuppressWarnings("unchecked") Class eventType = (Class) method.getParameterTypes()[0]; + + List eventHandlers = handlers.computeIfAbsent(eventType, s -> new ArrayList<>()); + eventHandlers.add(new SimpleEventLoop.ExecutorLink(annotation.priority().getValue(), annotation.ignoreCancelled(), method, object)); + eventHandlers.sort(Comparator.comparingInt(o -> o.priority)); + } } /** diff --git a/event-loop/src/main/java/mc/core/events/async/AsyncEventLoop.java b/event-loop/src/main/java/mc/core/events/async/AsyncEventLoop.java new file mode 100644 index 0000000..283d9dc --- /dev/null +++ b/event-loop/src/main/java/mc/core/events/async/AsyncEventLoop.java @@ -0,0 +1,105 @@ +package mc.core.events.async; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import mc.core.events.BaseEventLoop; +import mc.core.events.Event; +import mc.core.events.EventHandler; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; + +@SuppressWarnings("Duplicates") +@Slf4j +public class AsyncEventLoop extends BaseEventLoop { + private Map, List> handlers = new HashMap<>(); + + @Override + public void callEvent(Event event) { + Class eventType = event.getClass(); + if (handlers.containsKey(eventType)) { + for (ExecutorLink link : handlers.get(eventType)) { + if (link.getPreprocessMethod() != null) { + try { + link.getPreprocessMethod().invoke(link.object, event); + } catch (IllegalAccessException | InvocationTargetException e) { + log.error("Exception caught while attempting to run event preprocessing for {}.", eventType.getSimpleName(), e); + } + } + + try { + link.getMethod().invoke(link.object, event); + } catch (IllegalAccessException | InvocationTargetException e) { + log.error("Exception caught while attempting to dispatch {}.", eventType.getSimpleName(), e); + } + } + } + } + + @Override + public void addEventHandler(Object object) { + Map preprocessors = new HashMap<>(); + + for (Method method : object.getClass().getDeclaredMethods()) { + EventPreprocessor annotation = method.getAnnotation(EventPreprocessor.class); + if (annotation == null) + continue; + + if (!Modifier.isPublic(method.getModifiers())) { + log.error("Unable to register {} as an EventPreprocessor. Method must have a 'private' access modifier.", method.toString()); + continue; + + } + + if (method.getParameterCount() != 1) { + log.error("Unable to register {} as an EventPreprocessor. Method must have exactly one argument.", method.toString()); + continue; + } + + Class firstParamType = method.getParameterTypes()[0]; + if (!Event.class.isAssignableFrom(firstParamType)) { + log.error("Unable to register {} as an EventPreprocessor. First parameter type must implement 'Event' interface.", method.toString()); + continue; + } + + if (preprocessors.containsKey(annotation.eventName())) + log.warn("Attempted to register multiple EventPreprocessors for the event {}. Please note, that only the last one will be registered.", annotation.eventName()); + preprocessors.put(annotation.eventName(), method); + } + + + for (Method method : object.getClass().getDeclaredMethods()) { + EventHandler annotation = method.getAnnotation(EventHandler.class); + + if (notValidEventHandler(annotation, method)) + continue; + + @SuppressWarnings("unchecked") Class eventType = (Class) method.getParameterTypes()[0]; + + List eventHandlers = handlers.computeIfAbsent(eventType, s -> new ArrayList<>()); + eventHandlers.add(new ExecutorLink(annotation.priority().getValue(), annotation.ignoreCancelled(), method, object, preprocessors.remove(method.getName()))); + eventHandlers.sort(Comparator.comparingInt(o -> o.priority)); + } + + for (Map.Entry preprocessor : preprocessors.entrySet()) + log.error("EventPreprocessor ({}) missing target: unable to find target method ({})", preprocessor.getValue(), preprocessor.getKey()); + + } + + /** + * This class describes + */ + @RequiredArgsConstructor + @Getter + private static class ExecutorLink { + private final int priority; + private final boolean ignoreCancelled; + private final Method method; + private final Object object; + private final Method preprocessMethod; + } +} + diff --git a/event-loop/src/main/java/mc/core/events/async/EventPreprocessor.java b/event-loop/src/main/java/mc/core/events/async/EventPreprocessor.java new file mode 100644 index 0000000..905d661 --- /dev/null +++ b/event-loop/src/main/java/mc/core/events/async/EventPreprocessor.java @@ -0,0 +1,12 @@ +package mc.core.events.async; + + +import java.lang.annotation.*; + +@Documented +@Target(ElementType.METHOD) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface EventPreprocessor { + String eventName(); +} diff --git a/event-loop/src/test/java/ru/core/events/AsyncEventLoopTest.java b/event-loop/src/test/java/ru/core/events/AsyncEventLoopTest.java new file mode 100644 index 0000000..5d5f70d --- /dev/null +++ b/event-loop/src/test/java/ru/core/events/AsyncEventLoopTest.java @@ -0,0 +1,22 @@ +package ru.core.events; + +import mc.core.events.LoginEvent; +import mc.core.events.async.AsyncEventLoop; +import org.junit.Assert; +import org.junit.Test; +import ru.core.events.handlers.AsyncEventHandler; + +public class AsyncEventLoopTest { + + @Test + public void loopWorks() { + AsyncEventLoop asyncEventLoop = new AsyncEventLoop(); + asyncEventLoop.addEventHandler(new AsyncEventHandler()); + LoginEvent testEvent = new LoginEvent(null); + testEvent.setDenyReason("none"); + + asyncEventLoop.callEvent(testEvent); + + Assert.assertEquals("Event handler was not called", "Hello from Async Event Handler!", testEvent.getDenyReason()); + } +} diff --git a/event-loop/src/test/java/ru/core/events/handlers/AsyncEventHandler.java b/event-loop/src/test/java/ru/core/events/handlers/AsyncEventHandler.java new file mode 100644 index 0000000..634b9a1 --- /dev/null +++ b/event-loop/src/test/java/ru/core/events/handlers/AsyncEventHandler.java @@ -0,0 +1,18 @@ +package ru.core.events.handlers; + +import mc.core.events.EventHandler; +import mc.core.events.LoginEvent; +import mc.core.events.async.EventPreprocessor; + +public class AsyncEventHandler { + + @EventHandler + public void onLogin(LoginEvent event) { + event.setDenyReason("Hello from Async Event Handler!"); + } + + @EventPreprocessor(eventName = "onLogin") + public void onLoginPreprocess(LoginEvent event) { + + } +}