/*
* Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ks.eventsystem.bus;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;
import com.google.common.primitives.Primitives;
import de.ks.executor.JavaFXExecutorService;
import de.ks.reflection.ReflectionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.enterprise.inject.Vetoed;
import javax.enterprise.inject.spi.CDI;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
/**
*
*/
@Vetoed
public class EventBus {
/**
* Used to synchronous execute all events, mainly in tests
*/
public static boolean alwaysWait = false;
private static final Logger log = LoggerFactory.getLogger(EventBus.class);
protected final ArrayListMultimap<Class<?>, EventHandler> handlers = ArrayListMultimap.create();
protected final HandlerComparator handlerComparator = new HandlerComparator();
protected final ReadWriteLock lock = new ReentrantReadWriteLock(true);
protected ExecutorService executorService = null;
protected JavaFXExecutorService javaFXExecutorService = null;
public EventBus register(Object handler) {
@SuppressWarnings("unchecked") List<Method> methods = ReflectionUtil.getAllMethods(handler.getClass(),//
(Method m) -> m.isAnnotationPresent(Subscribe.class), //
(Method m) -> m.getParameters().length == 1 //
);
lock.writeLock().lock();
try {
for (Method method : methods) {
Class<?> eventType = getEventType(method);
handlers.put(eventType, new EventHandler(executorService, javaFXExecutorService, handler, method));
List<EventHandler> eventHandlers = handlers.get(eventType);//could be optimized by not sorting an event type multiple times
Collections.sort(eventHandlers, getHandlerComparator());
}
} finally {
lock.writeLock().unlock();
}
return this;
}
public EventBus unregister(Object handler) {
@SuppressWarnings("unchecked") List<Method> methods = ReflectionUtil.getAllMethods(handler.getClass(),//
(Method m) -> m.isAnnotationPresent(Subscribe.class), //
(Method m) -> m.getParameters().length == 1 //
);
Set<Class<?>> classes = methods.stream().map(this::getEventType).collect(Collectors.toSet());
for (Class<?> clazz : classes) {
List<EventHandler> eventHandlers = handlers.get(clazz);
removeSpecificHandler(handler, eventHandlers);
}
return this;
}
private void removeSpecificHandler(Object specificHandler, List<EventHandler> eventHandlers) {
lock.writeLock().lock();
try {
for (Iterator<EventHandler> iterator = eventHandlers.iterator(); iterator.hasNext(); ) {
EventHandler next = iterator.next();
Object instance = next.target.get();
if (instance != null) {
if (instance == specificHandler) {
iterator.remove();
}
} else {
iterator.remove();
}
}
} finally {
lock.writeLock().unlock();
}
}
public EventBus post(Object event) {
post(event, EventTarget.Default, alwaysWait);
return this;
}
public EventBus post(Object event, EventTarget target) {
post(event, target, alwaysWait);
return this;
}
public EventBus postAndWait(Object event) {
post(event, EventTarget.Default, true);
return this;
}
public EventBus postAndWait(Object event, EventTarget target) {
post(event, target, true);
return this;
}
protected void post(Object event, EventTarget target, boolean wait) {
if (target == EventTarget.Default) {
postToEventHandlers(event, wait);
} else {
CDI.current().getBeanManager().fireEvent(event);
}
}
private void postToEventHandlers(Object event, boolean wait) {
List<Class<?>> hierarchy = ReflectionUtil.getClassHierarchy(event.getClass(), false);
LinkedList<EventExecution> executions = new LinkedList<>();
lock.readLock().lock();
try {
for (Class<?> eventType : hierarchy) {
List<EventHandler> eventHandlers = handlers.get(eventType);
for (EventHandler eventHandler : eventHandlers) {
executions.add(new EventExecution(event, eventHandler));
}
}
} finally {
lock.readLock().unlock();
}
for (EventExecution execution : executions) {
boolean consumed = execution.handler.handleEvent(event, wait);
if (consumed) {
break;
}
}
boolean noHandlerFound = executions.isEmpty();
boolean isDeadEvent = event instanceof DeadEvent;
if (noHandlerFound && !isDeadEvent) {
postToEventHandlers(new DeadEvent(this, event), false);
} else if (noHandlerFound) {
log.warn("Could not find dead event handler.");
}
}
protected HandlerComparator getHandlerComparator() {
return handlerComparator;
}
protected Class<?> getEventType(Method method) {
Class<?> type = method.getParameterTypes()[0];
if (type.isPrimitive()) {
return Primitives.wrap(type);
}
return type;
}
public ExecutorService getExecutorService() {
return executorService;
}
public void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}
public JavaFXExecutorService getJavaFXExecutorService() {
return javaFXExecutorService;
}
public void setJavaFXExecutorService(JavaFXExecutorService javaFXExecutorService) {
this.javaFXExecutorService = javaFXExecutorService;
}
}