瀏覽代碼

Added I/O stats (work in progress)

Sebastian Stenzel 5 年之前
父節點
當前提交
f4ee8d0a15

+ 1 - 1
.idea/runConfigurations/Cryptomator_macOS.xml

@@ -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>

+ 4 - 0
main/commons/pom.xml

@@ -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>

+ 20 - 17
main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java

@@ -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};
 	}

+ 15 - 2
main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java

@@ -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();

+ 6 - 0
main/commons/src/main/java/org/cryptomator/common/vaults/VaultState.java

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

+ 102 - 0
main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java

@@ -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();
+	}
+}

+ 5 - 0
main/pom.xml

@@ -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>

+ 4 - 3
main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java

@@ -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();

+ 6 - 5
main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java

@@ -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;

+ 5 - 4
main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java

@@ -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);
 	}

+ 2 - 1
main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java

@@ -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;

+ 2 - 1
main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java

@@ -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);

+ 5 - 4
main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java

@@ -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);
 	}

+ 12 - 6
main/ui/src/main/resources/fxml/vault_detail.fxml

@@ -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"/>