From: Decebal Suiu Date: Wed, 4 Oct 2017 17:40:46 +0000 (+0300) Subject: Add CompoundPluginDescriptorFinder (#172) X-Git-Tag: release-2.0.0~7 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1822cdede37ba649033021583ab29ee662835071;p=pf4j.git Add CompoundPluginDescriptorFinder (#172) --- diff --git a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java index d2f2b42..936b6bf 100644 --- a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java +++ b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java @@ -780,8 +780,10 @@ public abstract class AbstractPluginManager implements PluginManager { } // retrieves the plugin descriptor + PluginDescriptorFinder pluginDescriptorFinder = getPluginDescriptorFinder(); + log.debug("Use '{}' to find plugins descriptors", pluginDescriptorFinder); log.debug("Finding plugin descriptor for plugin '{}'", pluginPath); - PluginDescriptor pluginDescriptor = getPluginDescriptorFinder().find(pluginPath); + PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(pluginPath); validatePluginDescriptor(pluginDescriptor); log.debug("Found descriptor {}", pluginDescriptor); String pluginClassName = pluginDescriptor.getPluginClass(); diff --git a/pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java new file mode 100644 index 0000000..e8c8318 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java @@ -0,0 +1,81 @@ +/* + * Copyright 2017 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pf4j; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Decebal Suiu + */ +public class CompoundPluginDescriptorFinder implements PluginDescriptorFinder { + + private static final Logger log = LoggerFactory.getLogger(CompoundPluginDescriptorFinder.class); + + private List finders = new ArrayList<>(); + + public CompoundPluginDescriptorFinder add(PluginDescriptorFinder finder) { + if (finder == null) { + throw new IllegalArgumentException("null not allowed"); + } + + finders.add(finder); + + return this; + } + + public int size() { + return finders.size(); + } + + @Override + public boolean isApplicable(Path pluginPath) { + for (PluginDescriptorFinder finder : finders) { + if (finder.isApplicable(pluginPath)) { + return true; + } + } + + return false; + } + + @Override + public PluginDescriptor find(Path pluginPath) throws PluginException { + for (PluginDescriptorFinder finder : finders) { + if (finder.isApplicable(pluginPath)) { + log.debug("'{}' is applicable for plugin '{}'", finder, pluginPath); + try { + PluginDescriptor pluginDescriptor = finder.find(pluginPath); + if (pluginDescriptor != null) { + return pluginDescriptor; + } + } catch (Exception e) { + // log the exception and continue with the next finder + log.error(e.getMessage()); // ?! + } + } else { + log.debug("'{}' is not applicable for plugin '{}'", finder, pluginPath); + } + } + + throw new PluginException("No PluginDescriptorFinder for plugin '{}'", pluginPath); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java index 43a03fb..ea2bfd7 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java +++ b/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java @@ -34,10 +34,8 @@ public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListe public DefaultExtensionFinder(PluginManager pluginManager) { this.pluginManager = pluginManager; - finders = new ArrayList<>(); - - addExtensionFinder(new LegacyExtensionFinder(pluginManager)); -// addExtensionFinder(new ServiceProviderExtensionFinder(pluginManager)); + add(new LegacyExtensionFinder(pluginManager)); +// add(new ServiceProviderExtensionFinder(pluginManager)); } @Override @@ -70,7 +68,6 @@ public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListe return extensions; } - @Override public Set findClassNames(String pluginId) { Set classNames = new HashSet<>(); @@ -91,10 +88,10 @@ public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListe } public DefaultExtensionFinder addServiceProviderExtensionFinder() { - return addExtensionFinder(new ServiceProviderExtensionFinder(pluginManager)); + return add(new ServiceProviderExtensionFinder(pluginManager)); } - public DefaultExtensionFinder addExtensionFinder(ExtensionFinder finder) { + public DefaultExtensionFinder add(ExtensionFinder finder) { finders.add(finder); return this; diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java deleted file mode 100644 index c803f07..0000000 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.jar.Manifest; - -/** - * The default implementation for {@link PluginDescriptorFinder}. - * Now, this class it's a "link" to {@link ManifestPluginDescriptorFinder}. - * - * @author Decebal Suiu - */ -public class DefaultPluginDescriptorFinder extends ManifestPluginDescriptorFinder { - - private static final Logger log = LoggerFactory.getLogger(ManifestPluginDescriptorFinder.class); - - private PluginClasspath pluginClasspath; - - public DefaultPluginDescriptorFinder(PluginClasspath pluginClasspath) { - this.pluginClasspath = pluginClasspath; - } - - @Override - public Manifest readManifest(Path pluginPath) throws PluginException { - // TODO it's ok with first classes root? Another idea is to specify in PluginClasspath the folder. - if (pluginClasspath.getClassesDirectories().size() == 0) { - throw new PluginException("Failed to read manifest, no classes folder in classpath"); - } - String classes = pluginClasspath.getClassesDirectories().get(0); - Path manifestPath = pluginPath.resolve(Paths.get(classes,"/META-INF/MANIFEST.MF")); - log.debug("Lookup plugin descriptor in '{}'", manifestPath); - if (Files.notExists(manifestPath)) { - throw new PluginException("Cannot find '{}' path", manifestPath); - } - - try (InputStream input = Files.newInputStream(manifestPath)) { - return new Manifest(input); - } catch (IOException e) { - throw new PluginException(e.getMessage(), e); - } - } - -} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java index 0d79e82..de1ef2f 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java @@ -37,6 +37,11 @@ public class DefaultPluginManager extends AbstractPluginManager { super(); } + /** + * Use {@link DefaultPluginManager#DefaultPluginManager(Path)}. + * + * @param pluginsDir + */ @Deprecated public DefaultPluginManager(File pluginsDir) { this(pluginsDir.toPath()); @@ -46,14 +51,11 @@ public class DefaultPluginManager extends AbstractPluginManager { super(pluginsRoot); } - /** - * By default if {@link DefaultPluginManager#isDevelopment()} returns {@code true} - * than a {@link PropertiesPluginDescriptorFinder} is returned - * else this method returns {@link DefaultPluginDescriptorFinder}. - */ @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new DefaultPluginDescriptorFinder(pluginClasspath); + protected CompoundPluginDescriptorFinder createPluginDescriptorFinder() { + return new CompoundPluginDescriptorFinder() + .add(new PropertiesPluginDescriptorFinder()) + .add(new ManifestPluginDescriptorFinder()); } @Override diff --git a/pf4j/src/main/java/org/pf4j/JarPluginManager.java b/pf4j/src/main/java/org/pf4j/JarPluginManager.java index 232a8eb..bd94299 100644 --- a/pf4j/src/main/java/org/pf4j/JarPluginManager.java +++ b/pf4j/src/main/java/org/pf4j/JarPluginManager.java @@ -19,15 +19,12 @@ import org.pf4j.util.AndFileFilter; import org.pf4j.util.DirectoryFileFilter; import org.pf4j.util.HiddenFilter; import org.pf4j.util.JarFileFilter; +import org.pf4j.util.NameFileFilter; import org.pf4j.util.NotFileFilter; import org.pf4j.util.OrFileFilter; -import org.pf4j.util.NameFileFilter; import java.io.FileFilter; -import java.io.IOException; import java.nio.file.Path; -import java.util.jar.JarFile; -import java.util.jar.Manifest; /** * It's a {@link PluginManager} that loads plugin from a jar file. @@ -43,11 +40,6 @@ public class JarPluginManager extends DefaultPluginManager { return new JarPluginRepository(getPluginsRoot(), isDevelopment()); } - @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new JarPluginDescriptorFinder(); - } - @Override protected PluginLoader createPluginLoader() { return new JarPluginLoader(this, pluginClasspath); @@ -79,19 +71,6 @@ public class JarPluginManager extends DefaultPluginManager { } - class JarPluginDescriptorFinder extends ManifestPluginDescriptorFinder { - - @Override - public Manifest readManifest(Path pluginPath) throws PluginException { - try { - return new JarFile(pluginPath.toFile()).getManifest(); - } catch (IOException e) { - throw new PluginException(e); - } - } - - } - class JarPluginLoader extends DefaultPluginLoader { public JarPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { diff --git a/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java index 1c3b9c2..aa9a929 100644 --- a/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java +++ b/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java @@ -15,10 +15,17 @@ */ package org.pf4j; +import org.pf4j.util.FileUtils; import org.pf4j.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.Path; import java.util.jar.Attributes; +import java.util.jar.JarFile; import java.util.jar.Manifest; /** @@ -26,7 +33,14 @@ import java.util.jar.Manifest; * * @author Decebal Suiu */ -public abstract class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { +public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { + + private static final Logger log = LoggerFactory.getLogger(ManifestPluginDescriptorFinder.class); + + @Override + public boolean isApplicable(Path pluginPath) { + return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isJarFile(pluginPath)); + } @Override public PluginDescriptor find(Path pluginPath) throws PluginException { @@ -35,7 +49,43 @@ public abstract class ManifestPluginDescriptorFinder implements PluginDescriptor return createPluginDescriptor(manifest); } - public abstract Manifest readManifest(Path pluginPath) throws PluginException; + protected Manifest readManifest(Path pluginPath) throws PluginException { + if (FileUtils.isJarFile(pluginPath)) { + try { + Manifest manifest = new JarFile(pluginPath.toFile()).getManifest(); + if (manifest != null) { + return manifest; + } + } catch (IOException e) { + throw new PluginException(e); + } + } + + Path manifestPath = getManifestPath(pluginPath); + if (manifestPath == null) { + throw new PluginException("Cannot find the manifest path"); + } + + log.debug("Lookup plugin descriptor in '{}'", manifestPath); + if (Files.notExists(manifestPath)) { + throw new PluginException("Cannot find '{}' path", manifestPath); + } + + try (InputStream input = Files.newInputStream(manifestPath)) { + return new Manifest(input); + } catch (IOException e) { + throw new PluginException(e); + } + } + + protected Path getManifestPath(Path pluginPath) throws PluginException { + if (Files.isDirectory(pluginPath)) { + // legacy (the path is something like "classes/META-INF/MANIFEST.MF") + return FileUtils.findFile(pluginPath,"MANIFEST.MF"); + } + + return null; + } protected PluginDescriptor createPluginDescriptor(Manifest manifest) { PluginDescriptor pluginDescriptor = createPluginDescriptorInstance(); diff --git a/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java index 2edfbb5..73fc560 100644 --- a/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java +++ b/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java @@ -19,13 +19,22 @@ import java.nio.file.Path; /** * Find a plugin descriptor for a plugin path. - * You can find in manifest file {@link DefaultPluginDescriptorFinder}, - * xml file, properties file, java services (with {@link java.util.ServiceLoader}), etc. + * You can find the plugin descriptor in manifest file {@link ManifestPluginDescriptorFinder}, + * properties file {@link PropertiesPluginDescriptorFinder}, xml file, + * java services (with {@link java.util.ServiceLoader}), etc. * * @author Decebal Suiu */ public interface PluginDescriptorFinder { + /** + * Returns true if this finder is applicable to the given {@link Path}. + * + * @param pluginPath + * @return + */ + boolean isApplicable(Path pluginPath); + PluginDescriptor find(Path pluginPath) throws PluginException; } diff --git a/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java index 31d4d53..4148287 100644 --- a/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java +++ b/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java @@ -15,9 +15,10 @@ */ package org.pf4j; +import org.pf4j.util.FileUtils; +import org.pf4j.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.pf4j.util.StringUtils; import java.io.IOException; import java.io.InputStream; @@ -47,7 +48,12 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder this.propertiesFileName = propertiesFileName; } - @Override + @Override + public boolean isApplicable(Path pluginPath) { + return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isJarFile(pluginPath)); + } + + @Override public PluginDescriptor find(Path pluginPath) throws PluginException { Properties properties = readProperties(pluginPath); @@ -56,6 +62,10 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder protected Properties readProperties(Path pluginPath) throws PluginException { Path propertiesPath = getPropertiesPath(pluginPath, propertiesFileName); + if (propertiesPath == null) { + throw new PluginException("Cannot find the properties path"); + } + log.debug("Lookup plugin descriptor in '{}'", propertiesPath); if (Files.notExists(propertiesPath)) { throw new PluginException("Cannot find '{}' path", propertiesPath); @@ -65,14 +75,23 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder try (InputStream input = Files.newInputStream(propertiesPath)) { properties.load(input); } catch (IOException e) { - throw new PluginException(e.getMessage(), e); + throw new PluginException(e); } return properties; } protected Path getPropertiesPath(Path pluginPath, String propertiesFileName) throws PluginException { - return pluginPath.resolve(Paths.get(propertiesFileName)); + if (Files.isDirectory(pluginPath)) { + return pluginPath.resolve(Paths.get(propertiesFileName)); + } else { + // it's a jar file + try { + return FileUtils.getPath(pluginPath, propertiesFileName); + } catch (IOException e) { + throw new PluginException(e); + } + } } protected PluginDescriptor createPluginDescriptor(Properties properties) { diff --git a/pf4j/src/main/java/org/pf4j/ServiceProviderExtensionFinder.java b/pf4j/src/main/java/org/pf4j/ServiceProviderExtensionFinder.java index bd7c768..3955c80 100644 --- a/pf4j/src/main/java/org/pf4j/ServiceProviderExtensionFinder.java +++ b/pf4j/src/main/java/org/pf4j/ServiceProviderExtensionFinder.java @@ -16,6 +16,7 @@ package org.pf4j; import org.pf4j.processor.ServiceProviderExtensionStorage; +import org.pf4j.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,8 +25,6 @@ import java.io.Reader; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -65,8 +64,7 @@ public class ServiceProviderExtensionFinder extends AbstractExtensionFinder { if (url != null) { Path extensionPath; if (url.toURI().getScheme().equals("jar")) { - FileSystem fileSystem = FileSystems.newFileSystem(url.toURI(), Collections.emptyMap()); - extensionPath = fileSystem.getPath(getExtensionsResource()); + extensionPath = FileUtils.getPath(url.toURI(), getExtensionsResource()); } else { extensionPath = Paths.get(url.toURI()); } @@ -100,8 +98,7 @@ public class ServiceProviderExtensionFinder extends AbstractExtensionFinder { if (url != null) { Path extensionPath; if (url.toURI().getScheme().equals("jar")) { - FileSystem fileSystem = FileSystems.newFileSystem(url.toURI(), Collections.emptyMap()); - extensionPath = fileSystem.getPath(getExtensionsResource()); + extensionPath = FileUtils.getPath(url.toURI(), getExtensionsResource()); } else { extensionPath = Paths.get(url.toURI()); } diff --git a/pf4j/src/main/java/org/pf4j/util/FileUtils.java b/pf4j/src/main/java/org/pf4j/util/FileUtils.java index 9399588..a17481b 100644 --- a/pf4j/src/main/java/org/pf4j/util/FileUtils.java +++ b/pf4j/src/main/java/org/pf4j/util/FileUtils.java @@ -23,7 +23,10 @@ import java.io.File; import java.io.FileFilter; import java.io.FileReader; import java.io.IOException; +import java.net.URI; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -32,6 +35,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; /** @@ -199,4 +203,49 @@ public class FileUtils { return Files.isRegularFile(path) && path.toString().toLowerCase().endsWith(".zip"); } + /** + * Return true only if path is a jar file. + * + * @param path to a file/dir + * @return true if file with {@code .jar} ending + */ + public static boolean isJarFile(Path path) { + return Files.isRegularFile(path) && path.toString().toLowerCase().endsWith(".jar"); + } + + public static Path getPath(Path path, String first, String... more) throws IOException { + URI uri = path.toUri(); + if (isJarFile(path)) { + uri = URI.create("jar:file:" + path.toString()); + } + + return getPath(uri, first, more); + } + + public static Path getPath(URI uri, String first, String... more) throws IOException { + FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap()); + + return fileSystem.getPath(first, more); + } + + public static Path findFile(Path directoryPath, String fileName) { + File[] files = directoryPath.toFile().listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile()) { + if (file.getName().equals(fileName)) { + return file.toPath(); + } + } else if (file.isDirectory()) { + Path foundFile = findFile(file.toPath(), fileName); + if (foundFile != null) { + return foundFile; + } + } + } + } + + return null; + } + } diff --git a/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java new file mode 100644 index 0000000..ec15500 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2017 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pf4j; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.pf4j.plugin.PluginZip; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @author Decebal Suiu + */ +public class CompoundPluginDescriptorFinderTest { + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Test + public void add() { + CompoundPluginDescriptorFinder instance = new CompoundPluginDescriptorFinder(); + assertEquals(0, instance.size()); + + instance.add(new PropertiesPluginDescriptorFinder()); + assertEquals(1, instance.size()); + } + + @Test + public void find() throws Exception { + Path pluginPath = testFolder.newFolder("test-plugin-1").toPath(); + Files.write(pluginPath.resolve("plugin.properties"), getPlugin1Properties(), StandardCharsets.UTF_8); + + PluginDescriptorFinder instance = new CompoundPluginDescriptorFinder() + .add(new PropertiesPluginDescriptorFinder()); + + PluginDescriptor pluginDescriptor = instance.find(pluginPath); + assertNotNull(pluginDescriptor); + assertEquals("test-plugin-1", pluginDescriptor.getPluginId()); + assertEquals("0.0.1", pluginDescriptor.getVersion()); + } + + @Test + public void findInJar() throws Exception { + PluginDescriptorFinder instance = new CompoundPluginDescriptorFinder() + .add(new PropertiesPluginDescriptorFinder()); + + PluginZip pluginJar = new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.jar"), "myPlugin") + .pluginVersion("1.2.3") + .build(); + + PluginDescriptor pluginDescriptor = instance.find(pluginJar.path()); + assertNotNull(pluginDescriptor); + assertEquals("myPlugin", pluginJar.pluginId()); + assertEquals("1.2.3", pluginJar.pluginVersion()); + } + + @Test(expected = PluginException.class) + public void testNotFound() throws Exception { + PluginDescriptorFinder instance = new CompoundPluginDescriptorFinder(); + instance.find(getPluginsRoot().resolve("test-plugin-3")); + } + + private List getPlugin1Properties() { + String[] lines = new String[] { + "plugin.id=test-plugin-1\n" + + "plugin.version=0.0.1\n" + + "plugin.description=Test Plugin 1\n" + + "plugin.provider=Decebal Suiu\n" + + "plugin.class=org.pf4j.plugin.TestPlugin\n" + + "plugin.dependencies=test-plugin-2,test-plugin-3@~1.0\n" + + "plugin.requires=>=1\n" + + "plugin.license=Apache-2.0\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private Path getPluginsRoot() { + return testFolder.getRoot().toPath(); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java b/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java index 5c46d66..6df9953 100644 --- a/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java +++ b/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java @@ -16,49 +16,42 @@ package org.pf4j; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; -import org.pf4j.plugin.MockPluginManager; +import org.junit.rules.TemporaryFolder; +import org.pf4j.plugin.PluginZip; -import java.io.BufferedWriter; -import java.io.FileWriter; import java.io.IOException; -import java.net.URI; -import java.nio.file.*; -import java.util.Collections; +import java.nio.file.Files; +import java.nio.file.Paths; -import static junit.framework.TestCase.assertNull; import static org.junit.Assert.*; public class LoadPluginsTest { - private Path tmpDir; - private MockPluginManager pluginManager; - private MockZipPlugin p1; - private MockZipPlugin p2; - private MockZipPlugin p3; + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + private DefaultPluginManager pluginManager; @Before public void setup() throws IOException { - tmpDir = Files.createTempDirectory("pf4j-test"); - tmpDir.toFile().deleteOnExit(); - p1 = new MockZipPlugin("myPlugin", "1.2.3", "my-plugin-1.2.3", "my-plugin-1.2.3.zip"); - p2 = new MockZipPlugin("myPlugin", "2.0.0", "my-plugin-2.0.0", "my-plugin-2.0.0.ZIP"); - p3 = new MockZipPlugin("other", "3.0.0", "other-3.0.0", "other-3.0.0.Zip"); - pluginManager = new MockPluginManager( - tmpDir, - new PropertiesPluginDescriptorFinder("my.properties")); + pluginManager = new DefaultPluginManager(testFolder.getRoot().toPath()); } @Test public void load() throws Exception { - p1.create(); - assertTrue(Files.exists(p1.zipFile)); + PluginZip pluginZip = new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") + .pluginVersion("1.2.3") + .build(); + + assertTrue(Files.exists(pluginZip.path())); assertEquals(0, pluginManager.getPlugins().size()); pluginManager.loadPlugins(); - assertTrue(Files.exists(p1.zipFile)); - assertTrue(Files.exists(p1.unzipped)); + assertTrue(Files.exists(pluginZip.path())); + assertTrue(Files.exists(pluginZip.unzippedPath())); assertEquals(1, pluginManager.getPlugins().size()); - assertEquals(p1.id, pluginManager.idForPath(p1.unzipped)); + assertEquals(pluginZip.pluginId(), pluginManager.idForPath(pluginZip.unzippedPath())); } @Test(expected = IllegalArgumentException.class) @@ -68,105 +61,93 @@ public class LoadPluginsTest { @Test public void loadTwiceFails() throws Exception { - p1.create(); - assertNotNull(pluginManager.loadPluginFromPath(p1.zipFile)); - assertNull(pluginManager.loadPluginFromPath(p1.zipFile)); + PluginZip pluginZip = new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") + .pluginVersion("1.2.3") + .build(); + + assertNotNull(pluginManager.loadPluginFromPath(pluginZip.path())); + assertNull(pluginManager.loadPluginFromPath(pluginZip.path())); } @Test public void loadUnloadLoad() throws Exception { - p1.create(); + PluginZip pluginZip = new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") + .pluginVersion("1.2.3") + .build(); + pluginManager.loadPlugins(); + assertEquals(1, pluginManager.getPlugins().size()); - assertTrue(pluginManager.unloadPlugin(pluginManager.idForPath(p1.unzipped))); + assertTrue(pluginManager.unloadPlugin(pluginManager.idForPath(pluginZip.unzippedPath()))); // duplicate check - assertNull(pluginManager.idForPath(p1.unzipped)); + assertNull(pluginManager.idForPath(pluginZip.unzippedPath())); // Double unload ok - assertFalse(pluginManager.unloadPlugin(pluginManager.idForPath(p1.unzipped))); - assertNotNull(pluginManager.loadPlugin(p1.unzipped)); + assertFalse(pluginManager.unloadPlugin(pluginManager.idForPath(pluginZip.unzippedPath()))); + assertNotNull(pluginManager.loadPlugin(pluginZip.unzippedPath())); } @Test public void upgrade() throws Exception { - p1.create(); + new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") + .pluginVersion("1.2.3") + .build(); + pluginManager.loadPlugins(); pluginManager.startPlugins(); + assertEquals(1, pluginManager.getPlugins().size()); - assertEquals("1.2.3", pluginManager.getPlugin(p2.id).getDescriptor().getVersion()); assertEquals(1, pluginManager.getStartedPlugins().size()); - p2.create(); + + PluginZip pluginZip2 = new PluginZip.Builder(testFolder.newFile("my-plugin-2.0.0.ZIP"), "myPlugin") + .pluginVersion("2.0.0") + .build(); + + assertEquals("1.2.3", pluginManager.getPlugin(pluginZip2.pluginId()).getDescriptor().getVersion()); + pluginManager.loadPlugins(); - pluginManager.startPlugin(p2.id); + pluginManager.startPlugin(pluginZip2.pluginId()); + assertEquals(1, pluginManager.getPlugins().size()); - assertEquals("2.0.0", pluginManager.getPlugin(p2.id).getDescriptor().getVersion()); + assertEquals("2.0.0", pluginManager.getPlugin(pluginZip2.pluginId()).getDescriptor().getVersion()); assertEquals("2.0.0", pluginManager.getStartedPlugins().get(1).getDescriptor().getVersion()); } @Test public void getRoot() throws Exception { - assertEquals(tmpDir, pluginManager.getPluginsRoot()); + assertEquals(testFolder.getRoot().toPath(), pluginManager.getPluginsRoot()); } @Test public void notAPlugin() throws Exception { - Path notAPlugin = tmpDir.resolve("not-a-zip"); - Files.createFile(notAPlugin); + testFolder.newFile("not-a-zip"); + pluginManager.loadPlugins(); + assertEquals(0, pluginManager.getPlugins().size()); } @Test public void deletePlugin() throws Exception { - p1.create(); - p3.create(); + PluginZip pluginZip1 = new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") + .pluginVersion("1.2.3") + .build(); + + PluginZip pluginZip3 = new PluginZip.Builder(testFolder.newFile("other-3.0.0.Zip"), "other") + .pluginVersion("3.0.0") + .build(); + pluginManager.loadPlugins(); pluginManager.startPlugins(); + assertEquals(2, pluginManager.getPlugins().size()); - pluginManager.deletePlugin(p1.id); - assertEquals(1, pluginManager.getPlugins().size()); - assertFalse(Files.exists(p1.zipFile)); - assertFalse(Files.exists(p1.unzipped)); - assertTrue(Files.exists(p3.zipFile)); - assertTrue(Files.exists(p3.unzipped)); - } - private class MockZipPlugin { - - public final String id; - public final String version; - public final String filename; - public final Path zipFile; - public final Path unzipped; - public final Path propsFile; - public final URI fileURI; - public String zipname; - - public MockZipPlugin(String id, String version, String filename, String zipname) throws IOException { - this.id = id; - this.version = version; - this.filename = filename; - this.zipname = zipname; - - zipFile = tmpDir.resolve(zipname).toAbsolutePath(); - unzipped = tmpDir.resolve(filename); - propsFile = tmpDir.resolve("my.properties"); - fileURI = URI.create("jar:file:"+zipFile.toString()); - } - - public void create() throws IOException { - try (FileSystem zipfs = FileSystems.newFileSystem(fileURI, Collections.singletonMap("create", "true"))) { - Path propsInZip = zipfs.getPath("/" + propsFile.getFileName().toString()); - BufferedWriter br = new BufferedWriter(new FileWriter(propsFile.toString())); - br.write("plugin.id=" + id); - br.newLine(); - br.write("plugin.version=" + version); - br.newLine(); - br.write("plugin.class=org.pf4j.plugin.TestPlugin"); - br.close(); - Files.move(propsFile, propsInZip); - } - } + pluginManager.deletePlugin(pluginZip1.pluginId()); + assertEquals(1, pluginManager.getPlugins().size()); + assertFalse(Files.exists(pluginZip1.path())); + assertFalse(Files.exists(pluginZip1.unzippedPath())); + assertTrue(Files.exists(pluginZip3.path())); + assertTrue(Files.exists(pluginZip3.unzippedPath())); } } diff --git a/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java index 8c19733..fcd0035 100644 --- a/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java +++ b/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java @@ -74,11 +74,11 @@ public class ManifestPluginDescriptorFinderTest { } /** - * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. + * Test of {@link ManifestPluginDescriptorFinder#find(Path)} method. */ @Test public void testFind() throws Exception { - PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + PluginDescriptorFinder instance = new ManifestPluginDescriptorFinder(); PluginDescriptor plugin1 = instance.find(getPluginsRoot().resolve("test-plugin-1")); PluginDescriptor plugin2 = instance.find(getPluginsRoot().resolve("test-plugin-2")); @@ -105,11 +105,11 @@ public class ManifestPluginDescriptorFinderTest { } /** - * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. + * Test of {@link ManifestPluginDescriptorFinder#find(Path)} method. */ @Test(expected = PluginException.class) public void testFindNotFound() throws Exception { - PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + PluginDescriptorFinder instance = new ManifestPluginDescriptorFinder(); instance.find(getPluginsRoot().resolve("test-plugin-3")); } diff --git a/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java index e308f59..a3e3c68 100644 --- a/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java +++ b/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java @@ -19,6 +19,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.pf4j.plugin.PluginZip; import java.io.IOException; import java.nio.charset.Charset; @@ -96,11 +97,26 @@ public class PropertiesPluginDescriptorFinderTest { } @Test(expected = PluginException.class) - public void testFindNotFound() throws Exception { + public void testNotFound() throws Exception { PluginDescriptorFinder instance = new PropertiesPluginDescriptorFinder(); instance.find(getPluginsRoot().resolve("test-plugin-3")); } + @Test + public void findInJar() throws Exception { + PluginZip pluginJar = new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.jar"), "myPlugin") + .pluginVersion("1.2.3") + .build(); + + assertTrue(Files.exists(pluginJar.path())); + + PluginDescriptorFinder instance = new PropertiesPluginDescriptorFinder(); + PluginDescriptor pluginDescriptor = instance.find(pluginJar.path()); + assertNotNull(pluginDescriptor); + assertEquals("myPlugin", pluginJar.pluginId()); + assertEquals("1.2.3", pluginJar.pluginVersion()); + } + private List getPlugin1Properties() { String[] lines = new String[] { "plugin.id=test-plugin-1\n" diff --git a/pf4j/src/test/java/org/pf4j/plugin/AnotherFailTestPlugin.java b/pf4j/src/test/java/org/pf4j/plugin/AnotherFailTestPlugin.java index aa124ad..a8da002 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/AnotherFailTestPlugin.java +++ b/pf4j/src/test/java/org/pf4j/plugin/AnotherFailTestPlugin.java @@ -18,7 +18,6 @@ package org.pf4j.plugin; import org.pf4j.Plugin; /** - * * @author Mario Franco */ public class AnotherFailTestPlugin extends Plugin { diff --git a/pf4j/src/test/java/org/pf4j/plugin/FailTestPlugin.java b/pf4j/src/test/java/org/pf4j/plugin/FailTestPlugin.java index c53ae38..5a6a8f3 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/FailTestPlugin.java +++ b/pf4j/src/test/java/org/pf4j/plugin/FailTestPlugin.java @@ -16,7 +16,6 @@ package org.pf4j.plugin; /** - * * @author Mario Franco */ public class FailTestPlugin { diff --git a/pf4j/src/test/java/org/pf4j/plugin/MockPluginManager.java b/pf4j/src/test/java/org/pf4j/plugin/MockPluginManager.java deleted file mode 100644 index eb1956d..0000000 --- a/pf4j/src/test/java/org/pf4j/plugin/MockPluginManager.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.pf4j.plugin; - -import org.pf4j.DefaultPluginClasspath; -import org.pf4j.DefaultPluginDescriptorFinder; -import org.pf4j.DefaultPluginManager; -import org.pf4j.PluginDescriptorFinder; - -import java.nio.file.Path; - -/** - * Manager for testing - */ -public class MockPluginManager extends DefaultPluginManager { - - private PluginDescriptorFinder finder = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); - - public MockPluginManager() { - super(); - } - - public MockPluginManager(Path root, PluginDescriptorFinder finder) { - super(root); - this.finder = finder; - } - - @Override - protected PluginDescriptorFinder getPluginDescriptorFinder() { - return finder; - } - - @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return finder; - } - -} diff --git a/pf4j/src/test/java/org/pf4j/plugin/PluginZip.java b/pf4j/src/test/java/org/pf4j/plugin/PluginZip.java new file mode 100644 index 0000000..3b56786 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/PluginZip.java @@ -0,0 +1,107 @@ +/* + * Copyright 2015 Decebal Suiu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pf4j.plugin; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Represents a plugin zip/jar file. + * The "plugin.properties" file is created on the fly from the information supplied in Builder. + * + * @author Decebal Suiu + */ +public class PluginZip { + + private final File file; + private final String pluginId; + private final String pluginVersion; + + protected PluginZip(Builder builder) { + this.file = builder.file; + this.pluginId = builder.pluginId; + this.pluginVersion = builder.pluginVersion; + } + + public File file() { + return file; + } + + public Path path() { + return file.toPath(); + } + + public String pluginId() { + return pluginId; + } + + public String pluginVersion() { + return pluginVersion; + } + + public Path unzippedPath() { + Path path = path(); + String fileName = path.getFileName().toString(); + + return path.getParent().resolve(fileName.substring(0, fileName.length() - 4)); // without ".zip" suffix + } + + public static class Builder { + + private final File file; + private final String pluginId; + + private String pluginVersion; + + public Builder(File file, String pluginId) { + this.file = file; + this.pluginId = pluginId; + } + + public Builder pluginVersion(String pluginVersion) { + this.pluginVersion = pluginVersion; + + return this; + } + + public PluginZip build() throws IOException { + createPropertiesFile(); + + return new PluginZip(this); + } + + protected void createPropertiesFile() throws IOException { + Properties properties = new Properties(); + properties.setProperty("plugin.id", pluginId); + properties.setProperty("plugin.version", pluginVersion); + properties.setProperty("plugin.class", "org.pf4j.plugin.TestPlugin"); + + ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(file)); + ZipEntry propertiesFile = new ZipEntry("plugin.properties"); + outputStream.putNextEntry(propertiesFile); + properties.store(outputStream, ""); + outputStream.closeEntry(); + outputStream.close(); + } + + } + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java b/pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java index f0f8c87..361bd87 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java +++ b/pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java @@ -19,7 +19,6 @@ import org.pf4j.Plugin; import org.pf4j.PluginWrapper; /** - * * @author Mario Franco */ public class TestPlugin extends Plugin { diff --git a/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java b/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java index 6b52fdf..38f9238 100644 --- a/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java +++ b/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java @@ -15,60 +15,36 @@ */ package org.pf4j.util; -import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.pf4j.plugin.PluginZip; -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; import static org.junit.Assert.*; public class FileUtilsTest { - private Path zipFile; - private Path tmpDir; - private Path propsFile; - - @Before - public void setup() throws IOException { - tmpDir = Files.createTempDirectory("pf4j-test"); - tmpDir.toFile().deleteOnExit(); - zipFile = tmpDir.resolve("my.zip").toAbsolutePath(); - propsFile = tmpDir.resolve("plugin.properties"); - URI file = URI.create("jar:file:"+zipFile.toString()); - try (FileSystem zipfs = FileSystems.newFileSystem(file, Collections.singletonMap("create", "true"))) { - Path propsInZip = zipfs.getPath("/plugin.properties"); - BufferedWriter br = new BufferedWriter(new FileWriter(propsFile.toString())); - br.write("plugin.id=test"); - br.newLine(); - br.write("plugin.version=1.2.3"); - br.newLine(); - br.write("plugin.class=foo.bar"); - br.close(); - Files.move(propsFile, propsInZip); - } - } + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); @Test public void expandIfZip() throws Exception { - Path unzipped = FileUtils.expandIfZip(zipFile); - assertEquals(tmpDir.resolve("my"), unzipped); - assertTrue(Files.exists(tmpDir.resolve("my/plugin.properties"))); + PluginZip pluginZip = new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") + .pluginVersion("1.2.3") + .build(); + + Path unzipped = FileUtils.expandIfZip(pluginZip.path()); + assertEquals(pluginZip.unzippedPath(), unzipped); + assertTrue(Files.exists(unzipped.resolve("plugin.properties"))); - // Non-zip file remains unchanged - assertEquals(propsFile, FileUtils.expandIfZip(propsFile)); // File without .suffix - Path extra = Files.createFile(tmpDir.resolve("extra")); + Path extra = testFolder.newFile("extra").toPath(); assertEquals(extra, FileUtils.expandIfZip(extra)); // Folder - Path folder = Files.createFile(tmpDir.resolve("folder")); + Path folder = testFolder.newFile("folder").toPath(); assertEquals(folder, FileUtils.expandIfZip(folder)); }