瀏覽代碼

Refactored "file open events" to "app launch events", fixes #55

Sebastian Stenzel 6 年之前
父節點
當前提交
debcab47e2

+ 3 - 3
main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java

@@ -4,10 +4,10 @@ import dagger.Module;
 import dagger.Provides;
 import org.cryptomator.common.settings.Settings;
 import org.cryptomator.common.settings.SettingsProvider;
+import org.cryptomator.ui.model.AppLaunchEvent;
 
 import javax.inject.Named;
 import javax.inject.Singleton;
-import java.nio.file.Path;
 import java.util.Optional;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
@@ -23,8 +23,8 @@ class CryptomatorModule {
 
 	@Provides
 	@Singleton
-	@Named("fileOpenRequests")
-	BlockingQueue<Path> provideFileOpenRequests() {
+	@Named("launchEventQueue")
+	BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
 		return new ArrayBlockingQueue<>(10);
 	}
 

+ 22 - 12
main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java

@@ -13,8 +13,13 @@ import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Objects;
 import java.util.concurrent.BlockingQueue;
+import java.util.function.Function;
+import java.util.stream.Stream;
 
+import org.cryptomator.ui.model.AppLaunchEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -26,11 +31,11 @@ import javax.inject.Singleton;
 class FileOpenRequestHandler {
 
 	private static final Logger LOG = LoggerFactory.getLogger(FileOpenRequestHandler.class);
-	private final BlockingQueue<Path> fileOpenRequests;
+	private final BlockingQueue<AppLaunchEvent> launchEventQueue;
 
 	@Inject
-	public FileOpenRequestHandler(@Named("fileOpenRequests") BlockingQueue<Path> fileOpenRequests) {
-		this.fileOpenRequests = fileOpenRequests;
+	public FileOpenRequestHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue) {
+		this.launchEventQueue = launchEventQueue;
 		try {
 			Desktop.getDesktop().setOpenFileHandler(this::openFiles);
 		} catch (UnsupportedOperationException e) {
@@ -39,7 +44,9 @@ class FileOpenRequestHandler {
 	}
 
 	private void openFiles(final OpenFilesEvent evt) {
-		evt.getFiles().stream().map(File::toPath).forEach(fileOpenRequests::add);
+		Stream<Path> pathsToOpen = evt.getFiles().stream().map(File::toPath);
+		AppLaunchEvent launchEvent = new AppLaunchEvent(pathsToOpen);
+		tryToEnqueueFileOpenRequest(launchEvent);
 	}
 
 	public void handleLaunchArgs(String[] args) {
@@ -48,19 +55,22 @@ class FileOpenRequestHandler {
 
 	// visible for testing
 	void handleLaunchArgs(FileSystem fs, String[] args) {
-		for (String arg : args) {
+		Stream<Path> pathsToOpen = Arrays.stream(args).map(str -> {
 			try {
-				Path path = fs.getPath(arg);
-				tryToEnqueueFileOpenRequest(path);
+				return fs.getPath(str);
 			} catch (InvalidPathException e) {
-				LOG.trace("{} not a valid path", arg);
+				LOG.trace("Argument not a valid path: {}", str);
+				return null;
 			}
-		}
+		}).filter(Objects::nonNull);
+		AppLaunchEvent launchEvent = new AppLaunchEvent(pathsToOpen);
+		tryToEnqueueFileOpenRequest(launchEvent);
 	}
 
-	private void tryToEnqueueFileOpenRequest(Path path) {
-		if (!fileOpenRequests.offer(path)) {
-			LOG.warn("{} could not be enqueued for opening.", path);
+
+	private void tryToEnqueueFileOpenRequest(AppLaunchEvent launchEvent) {
+		if (!launchEventQueue.offer(launchEvent)) {
+			LOG.warn("Could not enqueue application launch event.", launchEvent);
 		}
 	}
 

+ 36 - 39
main/launcher/src/test/java/org/cryptomator/launcher/FileOpenRequestHandlerTest.java

@@ -5,7 +5,13 @@
  *******************************************************************************/
 package org.cryptomator.launcher;
 
+import org.cryptomator.ui.model.AppLaunchEvent;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
@@ -13,64 +19,55 @@ import java.io.IOException;
 import java.nio.file.FileSystem;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.spi.FileSystemProvider;
+import java.nio.file.Paths;
+import java.util.List;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class FileOpenRequestHandlerTest {
 
+	private FileOpenRequestHandler inTest;
+	private BlockingQueue<AppLaunchEvent> queue;
+
+	@BeforeEach
+	public void setup() {
+		queue = new ArrayBlockingQueue<>(1);
+		inTest = new FileOpenRequestHandler(queue);
+	}
+
 	@Test
+	@DisplayName("./cryptomator.exe foo bar")
 	public void testOpenArgsWithCorrectPaths() throws IOException {
-		Path p1 = Mockito.mock(Path.class);
-		Path p2 = Mockito.mock(Path.class);
-		FileSystem fs = Mockito.mock(FileSystem.class);
-		FileSystemProvider provider = Mockito.mock(FileSystemProvider.class);
-		BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class);
-		Mockito.when(p1.getFileSystem()).thenReturn(fs);
-		Mockito.when(p2.getFileSystem()).thenReturn(fs);
-		Mockito.when(fs.provider()).thenReturn(provider);
-		Mockito.when(fs.getPath(Mockito.anyString())).thenReturn(p1, p2);
-		Mockito.when(provider.readAttributes(Mockito.any(), Mockito.eq(BasicFileAttributes.class))).thenReturn(attrs);
+		inTest.handleLaunchArgs(new String[]{"foo", "bar"});
 
-		BlockingQueue<Path> queue = new ArrayBlockingQueue<>(10);
-		FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
-		handler.handleLaunchArgs(fs, new String[] {"foo", "bar"});
-
-		Assertions.assertEquals(p1, queue.poll());
-		Assertions.assertEquals(p2, queue.poll());
+		AppLaunchEvent evt = queue.poll();
+		Assertions.assertNotNull(evt);
+		List<Path> paths = evt.getPathsToOpen().collect(Collectors.toList());
+		MatcherAssert.assertThat(paths, CoreMatchers.hasItems(Paths.get("foo"), Paths.get("bar")));
 	}
 
 	@Test
+	@DisplayName("./cryptomator.exe foo (with 'foo' being an invalid path)")
 	public void testOpenArgsWithIncorrectPaths() throws IOException {
 		FileSystem fs = Mockito.mock(FileSystem.class);
-		Mockito.when(fs.getPath(Mockito.anyString())).thenThrow(new InvalidPathException("foo", "foo is not a path"));
-
-		@SuppressWarnings("unchecked")
-		BlockingQueue<Path> queue = Mockito.mock(BlockingQueue.class);
-		FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
-		handler.handleLaunchArgs(fs, new String[] {"foo"});
+		Mockito.when(fs.getPath("foo")).thenThrow(new InvalidPathException("foo", "foo is not a path"));
+		inTest.handleLaunchArgs(fs, new String[]{"foo"});
 
-		Mockito.verifyNoMoreInteractions(queue);
+		AppLaunchEvent evt = queue.poll();
+		Assertions.assertNotNull(evt);
+		List<Path> paths = evt.getPathsToOpen().collect(Collectors.toList());
+		Assertions.assertTrue(paths.isEmpty());
 	}
 
 	@Test
+	@DisplayName("./cryptomator.exe foo (with full event queue)")
 	public void testOpenArgsWithFullQueue() throws IOException {
-		Path p = Mockito.mock(Path.class);
-		FileSystem fs = Mockito.mock(FileSystem.class);
-		FileSystemProvider provider = Mockito.mock(FileSystemProvider.class);
-		BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class);
-		Mockito.when(p.getFileSystem()).thenReturn(fs);
-		Mockito.when(fs.provider()).thenReturn(provider);
-		Mockito.when(fs.getPath(Mockito.anyString())).thenReturn(p);
-		Mockito.when(provider.readAttributes(Mockito.eq(p), Mockito.eq(BasicFileAttributes.class))).thenReturn(attrs);
-		Mockito.when(attrs.isRegularFile()).thenReturn(true);
+		queue.add(new AppLaunchEvent(Stream.empty()));
+		Assumptions.assumeTrue(queue.remainingCapacity() == 0);
 
-		@SuppressWarnings("unchecked")
-		BlockingQueue<Path> queue = Mockito.mock(BlockingQueue.class);
-		Mockito.when(queue.offer(Mockito.any())).thenReturn(false);
-		FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
-		handler.handleLaunchArgs(fs, new String[] {"foo"});
+		inTest.handleLaunchArgs(new String[]{"foo"});
 	}
 
 }

+ 6 - 5
main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java

@@ -50,6 +50,7 @@ import org.cryptomator.common.settings.VaultSettings;
 import org.cryptomator.ui.ExitUtil;
 import org.cryptomator.ui.controls.DirectoryListCell;
 import org.cryptomator.ui.l10n.Localization;
+import org.cryptomator.ui.model.AppLaunchEvent;
 import org.cryptomator.ui.model.AutoUnlocker;
 import org.cryptomator.ui.model.UpgradeStrategies;
 import org.cryptomator.ui.model.UpgradeStrategy;
@@ -93,7 +94,7 @@ public class MainController implements ViewController {
 	private final ExitUtil exitUtil;
 	private final Localization localization;
 	private final ExecutorService executorService;
-	private final BlockingQueue<Path> fileOpenRequests;
+	private final BlockingQueue<AppLaunchEvent> launchEventQueue;
 	private final VaultFactory vaultFactoy;
 	private final ViewControllerLoader viewControllerLoader;
 	private final ObjectProperty<ViewController> activeController = new SimpleObjectProperty<>();
@@ -110,11 +111,11 @@ public class MainController implements ViewController {
 	private Subscription subs = Subscription.EMPTY;
 
 	@Inject
-	public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("fileOpenRequests") BlockingQueue<Path> fileOpenRequests, ExitUtil exitUtil, Localization localization,
+	public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExitUtil exitUtil, Localization localization,
 						  VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker) {
 		this.mainWindow = mainWindow;
 		this.executorService = executorService;
-		this.fileOpenRequests = fileOpenRequests;
+		this.launchEventQueue = launchEventQueue;
 		this.exitUtil = exitUtil;
 		this.localization = localization;
 		this.vaultFactoy = vaultFactoy;
@@ -249,12 +250,12 @@ public class MainController implements ViewController {
 	}
 
 	private void listenToFileOpenRequests(Stage stage) {
-		Tasks.create(fileOpenRequests::take).onSuccess(path -> {
-			addVault(path, true);
+		Tasks.create(launchEventQueue::take).onSuccess(event -> {
 			stage.setIconified(false);
 			stage.show();
 			stage.toFront();
 			stage.requestFocus();
+			event.getPathsToOpen().forEach(path -> addVault(path, true));
 		}).schedulePeriodically(executorService, Duration.ZERO, Duration.ZERO);
 	}
 

+ 15 - 0
main/ui/src/main/java/org/cryptomator/ui/model/AppLaunchEvent.java

@@ -0,0 +1,15 @@
+package org.cryptomator.ui.model;
+
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+public class AppLaunchEvent {
+
+	private final Stream<Path> pathsToOpen;
+
+	public AppLaunchEvent(Stream<Path> pathsToOpen) {this.pathsToOpen = pathsToOpen;}
+
+	public Stream<Path> getPathsToOpen() {
+		return pathsToOpen;
+	}
+}