Browse Source

Improve PluginJar, add ClassDataProvider concept

pf4j_3
Decebal Suiu 6 years ago
parent
commit
6fd8ae2384
  1. 16
      pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java
  2. 33
      pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java
  3. 55
      pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java
  4. 5
      pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java
  5. 60
      pf4j/src/test/java/org/pf4j/plugin/PluginJar.java
  6. 5
      pf4j/src/test/java/org/pf4j/plugin/TestExtension.java
  7. 2
      pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java

16
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.Test;
import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.io.TempDir;
import org.pf4j.plugin.PluginJar; import org.pf4j.plugin.PluginJar;
import org.pf4j.plugin.TestExtension;
import org.pf4j.plugin.TestExtensionPoint;
import org.pf4j.plugin.TestPlugin; import org.pf4j.plugin.TestPlugin;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; 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") pluginJar = new PluginJar.Builder(pluginsPath.resolve("test-plugin.jar"), "test-plugin")
.pluginClass(TestPlugin.class.getName()) .pluginClass(TestPlugin.class.getName())
.pluginVersion("1.2.3") .pluginVersion("1.2.3")
.extension(TestExtension.class.getName())
.build(); .build();
pluginManager = new JarPluginManager(pluginsPath); pluginManager = new JarPluginManager(pluginsPath);
@ -53,6 +57,18 @@ public class JarPluginManagerTest {
pluginManager = null; pluginManager = null;
} }
@Test
public void getExtensions() {
pluginManager.loadPlugins();
pluginManager.startPlugins();
List<TestExtensionPoint> extensions = pluginManager.getExtensions(TestExtensionPoint.class);
assertEquals(1, extensions.size());
String something = extensions.get(0).saySomething();
assertEquals(new TestExtension().saySomething(), something);
}
@Test @Test
public void unloadPlugin() throws Exception { public void unloadPlugin() throws Exception {
pluginManager.loadPlugins(); pluginManager.loadPlugins();

33
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:
* <ul>
* <li>Class path - the class is already loaded by the class loader</li>
* <li>String - the string (the source code) is compiled dynamically via {@link javax.tools.JavaCompiler}</>
* <li>Generate the source code programmatically using something like {@code https://github.com/square/javapoet}</li>
* </ul>
*
* @author Decebal Suiu
*/
public interface ClassDataProvider {
byte[] getClassData(String className);
}

55
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);
}
}
}

5
pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java

@ -26,4 +26,9 @@ public class FailTestExtension implements TestExtensionPoint {
public FailTestExtension(String name) { public FailTestExtension(String name) {
} }
@Override
public String saySomething() {
return "I am a fail test extension";
}
} }

60
pf4j/src/test/java/org/pf4j/plugin/PluginJar.java

@ -17,18 +17,23 @@ package org.pf4j.plugin;
import org.pf4j.ManifestPluginDescriptorFinder; import org.pf4j.ManifestPluginDescriptorFinder;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
import java.util.jar.Manifest; import java.util.jar.Manifest;
/** /**
/**
* Represents a plugin {@code jar} file. * Represents a plugin {@code jar} file.
* The {@code MANIFEST.MF} file is created on the fly from the information supplied in {@link Builder}. * 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 pluginClass;
private String pluginVersion; private String pluginVersion;
private Map<String, String> manifestAttributes = new LinkedHashMap<>(); private Map<String, String> manifestAttributes = new LinkedHashMap<>();
private Set<String> extensions = new LinkedHashSet<>();
private ClassDataProvider classDataProvider = new DefaultClassDataProvider();
public Builder(Path path, String pluginId) { public Builder(Path path, String pluginId) {
this.path = path; this.path = path;
@ -125,13 +132,44 @@ public class PluginJar {
return this; 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 { 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); return new PluginJar(this);
} }
protected void createManifestFile() throws IOException { private Manifest createManifest() {
Map<String, String> map = new LinkedHashMap<>(); Map<String, String> map = new LinkedHashMap<>();
map.put(ManifestPluginDescriptorFinder.PLUGIN_ID, pluginId); map.put(ManifestPluginDescriptorFinder.PLUGIN_ID, pluginId);
map.put(ManifestPluginDescriptorFinder.PLUGIN_VERSION, pluginVersion); map.put(ManifestPluginDescriptorFinder.PLUGIN_VERSION, pluginVersion);
@ -142,9 +180,19 @@ public class PluginJar {
map.putAll(manifestAttributes); map.putAll(manifestAttributes);
} }
Manifest manifest = createManifest(map); return PluginJar.createManifest(map);
JarOutputStream outputStream = new JarOutputStream(new FileOutputStream(path.toFile()), manifest); }
outputStream.close();
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();
}
} }
} }

5
pf4j/src/test/java/org/pf4j/plugin/TestExtension.java

@ -23,4 +23,9 @@ import org.pf4j.Extension;
@Extension @Extension
public class TestExtension implements TestExtensionPoint { public class TestExtension implements TestExtensionPoint {
@Override
public String saySomething() {
return "I am a test extension";
}
} }

2
pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java

@ -22,4 +22,6 @@ import org.pf4j.ExtensionPoint;
*/ */
public interface TestExtensionPoint extends ExtensionPoint { public interface TestExtensionPoint extends ExtensionPoint {
String saySomething();
} }

Loading…
Cancel
Save