Skip to content

Commit

Permalink
Refactored "file open events" to "app launch events", fixes #55
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter committed Feb 20, 2019
1 parent 8814372 commit debcab4
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 59 deletions.
Expand Up @@ -4,10 +4,10 @@
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;
Expand All @@ -23,8 +23,8 @@ Settings provideSettings(SettingsProvider settingsProvider) {

@Provides
@Singleton
@Named("fileOpenRequests")
BlockingQueue<Path> provideFileOpenRequests() {
@Named("launchEventQueue")
BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
return new ArrayBlockingQueue<>(10);
}

Expand Down
Expand Up @@ -13,8 +13,13 @@
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;

Expand All @@ -26,11 +31,11 @@
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) {
Expand All @@ -39,7 +44,9 @@ public FileOpenRequestHandler(@Named("fileOpenRequests") BlockingQueue<Path> fil
}

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) {
Expand All @@ -48,19 +55,22 @@ public void handleLaunchArgs(String[] args) {

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

Expand Down
Expand Up @@ -5,72 +5,69 @@
*******************************************************************************/
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;

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

}
Expand Up @@ -50,6 +50,7 @@
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;
Expand Down Expand Up @@ -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<>();
Expand All @@ -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;
Expand Down Expand Up @@ -249,12 +250,12 @@ private void loadFont(String resourcePath) {
}

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

Expand Down
15 changes: 15 additions & 0 deletions 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;
}
}

0 comments on commit debcab4

Please sign in to comment.