Replace stub check detail description with info about check duration and count of warnings and critical errors

This commit is contained in:
Armin Schrenk
2021-05-31 15:28:30 +02:00
parent f74b9d3347
commit 186c129a30
4 changed files with 148 additions and 6 deletions

View File

@@ -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<Object> 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<Object> arg2Property() {
return arg2;
}
public Object getArg2() {
return arg2.get();
}
public void setArg2(Object arg2) {
this.arg2.set(arg2);
}
}

View File

@@ -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<ObservableList<DiagnosticResult>, WarnAndErrorEntry> cachedWarnAndErrorCounts;
private final Binding<ObservableList<DiagnosticResult>> results;
private final OptionalBinding<Worker.State> taskState;
private final Binding<String> taskName;
private final Binding<Number> taskDuration;
private final ResultListCellFactory resultListCellFactory;
private final BooleanBinding producingResults;
private final LongProperty numOfWarnings;
private final LongProperty numOfErrors;
public ListView<DiagnosticResult> resultsListView;
@Inject
public CheckDetailController(ObjectProperty<HealthCheckTask> 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<? extends HealthCheckTask> 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<? extends DiagnosticResult> 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<? extends DiagnosticResult> 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<Number> 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;
}
}

View File

@@ -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<Void> {
private final SecureRandom csprng;
private final HealthCheck check;
private final ObservableList<DiagnosticResult> 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<Void> {
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<Void> {
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<Void> {
return check;
}
public LongProperty durationInMillisProperty() {
return durationInMillis;
}
public long getDurationInMillis() {
return durationInMillis.get();
}
}

View File

@@ -1,15 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Text?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import org.cryptomator.ui.controls.FormattedLabel2?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.health.CheckDetailController"
spacing="6">
<FormattedLabel fx:id="checkTitle" styleClass="label-large" format="%health.check.result.header" arg1="${controller.taskName}"/>
<Text fx:id="checkDescription" styleClass="label" text="FIXME: Here could be a small description."/>
<ListView fx:id="resultsListView"/>
<Label text="The check is currently running..." visible="${controller.producingResults}" managed="${controller.producingResults}"/>
<!-- TODO: what if the check was canceled? Should be indicated in ui -->
<FormattedLabel fx:id="checkDescription" styleClass="label" format="The check finished after %d milliseconds." arg1="${controller.taskDuration}" visible="${!controller.producingResults}" managed="${!controller.producingResults}"/>
<FormattedLabel2 styleClass="label" format="Found %d Warnings and %d unfixable Errors." arg1="${controller.numOfWarnings}" arg2="${controller.numOfErrors}" />
<ListView fx:id="resultsListView" VBox.vgrow="ALWAYS"/>
</VBox>