mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-20 09:36:55 -04:00
Replaced AsyncTaskService by new Tasks utility using javafx.concurrent API
This commit is contained in:
@@ -8,41 +8,21 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import javax.inject.Inject;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.apache.commons.lang3.CharUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.settings.VolumeImpl;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
||||
import org.cryptomator.keychain.KeychainAccess;
|
||||
import org.cryptomator.ui.controls.SecPasswordField;
|
||||
import org.cryptomator.ui.l10n.Localization;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.model.WindowsDriveLetters;
|
||||
import org.cryptomator.ui.util.AsyncTaskService;
|
||||
import org.cryptomator.ui.util.DialogBuilderUtil;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -59,8 +39,28 @@ import javafx.scene.control.ProgressIndicator;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.StringConverter;
|
||||
import org.apache.commons.lang3.CharUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.settings.VolumeImpl;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
||||
import org.cryptomator.keychain.KeychainAccess;
|
||||
import org.cryptomator.ui.controls.SecPasswordField;
|
||||
import org.cryptomator.ui.l10n.Localization;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.model.WindowsDriveLetters;
|
||||
import org.cryptomator.ui.util.DialogBuilderUtil;
|
||||
import org.cryptomator.ui.util.Tasks;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class UnlockController implements ViewController {
|
||||
|
||||
@@ -73,23 +73,23 @@ public class UnlockController implements ViewController {
|
||||
|
||||
private final Application app;
|
||||
private final Localization localization;
|
||||
private final AsyncTaskService asyncTaskService;
|
||||
private final WindowsDriveLetters driveLetters;
|
||||
private final ChangeListener<Character> driveLetterChangeListener = this::winDriveLetterDidChange;
|
||||
private final Optional<KeychainAccess> keychainAccess;
|
||||
private final Settings settings;
|
||||
private final ExecutorService executor;
|
||||
private Vault vault;
|
||||
private Optional<UnlockListener> listener = Optional.empty();
|
||||
private Subscription vaultSubs = Subscription.EMPTY;
|
||||
|
||||
@Inject
|
||||
public UnlockController(Application app, Localization localization, AsyncTaskService asyncTaskService, WindowsDriveLetters driveLetters, Optional<KeychainAccess> keychainAccess, Settings settings) {
|
||||
public UnlockController(Application app, Localization localization, WindowsDriveLetters driveLetters, Optional<KeychainAccess> keychainAccess, Settings settings, ExecutorService executor) {
|
||||
this.app = app;
|
||||
this.localization = localization;
|
||||
this.asyncTaskService = asyncTaskService;
|
||||
this.driveLetters = driveLetters;
|
||||
this.keychainAccess = keychainAccess;
|
||||
this.settings = settings;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -423,7 +423,7 @@ public class UnlockController implements ViewController {
|
||||
progressIndicator.setVisible(true);
|
||||
|
||||
CharSequence password = passwordField.getCharacters();
|
||||
asyncTaskService.asyncTaskOf(() -> {
|
||||
Tasks.create(() -> {
|
||||
vault.unlock(password);
|
||||
if (keychainAccess.isPresent() && savePassword.isSelected()) {
|
||||
keychainAccess.get().storePassphrase(vault.getId(), password);
|
||||
@@ -450,16 +450,16 @@ public class UnlockController implements ViewController {
|
||||
}).onError(ServerLifecycleException.class, e -> {
|
||||
LOG.error("Unlock failed for technical reasons.", e);
|
||||
messageText.setText(localization.getString("unlock.errorMessage.unlockFailed"));
|
||||
}).onError(IOException.class, e -> {
|
||||
LOG.error("Unlock failed for technical reasons.", e);
|
||||
messageText.setText(localization.getString("unlock.errorMessage.unlockFailed"));
|
||||
}).onError(Exception.class, e -> {
|
||||
LOG.error("Unlock failed for technical reasons.", e);
|
||||
messageText.setText(localization.getString("unlock.errorMessage.unlockFailed"));
|
||||
}).andFinally(() -> {
|
||||
if (!savePassword.isSelected()) {
|
||||
passwordField.swipe();
|
||||
}
|
||||
advancedOptions.setDisable(false);
|
||||
progressIndicator.setVisible(false);
|
||||
}).run();
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
/* callback */
|
||||
|
||||
@@ -8,14 +8,13 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.beans.binding.ObjectExpression;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -35,11 +34,10 @@ import javafx.scene.control.ToggleButton;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.PopupWindow.AnchorLocation;
|
||||
import javafx.util.Duration;
|
||||
import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException;
|
||||
import org.cryptomator.ui.l10n.Localization;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.util.AsyncTaskService;
|
||||
import org.cryptomator.ui.util.DialogBuilderUtil;
|
||||
import org.cryptomator.ui.util.Tasks;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -54,9 +52,8 @@ public class UnlockedController implements ViewController {
|
||||
private static final double IO_SAMPLING_INTERVAL = 0.25;
|
||||
|
||||
private final Localization localization;
|
||||
private final AsyncTaskService asyncTaskService;
|
||||
private final ExecutorService executor;
|
||||
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||
private final ObjectExpression<Vault.State> vaultState = ObjectExpression.objectExpression(EasyBind.select(vault).selectObject(Vault::stateProperty));
|
||||
private Optional<LockListener> listener = Optional.empty();
|
||||
private Timeline ioAnimation;
|
||||
|
||||
@@ -79,9 +76,9 @@ public class UnlockedController implements ViewController {
|
||||
private VBox root;
|
||||
|
||||
@Inject
|
||||
public UnlockedController(Localization localization, AsyncTaskService asyncTaskService) {
|
||||
public UnlockedController(Localization localization, ExecutorService executor) {
|
||||
this.localization = localization;
|
||||
this.asyncTaskService = asyncTaskService;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,14 +112,14 @@ public class UnlockedController implements ViewController {
|
||||
}
|
||||
|
||||
private void regularLockVault(Runnable onSuccess) {
|
||||
asyncTaskService.asyncTaskOf(() -> {
|
||||
Tasks.create(() -> {
|
||||
vault.get().lock(false);
|
||||
}).onSuccess(() -> {
|
||||
LOG.trace("Regular unmount succeeded.");
|
||||
onSuccess.run();
|
||||
}).onError(Exception.class, e -> {
|
||||
onRegularUnmountVaultFailed(e, onSuccess);
|
||||
}).run();
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
private void onRegularUnmountVaultFailed(Exception e, Runnable onSuccess) {
|
||||
@@ -147,7 +144,7 @@ public class UnlockedController implements ViewController {
|
||||
}
|
||||
|
||||
private void forcedLockVault(Runnable onSuccess) {
|
||||
asyncTaskService.asyncTaskOf(() -> {
|
||||
Tasks.create(() -> {
|
||||
vault.get().lock(true);
|
||||
}).onSuccess(() -> {
|
||||
LOG.trace("Forced unmount succeeded.");
|
||||
@@ -155,7 +152,7 @@ public class UnlockedController implements ViewController {
|
||||
}).onError(Exception.class, e -> {
|
||||
LOG.error("Forced unmount failed.", e);
|
||||
messageLabel.setText(localization.getString("unlocked.label.unmountFailed"));
|
||||
}).run();
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -174,15 +171,15 @@ public class UnlockedController implements ViewController {
|
||||
}
|
||||
|
||||
void revealVault(Vault vault) {
|
||||
asyncTaskService.asyncTaskOf(() -> {
|
||||
Tasks.create(() -> {
|
||||
vault.reveal();
|
||||
}).onSuccess(() -> {
|
||||
LOG.trace("Reveal succeeded.");
|
||||
messageLabel.setText(null);
|
||||
}).onError(CommandFailedException.class, e -> {
|
||||
}).onError(Exception.class, e -> {
|
||||
LOG.error("Reveal failed.", e);
|
||||
messageLabel.setText(localization.getString("unlocked.label.revealFailed"));
|
||||
}).run();
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
|
||||
@@ -5,19 +5,10 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.cryptomator.ui.controls.SecPasswordField;
|
||||
import org.cryptomator.ui.l10n.Localization;
|
||||
import org.cryptomator.ui.model.UpgradeStrategies;
|
||||
import org.cryptomator.ui.model.UpgradeStrategy;
|
||||
import org.cryptomator.ui.model.UpgradeStrategy.UpgradeFailedException;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.util.AsyncTaskService;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javafx.beans.binding.BooleanExpression;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
@@ -30,19 +21,26 @@ import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import org.cryptomator.ui.controls.SecPasswordField;
|
||||
import org.cryptomator.ui.model.UpgradeStrategies;
|
||||
import org.cryptomator.ui.model.UpgradeStrategy;
|
||||
import org.cryptomator.ui.model.UpgradeStrategy.UpgradeFailedException;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.util.Tasks;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
public class UpgradeController implements ViewController {
|
||||
|
||||
private final ObjectProperty<UpgradeStrategy> strategy = new SimpleObjectProperty<>();
|
||||
private final UpgradeStrategies strategies;
|
||||
private final AsyncTaskService asyncTaskService;
|
||||
private final ExecutorService executor;
|
||||
private Optional<UpgradeListener> listener = Optional.empty();
|
||||
private Vault vault;
|
||||
|
||||
@Inject
|
||||
public UpgradeController(Localization localization, UpgradeStrategies strategies, AsyncTaskService asyncTaskService) {
|
||||
public UpgradeController(UpgradeStrategies strategies, ExecutorService executor) {
|
||||
this.strategies = strategies;
|
||||
this.asyncTaskService = asyncTaskService;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -122,8 +120,8 @@ public class UpgradeController implements ViewController {
|
||||
private void upgrade(UpgradeStrategy instruction) {
|
||||
passwordField.setDisable(true);
|
||||
progressIndicator.setVisible(true);
|
||||
asyncTaskService //
|
||||
.asyncTaskOf(() -> {
|
||||
Tasks //
|
||||
.create(() -> {
|
||||
if (!instruction.isApplicable(vault)) {
|
||||
throw new IllegalStateException("No ugprade needed for " + vault.getPath());
|
||||
}
|
||||
@@ -137,7 +135,7 @@ public class UpgradeController implements ViewController {
|
||||
progressIndicator.setVisible(false);
|
||||
passwordField.setDisable(false);
|
||||
passwordField.swipe();
|
||||
}).run();
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
private void showNextUpgrade() {
|
||||
|
||||
@@ -8,6 +8,20 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
@@ -28,23 +42,10 @@ import jdk.incubator.http.HttpResponse;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.ui.l10n.Localization;
|
||||
import org.cryptomator.ui.util.AsyncTaskService;
|
||||
import org.cryptomator.ui.util.Tasks;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.cryptomator.ui.util.DialogBuilderUtil.buildYesNoDialog;
|
||||
|
||||
@Singleton
|
||||
@@ -57,17 +58,17 @@ public class WelcomeController implements ViewController {
|
||||
private final Localization localization;
|
||||
private final Settings settings;
|
||||
private final Comparator<String> semVerComparator;
|
||||
private final AsyncTaskService asyncTaskService;
|
||||
private final ScheduledExecutorService executor;
|
||||
|
||||
@Inject
|
||||
public WelcomeController(Application app, @Named("applicationVersion") Optional<String> applicationVersion, Localization localization, Settings settings, @Named("SemVer") Comparator<String> semVerComparator,
|
||||
AsyncTaskService asyncTaskService) {
|
||||
ScheduledExecutorService executor) {
|
||||
this.app = app;
|
||||
this.applicationVersion = applicationVersion;
|
||||
this.localization = localization;
|
||||
this.settings = settings;
|
||||
this.semVerComparator = semVerComparator;
|
||||
this.asyncTaskService = asyncTaskService;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -110,7 +111,7 @@ public class WelcomeController implements ViewController {
|
||||
}
|
||||
|
||||
private void askForUpdateCheck() {
|
||||
asyncTaskService.runDelayedOnUiThread(1, TimeUnit.SECONDS, () -> {
|
||||
Tasks.create(() -> {}).onSuccess(() -> {
|
||||
Optional<ButtonType> result = buildYesNoDialog(
|
||||
localization.getString("welcome.askForUpdateCheck.dialog.title"),
|
||||
localization.getString("welcome.askForUpdateCheck.dialog.header"),
|
||||
@@ -123,13 +124,13 @@ public class WelcomeController implements ViewController {
|
||||
if (settings.checkForUpdates().get()) {
|
||||
this.checkForUpdates();
|
||||
}
|
||||
});
|
||||
}).scheduleOnce(executor, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void checkForUpdates() {
|
||||
checkForUpdatesStatus.setText(localization.getString("welcome.checkForUpdates.label.currentlyChecking"));
|
||||
checkForUpdatesIndicator.setVisible(true);
|
||||
asyncTaskService.asyncTaskOf(() -> {
|
||||
Tasks.create(() -> {
|
||||
String userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", applicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
@@ -138,7 +139,8 @@ public class WelcomeController implements ViewController {
|
||||
.header("User-Agent", userAgent)
|
||||
.timeout(Duration.ofSeconds(5))
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8));
|
||||
return client.send(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8));
|
||||
}).onSuccess(response -> {
|
||||
if (response.statusCode() == 200) {
|
||||
Gson gson = new GsonBuilder().setLenient().create();
|
||||
Map<String, String> map = gson.fromJson(response.body(), new TypeToken<Map<String, String>>() {
|
||||
@@ -147,13 +149,16 @@ public class WelcomeController implements ViewController {
|
||||
this.compareVersions(map);
|
||||
}
|
||||
}
|
||||
}).onError(Exception.class, e -> {
|
||||
LOG.error("Error checking for updates", e);
|
||||
}).andFinally(() -> {
|
||||
checkForUpdatesStatus.setText("");
|
||||
checkForUpdatesIndicator.setVisible(false);
|
||||
}).run();
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
private void compareVersions(final Map<String, String> latestVersions) {
|
||||
assert Platform.isFxApplicationThread();
|
||||
final String latestVersion;
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
latestVersion = latestVersions.get("mac");
|
||||
@@ -169,11 +174,9 @@ public class WelcomeController implements ViewController {
|
||||
LOG.info("Current version: {}, lastest version: {}", currentVersion, latestVersion);
|
||||
if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) {
|
||||
final String msg = String.format(localization.getString("welcome.newVersionMessage"), latestVersion, currentVersion);
|
||||
Platform.runLater(() -> {
|
||||
this.updateLink.setText(msg);
|
||||
this.updateLink.setVisible(true);
|
||||
this.updateLink.setDisable(false);
|
||||
});
|
||||
this.updateLink.setText(msg);
|
||||
this.updateLink.setVisible(true);
|
||||
this.updateLink.setDisable(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* 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.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import org.cryptomator.common.ConsumerThrowingException;
|
||||
import org.cryptomator.common.RunnableThrowingException;
|
||||
import org.cryptomator.common.SupplierThrowingException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
@Singleton
|
||||
public class AsyncTaskService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AsyncTaskService.class);
|
||||
|
||||
private final ScheduledExecutorService executor;
|
||||
|
||||
@Inject
|
||||
public AsyncTaskService(ScheduledExecutorService executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new async task
|
||||
*
|
||||
* @param task Tasks to be invoked in a background thread.
|
||||
* @return The async task
|
||||
*/
|
||||
public AsyncTaskWithoutSuccessHandler<Void> asyncTaskOf(RunnableThrowingException<?> task) {
|
||||
return new AsyncTaskImpl<>(() -> {
|
||||
task.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void runDelayedOnUiThread(long duration, TimeUnit unit, RunnableThrowingException<?> task) {
|
||||
asyncTaskOf(() -> null).onSuccess(task).runDelayedBy(duration, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new async task
|
||||
*
|
||||
* @param task Tasks to be invoked in a background thread.
|
||||
* @return The async task
|
||||
*/
|
||||
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() {
|
||||
runDelayedBy(0, MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runDelayedBy(long duration, TimeUnit unit) {
|
||||
requireNonNull(unit, "unit must not be null");
|
||||
if (duration < 0) {
|
||||
throw new IllegalArgumentException("duration must not be negative");
|
||||
}
|
||||
errorHandlers.add(ErrorHandler.LOGGING_HANDLER);
|
||||
executor.schedule(() -> 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));
|
||||
}
|
||||
}), duration, unit);
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||
* @return The async task
|
||||
*/
|
||||
AsyncTaskWithoutErrorHandler onSuccess(ConsumerThrowingException<ResultType, ?> handler);
|
||||
|
||||
/**
|
||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||
* @return The async task
|
||||
*/
|
||||
AsyncTaskWithoutErrorHandler onSuccess(RunnableThrowingException<?> handler);
|
||||
|
||||
}
|
||||
|
||||
public interface AsyncTaskWithoutErrorHandler extends AsyncTaskWithoutFinallyHandler {
|
||||
|
||||
/**
|
||||
* @param type Exception type to catch
|
||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||
* @return The async task
|
||||
*/
|
||||
<ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> handler);
|
||||
|
||||
/**
|
||||
* @param type Exception type to catch
|
||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||
* @return The async task
|
||||
*/
|
||||
<ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, RunnableThrowingException<?> handler);
|
||||
|
||||
}
|
||||
|
||||
public interface AsyncTaskWithoutFinallyHandler extends AsyncTask {
|
||||
|
||||
/**
|
||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
||||
* @return The async task
|
||||
*/
|
||||
AsyncTask andFinally(RunnableThrowingException<?> handler);
|
||||
|
||||
}
|
||||
|
||||
public interface AsyncTask extends Runnable {
|
||||
|
||||
/**
|
||||
* Starts the async task.
|
||||
*/
|
||||
@Override
|
||||
void run();
|
||||
|
||||
/**
|
||||
* Starts the async task delayed by {@code duration unit}
|
||||
*/
|
||||
void runDelayedBy(long duration, TimeUnit unit);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
192
main/ui/src/main/java/org/cryptomator/ui/util/Tasks.java
Normal file
192
main/ui/src/main/java/org/cryptomator/ui/util/Tasks.java
Normal file
@@ -0,0 +1,192 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.concurrent.ScheduledService;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.util.Duration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Tasks {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Tasks.class);
|
||||
public static <T> TaskBuilder<T> create(Callable<T> callable) {
|
||||
return new TaskBuilder<>(callable);
|
||||
}
|
||||
|
||||
public static TaskBuilder<Void> create(VoidCallable callable) {
|
||||
return create(() -> {
|
||||
callable.call();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static class TaskBuilder<T> {
|
||||
|
||||
private final Callable<T> callable;
|
||||
private Consumer<T> successHandler = x -> {
|
||||
};
|
||||
private List<ErrorHandler<?>> errorHandlers = new ArrayList<>();
|
||||
private Runnable finallyHandler = () -> {};
|
||||
|
||||
TaskBuilder(Callable<T> callable) {
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
public TaskBuilder<T> onSuccess(Runnable successHandler) {
|
||||
this.successHandler = x -> {
|
||||
successHandler.run();
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskBuilder<T> onSuccess(Consumer<T> successHandler) {
|
||||
this.successHandler = successHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public <E extends Throwable> TaskBuilder<T> onError(Class<E> type, Consumer<E> exceptionHandler) {
|
||||
errorHandlers.add(new ErrorHandler<>(type, exceptionHandler));
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskBuilder<T> andFinally(Runnable finallyHandler) {
|
||||
this.finallyHandler = finallyHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Task<T> build() {
|
||||
return new TaskImpl<>(callable, successHandler, errorHandlers, finallyHandler);
|
||||
}
|
||||
|
||||
public Task<T> runOnce(ExecutorService executorService) {
|
||||
Task<T> task = build();
|
||||
executorService.submit(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public Task<T> scheduleOnce(ScheduledExecutorService executorService, long delay, TimeUnit unit) {
|
||||
Task<T> task = build();
|
||||
executorService.schedule(task, delay, unit);
|
||||
return task;
|
||||
}
|
||||
|
||||
public ScheduledService<T> schedulePeriodically(ExecutorService executorService, Duration initialDelay, Duration period) {
|
||||
ScheduledService<T> service = new RestartingService<>(this::build);
|
||||
service.setExecutor(executorService);
|
||||
service.setDelay(initialDelay);
|
||||
service.setPeriod(period);
|
||||
Platform.runLater(service::start);
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ErrorHandler<ErrorType extends Throwable> {
|
||||
|
||||
private final Class<ErrorType> type;
|
||||
private final Consumer<ErrorType> errorHandler;
|
||||
|
||||
public ErrorHandler(Class<ErrorType> type, Consumer<ErrorType> errorHandler) {
|
||||
this.type = type;
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
public boolean handles(Throwable error) {
|
||||
return type.isInstance(error);
|
||||
}
|
||||
|
||||
public void handle(Throwable error) throws ClassCastException {
|
||||
ErrorType typedError = type.cast(error);
|
||||
errorHandler.accept(typedError);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter between java.util.function.* and javafx.concurrent.*
|
||||
*/
|
||||
private static class TaskImpl<T> extends Task<T> {
|
||||
|
||||
private final Callable<T> callable;
|
||||
private final Consumer<T> successHandler;
|
||||
private List<ErrorHandler<?>> errorHandlers;
|
||||
private final Runnable finallyHandler;
|
||||
|
||||
TaskImpl(Callable<T> callable, Consumer<T> successHandler, List<ErrorHandler<?>> errorHandlers, Runnable finallyHandler) {
|
||||
this.callable = callable;
|
||||
this.successHandler = successHandler;
|
||||
this.errorHandlers = errorHandlers;
|
||||
this.finallyHandler = finallyHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T call() throws Exception {
|
||||
return callable.call();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
try {
|
||||
successHandler.accept(getValue());
|
||||
} finally {
|
||||
finallyHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed() {
|
||||
try {
|
||||
Throwable exception = getException();
|
||||
errorHandlers.stream().filter(handler -> handler.handles(exception)).findFirst().ifPresentOrElse(exceptionHandler -> {
|
||||
exceptionHandler.handle(exception);
|
||||
}, () -> {
|
||||
LOG.error("Unhandled exception", exception);
|
||||
});
|
||||
} finally {
|
||||
finallyHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A service that keeps executing the next task long as tasks are succeeding.
|
||||
*/
|
||||
private static class RestartingService<T> extends ScheduledService<T> {
|
||||
|
||||
private final Supplier<Task<T>> taskFactory;
|
||||
|
||||
RestartingService(Supplier<Task<T>> taskFactory) {
|
||||
this.taskFactory = taskFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<T> createTask() {
|
||||
return taskFactory.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface VoidCallable {
|
||||
|
||||
void call() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user