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 |
||||
jdk: |
||||
- openjdk7 |
||||
# JDK7 is not supported anymore; https://github.com/travis-ci/travis-ci/issues/7884#issuecomment-308451879 |
||||
# - oraclejdk7 |
||||
- oraclejdk8 |
||||
- openjdk11 |
||||
after_success: |
||||
- 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