From 186c129a300d87f0feb672d02f39899c2d6664a0 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 31 May 2021 15:28:30 +0200 Subject: [PATCH] Replace stub check detail description with info about check duration and count of warnings and critical errors --- .../ui/controls/FormattedLabel2.java | 39 ++++++++ .../ui/health/CheckDetailController.java | 88 ++++++++++++++++++- .../ui/health/HealthCheckTask.java | 16 ++++ .../resources/fxml/health_check_details.fxml | 11 ++- 4 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/controls/FormattedLabel2.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/FormattedLabel2.java b/main/ui/src/main/java/org/cryptomator/ui/controls/FormattedLabel2.java new file mode 100644 index 000000000..426a6bc4f --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/controls/FormattedLabel2.java @@ -0,0 +1,39 @@ +package org.cryptomator.ui.controls; + +import javafx.beans.binding.Bindings; +import javafx.beans.binding.StringBinding; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + +public class FormattedLabel2 extends FormattedLabel { + + private final ObjectProperty arg2 = new SimpleObjectProperty<>(); + + public FormattedLabel2() { + textProperty().unbind(); + textProperty().bind(createStringBinding2()); + } + + private StringBinding createStringBinding2() { + return Bindings.createStringBinding(this::updateText, formatProperty(), arg1Property(), arg2); + } + + private String updateText() { + return String.format(getFormat(), getArg1(), arg2.get()); + } + + /* Getter & Setter */ + + public ObjectProperty arg2Property() { + return arg2; + } + + public Object getArg2() { + return arg2.get(); + } + + public void setArg2(Object arg2) { + this.arg2.set(arg2); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/health/CheckDetailController.java b/main/ui/src/main/java/org/cryptomator/ui/health/CheckDetailController.java index 12f6338e3..adbd12e2e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/health/CheckDetailController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/health/CheckDetailController.java @@ -8,37 +8,85 @@ import org.cryptomator.ui.common.FxController; import javax.inject.Inject; import javafx.beans.binding.Binding; import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.LongProperty; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.concurrent.Worker; import javafx.fxml.FXML; import javafx.scene.control.ListView; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; @HealthCheckScoped public class CheckDetailController implements FxController { + private final Map, WarnAndErrorEntry> cachedWarnAndErrorCounts; private final Binding> results; private final OptionalBinding taskState; private final Binding taskName; + private final Binding taskDuration; private final ResultListCellFactory resultListCellFactory; private final BooleanBinding producingResults; + private final LongProperty numOfWarnings; + private final LongProperty numOfErrors; public ListView resultsListView; @Inject public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory) { + selectedTask.addListener(this::rebindWarnAndErrorCount); this.results = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::results).orElse(FXCollections.emptyObservableList()); this.taskState = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::stateProperty); this.taskName = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::getTitle).orElse(""); + this.taskDuration = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::durationInMillisProperty).orElse(-1L); this.resultListCellFactory = resultListCellFactory; this.producingResults = taskState.filter(this::producesResults).isPresent(); + this.numOfWarnings = new SimpleLongProperty(0); + this.numOfErrors = new SimpleLongProperty(0); + this.cachedWarnAndErrorCounts = new IdentityHashMap<>(); //important to use an identity hashmap, because collections violate the immnutable hashkey contract + } + + private synchronized void rebindWarnAndErrorCount(ObservableValue observable, HealthCheckTask oldVal, HealthCheckTask newVal) { + //create and cache properites for the newList, if not already present + final var listToUpdate = newVal.results(); + cachedWarnAndErrorCounts.computeIfAbsent(listToUpdate, key -> { + var warnProperty = new SimpleLongProperty(countSeverityInList(listToUpdate, DiagnosticResult.Severity.WARN)); + var errProperty = new SimpleLongProperty(countSeverityInList(listToUpdate, DiagnosticResult.Severity.CRITICAL)); + return new WarnAndErrorEntry(warnProperty, errProperty); + }); + listToUpdate.addListener(this::updateListSpecificWarnAndErrorCount); + + //updateBindings + numOfErrors.bind(cachedWarnAndErrorCounts.get(listToUpdate).errorCount); + numOfWarnings.bind(cachedWarnAndErrorCounts.get(listToUpdate).warningCount); + } + + private synchronized void updateListSpecificWarnAndErrorCount(ListChangeListener.Change c) { + long tmpErr = cachedWarnAndErrorCounts.get(c.getList()).errorCount.get(); + long tmpWarn = cachedWarnAndErrorCounts.get(c.getList()).warningCount.get(); + while (c.next()) { + if (c.wasAdded()) { + tmpWarn += countSeverityInList(c.getAddedSubList(), DiagnosticResult.Severity.WARN); + tmpErr += countSeverityInList(c.getAddedSubList(), DiagnosticResult.Severity.CRITICAL); + } + } + cachedWarnAndErrorCounts.get(c.getList()).errorCount.set(tmpErr); + cachedWarnAndErrorCounts.get(c.getList()).warningCount.set(tmpWarn); + } + + private long countSeverityInList(List list, DiagnosticResult.Severity severityToCount) { + return list.stream().map(DiagnosticResult::getServerity).filter(severityToCount::equals).count(); } private boolean producesResults(Worker.State state) { return switch (state) { - case SCHEDULED, RUNNING, SUCCEEDED -> true; - case READY, CANCELLED, FAILED -> false; + case SCHEDULED, RUNNING -> true; + case READY, SUCCEEDED, CANCELLED, FAILED -> false; }; } @@ -65,4 +113,40 @@ public class CheckDetailController implements FxController { public BooleanBinding producingResultsProperty() { return producingResults; } + + public Number getTaskDuration() { + return taskDuration.getValue(); + } + + public Binding taskDurationProperty() { + return taskDuration; + } + + public long getNumOfWarnings() { + return numOfWarnings.get(); + } + + public LongProperty numOfWarningsProperty() { + return numOfWarnings; + } + + public long getNumOfErrors() { + return numOfErrors.get(); + } + + public LongProperty numOfErrorsProperty() { + return numOfErrors; + } + + private static class WarnAndErrorEntry { + + WarnAndErrorEntry(LongProperty warningCount, LongProperty errorCount) { + this.warningCount = warningCount; + this.errorCount = errorCount; + } + + final LongProperty warningCount; + final LongProperty errorCount; + } + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java b/main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java index 36543c4bc..7acbfc1c2 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java +++ b/main/ui/src/main/java/org/cryptomator/ui/health/HealthCheckTask.java @@ -8,11 +8,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javafx.application.Platform; +import javafx.beans.property.LongProperty; +import javafx.beans.property.SimpleLongProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.concurrent.Task; import java.nio.file.Path; import java.security.SecureRandom; +import java.time.Duration; +import java.time.Instant; import java.util.MissingResourceException; import java.util.Objects; import java.util.ResourceBundle; @@ -28,6 +32,7 @@ class HealthCheckTask extends Task { private final SecureRandom csprng; private final HealthCheck check; private final ObservableList results; + private final LongProperty durationInMillis; public HealthCheckTask(Path vaultPath, VaultConfig vaultConfig, Masterkey masterkey, SecureRandom csprng, HealthCheck check, ResourceBundle resourceBundle) { this.vaultPath = Objects.requireNonNull(vaultPath); @@ -42,10 +47,12 @@ class HealthCheckTask extends Task { LOG.warn("Missing proper name for health check {}, falling back to default.", check.identifier()); updateTitle(check.identifier()); } + this.durationInMillis = new SimpleLongProperty(-1); } @Override protected Void call() { + Instant start = Instant.now(); try (var masterkeyClone = masterkey.clone(); // var cryptor = vaultConfig.getCipherCombo().getCryptorProvider(csprng).withKey(masterkeyClone)) { check.check(vaultPath, vaultConfig, masterkeyClone, cryptor, result -> { @@ -66,6 +73,7 @@ class HealthCheckTask extends Task { Platform.runLater(() -> results.add(result)); }); } + Platform.runLater(() ->durationInMillis.set(Duration.between(start, Instant.now()).toMillis())); return null; } @@ -89,4 +97,12 @@ class HealthCheckTask extends Task { return check; } + public LongProperty durationInMillisProperty() { + return durationInMillis; + } + + public long getDurationInMillis() { + return durationInMillis.get(); + } + } diff --git a/main/ui/src/main/resources/fxml/health_check_details.fxml b/main/ui/src/main/resources/fxml/health_check_details.fxml index d93b47212..3ff0b7b98 100644 --- a/main/ui/src/main/resources/fxml/health_check_details.fxml +++ b/main/ui/src/main/resources/fxml/health_check_details.fxml @@ -1,15 +1,18 @@ + - - + - - + \ No newline at end of file