Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
class ClassLoaderFactory {

private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderFactory.class);
private static final String USER_HOME = System.getProperty("user.home");
private static final String PLUGIN_DIR_KEY = "cryptomator.pluginDir";
private static final String JAR_SUFFIX = ".jar";

Expand All @@ -30,21 +29,27 @@ class ClassLoaderFactory {
*/
@Contract(value = "-> new", pure = true)
public static URLClassLoader forPluginDir() {
String val = System.getProperty(PLUGIN_DIR_KEY, "");
final Path p;
if (val.startsWith("~/")) {
p = Path.of(USER_HOME).resolve(val.substring(2));
} else {
p = Path.of(val);
String val = System.getProperty(PLUGIN_DIR_KEY);
if (val == null) {
return URLClassLoader.newInstance(new URL[0]);
}

try {
if (val.isBlank()) {
throw new IllegalArgumentException("Plugin dir path is blank");
}
return forPluginDirWithPath(Path.of(val)); //Path.of() might throw InvalidPathException
} catch (IllegalArgumentException e) {
LOG.debug("{} contains illegal value. Skipping plugin directory.", PLUGIN_DIR_KEY, e);
return URLClassLoader.newInstance(new URL[0]);
}
return forPluginDirWithPath(p);
}

@VisibleForTesting
@Contract(value = "_ -> new", pure = true)
static URLClassLoader forPluginDirWithPath(Path path) throws UncheckedIOException {
var jars = findJars(path);
if (LOG.isDebugEnabled()) {
if (LOG.isDebugEnabled() && jars.length != 0) {
String jarList = Arrays.stream(jars).map(URL::getPath).collect(Collectors.joining(", "));
LOG.debug("Found jars in cryptomator.pluginDir: {}", jarList);
}
Expand All @@ -56,7 +61,7 @@ static URL[] findJars(Path path) {
try (var stream = Files.walk(path)) {
return stream.filter(ClassLoaderFactory::isJarFile).map(ClassLoaderFactory::toUrl).toArray(URL[]::new);
} catch (IOException | UncheckedIOException e) {
// unable to locate any jars // TODO: log a warning?
LOG.debug("Failed to read plugin dir {}", path, e);
return new URL[0];
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package org.cryptomator.integrations.common;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EmptySource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.io.ByteArrayInputStream;
Expand All @@ -17,6 +22,9 @@
import java.util.Arrays;
import java.util.Comparator;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;

public class ClassLoaderFactoryTest {

@Nested
Expand Down Expand Up @@ -91,21 +99,49 @@ public void testReadPluginDirFromSysProp() {
}
}

@Test
@DisplayName("read path from cryptomator.pluginDir and replace ~/ with user.home")
public void testReadPluginDirFromSysPropAndReplaceHome() {
var ucl = Mockito.mock(URLClassLoader.class, "ucl");
var relPath = "~/there/will/be/plugins";
var absPath = Path.of(System.getProperty("user.home")).resolve("there/will/be/plugins");
try (var mockedClass = Mockito.mockStatic(ClassLoaderFactory.class)) {
@Nested
@DisplayName("when the system property contains invalid values")
public class InvalidSystemProperty {

MockedStatic<ClassLoaderFactory> mockedClass;

@BeforeEach
public void beforeEach() {
mockedClass = Mockito.mockStatic(ClassLoaderFactory.class);
mockedClass.when(() -> ClassLoaderFactory.forPluginDir()).thenCallRealMethod();
mockedClass.when(() -> ClassLoaderFactory.forPluginDirWithPath(absPath)).thenReturn(ucl);
mockedClass.when(() -> ClassLoaderFactory.forPluginDirWithPath(any())).thenReturn(null);
}

System.setProperty("cryptomator.pluginDir", relPath);
@AfterEach
public void afterEach() {
mockedClass.close();
}


@Test
@DisplayName("Undefined cryptomator.pluginDir returns empty URLClassLoader")
public void testUndefinedSysProp() {
System.clearProperty("cryptomator.pluginDir");
var result = ClassLoaderFactory.forPluginDir();

Assertions.assertSame(ucl, result);
mockedClass.verify(() -> ClassLoaderFactory.forPluginDirWithPath(any()), never());
Assertions.assertNotNull(result);
Assertions.assertEquals(0, result.getURLs().length);
}

@ParameterizedTest
@DisplayName("Property cryptomator.pluginDir filled with blanks returns empty URLClassLoader")
@EmptySource
@ValueSource(strings = {"\t\t", " "})
public void testBlankSysProp(String propValue) {
System.setProperty("cryptomator.pluginDir", propValue);
var result = ClassLoaderFactory.forPluginDir();

mockedClass.verify(() -> ClassLoaderFactory.forPluginDirWithPath(any()), never());
Assertions.assertNotNull(result);
Assertions.assertEquals(0, result.getURLs().length);
}

}

@Test
Expand Down
Loading