Added I/O stats (work in progress)

This commit is contained in:
Sebastian Stenzel
2019-08-27 15:50:15 +02:00
parent 71e414ae5c
commit f4ee8d0a15
14 changed files with 189 additions and 44 deletions

View File

@@ -2,8 +2,8 @@
<configuration default="false" name="Cryptomator macOS" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.mountPointsDir=&quot;/Volumes/&quot; -Xss2m -Xmx512m" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.mountPointsDir=&quot;/Volumes/&quot; -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -37,6 +37,10 @@
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
</dependency>
<!-- EasyBind -->
<dependency>

View File

@@ -48,7 +48,7 @@ import java.util.function.Predicate;
@PerVault
public class Vault {
public static final Predicate<Vault> NOT_LOCKED = hasState(State.LOCKED).negate();
public static final Predicate<Vault> NOT_LOCKED = hasState(VaultState.LOCKED).negate();
private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator";
private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME);
@@ -56,8 +56,9 @@ public class Vault {
private final VaultSettings vaultSettings;
private final Provider<Volume> volumeProvider;
private final StringBinding defaultMountFlags;
private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
private final ObjectProperty<State> state = new SimpleObjectProperty<State>(State.LOCKED);
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
private final ObjectProperty<VaultState> state ;
private final VaultStats stats;
private final ObjectProperty<Path> accessPoint = new SimpleObjectProperty<>(Path.of(""));
private final StringBinding displayableName;
private final StringBinding displayablePath;
@@ -67,16 +68,14 @@ public class Vault {
private Volume volume;
public enum State {
LOCKED, PROCESSING, UNLOCKED
}
@Inject
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags) {
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, ObjectProperty<VaultState> state, VaultStats stats) {
this.vaultSettings = vaultSettings;
this.volumeProvider = volumeProvider;
this.defaultMountFlags = defaultMountFlags;
this.cryptoFileSystem = cryptoFileSystem;
this.state = state;
this.stats = stats;
this.displayableName = Bindings.createStringBinding(this::getDisplayableName, vaultSettings.path());
this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path());
this.locked = Bindings.createBooleanBinding(this::isLocked, state);
@@ -166,7 +165,7 @@ public class Vault {
volume.reveal();
}
public static Predicate<Vault> hasState(State state) {
public static Predicate<Vault> hasState(VaultState state) {
return vault -> {
return vault.getState() == state;
};
@@ -176,15 +175,15 @@ public class Vault {
// Observable Properties
// *******************************************************************************
public ObjectProperty<State> stateProperty() {
public ObjectProperty<VaultState> stateProperty() {
return state;
}
public State getState() {
public VaultState getState() {
return state.get();
}
public void setState(State value) {
public void setState(VaultState value) {
state.setValue(value);
}
@@ -193,7 +192,7 @@ public class Vault {
}
public boolean isLocked() {
return state.get() == State.LOCKED;
return state.get() == VaultState.LOCKED;
}
public BooleanBinding processingProperty() {
@@ -201,7 +200,7 @@ public class Vault {
}
public boolean isProcessing() {
return state.get() == State.PROCESSING;
return state.get() == VaultState.PROCESSING;
}
public BooleanBinding unlockedProperty() {
@@ -209,7 +208,7 @@ public class Vault {
}
public boolean isUnlocked() {
return state.get() == State.UNLOCKED;
return state.get() == VaultState.UNLOCKED;
}
public StringBinding displayableNameProperty() {
@@ -230,7 +229,7 @@ public class Vault {
}
private void setAccessPoint(Observable obs) {
if (this.getState() == State.UNLOCKED) {
if (this.getState() == VaultState.UNLOCKED) {
accessPoint.setValue(volume.getMountPointSafe().get());
} else {
accessPoint.setValue(Path.of(""));
@@ -256,6 +255,10 @@ public class Vault {
// Getter/Setter
// *******************************************************************************/
public VaultStats getStats() {
return stats;
}
public Observable[] observables() {
return new Observable[]{state};
}

View File

@@ -8,18 +8,18 @@ package org.cryptomator.common.vaults;
import dagger.Module;
import dagger.Provides;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
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.cryptofs.CryptoFileSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -27,12 +27,25 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;
@Module
public class VaultModule {
private static final Logger LOG = LoggerFactory.getLogger(VaultModule.class);
@Provides
@PerVault
public AtomicReference<CryptoFileSystem> provideCryptoFileSystemReference() {
return new AtomicReference<>();
}
@Provides
@PerVault
public ObjectProperty<VaultState> provideVaultState() {
return new SimpleObjectProperty<>(VaultState.LOCKED);
}
@Provides
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();

View File

@@ -0,0 +1,6 @@
package org.cryptomator.common.vaults;
public enum VaultState {
LOCKED, PROCESSING, UNLOCKED;
}

View File

@@ -0,0 +1,102 @@
package org.cryptomator.common.vaults;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.cryptofs.CryptoFileSystemStats;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
@PerVault
public class VaultStats {
private static final Logger LOG = LoggerFactory.getLogger(VaultStats.class);
private final AtomicReference<CryptoFileSystem> fs;
private final ObjectProperty<VaultState> state;
private final ScheduledService<Optional<CryptoFileSystemStats>> updateService;
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
@Inject
VaultStats(AtomicReference<CryptoFileSystem> fs, ObjectProperty<VaultState> state, ExecutorService executor) {
this.fs = fs;
this.state = state;
this.updateService = new UpdateStatsService();
updateService.setExecutor(executor);
updateService.setPeriod(Duration.seconds(1));
state.addListener(this::vaultStateChanged);
}
private void vaultStateChanged(@SuppressWarnings("unused") Observable observable) {
switch (state.get()) {
case UNLOCKED:
assert fs.get() != null;
LOG.debug("start recording stats");
updateService.start();
break;
case LOCKED:
LOG.debug("stop recording stats");
updateService.cancel();
break;
default:
break;
}
}
private void updateStats(Optional<CryptoFileSystemStats> stats) {
assert Platform.isFxApplicationThread();
bytesPerSecondRead.set(stats.map(CryptoFileSystemStats::pollBytesRead).orElse(0l));
bytesPerSecondWritten.set(stats.map(CryptoFileSystemStats::pollBytesWritten).orElse(0l));
}
private class UpdateStatsService extends ScheduledService<Optional<CryptoFileSystemStats>> {
@Override
protected Task<Optional<CryptoFileSystemStats>> createTask() {
return new Task<>() {
@Override
protected Optional<CryptoFileSystemStats> call() {
return Optional.ofNullable(fs.get()).map(CryptoFileSystem::getStats);
}
};
}
@Override
protected void succeeded() {
assert getValue() != null;
updateStats(getValue());
super.succeeded();
}
}
/* Observables */
public LongProperty bytesPerSecondReadProperty() {
return bytesPerSecondRead;
}
public long getBytesPerSecondRead() {
return bytesPerSecondRead.get();
}
public LongProperty bytesPerSecondWrittenProperty() {
return bytesPerSecondWritten;
}
public long getBytesPerSecondWritten() {
return bytesPerSecondWritten.get();
}
}

View File

@@ -119,6 +119,11 @@
<artifactId>javafx-base</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>

View File

@@ -44,6 +44,7 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.ui.ExitUtil;
@@ -100,9 +101,9 @@ public class MainController implements ViewController {
private final ObservableList<Vault> vaults;
private final BooleanBinding areAllVaultsLocked;
private final ObjectProperty<Vault> selectedVault = new SimpleObjectProperty<>();
private final ObjectExpression<Vault.State> selectedVaultState = ObjectExpression.objectExpression(EasyBind.select(selectedVault).selectObject(Vault::stateProperty));
private final ObjectExpression<VaultState> selectedVaultState = ObjectExpression.objectExpression(EasyBind.select(selectedVault).selectObject(Vault::stateProperty));
private final BooleanExpression isSelectedVaultValid = BooleanExpression.booleanExpression(EasyBind.monadic(selectedVault).map(Vault::isValidVaultDirectory).orElse(false));
private final BooleanExpression canEditSelectedVault = selectedVaultState.isEqualTo(Vault.State.LOCKED);
private final BooleanExpression canEditSelectedVault = selectedVaultState.isEqualTo(VaultState.LOCKED);
private final MonadicBinding<UpgradeStrategy> upgradeStrategyForSelectedVault;
private final BooleanBinding isShowingSettings;
private final Map<Vault, UnlockedController> unlockedVaults = new HashMap<>();
@@ -410,7 +411,7 @@ public class MainController implements ViewController {
if (newValue == null) {
return;
}
if (newValue.getState() != Vault.State.LOCKED) {
if (newValue.getState() != VaultState.LOCKED) {
this.showUnlockedView(newValue, false);
} else if (!newValue.doesVaultDirectoryExist()) {
this.showNotFoundView();

View File

@@ -9,6 +9,7 @@
package org.cryptomator.ui.controls;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.fxmisc.easybind.EasyBind;
import javafx.beans.binding.ObjectExpression;
@@ -35,7 +36,7 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
private ContextMenu vaultContextMenu;
public DirectoryListCell() {
ObjectExpression<Vault.State> vaultState = ObjectExpression.objectExpression(EasyBind.select(itemProperty()).selectObject(Vault::stateProperty));
ObjectExpression<VaultState> vaultState = ObjectExpression.objectExpression(EasyBind.select(itemProperty()).selectObject(Vault::stateProperty));
hbox.setAlignment(Pos.CENTER_LEFT);
hbox.setPrefWidth(1);
@@ -62,7 +63,7 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
private String getStatusIconText(Vault.State state) {
private String getStatusIconText(VaultState state) {
if (state == null) {
return "";
}
@@ -76,7 +77,7 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
}
}
private Paint getStatusIconColor(Vault.State state, Paint lockedValue) {
private Paint getStatusIconColor(VaultState state, Paint lockedValue) {
if (state == null) {
return lockedValue;
}
@@ -90,8 +91,8 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
}
}
private ContextMenu getContextMenu(Vault.State state) {
if (state == Vault.State.LOCKED) {
private ContextMenu getContextMenu(VaultState state) {
if (state == VaultState.LOCKED) {
return vaultContextMenu;
} else {
return null;

View File

@@ -8,6 +8,7 @@ import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.input.MouseEvent;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
import org.cryptomator.ui.changepassword.ChangePasswordComponent;
import org.cryptomator.ui.common.FxController;
@@ -46,7 +47,7 @@ public class VaultDetailController implements FxController {
this.anyVaultSelected = vault.isNotNull();
}
private FontAwesome5Icon getGlyphForVaultState(Vault.State state) {
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
switch (state) {
case LOCKED:
return FontAwesome5Icon.LOCK_ALT;
@@ -67,14 +68,14 @@ public class VaultDetailController implements FxController {
@FXML
public void lock() {
Vault v = vault.get();
v.setState(Vault.State.PROCESSING);
v.setState(VaultState.PROCESSING);
Tasks.create(() -> {
v.lock(false);
}).onSuccess(() -> {
LOG.trace("Regular unmount succeeded.");
v.setState(Vault.State.LOCKED);
v.setState(VaultState.LOCKED);
}).onError(Exception.class, e -> {
v.setState(Vault.State.UNLOCKED);
v.setState(VaultState.UNLOCKED);
// TODO
}).runOnce(executor);
}

View File

@@ -3,6 +3,7 @@ package org.cryptomator.ui.mainwindow;
import javafx.beans.binding.Binding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.controls.FontAwesome5Icon;
@@ -21,7 +22,7 @@ public class VaultListCellController implements FxController {
this.glyph = EasyBind.select(vault).selectObject(Vault::stateProperty).map(this::getGlyphForVaultState).orElse(FontAwesome5Icon.EXCLAMATION_TRIANGLE);
}
private FontAwesome5Icon getGlyphForVaultState(Vault.State state) {
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
switch (state) {
case LOCKED:
return FontAwesome5Icon.LOCK_ALT;

View File

@@ -9,6 +9,7 @@ import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.stage.Stage;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
import org.cryptomator.ui.common.FxController;
import org.slf4j.Logger;
@@ -83,7 +84,7 @@ public class QuitController implements FxController {
}
};
task.setOnSucceeded(evt -> {
vault.setState(Vault.State.LOCKED);
vault.setState(VaultState.LOCKED);
});
task.setOnFailed(evt -> {
LOG.warn("Failed to lock vault", vault);

View File

@@ -15,6 +15,7 @@ import javafx.scene.control.ContentDisplay;
import javafx.stage.Stage;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
import org.cryptomator.keychain.KeychainAccess;
@@ -70,7 +71,7 @@ public class UnlockController implements FxController {
} else {
savePassword.setDisable(true);
}
unlockButtonDisabled.bind(vault.stateProperty().isNotEqualTo(Vault.State.LOCKED).or(passwordField.textProperty().isEmpty()));
unlockButtonDisabled.bind(vault.stateProperty().isNotEqualTo(VaultState.LOCKED).or(passwordField.textProperty().isEmpty()));
}
@FXML
@@ -83,14 +84,14 @@ public class UnlockController implements FxController {
public void unlock() {
LOG.trace("UnlockController.unlock()");
CharSequence password = passwordField.getCharacters();
vault.setState(Vault.State.PROCESSING);
vault.setState(VaultState.PROCESSING);
Tasks.create(() -> {
vault.unlock(password);
if (keychainAccess.isPresent() && savePassword.isSelected()) {
keychainAccess.get().storePassphrase(vault.getId(), password);
}
}).onSuccess(() -> {
vault.setState(Vault.State.UNLOCKED);
vault.setState(VaultState.UNLOCKED);
passwordField.swipe();
LOG.info("Unlock of '{}' succeeded.", vault.getDisplayableName());
window.setScene(successScene.get());
@@ -110,7 +111,7 @@ public class UnlockController implements FxController {
// TODO
}).andFinally(() -> {
if (!vault.isUnlocked()) {
vault.setState(Vault.State.LOCKED);
vault.setState(VaultState.LOCKED);
}
}).runOnce(executor);
}

View File

@@ -62,15 +62,21 @@
<Region HBox.hgrow="ALWAYS"/>
<Label styleClass="button-group-action" text="TODO REVEAL"/>
</HBox>
<Region styleClass="button-group-separator"/>
<HBox styleClass="button-group,last" alignment="CENTER">
<VBox styleClass="button-group-labels">
<Label styleClass="button-group-heading" text="example heading"/>
<Label text="example text"/>
<HBox styleClass="button-group,last" spacing="24" alignment="CENTER">
<VBox styleClass="button-group-labels" HBox.hgrow="ALWAYS">
<Label styleClass="button-group-heading" text="TODO Read"/>
<Label text="${controller.vault.stats.bytesPerSecondRead}"/>
</VBox>
<VBox styleClass="button-group-labels" HBox.hgrow="ALWAYS">
<Label styleClass="button-group-heading" text="TODO Written"/>
<Label text="${controller.vault.stats.bytesPerSecondWritten}"/>
</VBox>
<!-- Future use:
<Region HBox.hgrow="ALWAYS"/>
<Label styleClass="button-group-action" text="EXAMPLE ACTION"/>
<Label styleClass="button-group-action" text="SHOW CHART"/>
-->
</HBox>
<Region styleClass="button-group-separator"/>
</VBox>
<Region prefHeight="24" VBox.vgrow="NEVER"/>