Browse Source

load certain services from jars located in plugin dir

Sebastian Stenzel 3 years ago
parent
commit
e5509bd63f

+ 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