From e5509bd63f114cb973f171097dbf44e1f0bc9df6 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 24 Aug 2021 16:33:05 +0200 Subject: [PATCH 1/2] load certain services from jars located in plugin dir --- .../org/cryptomator/common/Environment.java | 5 ++ .../cryptomator/common/PluginClassLoader.java | 66 +++++++++++++++++++ .../common/keychain/KeychainModule.java | 5 +- .../ui/launcher/UiLauncherModule.java | 13 ++-- 4 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/cryptomator/common/PluginClassLoader.java diff --git a/src/main/java/org/cryptomator/common/Environment.java b/src/main/java/org/cryptomator/common/Environment.java index bec4a2986..b42b1a356 100644 --- a/src/main/java/org/cryptomator/common/Environment.java +++ b/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 getPluginDir() { + return getPath("cryptomator.pluginDir").map(this::replaceHomeDir); + } + public Optional getMountPointsDir() { return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir); } diff --git a/src/main/java/org/cryptomator/common/PluginClassLoader.java b/src/main/java/org/cryptomator/common/PluginClassLoader.java new file mode 100644 index 000000000..16932923b --- /dev/null +++ b/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 { + + private final List 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; + } + } + +} diff --git a/src/main/java/org/cryptomator/common/keychain/KeychainModule.java b/src/main/java/org/cryptomator/common/keychain/KeychainModule.java index 01a221ed7..6356c4966 100644 --- a/src/main/java/org/cryptomator/common/keychain/KeychainModule.java +++ b/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> provideAvailableKeychainAccessProviderFactories() { - return ServiceLoader.load(KeychainAccessProvider.class).stream().collect(Collectors.toUnmodifiableSet()); + static Set> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) { + return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet()); } @Provides diff --git a/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java b/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java index f85659c53..c30efa30e 100644 --- a/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java +++ b/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 provideAppearanceProvider() { - return ServiceLoader.load(UiAppearanceProvider.class).findFirst(); + static Optional provideAppearanceProvider(PluginClassLoader classLoader) { + return ServiceLoader.load(UiAppearanceProvider.class, classLoader).findFirst(); } @Provides @Singleton - static Optional provideAutostartProvider() { - return ServiceLoader.load(AutoStartProvider.class).findFirst(); + static Optional provideAutostartProvider(PluginClassLoader classLoader) { + return ServiceLoader.load(AutoStartProvider.class, classLoader).findFirst(); } @Provides @Singleton - static Optional provideTrayIntegrationProvider() { - return ServiceLoader.load(TrayIntegrationProvider.class).findFirst(); + static Optional provideTrayIntegrationProvider(PluginClassLoader classLoader) { + return ServiceLoader.load(TrayIntegrationProvider.class, classLoader).findFirst(); } @Provides From ec9a4465eb1e5345de5dfa0df9979c2cc95f61b4 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 24 Aug 2021 16:35:07 +0200 Subject: [PATCH 2/2] add cryptomator.pluginDir environment variable --- .github/workflows/release.yml | 3 +++ .idea/runConfigurations/Cryptomator_Linux.xml | 2 +- .idea/runConfigurations/Cryptomator_Linux_Dev.xml | 2 +- .idea/runConfigurations/Cryptomator_Windows.xml | 2 +- .idea/runConfigurations/Cryptomator_Windows_Dev.xml | 2 +- .idea/runConfigurations/Cryptomator_macOS.xml | 2 +- .idea/runConfigurations/Cryptomator_macOS_Dev.xml | 2 +- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a92e032eb..4b020b278 100644 --- a/.github/workflows/release.yml +++ b/.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" diff --git a/.idea/runConfigurations/Cryptomator_Linux.xml b/.idea/runConfigurations/Cryptomator_Linux.xml index 735f60069..97bcd58df 100644 --- a/.idea/runConfigurations/Cryptomator_Linux.xml +++ b/.idea/runConfigurations/Cryptomator_Linux.xml @@ -2,7 +2,7 @@