aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--demo/maven/pom.xml2
-rw-r--r--pf4j/src/main/java/org/pf4j/AbstractPluginManager.java1
-rw-r--r--pf4j/src/main/java/org/pf4j/DefaultPluginDescriptor.java14
-rw-r--r--pf4j/src/main/java/org/pf4j/DefaultPluginManager.java2
-rw-r--r--pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java4
-rw-r--r--pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java12
-rw-r--r--pf4j/src/test/java/org/pf4j/PluginClassLoaderTest.java97
-rw-r--r--pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java12
-rw-r--r--pf4j/src/test/java/org/pf4j/test/ManifestUtils.java40
-rw-r--r--pf4j/src/test/java/org/pf4j/test/PluginJar.java46
-rw-r--r--pf4j/src/test/java/org/pf4j/test/PluginZip.java84
-rw-r--r--pf4j/src/test/java/org/pf4j/test/PropertiesUtils.java35
13 files changed, 260 insertions, 95 deletions
diff --git a/README.md b/README.md
index 562dae6..509063f 100644
--- a/README.md
+++ b/README.md
@@ -159,3 +159,9 @@ Quickstart (call to action)
1. Read this file to have an overview about what this project does
2. Read [Getting started](https://pf4j.org/doc/getting-started.html) section of documentation to understand the basic concepts
3. Read [Quickstart](https://pf4j.org/dev/quickstart.html) section of documentation to create your first PF4J-based modular application
+
+Credits
+-------
+Many thanks to:
+* [JetBrains](https://www.jetbrains.com) for their free OpenSource license
+* [JProfiler](https://www.ej-technologies.com/jprofiler) for their free OpenSource license
diff --git a/demo/maven/pom.xml b/demo/maven/pom.xml
index 743ce31..61033f0 100644
--- a/demo/maven/pom.xml
+++ b/demo/maven/pom.xml
@@ -5,7 +5,7 @@
<groupId>org.pf4j</groupId>
<artifactId>pf4j-parent</artifactId>
<version>3.14.0-SNAPSHOT</version>
- <relativePath>../..</relativePath>
+ <relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
index 626dbcb..ac41519 100644
--- a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
+++ b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
@@ -337,6 +337,7 @@ public abstract class AbstractPluginManager implements PluginManager {
if (classLoader instanceof Closeable) {
try {
((Closeable) classLoader).close();
+ classLoader = null; // help GC to collect the classloader
} catch (IOException e) {
throw new PluginRuntimeException(e, "Cannot close classloader");
}
diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptor.java b/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptor.java
index e8ff380..db74238 100644
--- a/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptor.java
+++ b/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptor.java
@@ -134,13 +134,13 @@ public class DefaultPluginDescriptor implements PluginDescriptor {
return this;
}
- protected PluginDescriptor setPluginDescription(String pluginDescription) {
+ protected DefaultPluginDescriptor setPluginDescription(String pluginDescription) {
this.pluginDescription = pluginDescription;
return this;
}
- protected PluginDescriptor setPluginClass(String pluginClassName) {
+ protected DefaultPluginDescriptor setPluginClass(String pluginClassName) {
this.pluginClass = pluginClassName;
return this;
@@ -152,19 +152,19 @@ public class DefaultPluginDescriptor implements PluginDescriptor {
return this;
}
- protected PluginDescriptor setProvider(String provider) {
+ protected DefaultPluginDescriptor setProvider(String provider) {
this.provider = provider;
return this;
}
- protected PluginDescriptor setRequires(String requires) {
+ protected DefaultPluginDescriptor setRequires(String requires) {
this.requires = requires;
return this;
}
- protected PluginDescriptor setDependencies(String dependencies) {
+ protected DefaultPluginDescriptor setDependencies(String dependencies) {
this.dependencies = new ArrayList<>();
if (dependencies != null) {
@@ -177,7 +177,7 @@ public class DefaultPluginDescriptor implements PluginDescriptor {
return this;
}
- protected PluginDescriptor setDependencies(String... dependencies) {
+ protected DefaultPluginDescriptor setDependencies(String... dependencies) {
for (String dependency : dependencies) {
dependency = dependency.trim();
if (!dependency.isEmpty()) {
@@ -188,7 +188,7 @@ public class DefaultPluginDescriptor implements PluginDescriptor {
return this;
}
- public PluginDescriptor setLicense(String license) {
+ public DefaultPluginDescriptor setLicense(String license) {
this.license = license;
return this;
diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java
index e2667d4..a47c892 100644
--- a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java
+++ b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java
@@ -25,7 +25,7 @@ import java.util.List;
/**
* Default implementation of the {@link PluginManager} interface.
- * In essence it is a {@link ZipPluginManager} plus a {@link JarPluginManager}.
+ * In essence, it is a {@link ZipPluginManager} plus a {@link JarPluginManager}.
* So, it can load plugins from jar and zip, simultaneous.
*
* <p>This class is not thread-safe.
diff --git a/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java
index f80c19e..9823882 100644
--- a/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java
+++ b/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java
@@ -18,8 +18,8 @@ package org.pf4j;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.pf4j.test.PluginJar;
-import org.pf4j.test.PluginZip;
import org.pf4j.test.TestPlugin;
+import org.pf4j.test.PropertiesUtils;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -113,7 +113,7 @@ public class CompoundPluginDescriptorFinderTest {
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, ">=1");
map.put(PropertiesPluginDescriptorFinder.PLUGIN_LICENSE, "Apache-2.0");
- return PluginZip.createProperties(map);
+ return PropertiesUtils.createProperties(map);
}
private void storePropertiesToPath(Properties properties, Path pluginPath) throws IOException {
diff --git a/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java
index 079d8e2..d34a77d 100644
--- a/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java
+++ b/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java
@@ -18,7 +18,7 @@ package org.pf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import org.pf4j.test.PluginJar;
+import org.pf4j.test.ManifestUtils;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -121,7 +121,7 @@ public class ManifestPluginDescriptorFinderTest {
map.put(ManifestPluginDescriptorFinder.PLUGIN_REQUIRES, "*");
map.put(ManifestPluginDescriptorFinder.PLUGIN_LICENSE, "Apache-2.0");
- return PluginJar.createManifest(map);
+ return ManifestUtils.createManifest(map);
}
private Manifest getPlugin2Manifest() {
@@ -132,7 +132,7 @@ public class ManifestPluginDescriptorFinderTest {
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu");
map.put(ManifestPluginDescriptorFinder.PLUGIN_DEPENDENCIES, "");
- return PluginJar.createManifest(map);
+ return ManifestUtils.createManifest(map);
}
private Manifest getPlugin4Manifest() {
@@ -141,7 +141,7 @@ public class ManifestPluginDescriptorFinderTest {
map.put(ManifestPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1");
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu");
- return PluginJar.createManifest(map);
+ return ManifestUtils.createManifest(map);
}
private Manifest getPlugin5Manifest() {
@@ -150,7 +150,7 @@ public class ManifestPluginDescriptorFinderTest {
map.put(ManifestPluginDescriptorFinder.PLUGIN_CLASS, "org.pf4j.plugin.TestPlugin");
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu");
- return PluginJar.createManifest(map);
+ return ManifestUtils.createManifest(map);
}
private Manifest getPlugin6Manifest() {
@@ -158,7 +158,7 @@ public class ManifestPluginDescriptorFinderTest {
map.put(ManifestPluginDescriptorFinder.PLUGIN_CLASS, "org.pf4j.plugin.TestPlugin");
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu");
- return PluginJar.createManifest(map);
+ return ManifestUtils.createManifest(map);
}
private void storeManifestToPath(Manifest manifest, Path pluginPath) throws IOException {
diff --git a/pf4j/src/test/java/org/pf4j/PluginClassLoaderTest.java b/pf4j/src/test/java/org/pf4j/PluginClassLoaderTest.java
index b19bd3b..b43d6b9 100644
--- a/pf4j/src/test/java/org/pf4j/PluginClassLoaderTest.java
+++ b/pf4j/src/test/java/org/pf4j/PluginClassLoaderTest.java
@@ -21,20 +21,27 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.pf4j.processor.LegacyExtensionStorage;
+import org.pf4j.test.JavaFileObjectUtils;
+import org.pf4j.test.JavaSources;
import org.pf4j.test.PluginZip;
import org.pf4j.util.FileUtils;
+import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.AbstractMap;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -58,6 +65,8 @@ class PluginClassLoaderTest {
private PluginClassLoader parentLastPluginDependencyClassLoader;
private PluginClassLoader parentFirstPluginDependencyClassLoader;
+ private PluginZip pluginDependencyZip;
+
@TempDir
Path pluginsPath;
@@ -65,16 +74,17 @@ class PluginClassLoaderTest {
static void setUpGlobal() throws IOException, URISyntaxException {
Path parentClassPathBase = Paths.get(PluginClassLoaderTest.class.getClassLoader().getResource(".").toURI());
- File metaInfFile = parentClassPathBase.resolve("META-INF").toFile();
+ Path metaInfPath = parentClassPathBase.resolve("META-INF");
+ File metaInfFile = metaInfPath.toFile();
if (metaInfFile.mkdir()) {
// Only delete the directory if this test created it, guarding for any future usages of the directory.
metaInfFile.deleteOnExit();
}
- createFile(parentClassPathBase.resolve("META-INF").resolve("file-only-in-parent"));
- createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-dependency-and-plugin"));
- createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-dependency"));
- createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-plugin"));
+ createFile(metaInfPath.resolve("file-only-in-parent"));
+ createFile(metaInfPath.resolve("file-in-both-parent-and-dependency-and-plugin"));
+ createFile(metaInfPath.resolve("file-in-both-parent-and-dependency"));
+ createFile(metaInfPath.resolve("file-in-both-parent-and-plugin"));
createFile(parentClassPathBase.resolve(LegacyExtensionStorage.EXTENSIONS_RESOURCE));
}
@@ -93,25 +103,37 @@ class PluginClassLoaderTest {
pluginManager = new TestPluginManager(pluginsPath);
pluginManagerParentFirst = new TestPluginManager(pluginsPath);
- pluginDependencyDescriptor = new DefaultPluginDescriptor();
- pluginDependencyDescriptor.setPluginId("myDependency");
- pluginDependencyDescriptor.setPluginVersion("1.2.3");
- pluginDependencyDescriptor.setPluginDescription("My plugin");
- pluginDependencyDescriptor.setDependencies("");
- pluginDependencyDescriptor.setProvider("Me");
- pluginDependencyDescriptor.setRequires("5.0.0");
+ pluginDependencyDescriptor = new DefaultPluginDescriptor()
+ .setPluginId("myDependency")
+ .setPluginVersion("1.2.3")
+ .setPluginDescription("My plugin")
+ .setDependencies("")
+ .setProvider("Me")
+ .setRequires("5.0.0");
+
+ Map<String, JavaFileObject> generatedClasses = JavaSources.compileAll(JavaSources.GREETING, JavaSources.WHAZZUP_GREETING)
+ .stream()
+ .map(javaFileObject -> new AbstractMap.SimpleEntry<>(JavaFileObjectUtils.getClassName(javaFileObject), javaFileObject))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ Path classesPath = Paths.get("classes");
+ Path metaInfPath = classesPath.resolve("META-INF");
+
+ Path greetingClassPath = classesPath.resolve(JavaSources.GREETING_CLASS_NAME.replace('.', '/') + ".class");
+ Path whaszzupGreetingClassPath = classesPath.resolve(JavaSources.WHAZZUP_GREETING_CLASS_NAME.replace('.', '/') + ".class");
Path pluginDependencyPath = pluginsPath.resolve(pluginDependencyDescriptor.getPluginId() + "-" + pluginDependencyDescriptor.getVersion() + ".zip");
- PluginZip pluginDependencyZip = new PluginZip.Builder(pluginDependencyPath, pluginDependencyDescriptor.getPluginId())
+ pluginDependencyZip = new PluginZip.Builder(pluginDependencyPath, pluginDependencyDescriptor.getPluginId())
.pluginVersion(pluginDependencyDescriptor.getVersion())
- .addFile(Paths.get("classes/META-INF/dependency-file"), "dependency")
- .addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency-and-plugin"), "dependency")
- .addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency"), "dependency")
- .addFile(Paths.get("classes/" + LegacyExtensionStorage.EXTENSIONS_RESOURCE), "dependency")
+ .addFile(metaInfPath.resolve("dependency-file"), "dependency")
+ .addFile(metaInfPath.resolve("file-in-both-parent-and-dependency-and-plugin"), "dependency")
+ .addFile(metaInfPath.resolve("file-in-both-parent-and-dependency"), "dependency")
+ .addFile(classesPath.resolve(LegacyExtensionStorage.EXTENSIONS_RESOURCE), "dependency")
+ .addFile(greetingClassPath, JavaFileObjectUtils.getAllBytes(generatedClasses.get(JavaSources.GREETING_CLASS_NAME)))
+ .addFile(whaszzupGreetingClassPath, JavaFileObjectUtils.getAllBytes(generatedClasses.get(JavaSources.WHAZZUP_GREETING_CLASS_NAME)))
.build();
- FileUtils.expandIfZip(pluginDependencyZip.path());
+ pluginDependencyZip.unzip();
PluginClasspath pluginDependencyClasspath = new DefaultPluginClasspath();
@@ -121,7 +143,6 @@ class PluginClassLoaderTest {
pluginManager.addClassLoader(pluginDependencyDescriptor.getPluginId(), parentLastPluginDependencyClassLoader);
pluginManagerParentFirst.addClassLoader(pluginDependencyDescriptor.getPluginId(), parentFirstPluginDependencyClassLoader);
-
for (String classesDirectory : pluginDependencyClasspath.getClassesDirectories()) {
File classesDirectoryFile = pluginDependencyZip.unzippedPath().resolve(classesDirectory).toFile();
parentLastPluginDependencyClassLoader.addFile(classesDirectoryFile);
@@ -148,13 +169,13 @@ class PluginClassLoaderTest {
Path pluginPath = pluginsPath.resolve(pluginDescriptor.getPluginId() + "-" + pluginDescriptor.getVersion() + ".zip");
PluginZip pluginZip = new PluginZip.Builder(pluginPath, pluginDescriptor.getPluginId())
.pluginVersion(pluginDescriptor.getVersion())
- .addFile(Paths.get("classes/META-INF/plugin-file"), "plugin")
- .addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency-and-plugin"), "plugin")
- .addFile(Paths.get("classes/META-INF/file-in-both-parent-and-plugin"), "plugin")
- .addFile(Paths.get("classes/" + LegacyExtensionStorage.EXTENSIONS_RESOURCE), "plugin")
+ .addFile(metaInfPath.resolve("plugin-file"), "plugin")
+ .addFile(metaInfPath.resolve("file-in-both-parent-and-dependency-and-plugin"), "plugin")
+ .addFile(metaInfPath.resolve("file-in-both-parent-and-plugin"), "plugin")
+ .addFile(classesPath.resolve(LegacyExtensionStorage.EXTENSIONS_RESOURCE), "plugin")
.build();
- FileUtils.expandIfZip(pluginZip.path());
+ pluginZip.unzip();
PluginClasspath pluginClasspath = new DefaultPluginClasspath();
@@ -347,6 +368,34 @@ class PluginClassLoaderTest {
assertTrue(parentLastPluginClassLoader.isClosed());
}
+ @Test
+ void collectClassLoader() throws IOException, ClassNotFoundException, InterruptedException {
+ // Create a new classloader
+ PluginClassLoader classLoader = new PluginClassLoader(pluginManager, pluginDependencyDescriptor, PluginClassLoaderTest.class.getClassLoader());
+ PluginClasspath pluginDependencyClasspath = new DefaultPluginClasspath();
+ for (String classesDirectory : pluginDependencyClasspath.getClassesDirectories()) {
+ File classesDirectoryFile = pluginDependencyZip.unzippedPath().resolve(classesDirectory).toFile();
+ classLoader.addFile(classesDirectoryFile);
+ }
+
+ WeakReference<PluginClassLoader> weakRef = new WeakReference<>(classLoader);
+
+ // Use the classloader
+ classLoader.loadClass(JavaSources.GREETING_CLASS_NAME);
+
+ // Clear strong reference
+ classLoader.close();
+ classLoader = null;
+
+ // Try to force GC
+ System.gc();
+ System.runFinalization();
+ Thread.sleep(100); // Give GC a chance to run
+
+ // Check if ClassLoader was collected
+ assertNull(weakRef.get(), "ClassLoader was not garbage collected");
+ }
+
private static void assertFirstLine(String expected, URL resource) throws URISyntaxException, IOException {
assertNotNull(resource);
assertEquals(expected, Files.readAllLines(Paths.get(resource.toURI())).get(0));
diff --git a/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java
index 4d73a4f..987ee13 100644
--- a/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java
+++ b/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java
@@ -18,8 +18,8 @@ package org.pf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import org.pf4j.test.PluginZip;
import org.pf4j.test.TestPlugin;
+import org.pf4j.test.PropertiesUtils;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -118,7 +118,7 @@ public class PropertiesPluginDescriptorFinderTest {
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, ">=1");
map.put(PropertiesPluginDescriptorFinder.PLUGIN_LICENSE, "Apache-2.0");
- return PluginZip.createProperties(map);
+ return PropertiesUtils.createProperties(map);
}
private Properties getPlugin2Properties() {
@@ -129,7 +129,7 @@ public class PropertiesPluginDescriptorFinderTest {
map.put(PropertiesPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu");
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, "");
- return PluginZip.createProperties(map);
+ return PropertiesUtils.createProperties(map);
}
private Properties getPlugin4Properties() {
@@ -140,7 +140,7 @@ public class PropertiesPluginDescriptorFinderTest {
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, "");
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, "*");
- return PluginZip.createProperties(map);
+ return PropertiesUtils.createProperties(map);
}
private Properties getPlugin5Properties() {
@@ -151,7 +151,7 @@ public class PropertiesPluginDescriptorFinderTest {
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, "");
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, "*");
- return PluginZip.createProperties(map);
+ return PropertiesUtils.createProperties(map);
}
private Properties getPlugin6Properties() {
@@ -162,7 +162,7 @@ public class PropertiesPluginDescriptorFinderTest {
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, "");
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, "*");
- return PluginZip.createProperties(map);
+ return PropertiesUtils.createProperties(map);
}
private void storePropertiesToPath(Properties properties, Path pluginPath) throws IOException {
diff --git a/pf4j/src/test/java/org/pf4j/test/ManifestUtils.java b/pf4j/src/test/java/org/pf4j/test/ManifestUtils.java
new file mode 100644
index 0000000..e4dd33f
--- /dev/null
+++ b/pf4j/src/test/java/org/pf4j/test/ManifestUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012-present the original author or authors.
+ *
+ * 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.test;
+
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+public class ManifestUtils {
+
+ private ManifestUtils() {}
+
+ /**
+ * Creates a {@link Manifest} object from the given map.
+ */
+ public static Manifest createManifest(Map<String, String> map) {
+ Manifest manifest = new Manifest();
+ Attributes attributes = manifest.getMainAttributes();
+ attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ attributes.put(new Attributes.Name(entry.getKey()), entry.getValue());
+ }
+
+ return manifest;
+ }
+
+}
diff --git a/pf4j/src/test/java/org/pf4j/test/PluginJar.java b/pf4j/src/test/java/org/pf4j/test/PluginJar.java
index 034b10f..1ad4d21 100644
--- a/pf4j/src/test/java/org/pf4j/test/PluginJar.java
+++ b/pf4j/src/test/java/org/pf4j/test/PluginJar.java
@@ -16,6 +16,7 @@
package org.pf4j.test;
import org.pf4j.ManifestPluginDescriptorFinder;
+import org.pf4j.processor.LegacyExtensionStorage;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -28,7 +29,6 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
-import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
@@ -53,46 +53,50 @@ public class PluginJar {
this.pluginVersion = builder.pluginVersion;
}
+ /**
+ * Returns the {@code jar} file path.
+ */
public Path path() {
return path;
}
+ /**
+ * Returns the {@code jar} file.
+ */
public File file() {
return path.toFile();
}
+ /**
+ * Returns the plugin class.
+ */
public String pluginClass() {
return pluginClass;
}
+ /**
+ * Returns the plugin id.
+ */
public String pluginId() {
return pluginId;
}
+ /**
+ * Returns the plugin version.
+ */
public String pluginVersion() {
return pluginVersion;
}
- public static Manifest createManifest(Map<String, String> map) {
- Manifest manifest = new Manifest();
- Attributes attributes = manifest.getMainAttributes();
- attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
- for (Map.Entry<String, String> entry : map.entrySet()) {
- attributes.put(new Attributes.Name(entry.getKey()), entry.getValue());
- }
-
- return manifest;
- }
-
public static class Builder {
private final Path path;
private final String pluginId;
+ private final Map<String, String> manifestAttributes = new LinkedHashMap<>();
+ private final Set<String> extensions = new LinkedHashSet<>();
private String pluginClass;
private String pluginVersion;
- private Map<String, String> manifestAttributes = new LinkedHashMap<>();
- private Set<String> extensions = new LinkedHashSet<>();
private ClassDataProvider classDataProvider = new DefaultClassDataProvider();
public Builder(Path path, String pluginId) {
@@ -144,13 +148,15 @@ public class PluginJar {
return this;
}
+ /**
+ * Builds the {@link PluginJar} instance.
+ */
public PluginJar build() throws IOException {
- Manifest manifest = createManifest();
try (OutputStream outputStream = new FileOutputStream(path.toFile());
- JarOutputStream jarOutputStream = new JarOutputStream(outputStream, manifest)) {
+ JarOutputStream jarOutputStream = new JarOutputStream(outputStream, createManifest())) {
if (!extensions.isEmpty()) {
// add extensions.idx
- JarEntry jarEntry = new JarEntry("META-INF/extensions.idx");
+ JarEntry jarEntry = new JarEntry(LegacyExtensionStorage.EXTENSIONS_RESOURCE);
jarOutputStream.putNextEntry(jarEntry);
jarOutputStream.write(extensionsAsByteArray());
jarOutputStream.closeEntry();
@@ -175,11 +181,9 @@ public class PluginJar {
if (pluginClass != null) {
map.put(ManifestPluginDescriptorFinder.PLUGIN_CLASS, pluginClass);
}
- if (manifestAttributes != null) {
- map.putAll(manifestAttributes);
- }
+ map.putAll(manifestAttributes);
- return PluginJar.createManifest(map);
+ return ManifestUtils.createManifest(map);
}
private byte[] extensionsAsByteArray() throws IOException {
diff --git a/pf4j/src/test/java/org/pf4j/test/PluginZip.java b/pf4j/src/test/java/org/pf4j/test/PluginZip.java
index 4b9a0c9..45fb3b2 100644
--- a/pf4j/src/test/java/org/pf4j/test/PluginZip.java
+++ b/pf4j/src/test/java/org/pf4j/test/PluginZip.java
@@ -16,6 +16,7 @@
package org.pf4j.test;
import org.pf4j.PropertiesPluginDescriptorFinder;
+import org.pf4j.util.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
@@ -49,52 +50,79 @@ public class PluginZip {
this.pluginDependencies = builder.pluginDependencies;
}
+ /**
+ * Returns the path of the {@code zip} file.
+ */
public Path path() {
return path;
}
+ /**
+ * Returns the {@code zip} file.
+ */
public File file() {
return path.toFile();
}
+ /**
+ * Returns the plugin id.
+ */
public String pluginId() {
return pluginId;
}
+ /**
+ * Returns the plugin class.
+ */
public String pluginClass() {
return pluginClass;
}
+ /**
+ * Returns the plugin version.
+ */
public String pluginVersion() {
return pluginVersion;
}
+ /**
+ * Returns the plugin dependencies.
+ */
public String pluginDependencies() { return pluginDependencies; }
+ /**
+ * Returns the path where the {@code zip} file will be unzipped.
+ */
public Path unzippedPath() {
- Path path = path();
- String fileName = path.getFileName().toString();
+ Path zipPath = path();
+ String fileName = zipPath.getFileName().toString();
- return path.getParent().resolve(fileName.substring(0, fileName.length() - 4)); // without ".zip" suffix
+ return zipPath.getParent().resolve(fileName.substring(0, fileName.length() - 4)); // without ".zip" suffix
}
- public static Properties createProperties(Map<String, String> map) {
- Properties properties = new Properties();
- properties.putAll(map);
-
- return properties;
+ /**
+ * Unzips the {@code zip} file.
+ */
+ public Path unzip() throws IOException {
+ return FileUtils.expandIfZip(path());
}
+ /**
+ * Builder for {@link PluginZip}.
+ * The {@code plugin.properties} file is created on the fly from the information supplied in this builder.
+ * The {@code plugin.properties} file is created in the root of the {@code zip} file.
+ * The {@code zip} file can contain extra files.
+ */
public static class Builder {
private final Path path;
private final String pluginId;
+ private final Map<String, String> properties = new LinkedHashMap<>();
+ private final Map<Path, byte[]> files = new LinkedHashMap<>();
private String pluginClass;
private String pluginVersion;
private String pluginDependencies;
- private Map<String, String> properties = new LinkedHashMap<>();
- private Map<Path, byte[]> files = new LinkedHashMap<>();
public Builder(Path path, String pluginId) {
this.path = path;
@@ -163,13 +191,28 @@ public class PluginZip {
return this;
}
+ /**
+ * Builds the {@link PluginZip} instance.
+ */
public PluginZip build() throws IOException {
- createPropertiesFile();
+ try (ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(path.toFile()))) {
+ ZipEntry propertiesFile = new ZipEntry(PropertiesPluginDescriptorFinder.DEFAULT_PROPERTIES_FILE_NAME);
+ outputStream.putNextEntry(propertiesFile);
+ createProperties().store(outputStream, "");
+ outputStream.closeEntry();
+
+ for (Map.Entry<Path, byte[]> fileEntry : files.entrySet()) {
+ ZipEntry file = new ZipEntry(fileEntry.getKey().toString());
+ outputStream.putNextEntry(file);
+ outputStream.write(fileEntry.getValue());
+ outputStream.closeEntry();
+ }
+ }
return new PluginZip(this);
}
- protected void createPropertiesFile() throws IOException {
+ private Properties createProperties() {
Map<String, String> map = new LinkedHashMap<>();
map.put(PropertiesPluginDescriptorFinder.PLUGIN_ID, pluginId);
map.put(PropertiesPluginDescriptorFinder.PLUGIN_VERSION, pluginVersion);
@@ -179,23 +222,10 @@ public class PluginZip {
if (pluginClass != null) {
map.put(PropertiesPluginDescriptorFinder.PLUGIN_CLASS, pluginClass);
}
- if (properties != null) {
- map.putAll(properties);
- }
- try (ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(path.toFile()))) {
- ZipEntry propertiesFile = new ZipEntry(PropertiesPluginDescriptorFinder.DEFAULT_PROPERTIES_FILE_NAME);
- outputStream.putNextEntry(propertiesFile);
- createProperties(map).store(outputStream, "");
- outputStream.closeEntry();
+ map.putAll(properties);
- for (Map.Entry<Path, byte[]> fileEntry : files.entrySet()) {
- ZipEntry file = new ZipEntry(fileEntry.getKey().toString());
- outputStream.putNextEntry(file);
- outputStream.write(fileEntry.getValue());
- outputStream.closeEntry();
- }
- }
+ return PropertiesUtils.createProperties(map);
}
}
diff --git a/pf4j/src/test/java/org/pf4j/test/PropertiesUtils.java b/pf4j/src/test/java/org/pf4j/test/PropertiesUtils.java
new file mode 100644
index 0000000..72b3751
--- /dev/null
+++ b/pf4j/src/test/java/org/pf4j/test/PropertiesUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012-present the original author or authors.
+ *
+ * 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.test;
+
+import java.util.Map;
+import java.util.Properties;
+
+public class PropertiesUtils {
+
+ private PropertiesUtils() {}
+
+ /**
+ * Creates a {@link Properties} object from the given map.
+ */
+ public static Properties createProperties(Map<String, String> map) {
+ Properties properties = new Properties();
+ properties.putAll(map);
+
+ return properties;
+ }
+
+}