From 6fd8ae23848fafaadb37d0e9e9169e0c1dc4ccb2 Mon Sep 17 00:00:00 2001 From: Decebal Suiu Date: Fri, 31 May 2019 20:02:09 +0300 Subject: Improve PluginJar, add ClassDataProvider concept --- .../test/java/org/pf4j/JarPluginManagerTest.java | 16 ++++++ .../java/org/pf4j/plugin/ClassDataProvider.java | 33 ++++++++++++ .../org/pf4j/plugin/DefaultClassDataProvider.java | 55 ++++++++++++++++++++ .../java/org/pf4j/plugin/FailTestExtension.java | 5 ++ pf4j/src/test/java/org/pf4j/plugin/PluginJar.java | 60 +++++++++++++++++++--- .../test/java/org/pf4j/plugin/TestExtension.java | 5 ++ .../java/org/pf4j/plugin/TestExtensionPoint.java | 2 + 7 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java create mode 100644 pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java diff --git a/pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java b/pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java index 45c3999..d9ca90a 100644 --- a/pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java +++ b/pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java @@ -20,10 +20,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.pf4j.plugin.PluginJar; +import org.pf4j.plugin.TestExtension; +import org.pf4j.plugin.TestExtensionPoint; import org.pf4j.plugin.TestPlugin; import java.io.IOException; import java.nio.file.Path; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -42,6 +45,7 @@ public class JarPluginManagerTest { pluginJar = new PluginJar.Builder(pluginsPath.resolve("test-plugin.jar"), "test-plugin") .pluginClass(TestPlugin.class.getName()) .pluginVersion("1.2.3") + .extension(TestExtension.class.getName()) .build(); pluginManager = new JarPluginManager(pluginsPath); @@ -53,6 +57,18 @@ public class JarPluginManagerTest { pluginManager = null; } + @Test + public void getExtensions() { + pluginManager.loadPlugins(); + pluginManager.startPlugins(); + + List extensions = pluginManager.getExtensions(TestExtensionPoint.class); + assertEquals(1, extensions.size()); + + String something = extensions.get(0).saySomething(); + assertEquals(new TestExtension().saySomething(), something); + } + @Test public void unloadPlugin() throws Exception { pluginManager.loadPlugins(); diff --git a/pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java b/pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java new file mode 100644 index 0000000..ff55967 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java @@ -0,0 +1,33 @@ +/* + * 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.plugin; + +/** + * Defines the interface for classes that know to supply class data for a class name. + * The idea is to have the possibility to retrieve the data for a class from different sources: + * + * + * @author Decebal Suiu + */ +public interface ClassDataProvider { + + byte[] getClassData(String className); + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java b/pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java new file mode 100644 index 0000000..ef0eaf9 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java @@ -0,0 +1,55 @@ +/* + * 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.plugin; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Get class data from the class path. + * + * @author Decebal Suiu + */ +public class DefaultClassDataProvider implements ClassDataProvider { + + @Override + public byte[] getClassData(String className) { + String path = className.replace('.', '/') + ".class"; + InputStream classDataStream = getClass().getClassLoader().getResourceAsStream(path); + if (classDataStream == null) { + throw new RuntimeException("Cannot find class data"); + } + + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + copyStream(classDataStream, outputStream); + return outputStream.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private void copyStream(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java b/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java index 9cc37e0..13b51ee 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java +++ b/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java @@ -26,4 +26,9 @@ public class FailTestExtension implements TestExtensionPoint { public FailTestExtension(String name) { } + @Override + public String saySomething() { + return "I am a fail test extension"; + } + } diff --git a/pf4j/src/test/java/org/pf4j/plugin/PluginJar.java b/pf4j/src/test/java/org/pf4j/plugin/PluginJar.java index 366c22a..a75b68f 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/PluginJar.java +++ b/pf4j/src/test/java/org/pf4j/plugin/PluginJar.java @@ -17,18 +17,23 @@ package org.pf4j.plugin; import org.pf4j.ManifestPluginDescriptorFinder; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; import java.nio.file.Path; 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; /** - /** * Represents a plugin {@code jar} file. * The {@code MANIFEST.MF} file is created on the fly from the information supplied in {@link Builder}. * @@ -87,6 +92,8 @@ public class PluginJar { private String pluginClass; private String pluginVersion; private Map manifestAttributes = new LinkedHashMap<>(); + private Set extensions = new LinkedHashSet<>(); + private ClassDataProvider classDataProvider = new DefaultClassDataProvider(); public Builder(Path path, String pluginId) { this.path = path; @@ -125,13 +132,44 @@ public class PluginJar { return this; } + public Builder extension(String extensionClassName) { + extensions.add(extensionClassName); + + return this; + } + + public Builder classDataProvider(ClassDataProvider classDataProvider) { + this.classDataProvider = classDataProvider; + + return this; + } + public PluginJar build() throws IOException { - createManifestFile(); + Manifest manifest = createManifest(); + try (OutputStream outputStream = new FileOutputStream(path.toFile())) { + JarOutputStream jarOutputStream = new JarOutputStream(outputStream, manifest); + if (!extensions.isEmpty()) { + // add extensions.idx + JarEntry jarEntry = new JarEntry("META-INF/extensions.idx"); + jarOutputStream.putNextEntry(jarEntry); + jarOutputStream.write(extensionsAsByteArray()); + jarOutputStream.closeEntry(); + // add extensions classes + for (String extension : extensions) { + String extensionPath = extension.replace('.', '/') + ".class"; + JarEntry classEntry = new JarEntry(extensionPath); + jarOutputStream.putNextEntry(classEntry); + jarOutputStream.write(classDataProvider.getClassData(extension)); + jarOutputStream.closeEntry(); + } + } + jarOutputStream.close(); + } return new PluginJar(this); } - protected void createManifestFile() throws IOException { + private Manifest createManifest() { Map map = new LinkedHashMap<>(); map.put(ManifestPluginDescriptorFinder.PLUGIN_ID, pluginId); map.put(ManifestPluginDescriptorFinder.PLUGIN_VERSION, pluginVersion); @@ -142,9 +180,19 @@ public class PluginJar { map.putAll(manifestAttributes); } - Manifest manifest = createManifest(map); - JarOutputStream outputStream = new JarOutputStream(new FileOutputStream(path.toFile()), manifest); - outputStream.close(); + return PluginJar.createManifest(map); + } + + private byte[] extensionsAsByteArray() throws IOException { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + PrintWriter writer = new PrintWriter(outputStream); + for (String extension : extensions) { + writer.println(extension); + } + writer.flush(); + + return outputStream.toByteArray(); + } } } diff --git a/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java b/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java index 5f48c9a..83bc0b5 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java +++ b/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java @@ -23,4 +23,9 @@ import org.pf4j.Extension; @Extension public class TestExtension implements TestExtensionPoint { + @Override + public String saySomething() { + return "I am a test extension"; + } + } diff --git a/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java b/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java index a33ac40..d29a7ab 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java +++ b/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java @@ -22,4 +22,6 @@ import org.pf4j.ExtensionPoint; */ public interface TestExtensionPoint extends ExtensionPoint { + String saySomething(); + } -- cgit v1.2.3