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.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<TestExtensionPoint> 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();

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) {
}
@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 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<String, String> manifestAttributes = new LinkedHashMap<>();
private Set<String> 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<String, String> 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();
}
}
}

5
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";
}
}

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

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

Loading…
Cancel
Save