/*
* 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.util.concurrent.ThreadFactoryBuilder;
import de.ks.executor.JavaFXExecutorService;
import de.ks.executor.LoggingUncaughtExceptionHandler;
import de.ks.reflection.ReflectionUtil;
import javafx.application.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.concurrent.*;
/**
*
*/
class EventHandler {
private static final Logger log = LoggerFactory.getLogger(EventHandler.class);
protected final WeakReference<Object> target;
protected final Method method;
protected final Integer priority;
protected final HandlingThread handlingThread;
protected final ExecutorService service;
protected final JavaFXExecutorService fxExecutor;
protected EventHandler(ExecutorService service, JavaFXExecutorService fxExecutor, Object target, Method method) {
this.target = new WeakReference<>(target);
this.method = method;
if (method.isAnnotationPresent(Priority.class)) {
Priority annotation = method.getAnnotation(Priority.class);
priority = annotation.value();
} else {
priority = Integer.MAX_VALUE;
}
if (method.isAnnotationPresent(Threading.class)) {
handlingThread = method.getAnnotation(Threading.class).value();
} else {
handlingThread = HandlingThread.Sync;
}
if (service == null) {
ThreadFactory threadFactory = new ThreadFactoryBuilder()//
.setDaemon(true)//
.setNameFormat("Eventsystem-%d")//
.setUncaughtExceptionHandler(new LoggingUncaughtExceptionHandler())//
.build();
this.service = Executors.newCachedThreadPool(threadFactory);
} else {
this.service = service;
}
if (fxExecutor == null) {
this.fxExecutor = new JavaFXExecutorService();
} else {
this.fxExecutor = fxExecutor;
}
}
public boolean handleEvent(Object event, boolean wait) {
Object targetInstance = target.get();
if (targetInstance != null) {
Object retval = null;
switch (this.handlingThread) {
case Sync:
retval = ReflectionUtil.invokeMethod(method, targetInstance, event);
break;
case Async:
executeAsync(event, targetInstance, wait);
break;
case JavaFX:
retval = handleInJavaFXThread(event, targetInstance, wait);
break;
}
if (retval instanceof Boolean) {
return (Boolean) retval;
} else if (retval != null && retval.getClass().isPrimitive() && Boolean.TYPE.equals(retval.getClass())) {
return (boolean) retval;
}
}
return false;
}
protected void executeAsync(Object event, Object targetInstance, boolean wait) {
Future<?> future = service.submit((Runnable) () -> ReflectionUtil.invokeMethod(method, targetInstance, event));
if (wait) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
log.error("Could not execute event asynchronously ", e);
}
}
}
protected Object handleInJavaFXThread(Object event, Object targetInstance, boolean wait) {
if (Platform.isFxApplicationThread()) {
ReflectionUtil.invokeMethod(method, targetInstance, event);
} else {
FutureTask<Class<Void>> task = new FutureTask<>(() -> ReflectionUtil.invokeMethod(method, targetInstance, event), Void.class);
fxExecutor.submit(task);
if (wait) {
try {
task.get();
} catch (InterruptedException | ExecutionException e) {
log.error("Could not execute event in JavaFX thread", e);
}
}
}
return null;
}
}