From 8a243a01aa31dcc1ac7ad9c7a5e4f31b407329ea Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 6 Nov 2025 15:35:12 +0100 Subject: [PATCH] cleanup + error handling --- .../ui/mainwindow/MainWindowController.java | 2 +- .../ui/preferences/AboutController.java | 2 +- .../ui/preferences/PreferencesController.java | 2 +- .../UpdatesPreferencesController.java | 33 +++++++++++++++---- .../UpdateReminderController.java | 2 +- .../DelegatingHttpClient.java | 2 +- .../updater/FallbackUpdateMechanism.java | 11 ++----- .../updater/MacOsDmgUpdateMechanism.java | 3 +- .../{ui/fxapp => updater}/UpdateChecker.java | 14 +++----- .../UpdateCheckerHttpClient.java | 5 +-- .../cryptomator/updater/UpdateService.java | 13 ++++++++ .../resources/fxml/preferences_updates.fxml | 6 ++-- src/main/resources/i18n/strings.properties | 2 ++ 13 files changed, 59 insertions(+), 38 deletions(-) rename src/main/java/org/cryptomator/{ui/fxapp => updater}/DelegatingHttpClient.java (98%) rename src/main/java/org/cryptomator/{ui/fxapp => updater}/UpdateChecker.java (95%) rename src/main/java/org/cryptomator/{ui/fxapp => updater}/UpdateCheckerHttpClient.java (95%) diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java index c6e084518..a642dcee9 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java @@ -7,7 +7,7 @@ import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.fxapp.FxApplicationWindows; -import org.cryptomator.ui.fxapp.UpdateChecker; +import org.cryptomator.updater.UpdateChecker; import org.cryptomator.ui.preferences.SelectedPreferencesTab; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/cryptomator/ui/preferences/AboutController.java b/src/main/java/org/cryptomator/ui/preferences/AboutController.java index 1f1b1864b..0598e0d38 100644 --- a/src/main/java/org/cryptomator/ui/preferences/AboutController.java +++ b/src/main/java/org/cryptomator/ui/preferences/AboutController.java @@ -3,7 +3,7 @@ package org.cryptomator.ui.preferences; import com.google.common.io.CharStreams; import org.cryptomator.common.Environment; import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.fxapp.UpdateChecker; +import org.cryptomator.updater.UpdateChecker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java index ad3d08491..57d087643 100644 --- a/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java @@ -2,7 +2,7 @@ package org.cryptomator.ui.preferences; import org.cryptomator.common.Environment; import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.fxapp.UpdateChecker; +import org.cryptomator.updater.UpdateChecker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index 072b46f8c..89c03800e 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -6,7 +6,7 @@ import org.cryptomator.common.vaults.Vault; import org.cryptomator.integrations.update.UpdateStep; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.VaultService; -import org.cryptomator.ui.fxapp.UpdateChecker; +import org.cryptomator.updater.UpdateChecker; import org.cryptomator.updater.FallbackUpdateInfo; import org.cryptomator.updater.UpdateService; import org.slf4j.Logger; @@ -22,7 +22,10 @@ import javafx.beans.binding.BooleanExpression; import javafx.beans.binding.ObjectBinding; import javafx.beans.binding.StringBinding; import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; import javafx.concurrent.Worker; @@ -60,9 +63,9 @@ public class UpdatesPreferencesController implements FxController { private final ObjectBinding updateButtonState; private final ObservableValue timeDifferenceMessage; private final StringBinding lastUpdateCheckMessage; - private final BooleanBinding prohibitUpdateWhileUnlocked; private final BooleanBinding updateButtonDisabled; + private final StringProperty errorMessage = new SimpleStringProperty(""); private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false); /* FXML */ @@ -76,6 +79,7 @@ public class UpdatesPreferencesController implements FxController { this.settings = settings; this.updateChecker = updateChecker; this.updateService = new UpdateService(updateChecker.updateProperty()); + this.unlockedVaults = vaults.filtered(Vault::isUnlocked); this.vaultService = vaultService; this.worker = Bindings.when(updateChecker.updateAvailableProperty()).>then(this.updateService).otherwise(this.updateChecker); this.running = Bindings.createBooleanBinding(this::isRunning, updateService.stateProperty(), updateChecker.stateProperty()); @@ -83,7 +87,6 @@ public class UpdatesPreferencesController implements FxController { this.updateButtonState = Bindings.createObjectBinding(this::getUpdateButtonState, updateChecker.stateProperty(), updateService.stateProperty()); this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, updateChecker.lastSuccessfulUpdateCheckProperty()); this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, updateChecker.lastSuccessfulUpdateCheckProperty()); - this.unlockedVaults = vaults.filtered(Vault::isUnlocked); this.prohibitUpdateWhileUnlocked = Bindings.createBooleanBinding(this::isProhibitUpdateWhileUnlocked, unlockedVaults, updateChecker.updateProperty()); this.updateButtonDisabled = Bindings.when(worker.isEqualTo(updateChecker)).then(running).otherwise(prohibitUpdateWhileUnlocked.or(running)); } @@ -98,6 +101,7 @@ public class UpdatesPreferencesController implements FxController { delay.play(); } }); + updateChecker.setOnFailed(this::checkFailed); updateService.setOnSucceeded(this::updateSucceeded); updateService.setOnFailed(this::updateFailed); } @@ -119,6 +123,12 @@ public class UpdatesPreferencesController implements FxController { } } + private void checkFailed(WorkerStateEvent workerStateEvent) { + assert workerStateEvent.getSource() == updateChecker; + LOG.error("Update check failed.", updateChecker.getException()); + errorMessage.set(resourceBundle.getString("preferences.updates.checkFailed")); + } + private void updateSucceeded(WorkerStateEvent workerStateEvent) { assert workerStateEvent.getSource() == updateService; var lastStep = updateService.getValue(); @@ -138,9 +148,10 @@ public class UpdatesPreferencesController implements FxController { private void updateFailed(WorkerStateEvent workerStateEvent) { assert workerStateEvent.getSource() == updateService; LOG.error("Update failed.", updateService.getException()); + updateService.reset(); + errorMessage.set(resourceBundle.getString("preferences.updates.updateFailed")); // try fallback mechanism: updateChecker.recheckWithFallbackMechanism(); - updateService.reset(); } @FXML @@ -180,9 +191,9 @@ public class UpdatesPreferencesController implements FxController { } else { return switch (updateService.getState()) { case READY -> updateChecker.getUpdate().updateMechanism().getName(); - case SCHEDULED, RUNNING -> updateService.getMessage(); // "Preparing Update..."; // TODO: resourceBundle.getString("preferences.updates.preparingUpdate")... - case SUCCEEDED -> "Restart to Update"; // TODO: resourceBundle.getString("preferences.updates.readyToRestart")... - case FAILED, CANCELLED -> "failed"; + case SCHEDULED, RUNNING -> updateService.getMessage(); + case SUCCEEDED -> resourceBundle.getString("generic.button.done"); + case FAILED, CANCELLED -> "failed"; // should never be visible }; } } @@ -233,6 +244,14 @@ public class UpdatesPreferencesController implements FxController { } } + public String getErrorMessage() { + return errorMessage.get(); + } + + public ReadOnlyStringProperty errorMessageProperty() { + return errorMessage; + } + public boolean isProhibitUpdateWhileUnlocked() { // If the result of the last update check was from the fallback mechanism, we don't need to show the warning return !unlockedVaults.isEmpty() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate()); diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java index a6fce0b79..91b333279 100644 --- a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java @@ -2,7 +2,7 @@ package org.cryptomator.ui.updatereminder; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.fxapp.UpdateChecker; +import org.cryptomator.updater.UpdateChecker; import javax.inject.Inject; import javafx.fxml.FXML; diff --git a/src/main/java/org/cryptomator/ui/fxapp/DelegatingHttpClient.java b/src/main/java/org/cryptomator/updater/DelegatingHttpClient.java similarity index 98% rename from src/main/java/org/cryptomator/ui/fxapp/DelegatingHttpClient.java rename to src/main/java/org/cryptomator/updater/DelegatingHttpClient.java index d8ac6db3a..b86b72ed3 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/DelegatingHttpClient.java +++ b/src/main/java/org/cryptomator/updater/DelegatingHttpClient.java @@ -1,4 +1,4 @@ -package org.cryptomator.ui.fxapp; +package org.cryptomator.updater; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; diff --git a/src/main/java/org/cryptomator/updater/FallbackUpdateMechanism.java b/src/main/java/org/cryptomator/updater/FallbackUpdateMechanism.java index 055a96386..2fff9ab18 100644 --- a/src/main/java/org/cryptomator/updater/FallbackUpdateMechanism.java +++ b/src/main/java/org/cryptomator/updater/FallbackUpdateMechanism.java @@ -5,15 +5,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Environment; -import org.cryptomator.integrations.common.DisplayName; -import org.cryptomator.integrations.common.Priority; -import org.cryptomator.integrations.update.BasicUpdateInfo; -import org.cryptomator.integrations.update.UpdateInfo; +import org.cryptomator.integrations.common.LocalizedDisplayName; import org.cryptomator.integrations.update.UpdateMechanism; import org.cryptomator.integrations.update.UpdateStep; -import org.cryptomator.integrations.update.UpdateStepAdapter; import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,11 +23,9 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; @FxApplicationScoped -@Priority(Priority.FALLBACK) -@DisplayName("Show Download Page") // TODO localize +@LocalizedDisplayName(bundle = "i18n.strings", key = "preferences.updates.visitDownloadPage") public class FallbackUpdateMechanism implements UpdateMechanism { private static final Logger LOG = LoggerFactory.getLogger(FallbackUpdateMechanism.class); diff --git a/src/main/java/org/cryptomator/updater/MacOsDmgUpdateMechanism.java b/src/main/java/org/cryptomator/updater/MacOsDmgUpdateMechanism.java index 79b55b706..91205b610 100644 --- a/src/main/java/org/cryptomator/updater/MacOsDmgUpdateMechanism.java +++ b/src/main/java/org/cryptomator/updater/MacOsDmgUpdateMechanism.java @@ -110,8 +110,7 @@ public class MacOsDmgUpdateMechanism extends DownloadUpdateMechanism { } else if (selfPath.contains("/Cryptomator.app/")) { installPath = selfPath.substring(0, selfPath.indexOf("/Cryptomator.app/")) + "/Cryptomator.app"; } else { - installPath = "/Applications/Cryptomator.app"; - // throw new UpdateFailedException("Cannot determine destination path for Cryptomator.app, current path: " + selfPath); + throw new UpdateFailedException("Cannot determine destination path for Cryptomator.app, current path: " + selfPath); } LOG.info("Restarting to apply Update in {} now...", workDir); String script = """ diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/updater/UpdateChecker.java similarity index 95% rename from src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java rename to src/main/java/org/cryptomator/updater/UpdateChecker.java index 0b593ef94..9c1665e25 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java +++ b/src/main/java/org/cryptomator/updater/UpdateChecker.java @@ -1,11 +1,11 @@ -package org.cryptomator.ui.fxapp; +package org.cryptomator.updater; import org.cryptomator.common.Environment; import org.cryptomator.common.settings.Settings; import org.cryptomator.integrations.update.UpdateFailedException; import org.cryptomator.integrations.update.UpdateInfo; import org.cryptomator.integrations.update.UpdateMechanism; -import org.cryptomator.updater.FallbackUpdateMechanism; +import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,7 +19,6 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; import javafx.util.Duration; -import java.net.http.HttpClient; import java.time.Instant; import java.util.concurrent.Executors; @@ -46,19 +45,16 @@ public class UpdateChecker extends ScheduledService> { private final BooleanBinding updateAvailable = update.isNotNull(); private final ObjectBinding updateState = Bindings.createObjectBinding(this::getUpdateCheckState, stateProperty()); private final BooleanBinding checkFailed = Bindings.equal(UpdateCheckState.CHECK_FAILED, updateState); - private final HttpClient httpClient; private final UpdateMechanism fallbackUpdateMechanism; private UpdateMechanism updateMechanism; @Inject UpdateChecker(Settings settings, // Environment env, - FallbackUpdateMechanism fallbackUpdateMechanism, - UpdateCheckerHttpClient httpClient) { + FallbackUpdateMechanism fallbackUpdateMechanism) { this.env = env; this.settings = settings; this.lastSuccessfulUpdateCheck = settings.lastSuccessfulUpdateCheck; - this.httpClient = httpClient; this.fallbackUpdateMechanism = fallbackUpdateMechanism; // Prefer the safer fallback mechanism if the last update attempt was already made by this app version @@ -178,7 +174,7 @@ public class UpdateChecker extends ScheduledService> { @Override protected UpdateInfo call() { - try { + try (var httpClient = new UpdateCheckerHttpClient(env)) { var result = updateMechanism.checkForUpdate(env.getAppVersion(), httpClient); if (result != null) { return result; @@ -190,7 +186,7 @@ public class UpdateChecker extends ScheduledService> { return null; } LOG.debug("Trying fallback update check..."); - try { + try (var httpClient = new UpdateCheckerHttpClient(env)) { return fallbackUpdateMechanism.checkForUpdate(env.getAppVersion(), httpClient); } catch (UpdateFailedException e) { LOG.error("Fallback update check failed.", e); diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateCheckerHttpClient.java b/src/main/java/org/cryptomator/updater/UpdateCheckerHttpClient.java similarity index 95% rename from src/main/java/org/cryptomator/ui/fxapp/UpdateCheckerHttpClient.java rename to src/main/java/org/cryptomator/updater/UpdateCheckerHttpClient.java index 0a442c32a..4f32d1953 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateCheckerHttpClient.java +++ b/src/main/java/org/cryptomator/updater/UpdateCheckerHttpClient.java @@ -1,6 +1,5 @@ -package org.cryptomator.ui.fxapp; +package org.cryptomator.updater; -import jakarta.inject.Inject; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Environment; @@ -12,12 +11,10 @@ import java.net.http.HttpResponse; import java.time.Duration; import java.util.concurrent.CompletableFuture; -@FxApplicationScoped public class UpdateCheckerHttpClient extends DelegatingHttpClient { private final String userAgent; - @Inject public UpdateCheckerHttpClient(Environment env) { var delegate = HttpClient.newBuilder() // .followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes diff --git a/src/main/java/org/cryptomator/updater/UpdateService.java b/src/main/java/org/cryptomator/updater/UpdateService.java index 05c03036b..fbb2d75f4 100644 --- a/src/main/java/org/cryptomator/updater/UpdateService.java +++ b/src/main/java/org/cryptomator/updater/UpdateService.java @@ -4,6 +4,8 @@ import org.cryptomator.integrations.update.UpdateInfo; import org.cryptomator.integrations.update.UpdateMechanism; import org.cryptomator.integrations.update.UpdateStep; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; import javafx.beans.value.ObservableValue; import javafx.concurrent.Service; import javafx.concurrent.Task; @@ -18,6 +20,8 @@ import java.util.concurrent.TimeUnit; */ public class UpdateService extends Service { + private final BooleanBinding updateFailed = Bindings.equal(State.FAILED, stateProperty()); + private ObservableValue> updateInfo; public UpdateService(ObservableValue> updateInfo) { @@ -65,4 +69,13 @@ public class UpdateService extends Service { } } + /* Observable Properties */ + + public boolean isUpdateFailed() { + return updateFailed.get(); + } + + public BooleanBinding updateFailedProperty() { + return updateFailed; + } } diff --git a/src/main/resources/fxml/preferences_updates.fxml b/src/main/resources/fxml/preferences_updates.fxml index 8be624717..c1d2dd071 100644 --- a/src/main/resources/fxml/preferences_updates.fxml +++ b/src/main/resources/fxml/preferences_updates.fxml @@ -52,18 +52,20 @@ - + - + + +