Parcourir la source

Improve PluginJar, add ClassDataProvider concept

tags/release-3.0.0
Decebal Suiu il y a 5 ans
Parent
révision
6fd8ae2384

+ 16
- 0
pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java Voir le fichier

@@ -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
- 0
pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java Voir le fichier

@@ -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
- 0
pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java Voir le fichier

@@ -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
- 0
pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java Voir le fichier

@@ -26,4 +26,9 @@ public class FailTestExtension implements TestExtensionPoint {
public FailTestExtension(String name) {
}

@Override
public String saySomething() {
return "I am a fail test extension";
}

}

+ 54
- 6
pf4j/src/test/java/org/pf4j/plugin/PluginJar.java Voir le fichier

@@ -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
- 0
pf4j/src/test/java/org/pf4j/plugin/TestExtension.java Voir le fichier

@@ -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
- 0
pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java Voir le fichier

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

String saySomething();

}

Chargement…
Annuler
Enregistrer