/* * Quartz * Copyright (c) 2015, Minecrell <https://github.com/Minecrell> * * Based on Sponge and SpongeAPI, licensed under the MIT License (MIT). * Copyright (c) SpongePowered.org <http://www.spongepowered.org> * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package net.minecrell.quartz.event; import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import com.google.common.base.Optional; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.reflect.TypeToken; import net.minecrell.quartz.Quartz; import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.plugin.PluginManager; import org.spongepowered.api.service.event.EventManager; import org.spongepowered.api.util.event.Cancellable; import org.spongepowered.api.util.event.Event; import org.spongepowered.api.util.event.Subscribe; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; @Singleton public class QuartzEventManager implements EventManager { private final Object lock = new Object(); private final Quartz quartz; private final PluginManager pluginManager; private final EventHandlerFactory handlerFactory = new EventHandlerClassFactory(getClass().getPackage().getName() + ".handler"); private final Multimap<Class<?>, RegisteredHandler> handlersByEvent = HashMultimap.create(); private final LoadingCache<Class<? extends Event>, RegisteredHandler[]> handlersCache = CacheBuilder.newBuilder().build(new CacheLoader<Class<? extends Event>, RegisteredHandler[]>() { @Override public RegisteredHandler[] load(Class<? extends Event> key) throws Exception { return bakeHandlers(key); } }); @Inject public QuartzEventManager(Quartz quartz, PluginManager pluginManager) { this.quartz = requireNonNull(quartz, "quartz"); this.pluginManager = requireNonNull(pluginManager, "pluginManager"); } @SuppressWarnings({"unchecked", "rawtypes"}) private RegisteredHandler[] bakeHandlers(Class<? extends Event> rootEvent) { List<RegisteredHandler> registrations = new ArrayList<>(); Set<Class<?>> types = (Set) TypeToken.of(rootEvent).getTypes().rawTypes(); synchronized (lock) { /*for (Class<?> type : types) { if (Event.class.isAssignableFrom(type)) { registrations.addAll(this.handlersByEvent.get(type)); } }*/ // Wat types.stream().filter(Event.class::isAssignableFrom).forEach(type -> registrations.addAll(this.handlersByEvent.get(type))); } RegisteredHandler[] handlers = registrations.toArray(new RegisteredHandler[registrations.size()]); Arrays.sort(handlers); return handlers; } private static boolean isValidHandler(Method method) { int modifiers = method.getModifiers(); if (!Modifier.isPublic(modifiers) || Modifier.isAbstract(modifiers) || Modifier.isInterface(method.getDeclaringClass().getModifiers()) || method.getReturnType() != void.class) { return false; } Class<?>[] parameters = method.getParameterTypes(); return parameters.length == 1 && Event.class.isAssignableFrom(parameters[0]); } @SuppressWarnings("unchecked") public void register(PluginContainer container, Object listener) { requireNonNull(container, "container"); requireNonNull(listener, "listener"); List<RegisteredHandler> handlers = new ArrayList<>(); Class<?> handle = listener.getClass(); for (Method method : handle.getMethods()) { Subscribe subscribe = method.getAnnotation(Subscribe.class); if (subscribe != null) { if (isValidHandler(method)) { try { Class<? extends Event> eventClass = (Class<? extends Event>) method.getParameterTypes()[0]; EventHandler handler = handlerFactory.get(listener, method); handlers.add(new RegisteredHandler(container, eventClass, subscribe.order(), handler, method, subscribe.ignoreCancelled())); } catch (Exception e) { quartz.getLogger().error("Failed to create handler for " + method + " on " + method.getDeclaringClass().getName(), e); } } else { quartz.getLogger().warn("The method {} on {} has @{} but has the wrong signature", method, method.getDeclaringClass().getName(), Subscribe.class.getName()); } } } synchronized (this.lock) { boolean changed = false; for (RegisteredHandler handler : handlers) { if (handlersByEvent.put(handler.getEventClass(), handler)) { changed = true; } } if (changed) { handlersCache.invalidateAll(); } } } @Override public void register(Object plugin, Object listener) { Optional<PluginContainer> container = pluginManager.fromInstance(plugin); checkArgument(container.isPresent(), "Unknown plugin: %s", plugin); register(container.get(), listener); } @Override public void unregister(Object listener) { requireNonNull(listener, "listener"); synchronized (this.lock) { boolean changed = false; Iterator<RegisteredHandler> itr = handlersByEvent.values().iterator(); while (itr.hasNext()) { RegisteredHandler handler = itr.next(); if (listener.equals(handler.getHandle())) { itr.remove(); changed = true; } } if (changed) { this.handlersCache.invalidateAll(); } } } @Override public boolean post(Event event) { requireNonNull(event, "event"); for (RegisteredHandler handler : handlersCache.getUnchecked(event.getClass())) { try { handler.handle(event); } catch (Throwable e) { quartz.getLogger().error("Could not pass " + event.getClass().getSimpleName() + " to " + handler.getPlugin(), e); } } return event instanceof Cancellable && ((Cancellable) event).isCancelled(); } }