Browse Source

Merge pull request #1759 from cryptomator/feature/plugin-loader

Sebastian Stenzel 3 years ago
parent
commit
e7b9f73516

+ 3 - 0
.github/workflows/release.yml

@@ -107,6 +107,7 @@ jobs:
             --app-version "${{ needs.metadata.outputs.versionNum }}.${{ needs.metadata.outputs.revNum }}"
             --java-options "-Dfile.encoding=\"utf-8\""
             --java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\""
+            --java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\""
             --java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\""
             --java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\""
             --java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
@@ -119,6 +120,7 @@ jobs:
             --app-version "${{ needs.metadata.outputs.versionNum }}.${{ needs.metadata.outputs.revNum }}"
             --java-options "-Dfile.encoding=\"utf-8\""
             --java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
+            --java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
             --java-options "-Dcryptomator.settingsPath=\"~/AppData/Roaming/Cryptomator/settings.json\""
             --java-options "-Dcryptomator.ipcSocketPath=\"~/AppData/Roaming/Cryptomator/ipc.socket\""
             --java-options "-Dcryptomator.keychainPath=\"~/AppData/Roaming/Cryptomator/keychain.json\""
@@ -133,6 +135,7 @@ jobs:
             --app-version "${{ needs.metadata.outputs.versionNum }}"
             --java-options "-Dfile.encoding=\"utf-8\""
             --java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\""
+            --java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Cryptomator/Plugins\""
             --java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
             --java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
             --java-options "-Dcryptomator.showTrayIcon=true"

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

@@ -2,7 +2,7 @@
   <configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
     <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
     <module name="cryptomator" />
-    <option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
+    <option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
     <method v="2">
       <option name="Make" enabled="true" />
     </method>

File diff suppressed because it is too large
+ 1 - 1
.idea/runConfigurations/Cryptomator_Linux_Dev.xml


File diff suppressed because it is too large
+ 1 - 1
.idea/runConfigurations/Cryptomator_Windows.xml


File diff suppressed because it is too large
+ 1 - 1
.idea/runConfigurations/Cryptomator_Windows_Dev.xml


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

@@ -5,7 +5,7 @@
     </envs>
     <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
     <module name="cryptomator" />
-    <option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
+    <option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
     <method v="2">
       <option name="Make" enabled="true" />
     </method>

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

@@ -5,7 +5,7 @@
     </envs>
     <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
     <module name="cryptomator" />
-    <option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
+    <option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
     <method v="2">
       <option name="Make" enabled="true" />
     </method>

+ 5 - 0
src/main/java/org/cryptomator/common/Environment.java

@@ -36,6 +36,7 @@ public class Environment {
 		LOG.debug("cryptomator.ipcSocketPath: {}", System.getProperty("cryptomator.ipcSocketPath"));
 		LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath"));
 		LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir"));
+		LOG.debug("cryptomator.pluginDir: {}", System.getProperty("cryptomator.pluginDir"));
 		LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
 		LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength"));
 		LOG.debug("cryptomator.appVersion: {}", System.getProperty("cryptomator.appVersion"));
@@ -64,6 +65,10 @@ public class Environment {
 		return getPath("cryptomator.logDir").map(this::replaceHomeDir);
 	}
 
+	public Optional<Path> getPluginDir() {
+		return getPath("cryptomator.pluginDir").map(this::replaceHomeDir);
+	}
+
 	public Optional<Path> getMountPointsDir() {
 		return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
 	}

+ 66 - 0
src/main/java/org/cryptomator/common/PluginClassLoader.java

@@ -0,0 +1,66 @@
+package org.cryptomator.common;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.FileVisitOption;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+@Singleton
+public class PluginClassLoader extends URLClassLoader {
+
+	private static final Logger LOG = LoggerFactory.getLogger(PluginClassLoader.class);
+	private static final String NAME = "PluginClassLoader";
+	private static final String JAR_SUFFIX = ".jar";
+
+	@Inject
+	public PluginClassLoader(Environment env) {
+		super(NAME, env.getPluginDir().map(PluginClassLoader::findJars).orElse(new URL[0]), PluginClassLoader.class.getClassLoader());
+	}
+
+	private static URL[] findJars(Path path) {
+		if (!Files.isDirectory(path)) {
+			return new URL[0];
+		} else {
+			try {
+				var visitor = new JarVisitor();
+				Files.walkFileTree(path, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, visitor);
+				return visitor.urls.toArray(URL[]::new);
+			} catch (IOException e) {
+				LOG.warn("Failed to scan plugin dir " + path, e);
+				return new URL[0];
+			}
+		}
+	}
+
+	private static final class JarVisitor extends SimpleFileVisitor<Path> {
+
+		private final List<URL> urls = new ArrayList<>();
+
+		@Override
+		public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+			if (attrs.isRegularFile() && file.getFileName().toString().toLowerCase().endsWith(JAR_SUFFIX)) {
+				try {
+					urls.add(file.toUri().toURL());
+				} catch (MalformedURLException e) {
+					LOG.warn("Failed to create URL for jar file {}", file);
+				}
+			}
+			return FileVisitResult.CONTINUE;
+		}
+	}
+
+}

+ 3 - 2
src/main/java/org/cryptomator/common/keychain/KeychainModule.java

@@ -2,6 +2,7 @@ package org.cryptomator.common.keychain;
 
 import dagger.Module;
 import dagger.Provides;
+import org.cryptomator.common.PluginClassLoader;
 import org.cryptomator.common.settings.Settings;
 import org.cryptomator.integrations.keychain.KeychainAccessProvider;
 
@@ -17,8 +18,8 @@ public class KeychainModule {
 
 	@Provides
 	@Singleton
-	static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories() {
-		return ServiceLoader.load(KeychainAccessProvider.class).stream().collect(Collectors.toUnmodifiableSet());
+	static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) {
+		return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet());
 	}
 
 	@Provides

+ 7 - 6
src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java

@@ -2,6 +2,7 @@ package org.cryptomator.ui.launcher;
 
 import dagger.Module;
 import dagger.Provides;
+import org.cryptomator.common.PluginClassLoader;
 import org.cryptomator.integrations.autostart.AutoStartProvider;
 import org.cryptomator.integrations.tray.TrayIntegrationProvider;
 import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
@@ -33,21 +34,21 @@ public abstract class UiLauncherModule {
 
 	@Provides
 	@Singleton
-	static Optional<UiAppearanceProvider> provideAppearanceProvider() {
-		return ServiceLoader.load(UiAppearanceProvider.class).findFirst();
+	static Optional<UiAppearanceProvider> provideAppearanceProvider(PluginClassLoader classLoader) {
+		return ServiceLoader.load(UiAppearanceProvider.class, classLoader).findFirst();
 	}
 
 	@Provides
 	@Singleton
-	static Optional<AutoStartProvider> provideAutostartProvider() {
-		return ServiceLoader.load(AutoStartProvider.class).findFirst();
+	static Optional<AutoStartProvider> provideAutostartProvider(PluginClassLoader classLoader) {
+		return ServiceLoader.load(AutoStartProvider.class, classLoader).findFirst();
 	}
 
 
 	@Provides
 	@Singleton
-	static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider() {
-		return ServiceLoader.load(TrayIntegrationProvider.class).findFirst();
+	static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider(PluginClassLoader classLoader) {
+		return ServiceLoader.load(TrayIntegrationProvider.class, classLoader).findFirst();
 	}
 
 	@Provides