/******************************************************************************* * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). * All rights reserved. * * This class is licensed under the LGPL 3.0 (https://www.gnu.org/licenses/lgpl-3.0.de.html). *******************************************************************************/ package org.cryptomator.launcher; import java.io.File; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.List; import java.util.concurrent.BlockingQueue; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class FileOpenRequestHandler { private static final Logger LOG = LoggerFactory.getLogger(FileOpenRequestHandler.class); private final BlockingQueue<Path> fileOpenRequests; public FileOpenRequestHandler(BlockingQueue<Path> fileOpenRequests) { this.fileOpenRequests = fileOpenRequests; if (SystemUtils.IS_OS_MAC_OSX) { addOsxFileOpenHandler(); } } public void handleLaunchArgs(String[] args) { handleLaunchArgs(FileSystems.getDefault(), args); } // visible for testing void handleLaunchArgs(FileSystem fs, String[] args) { for (String arg : args) { try { Path path = fs.getPath(arg); tryToEnqueueFileOpenRequest(path); } catch (InvalidPathException e) { LOG.trace("{} not a valid path", arg); } } } private void tryToEnqueueFileOpenRequest(Path path) { if (!fileOpenRequests.offer(path)) { LOG.warn("{} could not be enqueued for opening.", path); } } /** * Event subscription code inspired by https://gitlab.com/axet/desktop/blob/master/java/src/main/java/com/github/axet/desktop/os/mac/AppleHandlers.java */ private void addOsxFileOpenHandler() { try { final Class<?> applicationClass = Class.forName("com.apple.eawt.Application"); final Class<?> openFilesHandlerClass = Class.forName("com.apple.eawt.OpenFilesHandler"); final Method getApplication = applicationClass.getMethod("getApplication"); final Object application = getApplication.invoke(null); final Method setOpenFileHandler = applicationClass.getMethod("setOpenFileHandler", openFilesHandlerClass); final ClassLoader openFilesHandlerClassLoader = openFilesHandlerClass.getClassLoader(); final OpenFilesEventInvocationHandler openFilesHandler = new OpenFilesEventInvocationHandler(); final Object openFilesHandlerObject = Proxy.newProxyInstance(openFilesHandlerClassLoader, new Class<?>[] {openFilesHandlerClass}, openFilesHandler); setOpenFileHandler.invoke(application, openFilesHandlerObject); } catch (ReflectiveOperationException | RuntimeException e) { // Since we're trying to call OS-specific code, we'll just have to hope for the best. LOG.error("Exception adding OS X file open handler", e); } } /** * Handler class inspired by https://gitlab.com/axet/desktop/blob/master/java/src/main/java/com/github/axet/desktop/os/mac/AppleHandlers.java */ private class OpenFilesEventInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("openFiles")) { final Class<?> openFilesEventClass = Class.forName("com.apple.eawt.AppEvent$OpenFilesEvent"); final Method getFiles = openFilesEventClass.getMethod("getFiles"); Object e = args[0]; try { @SuppressWarnings("unchecked") final List<File> ff = (List<File>) getFiles.invoke(e); ff.stream().map(File::toPath).forEach(fileOpenRequests::add); } catch (RuntimeException ee) { throw ee; } catch (Exception ee) { throw new RuntimeException(ee); } } return null; } } }