mirror of https://github.com/pf4j/pf4j.git
Decebal Suiu
6 years ago
committed by
GitHub
100 changed files with 2659 additions and 940 deletions
@ -1,9 +1,5 @@ |
|||||||
language: java |
language: java |
||||||
jdk: |
jdk: |
||||||
- openjdk7 |
|
||||||
# JDK7 is not supported anymore; https://github.com/travis-ci/travis-ci/issues/7884#issuecomment-308451879 |
|
||||||
# - oraclejdk7 |
|
||||||
- oraclejdk8 |
|
||||||
- openjdk11 |
- openjdk11 |
||||||
after_success: |
after_success: |
||||||
- mvn clean cobertura:cobertura coveralls:report |
- mvn clean cobertura:cobertura coveralls:report |
||||||
|
@ -0,0 +1,68 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<groupId>org.pf4j</groupId> |
||||||
|
<artifactId>pf4j-parent</artifactId> |
||||||
|
<version>3.0.0-SNAPSHOT</version> |
||||||
|
<relativePath>../../pom.xml</relativePath> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>pf4j-quickstart</artifactId> |
||||||
|
<packaging>maven-archetype</packaging> |
||||||
|
<name>Quickstart Archetype</name> |
||||||
|
|
||||||
|
<build> |
||||||
|
<!-- http://stackoverflow.com/questions/7223031/how-to-embed-archetype-project-version-in-maven-archetype --> |
||||||
|
<resources> |
||||||
|
<resource> |
||||||
|
<filtering>true</filtering> |
||||||
|
<directory>src/main/resources</directory> |
||||||
|
<includes> |
||||||
|
<include>archetype-resources/**/pom.xml</include> |
||||||
|
</includes> |
||||||
|
</resource> |
||||||
|
|
||||||
|
<resource> |
||||||
|
<filtering>false</filtering> |
||||||
|
<directory>src/main/resources</directory> |
||||||
|
<excludes> |
||||||
|
<exclude>archetype-resources/**/pom.xml</exclude> |
||||||
|
</excludes> |
||||||
|
</resource> |
||||||
|
</resources> |
||||||
|
|
||||||
|
<extensions> |
||||||
|
<extension> |
||||||
|
<groupId>org.apache.maven.archetype</groupId> |
||||||
|
<artifactId>archetype-packaging</artifactId> |
||||||
|
<version>2.3</version> |
||||||
|
</extension> |
||||||
|
</extensions> |
||||||
|
|
||||||
|
<pluginManagement> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-archetype-plugin</artifactId> |
||||||
|
<version>2.3</version> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<!-- http://stackoverflow.com/questions/7223031/how-to-embed-archetype-project-version-in-maven-archetype --> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-resources-plugin</artifactId> |
||||||
|
<version>2.5</version> |
||||||
|
<configuration> |
||||||
|
<delimiters> |
||||||
|
<delimiter>{{*}}</delimiter> |
||||||
|
</delimiters> |
||||||
|
<useDefaultDelimiters>false</useDefaultDelimiters> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</pluginManagement> |
||||||
|
</build> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,86 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<archetype-descriptor name="quickstart"> |
||||||
|
|
||||||
|
<fileSets> |
||||||
|
<fileSet encoding="UTF-8"> |
||||||
|
<directory></directory> |
||||||
|
<includes> |
||||||
|
<include>run.sh</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
|
||||||
|
<modules> |
||||||
|
<module id="${rootArtifactId}-app" dir="app" name="${rootArtifactId}-app"> |
||||||
|
<fileSets> |
||||||
|
<fileSet filtered="true" packaged="true" encoding="UTF-8"> |
||||||
|
<directory>src/main/java</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.java</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
<fileSet filtered="true" encoding="UTF-8"> |
||||||
|
<directory>src/main/resources</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.properties</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
<fileSet filtered="true" encoding="UTF-8"> |
||||||
|
<directory>src/main/assembly</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.xml</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
</module> |
||||||
|
|
||||||
|
<module id="${rootArtifactId}-plugins" dir="plugins" name="${rootArtifactId}-plugins"> |
||||||
|
<fileSets> |
||||||
|
<fileSet filtered="true" encoding="UTF-8"> |
||||||
|
<directory></directory> |
||||||
|
<includes> |
||||||
|
<include>enabled.txt</include> |
||||||
|
<include>disabled.txt</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
|
||||||
|
<modules> |
||||||
|
<module id="hello-plugin" dir="hello" name="hello-plugin"> |
||||||
|
<fileSets> |
||||||
|
<fileSet filtered="true" packaged="true" encoding="UTF-8"> |
||||||
|
<directory>src/main/java</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.java</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
<fileSet filtered="true" encoding="UTF-8"> |
||||||
|
<directory></directory> |
||||||
|
<includes> |
||||||
|
<include>plugin.properties</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
</module> |
||||||
|
|
||||||
|
<module id="welcome-plugin" dir="welcome" name="welcome-plugin"> |
||||||
|
<fileSets> |
||||||
|
<fileSet filtered="true" packaged="true" encoding="UTF-8"> |
||||||
|
<directory>src/main/java</directory> |
||||||
|
<includes> |
||||||
|
<include>**/*.java</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
<fileSet filtered="true" encoding="UTF-8"> |
||||||
|
<directory></directory> |
||||||
|
<includes> |
||||||
|
<include>plugin.properties</include> |
||||||
|
</includes> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
</module> |
||||||
|
</modules> |
||||||
|
</module> |
||||||
|
</modules> |
||||||
|
|
||||||
|
</archetype-descriptor> |
@ -0,0 +1,86 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<groupId>${groupId}</groupId> |
||||||
|
<artifactId>${rootArtifactId}</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>${artifactId}</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>App</name> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<main.class>${package}.Boot</main.class> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<artifactId>maven-assembly-plugin</artifactId> |
||||||
|
<version>2.3</version> |
||||||
|
<configuration> |
||||||
|
<descriptors> |
||||||
|
<descriptor>src/main/assembly/assembly.xml</descriptor> |
||||||
|
</descriptors> |
||||||
|
<appendAssemblyId>false</appendAssemblyId> |
||||||
|
</configuration> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<id>make-assembly</id> |
||||||
|
<phase>package</phase> |
||||||
|
<goals> |
||||||
|
<goal>attached</goal> |
||||||
|
</goals> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-jar-plugin</artifactId> |
||||||
|
<version>2.3.1</version> |
||||||
|
<configuration> |
||||||
|
<archive> |
||||||
|
<manifest> |
||||||
|
<addClasspath>true</addClasspath> |
||||||
|
<classpathPrefix>lib/</classpathPrefix> |
||||||
|
<mainClass>${main.class}</mainClass> |
||||||
|
</manifest> |
||||||
|
</archive> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j</groupId> |
||||||
|
<artifactId>pf4j</artifactId> |
||||||
|
<version>${pf4j.version}</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- Logs --> |
||||||
|
<dependency> |
||||||
|
<groupId>log4j</groupId> |
||||||
|
<artifactId>log4j</artifactId> |
||||||
|
<version>1.2.16</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.slf4j</groupId> |
||||||
|
<artifactId>slf4j-log4j12</artifactId> |
||||||
|
<version>${slf4j.version}</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>commons-lang</groupId> |
||||||
|
<artifactId>commons-lang</artifactId> |
||||||
|
<version>2.4</version> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,30 @@ |
|||||||
|
<assembly> |
||||||
|
<id>app</id> |
||||||
|
<formats> |
||||||
|
<format>dir</format> |
||||||
|
<format>zip</format> |
||||||
|
</formats> |
||||||
|
<includeBaseDirectory>false</includeBaseDirectory> |
||||||
|
<dependencySets> |
||||||
|
<dependencySet> |
||||||
|
<useProjectArtifact>false</useProjectArtifact> |
||||||
|
<outputDirectory>lib</outputDirectory> |
||||||
|
<includes> |
||||||
|
<include>*:jar:*</include> |
||||||
|
</includes> |
||||||
|
</dependencySet> |
||||||
|
</dependencySets> |
||||||
|
<fileSets> |
||||||
|
<fileSet> |
||||||
|
<directory>${project.build.directory}</directory> |
||||||
|
<outputDirectory></outputDirectory> |
||||||
|
<includes> |
||||||
|
<include>*.jar</include> |
||||||
|
</includes> |
||||||
|
<excludes> |
||||||
|
<exclude>*-javadoc.jar</exclude> |
||||||
|
<exclude>*-sources.jar</exclude> |
||||||
|
</excludes> |
||||||
|
</fileSet> |
||||||
|
</fileSets> |
||||||
|
</assembly> |
@ -0,0 +1,102 @@ |
|||||||
|
package ${package}; |
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
import org.pf4j.DefaultPluginManager; |
||||||
|
import org.pf4j.ExtensionFinder; |
||||||
|
import org.pf4j.PluginManager; |
||||||
|
import org.pf4j.PluginWrapper; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* A boot class that start the application. |
||||||
|
*/ |
||||||
|
public class Boot { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
// create the plugin manager
|
||||||
|
PluginManager pluginManager = new DefaultPluginManager(); |
||||||
|
|
||||||
|
// load the plugins
|
||||||
|
pluginManager.loadPlugins(); |
||||||
|
|
||||||
|
// enable a disabled plugin
|
||||||
|
// pluginManager.enablePlugin("welcome-plugin");
|
||||||
|
|
||||||
|
// start (active/resolved) the plugins
|
||||||
|
pluginManager.startPlugins(); |
||||||
|
|
||||||
|
// retrieves the extensions for Greeting extension point
|
||||||
|
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class); |
||||||
|
System.out.println(String.format("Found %d extensions for extension point '%s'", greetings.size(), Greeting.class.getName())); |
||||||
|
for (Greeting greeting : greetings) { |
||||||
|
System.out.println(">>> " + greeting.getGreeting()); |
||||||
|
} |
||||||
|
|
||||||
|
// print extensions from classpath (non plugin)
|
||||||
|
System.out.println("Extensions added by classpath:"); |
||||||
|
Set<String> extensionClassNames = pluginManager.getExtensionClassNames(null); |
||||||
|
for (String extension : extensionClassNames) { |
||||||
|
System.out.println(" " + extension); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println("Extension classes by classpath:"); |
||||||
|
List<Class<? extends Greeting>> greetingsClasses = pluginManager.getExtensionClasses(Greeting.class); |
||||||
|
for (Class<? extends Greeting> greeting : greetingsClasses) { |
||||||
|
System.out.println(" Class: " + greeting.getCanonicalName()); |
||||||
|
} |
||||||
|
|
||||||
|
// print extensions ids for each started plugin
|
||||||
|
List<PluginWrapper> startedPlugins = pluginManager.getStartedPlugins(); |
||||||
|
for (PluginWrapper plugin : startedPlugins) { |
||||||
|
String pluginId = plugin.getDescriptor().getPluginId(); |
||||||
|
System.out.println(String.format("Extensions added by plugin '%s':", pluginId)); |
||||||
|
extensionClassNames = pluginManager.getExtensionClassNames(pluginId); |
||||||
|
for (String extension : extensionClassNames) { |
||||||
|
System.out.println(" " + extension); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// print extensions instances for Greeting extension point for each started plugin
|
||||||
|
for (PluginWrapper plugin : startedPlugins) { |
||||||
|
String pluginId = plugin.getDescriptor().getPluginId(); |
||||||
|
System.out.println(String.format("Extensions instances added by plugin '%s' for extension point '%s':", pluginId, Greeting.class.getName())); |
||||||
|
List<Greeting> extensions = pluginManager.getExtensions(Greeting.class, pluginId); |
||||||
|
for (Object extension : extensions) { |
||||||
|
System.out.println(" " + extension); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// print extensions instances from classpath (non plugin)
|
||||||
|
System.out.println("Extensions instances added by classpath:"); |
||||||
|
List extensions = pluginManager.getExtensions((String) null); |
||||||
|
for (Object extension : extensions) { |
||||||
|
System.out.println(" " + extension); |
||||||
|
} |
||||||
|
|
||||||
|
// print extensions instances for each started plugin
|
||||||
|
for (PluginWrapper plugin : startedPlugins) { |
||||||
|
String pluginId = plugin.getDescriptor().getPluginId(); |
||||||
|
System.out.println(String.format("Extensions instances added by plugin '%s':", pluginId)); |
||||||
|
extensions = pluginManager.getExtensions(pluginId); |
||||||
|
for (Object extension : extensions) { |
||||||
|
System.out.println(" " + extension); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// stop the plugins
|
||||||
|
pluginManager.stopPlugins(); |
||||||
|
/* |
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
pluginManager.stopPlugins(); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
package ${package}; |
||||||
|
|
||||||
|
import org.pf4j.ExtensionPoint; |
||||||
|
|
||||||
|
public interface Greeting extends ExtensionPoint { |
||||||
|
|
||||||
|
String getGreeting(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package ${package}; |
||||||
|
|
||||||
|
import org.pf4j.Extension; |
||||||
|
|
||||||
|
@Extension |
||||||
|
public class WhazzupGreeting implements Greeting { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getGreeting() { |
||||||
|
return "Whazzup"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
log4j.rootLogger=DEBUG, Console |
||||||
|
|
||||||
|
# |
||||||
|
# PF4J log |
||||||
|
# |
||||||
|
log4j.logger.org.pf4j=DEBUG, Console |
||||||
|
# !!! Put the bellow classes on level TRACE when you are in trouble |
||||||
|
log4j.logger.org.pf4j.PluginClassLoader=DEBUG, Console |
||||||
|
log4j.logger.org.pf4j.AbstractExtensionFinder=DEBUG, Console |
||||||
|
log4j.additivity.org.pf4j=false |
||||||
|
log4j.additivity.org.pf4j.PluginClassLoader=false |
||||||
|
log4j.additivity.org.pf4j.AbstractExtensionFinder=false |
||||||
|
|
||||||
|
# |
||||||
|
# Appenders |
||||||
|
# |
||||||
|
log4j.appender.Console=org.apache.log4j.ConsoleAppender |
||||||
|
log4j.appender.Console.layout=org.apache.log4j.PatternLayout |
||||||
|
#log4j.appender.Console.layout.conversionPattern=%-5p - %-32.32c{1} - %m\n |
||||||
|
log4j.appender.Console.layout.ConversionPattern=%d %p %c - %m%n |
@ -0,0 +1,6 @@ |
|||||||
|
######################################## |
||||||
|
# - load all plugins except these |
||||||
|
# - add one plugin id on each line |
||||||
|
# - put this file in plugins folder |
||||||
|
######################################## |
||||||
|
#welcome-plugin |
@ -0,0 +1,6 @@ |
|||||||
|
######################################## |
||||||
|
# - load only these plugins |
||||||
|
# - add one plugin id on each line |
||||||
|
# - put this file in plugins folder |
||||||
|
######################################## |
||||||
|
#welcome-plugin |
@ -0,0 +1,5 @@ |
|||||||
|
plugin.id=hello-plugin |
||||||
|
plugin.class=${package}.hello.HelloPlugin |
||||||
|
plugin.version=${version} |
||||||
|
plugin.provider= |
||||||
|
plugin.dependencies= |
@ -0,0 +1,24 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<groupId>${groupId}</groupId> |
||||||
|
<artifactId>${rootArtifactId}-plugins</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>hello-plugin</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>Hello Plugin</name> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<plugin.id>hello-plugin</plugin.id> |
||||||
|
<plugin.class>${package}.hello.HelloPlugin</plugin.class> |
||||||
|
<plugin.version>${version}</plugin.version> |
||||||
|
<plugin.provider/> |
||||||
|
<plugin.dependencies/> |
||||||
|
</properties> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,37 @@ |
|||||||
|
package ${package}.hello; |
||||||
|
|
||||||
|
import org.pf4j.Extension; |
||||||
|
import org.pf4j.Plugin; |
||||||
|
import org.pf4j.PluginWrapper; |
||||||
|
import ${package}.Greeting; |
||||||
|
|
||||||
|
/** |
||||||
|
* A very simple plugin. |
||||||
|
*/ |
||||||
|
public class HelloPlugin extends Plugin { |
||||||
|
|
||||||
|
public HelloPlugin(PluginWrapper wrapper) { |
||||||
|
super(wrapper); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
System.out.println("HelloPlugin.start()"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void stop() { |
||||||
|
System.out.println("HelloPlugin.stop()"); |
||||||
|
} |
||||||
|
|
||||||
|
@Extension(ordinal=1) |
||||||
|
public static class HelloGreeting implements Greeting { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getGreeting() { |
||||||
|
return "Hello"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<groupId>${groupId}</groupId> |
||||||
|
<artifactId>${rootArtifactId}</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>${artifactId}</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
<packaging>pom</packaging> |
||||||
|
<name>Plugins Parent</name> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
||||||
|
|
||||||
|
<!-- Override below properties in each plugin's pom.xml --> |
||||||
|
<plugin.id></plugin.id> |
||||||
|
<plugin.class></plugin.class> |
||||||
|
<plugin.version></plugin.version> |
||||||
|
<plugin.provider></plugin.provider> |
||||||
|
<plugin.dependencies></plugin.dependencies> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-assembly-plugin</artifactId> |
||||||
|
<version>3.1.0</version> |
||||||
|
<configuration> |
||||||
|
<descriptorRefs> |
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef> |
||||||
|
</descriptorRefs> |
||||||
|
<finalName>${project.artifactId}-${project.version}-all</finalName> |
||||||
|
<appendAssemblyId>false</appendAssemblyId> |
||||||
|
<attach>false</attach> |
||||||
|
<archive> |
||||||
|
<manifest> |
||||||
|
<addDefaultImplementationEntries>true</addDefaultImplementationEntries> |
||||||
|
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> |
||||||
|
</manifest> |
||||||
|
<manifestEntries> |
||||||
|
<Plugin-Id>${plugin.id}</Plugin-Id> |
||||||
|
<Plugin-Version>${plugin.version}</Plugin-Version> |
||||||
|
<Plugin-Provider>${plugin.provider}</Plugin-Provider> |
||||||
|
<Plugin-Class>${plugin.class}</Plugin-Class> |
||||||
|
<Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> |
||||||
|
</manifestEntries> |
||||||
|
</archive> |
||||||
|
</configuration> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<id>make-assembly</id> |
||||||
|
<phase>package</phase> |
||||||
|
<goals> |
||||||
|
<goal>single</goal> |
||||||
|
</goals> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.pf4j</groupId> |
||||||
|
<artifactId>pf4j</artifactId> |
||||||
|
<version>${pf4j.version}</version> |
||||||
|
<!-- !!! VERY IMPORTANT --> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>${groupId}</groupId> |
||||||
|
<artifactId>${rootArtifactId}-app</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
<!-- !!! VERY IMPORTANT --> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,5 @@ |
|||||||
|
plugin.id=welcome-plugin |
||||||
|
plugin.class=${package}.welcome.WelcomePlugin |
||||||
|
plugin.version=${version} |
||||||
|
plugin.provider= |
||||||
|
plugin.dependencies= |
@ -0,0 +1,32 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<groupId>${groupId}</groupId> |
||||||
|
<artifactId>${rootArtifactId}-plugins</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<artifactId>welcome-plugin</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>Welcom Plugin</name> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<plugin.id>welcome-plugin</plugin.id> |
||||||
|
<plugin.class>${package}.welcome.WelcomePlugin</plugin.class> |
||||||
|
<plugin.version>${version}</plugin.version> |
||||||
|
<plugin.provider/> |
||||||
|
<plugin.dependencies/> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>commons-lang</groupId> |
||||||
|
<artifactId>commons-lang</artifactId> |
||||||
|
<version>2.6</version> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,41 @@ |
|||||||
|
package ${package}.welcome; |
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
|
||||||
|
import org.pf4j.PluginWrapper; |
||||||
|
import org.pf4j.RuntimeMode; |
||||||
|
import org.pf4j.Extension; |
||||||
|
import org.pf4j.Plugin; |
||||||
|
import ${package}.Greeting; |
||||||
|
|
||||||
|
public class WelcomePlugin extends Plugin { |
||||||
|
|
||||||
|
public WelcomePlugin(PluginWrapper wrapper) { |
||||||
|
super(wrapper); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
System.out.println("WelcomePlugin.start()"); |
||||||
|
// for testing the development mode
|
||||||
|
if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { |
||||||
|
System.out.println(StringUtils.upperCase("WelcomePlugin")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void stop() { |
||||||
|
System.out.println("WelcomePlugin.stop()"); |
||||||
|
} |
||||||
|
|
||||||
|
@Extension |
||||||
|
public static class WelcomeGreeting implements Greeting { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getGreeting() { |
||||||
|
return "Welcome"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
<?xml version="1.0"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<groupId>${groupId}</groupId> |
||||||
|
<artifactId>${artifactId}</artifactId> |
||||||
|
<version>${version}</version> |
||||||
|
<packaging>pom</packaging> |
||||||
|
<name>PF4J Quickstart</name> |
||||||
|
|
||||||
|
<repositories> |
||||||
|
<repository> |
||||||
|
<id>sonatype-nexus-snapshots</id> |
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots</url> |
||||||
|
<releases> |
||||||
|
<enabled>false</enabled> |
||||||
|
</releases> |
||||||
|
<snapshots> |
||||||
|
<enabled>true</enabled> |
||||||
|
</snapshots> |
||||||
|
</repository> |
||||||
|
</repositories> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||||
|
<maven.compiler.release>8</maven.compiler.release> |
||||||
|
|
||||||
|
<pf4j.version>{{project.version}}</pf4j.version> |
||||||
|
<slf4j.version>1.7.7</slf4j.version> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<build> |
||||||
|
<resources> |
||||||
|
<resource> |
||||||
|
<filtering>false</filtering> |
||||||
|
<directory>src/main/java</directory> |
||||||
|
<excludes> |
||||||
|
<exclude>**/*.java</exclude> |
||||||
|
</excludes> |
||||||
|
</resource> |
||||||
|
|
||||||
|
<resource> |
||||||
|
<directory>src/main/resources</directory> |
||||||
|
</resource> |
||||||
|
</resources> |
||||||
|
</build> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,23 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
# create artifacts using Maven |
||||||
|
mvn clean package -DskipTests |
||||||
|
|
||||||
|
# create "dist" directory |
||||||
|
rm -fr dist |
||||||
|
mkdir -p dist/plugins |
||||||
|
|
||||||
|
# copy plugins to "dist" directory |
||||||
|
cp plugins/*/target/*-all.jar dist/plugins/ |
||||||
|
cp plugins/enabled.txt dist/plugins/ |
||||||
|
cp plugins/disabled.txt dist/plugins/ |
||||||
|
|
||||||
|
cd dist |
||||||
|
|
||||||
|
# unzip app to "dist" directory |
||||||
|
jar xf ../app/target/*.zip |
||||||
|
|
||||||
|
# run app |
||||||
|
java -jar *.jar |
||||||
|
|
||||||
|
cd - |
@ -0,0 +1,47 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Module descriptor for PF4J. |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
* @author Andreas Rudolph |
||||||
|
*/ |
||||||
|
module org.pf4j { |
||||||
|
requires java.base; |
||||||
|
|
||||||
|
// provides javax.annotation
|
||||||
|
requires java.compiler; |
||||||
|
|
||||||
|
// provided by the ASM library
|
||||||
|
requires org.objectweb.asm; |
||||||
|
|
||||||
|
// The SLF4J library currently does not provide a module.
|
||||||
|
// Version 1.8 provides a module called "org.slf4j". But this version is
|
||||||
|
// currently in beta stage. Therefore I'm not sure, if we already like to
|
||||||
|
// use it.
|
||||||
|
requires slf4j.api; |
||||||
|
|
||||||
|
// The java-semver library currently does not provide a module.
|
||||||
|
// Maybe we should send them a pull request, that at least they provide an
|
||||||
|
// automatic module name in their MANIFEST file.
|
||||||
|
requires java.semver; |
||||||
|
|
||||||
|
// Maybe we should reconsider the package hierarchy, that only classes are
|
||||||
|
// exported, which are required by 3rd party developers.
|
||||||
|
exports org.pf4j; |
||||||
|
exports org.pf4j.processor; |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.pf4j.util.FileUtils; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Load all information needed by a plugin. |
||||||
|
* This means add to the plugin's {@link ClassLoader} all the jar files and |
||||||
|
* all the class files specified in the {@link PluginClasspath}. |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class BasePluginLoader implements PluginLoader { |
||||||
|
|
||||||
|
protected PluginManager pluginManager; |
||||||
|
protected PluginClasspath pluginClasspath; |
||||||
|
|
||||||
|
public BasePluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { |
||||||
|
this.pluginManager = pluginManager; |
||||||
|
this.pluginClasspath = pluginClasspath; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isApplicable(Path pluginPath) { |
||||||
|
return Files.exists(pluginPath); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { |
||||||
|
PluginClassLoader pluginClassLoader = createPluginClassLoader(pluginPath, pluginDescriptor); |
||||||
|
|
||||||
|
loadClasses(pluginPath, pluginClassLoader); |
||||||
|
loadJars(pluginPath, pluginClassLoader); |
||||||
|
|
||||||
|
return pluginClassLoader; |
||||||
|
} |
||||||
|
|
||||||
|
protected PluginClassLoader createPluginClassLoader(Path pluginPath, PluginDescriptor pluginDescriptor) { |
||||||
|
return new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add all {@code *.class} files from {@link PluginClasspath#getClassesDirectories()} |
||||||
|
* to the plugin's {@link ClassLoader}. |
||||||
|
*/ |
||||||
|
protected void loadClasses(Path pluginPath, PluginClassLoader pluginClassLoader) { |
||||||
|
for (String directory : pluginClasspath.getClassesDirectories()) { |
||||||
|
File file = pluginPath.resolve(directory).toFile(); |
||||||
|
if (file.exists() && file.isDirectory()) { |
||||||
|
pluginClassLoader.addFile(file); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add all {@code *.jar} files from {@link PluginClasspath#getJarsDirectories()} |
||||||
|
* to the plugin's {@link ClassLoader}. |
||||||
|
*/ |
||||||
|
protected void loadJars(Path pluginPath, PluginClassLoader pluginClassLoader) { |
||||||
|
for (String jarsDirectory : pluginClasspath.getJarsDirectories()) { |
||||||
|
Path file = pluginPath.resolve(jarsDirectory); |
||||||
|
List<File> jars = FileUtils.getJars(file); |
||||||
|
for (File jar : jars) { |
||||||
|
pluginClassLoader.addFile(jar); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
/** |
||||||
|
* Load all information needed by a plugin from {@link DevelopmentPluginClasspath}. |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class DevelopmentPluginLoader extends BasePluginLoader { |
||||||
|
|
||||||
|
public DevelopmentPluginLoader(PluginManager pluginManager) { |
||||||
|
super(pluginManager, new DevelopmentPluginClasspath()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
|
||||||
|
import org.pf4j.util.AndFileFilter; |
||||||
|
import org.pf4j.util.DirectoryFileFilter; |
||||||
|
import org.pf4j.util.HiddenFilter; |
||||||
|
import org.pf4j.util.NameFileFilter; |
||||||
|
import org.pf4j.util.NotFileFilter; |
||||||
|
import org.pf4j.util.OrFileFilter; |
||||||
|
|
||||||
|
import java.io.FileFilter; |
||||||
|
import java.nio.file.Path; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class DevelopmentPluginRepository extends BasePluginRepository { |
||||||
|
|
||||||
|
public static final String MAVEN_BUILD_DIR = "target"; |
||||||
|
public static final String GRADLE_BUILD_DIR = "build"; |
||||||
|
|
||||||
|
public DevelopmentPluginRepository(Path pluginsRoot) { |
||||||
|
super(pluginsRoot); |
||||||
|
|
||||||
|
AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); |
||||||
|
pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter())); |
||||||
|
setFilter(pluginsFilter); |
||||||
|
} |
||||||
|
|
||||||
|
protected FileFilter createHiddenPluginFilter() { |
||||||
|
OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); |
||||||
|
|
||||||
|
// skip default build output folders since these will cause errors in the logs
|
||||||
|
hiddenPluginFilter |
||||||
|
.addFileFilter(new NameFileFilter(MAVEN_BUILD_DIR)) |
||||||
|
.addFileFilter(new NameFileFilter(GRADLE_BUILD_DIR)); |
||||||
|
|
||||||
|
return hiddenPluginFilter; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import java.nio.file.Path; |
||||||
|
|
||||||
|
/** |
||||||
|
* It's a {@link PluginManager} that loads each plugin from a {@code jar} file. |
||||||
|
* Actually, a plugin is a fat jar, a jar which contains classes from all the libraries, |
||||||
|
* on which your project depends and, of course, the classes of current project. |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class JarPluginManager extends DefaultPluginManager { |
||||||
|
|
||||||
|
public JarPluginManager() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
public JarPluginManager(Path pluginsRoot) { |
||||||
|
super(pluginsRoot); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginDescriptorFinder createPluginDescriptorFinder() { |
||||||
|
return new ManifestPluginDescriptorFinder(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginLoader createPluginLoader() { |
||||||
|
return new CompoundPluginLoader() |
||||||
|
.add(new DevelopmentPluginLoader(this), this::isDevelopment) |
||||||
|
.add(new JarPluginLoader(this), this::isNotDevelopment); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginRepository createPluginRepository() { |
||||||
|
return new CompoundPluginRepository() |
||||||
|
.add(new DevelopmentPluginRepository(getPluginsRoot()), this::isDevelopment) |
||||||
|
.add(new JarPluginRepository(getPluginsRoot()), this::isNotDevelopment); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
/** |
||||||
|
* It's a {@link PluginManager} that loads each plugin from a {@code zip} file. |
||||||
|
* The structure of the zip file is: |
||||||
|
* <ul> |
||||||
|
* <li>{@code lib} directory that contains all dependencies (as jar files); it's optional (no dependencies) |
||||||
|
* <li>{@code classes} directory that contains all plugin's classes |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class ZipPluginManager extends DefaultPluginManager { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginDescriptorFinder createPluginDescriptorFinder() { |
||||||
|
return new PropertiesPluginDescriptorFinder(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginLoader createPluginLoader() { |
||||||
|
return new CompoundPluginLoader() |
||||||
|
.add(new DevelopmentPluginLoader(this), this::isDevelopment) |
||||||
|
.add(new DefaultPluginLoader(this), this::isNotDevelopment); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginRepository createPluginRepository() { |
||||||
|
return new CompoundPluginRepository() |
||||||
|
.add(new DevelopmentPluginRepository(getPluginsRoot()), this::isDevelopment) |
||||||
|
.add(new DefaultPluginRepository(getPluginsRoot()), this::isNotDevelopment); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.pf4j.plugin.TestExtension; |
||||||
|
import org.pf4j.plugin.TestExtensionPoint; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||||
|
import static org.mockito.Mockito.CALLS_REAL_METHODS; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
import static org.mockito.Mockito.when; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class AbstractPluginManagerTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void getExtensionsByType() { |
||||||
|
AbstractPluginManager pluginManager = mock(AbstractPluginManager.class, CALLS_REAL_METHODS); |
||||||
|
|
||||||
|
ExtensionFinder extensionFinder = mock(ExtensionFinder.class); |
||||||
|
List<ExtensionWrapper<TestExtensionPoint>> extensionList = new ArrayList<>(1); |
||||||
|
extensionList.add(new ExtensionWrapper<>(new ExtensionDescriptor(0, TestExtension.class), new DefaultExtensionFactory())); |
||||||
|
when(extensionFinder.find(TestExtensionPoint.class)).thenReturn(extensionList); |
||||||
|
|
||||||
|
pluginManager.extensionFinder = extensionFinder; |
||||||
|
List<TestExtensionPoint> extensions = pluginManager.getExtensions(TestExtensionPoint.class); |
||||||
|
assertEquals(1, extensions.size()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.io.TempDir; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Files; |
||||||
|
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; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class DevelopmentPluginRepositoryTest { |
||||||
|
|
||||||
|
@TempDir |
||||||
|
Path pluginsPath; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
public void setUp() throws IOException { |
||||||
|
// standard maven/gradle bin folder - these should be skipped in development mode because the cause errors
|
||||||
|
Files.createDirectory(pluginsPath.resolve(DevelopmentPluginRepository.MAVEN_BUILD_DIR)); |
||||||
|
Files.createDirectory(pluginsPath.resolve(DevelopmentPluginRepository.GRADLE_BUILD_DIR)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testGetPluginArchivesInDevelopmentMode() { |
||||||
|
PluginRepository repository = new DevelopmentPluginRepository(pluginsPath); |
||||||
|
|
||||||
|
List<Path> pluginPaths = repository.getPluginPaths(); |
||||||
|
|
||||||
|
// target and build should be ignored
|
||||||
|
assertEquals(0, pluginPaths.size()); |
||||||
|
assertPathDoesNotExists(pluginPaths, pluginsPath.resolve(DevelopmentPluginRepository.MAVEN_BUILD_DIR)); |
||||||
|
assertPathDoesNotExists(pluginPaths, pluginsPath.resolve(DevelopmentPluginRepository.GRADLE_BUILD_DIR)); |
||||||
|
} |
||||||
|
|
||||||
|
private void assertPathDoesNotExists(List<Path> paths, Path path) { |
||||||
|
assertFalse(paths.contains(path), "The directory must not contain the file " + path); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach; |
||||||
|
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; |
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue; |
||||||
|
|
||||||
|
public class JarPluginManagerTest { |
||||||
|
|
||||||
|
private PluginJar pluginJar; |
||||||
|
private JarPluginManager pluginManager; |
||||||
|
|
||||||
|
@TempDir |
||||||
|
Path pluginsPath; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
public void setUp() throws IOException { |
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
@AfterEach |
||||||
|
public void tearDown() { |
||||||
|
pluginJar = 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 |
||||||
|
public void unloadPlugin() throws Exception { |
||||||
|
pluginManager.loadPlugins(); |
||||||
|
|
||||||
|
assertEquals(1, pluginManager.getPlugins().size()); |
||||||
|
|
||||||
|
boolean unloaded = pluginManager.unloadPlugin(pluginJar.pluginId()); |
||||||
|
assertTrue(unloaded); |
||||||
|
|
||||||
|
assertTrue(pluginJar.file().exists()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void deletePlugin() throws Exception { |
||||||
|
pluginManager.loadPlugins(); |
||||||
|
|
||||||
|
assertEquals(1, pluginManager.getPlugins().size()); |
||||||
|
|
||||||
|
boolean deleted = pluginManager.deletePlugin(pluginJar.pluginId()); |
||||||
|
assertTrue(deleted); |
||||||
|
|
||||||
|
assertFalse(pluginJar.file().exists()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.io.TempDir; |
||||||
|
|
||||||
|
import java.nio.file.Files; |
||||||
|
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; |
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class JarPluginRepositoryTest { |
||||||
|
|
||||||
|
@TempDir |
||||||
|
Path pluginsPath; |
||||||
|
|
||||||
|
/** |
||||||
|
* Test of {@link JarPluginRepository#deletePluginPath(Path)} method. |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void testDeletePluginPath() throws Exception { |
||||||
|
PluginRepository repository = new JarPluginRepository(pluginsPath); |
||||||
|
|
||||||
|
Path plugin1Path = Files.createDirectory(pluginsPath.resolve("plugin-1")); |
||||||
|
Path plugin1JarPath = Files.createFile(pluginsPath.resolve("plugin-1.jar")); |
||||||
|
|
||||||
|
assertFalse(repository.deletePluginPath(plugin1Path)); |
||||||
|
|
||||||
|
List<Path> pluginPaths = repository.getPluginPaths(); |
||||||
|
assertEquals(1, pluginPaths.size()); |
||||||
|
|
||||||
|
assertTrue(repository.deletePluginPath(plugin1JarPath)); |
||||||
|
|
||||||
|
pluginPaths = repository.getPluginPaths(); |
||||||
|
assertEquals(0, pluginPaths.size()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.condition.EnabledOnOs; |
||||||
|
import org.junit.jupiter.api.io.TempDir; |
||||||
|
import org.pf4j.plugin.PluginJar; |
||||||
|
import org.pf4j.plugin.TestExtension; |
||||||
|
import org.pf4j.plugin.TestPlugin; |
||||||
|
|
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat; |
||||||
|
import static org.hamcrest.Matchers.contains; |
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse; |
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull; |
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue; |
||||||
|
import static org.junit.jupiter.api.condition.OS.WINDOWS; |
||||||
|
|
||||||
|
public class LegacyExtensionFinderTest { |
||||||
|
|
||||||
|
@TempDir |
||||||
|
Path pluginsPath; |
||||||
|
|
||||||
|
@Test |
||||||
|
@EnabledOnOs(WINDOWS) |
||||||
|
public void shouldUnlockFileAfterReadingExtensionsFromPlugin() throws Exception { |
||||||
|
PluginJar pluginJar = new PluginJar.Builder(pluginsPath.resolve("test-plugin.jar"), "test-plugin") |
||||||
|
.pluginClass(TestPlugin.class.getName()) |
||||||
|
.pluginVersion("1.2.3") |
||||||
|
.extension(TestExtension.class.getName()) |
||||||
|
.build(); |
||||||
|
|
||||||
|
assertTrue(pluginJar.file().exists()); |
||||||
|
|
||||||
|
PluginManager pluginManager = new JarPluginManager(pluginsPath); |
||||||
|
pluginManager.loadPlugins(); |
||||||
|
|
||||||
|
assertEquals(1, pluginManager.getPlugins().size()); |
||||||
|
|
||||||
|
LegacyExtensionFinder extensionFinder = new LegacyExtensionFinder(pluginManager); |
||||||
|
Map<String, Set<String>> pluginsStorages = extensionFinder.readPluginsStorages(); |
||||||
|
assertNotNull(pluginsStorages); |
||||||
|
|
||||||
|
pluginManager.unloadPlugin(pluginJar.pluginId()); |
||||||
|
boolean fileDeleted = pluginJar.file().delete(); |
||||||
|
|
||||||
|
Set<String> pluginStorages = pluginsStorages.get(pluginJar.pluginId()); |
||||||
|
assertNotNull(pluginStorages); |
||||||
|
assertEquals(1, pluginStorages.size()); |
||||||
|
assertThat(pluginStorages, contains(TestExtension.class.getName())); |
||||||
|
assertTrue(fileDeleted); |
||||||
|
assertFalse(pluginJar.file().exists()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,200 @@ |
|||||||
|
/* |
||||||
|
* 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 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}. |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class PluginJar { |
||||||
|
|
||||||
|
private final Path path; |
||||||
|
private final String pluginId; |
||||||
|
private final String pluginClass; |
||||||
|
private final String pluginVersion; |
||||||
|
|
||||||
|
protected PluginJar(Builder builder) { |
||||||
|
this.path = builder.path; |
||||||
|
this.pluginId = builder.pluginId; |
||||||
|
this.pluginClass = builder.pluginClass; |
||||||
|
this.pluginVersion = builder.pluginVersion; |
||||||
|
} |
||||||
|
|
||||||
|
public Path path() { |
||||||
|
return path; |
||||||
|
} |
||||||
|
|
||||||
|
public File file() { |
||||||
|
return path.toFile(); |
||||||
|
} |
||||||
|
|
||||||
|
public String pluginClass() { |
||||||
|
return pluginClass; |
||||||
|
} |
||||||
|
|
||||||
|
public String pluginId() { |
||||||
|
return pluginId; |
||||||
|
} |
||||||
|
|
||||||
|
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 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; |
||||||
|
this.pluginId = pluginId; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder pluginClass(String pluginClass) { |
||||||
|
this.pluginClass = pluginClass; |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder pluginVersion(String pluginVersion) { |
||||||
|
this.pluginVersion = pluginVersion; |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add extra attributes to the {@code manifest} file. |
||||||
|
* As possible attribute name please see {@link ManifestPluginDescriptorFinder}. |
||||||
|
*/ |
||||||
|
public Builder manifestAttributes(Map<String, String> manifestAttributes) { |
||||||
|
this.manifestAttributes.putAll(manifestAttributes); |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add extra attribute to the {@code manifest} file. |
||||||
|
* As possible attribute name please see {@link ManifestPluginDescriptorFinder}. |
||||||
|
*/ |
||||||
|
public Builder manifestAttribute(String name, String value) { |
||||||
|
manifestAttributes.put(name, value); |
||||||
|
|
||||||
|
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 { |
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
private Manifest createManifest() { |
||||||
|
Map<String, String> map = new LinkedHashMap<>(); |
||||||
|
map.put(ManifestPluginDescriptorFinder.PLUGIN_ID, pluginId); |
||||||
|
map.put(ManifestPluginDescriptorFinder.PLUGIN_VERSION, pluginVersion); |
||||||
|
if (pluginClass != null) { |
||||||
|
map.put(ManifestPluginDescriptorFinder.PLUGIN_CLASS, pluginClass); |
||||||
|
} |
||||||
|
if (manifestAttributes != null) { |
||||||
|
map.putAll(manifestAttributes); |
||||||
|
} |
||||||
|
|
||||||
|
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(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2015 Decebal Suiu |
||||||
|
* |
||||||
|
* 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.util; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeAll; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class DirectedGraphTest { |
||||||
|
|
||||||
|
private static DirectedGraph<Character> graph; |
||||||
|
|
||||||
|
@BeforeAll |
||||||
|
public static void setUp() { |
||||||
|
graph = new DirectedGraph<>(); |
||||||
|
|
||||||
|
// add vertex
|
||||||
|
graph.addVertex('A'); |
||||||
|
graph.addVertex('B'); |
||||||
|
graph.addVertex('C'); |
||||||
|
graph.addVertex('D'); |
||||||
|
graph.addVertex('E'); |
||||||
|
graph.addVertex('F'); |
||||||
|
graph.addVertex('G'); |
||||||
|
|
||||||
|
// add edges
|
||||||
|
graph.addEdge('A', 'B'); |
||||||
|
graph.addEdge('B', 'C'); |
||||||
|
graph.addEdge('B', 'F'); |
||||||
|
graph.addEdge('D', 'E'); |
||||||
|
graph.addEdge('F', 'G'); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void reverseTopologicalSort() { |
||||||
|
List<Character> result = graph.reverseTopologicalSort(); |
||||||
|
List<Character> expected = Arrays.asList('C', 'G', 'F', 'B', 'A', 'E', 'D'); |
||||||
|
assertEquals(expected, result); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void topologicalSort() { |
||||||
|
List<Character> result = graph.topologicalSort(); |
||||||
|
List<Character> expected = Arrays.asList('D', 'E', 'A', 'B', 'F', 'G', 'C'); |
||||||
|
assertEquals(expected, result); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void inDegree() { |
||||||
|
Map<Character, Integer> result = graph.inDegree(); |
||||||
|
Map<Character, Integer> expected = new HashMap<>(7); |
||||||
|
expected.put('A', 0); |
||||||
|
expected.put('B', 1); |
||||||
|
expected.put('C', 1); |
||||||
|
expected.put('D', 0); |
||||||
|
expected.put('E', 1); |
||||||
|
expected.put('F', 1); |
||||||
|
expected.put('G', 1); |
||||||
|
assertEquals(expected, result); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void outDegree() { |
||||||
|
Map<Character, Integer> result = graph.outDegree(); |
||||||
|
Map<Character, Integer> expected = new HashMap<>(7); |
||||||
|
expected.put('A', 1); |
||||||
|
expected.put('B', 2); |
||||||
|
expected.put('C', 0); |
||||||
|
expected.put('D', 1); |
||||||
|
expected.put('E', 0); |
||||||
|
expected.put('F', 1); |
||||||
|
expected.put('G', 0); |
||||||
|
assertEquals(expected, result); |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue