/******************************************************************************* * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). * All rights reserved. This program and the accompanying materials * are made available under the terms of the accompanying LICENSE file. *******************************************************************************/ package org.cryptomator.ui.util; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import javax.inject.Inject; import javax.inject.Singleton; import org.cryptomator.common.ConsumerThrowingException; import org.cryptomator.common.RunnableThrowingException; import org.cryptomator.common.SupplierThrowingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javafx.application.Platform; @Singleton public class AsyncTaskService { private static final Logger LOG = LoggerFactory.getLogger(AsyncTaskService.class); private final ExecutorService executor; @Inject public AsyncTaskService(ExecutorService executor) { this.executor = executor; } public AsyncTaskWithoutSuccessHandler<Void> asyncTaskOf(RunnableThrowingException<?> task) { return new AsyncTaskImpl<>(() -> { task.run(); return null; }); } public <ResultType> AsyncTaskWithoutSuccessHandler<ResultType> asyncTaskOf(SupplierThrowingException<ResultType, ?> task) { return new AsyncTaskImpl<>(task); } private class AsyncTaskImpl<ResultType> implements AsyncTaskWithoutSuccessHandler<ResultType> { private final SupplierThrowingException<ResultType, ?> task; private ConsumerThrowingException<ResultType, ?> successHandler = value -> { }; private final List<ErrorHandler<Throwable>> errorHandlers = new ArrayList<>(); private RunnableThrowingException<?> finallyHandler = () -> { }; public AsyncTaskImpl(SupplierThrowingException<ResultType, ?> task) { this.task = task; } @Override public AsyncTaskWithoutErrorHandler onSuccess(ConsumerThrowingException<ResultType, ?> handler) { successHandler = handler; return this; } @Override public AsyncTaskWithoutErrorHandler onSuccess(RunnableThrowingException<?> handler) { return onSuccess(result -> handler.run()); } @SuppressWarnings({"unchecked", "rawtypes"}) @Override public <ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> handler) { errorHandlers.add((ErrorHandler) new ErrorHandler<>(type, handler)); return this; } @Override public <ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, RunnableThrowingException<?> handler) { return onError(type, error -> handler.run()); } @Override public AsyncTask andFinally(RunnableThrowingException<?> handler) { finallyHandler = handler; return this; } @Override public void run() { errorHandlers.add(ErrorHandler.LOGGING_HANDLER); executor.execute(() -> logExceptions(() -> { try { ResultType result = task.get(); Platform.runLater(() -> { try { successHandler.accept(result); } catch (Throwable e) { LOG.error("Uncaught exception", e); } }); } catch (Throwable e) { ErrorHandler<Throwable> errorHandler = errorHandlerFor(e); Platform.runLater(toRunnableLoggingException(() -> errorHandler.accept(e))); } finally { Platform.runLater(toRunnableLoggingException(finallyHandler)); } })); } private ErrorHandler<Throwable> errorHandlerFor(Throwable e) { return errorHandlers.stream().filter(handler -> handler.handles(e)).findFirst().get(); } } private static Runnable toRunnableLoggingException(RunnableThrowingException<?> delegate) { return () -> logExceptions(delegate); } private static void logExceptions(RunnableThrowingException<?> delegate) { try { delegate.run(); } catch (Throwable e) { LOG.error("Uncaught exception", e); } } private static class ErrorHandler<ErrorType> implements ConsumerThrowingException<ErrorType, Throwable> { public static final ErrorHandler<Throwable> LOGGING_HANDLER = new ErrorHandler<Throwable>(Throwable.class, error -> { LOG.error("Uncaught exception", error); }); private final Class<ErrorType> type; private final ConsumerThrowingException<ErrorType, ?> delegate; public ErrorHandler(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> delegate) { this.type = type; this.delegate = delegate; } public boolean handles(Throwable error) { return type.isInstance(error); } @Override public void accept(ErrorType error) throws Throwable { delegate.accept(error); } } public interface AsyncTaskWithoutSuccessHandler<ResultType> extends AsyncTaskWithoutErrorHandler { AsyncTaskWithoutErrorHandler onSuccess(ConsumerThrowingException<ResultType, ?> handler); AsyncTaskWithoutErrorHandler onSuccess(RunnableThrowingException<?> handler); } public interface AsyncTaskWithoutErrorHandler extends AsyncTaskWithoutFinallyHandler { <ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> handler); <ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, RunnableThrowingException<?> handler); } public interface AsyncTaskWithoutFinallyHandler extends AsyncTask { AsyncTask andFinally(RunnableThrowingException<?> handler); } public interface AsyncTask extends Runnable { } }