@@ -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 |
@@ -4,12 +4,12 @@ | |||
<parent> | |||
<groupId>org.pf4j.demo</groupId> | |||
<artifactId>pf4j-demo-parent</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>pf4j-demo-api</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
<packaging>jar</packaging> | |||
<name>Demo Api</name> | |||
@@ -4,12 +4,12 @@ | |||
<parent> | |||
<groupId>org.pf4j.demo</groupId> | |||
<artifactId>pf4j-demo-parent</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>pf4j-demo-app</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
<packaging>jar</packaging> | |||
<name>Demo App</name> | |||
@@ -73,8 +73,8 @@ public class Boot { | |||
} | |||
System.out.println("Extension classes by classpath:"); | |||
List<Class<Greeting>> greetingsClasses = pluginManager.getExtensionClasses(Greeting.class); | |||
for (Class<Greeting> greeting : greetingsClasses) { | |||
List<Class<? extends Greeting>> greetingsClasses = pluginManager.getExtensionClasses(Greeting.class); | |||
for (Class<? extends Greeting> greeting : greetingsClasses) { | |||
System.out.println(" Class: " + greeting.getCanonicalName()); | |||
} | |||
@@ -4,12 +4,12 @@ | |||
<parent> | |||
<groupId>org.pf4j.demo</groupId> | |||
<artifactId>pf4j-demo-plugins</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>pf4j-demo-plugin1</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
<packaging>jar</packaging> | |||
<name>Demo Plugin #1</name> | |||
@@ -4,12 +4,12 @@ | |||
<parent> | |||
<groupId>org.pf4j.demo</groupId> | |||
<artifactId>pf4j-demo-plugins</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>pf4j-demo-plugin2</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
<packaging>jar</packaging> | |||
<name>Demo Plugin #2</name> | |||
@@ -4,19 +4,18 @@ | |||
<parent> | |||
<groupId>org.pf4j.demo</groupId> | |||
<artifactId>pf4j-demo-parent</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>pf4j-demo-plugins</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
<packaging>pom</packaging> | |||
<name>Demo Plugins Parent</name> | |||
<properties> | |||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |||
<java.version>1.7</java.version> | |||
<!-- Override below properties in each plugin's pom.xml --> | |||
<plugin.id /> | |||
@@ -28,14 +27,6 @@ | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
<configuration> | |||
<source>${java.version}</source> | |||
<target>${java.version}</target> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-assembly-plugin</artifactId> |
@@ -4,13 +4,13 @@ | |||
<parent> | |||
<groupId>org.pf4j</groupId> | |||
<artifactId>pf4j-parent</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>org.pf4j.demo</groupId> | |||
<artifactId>pf4j-demo-parent</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
<packaging>pom</packaging> | |||
<name>Demo Parent</name> | |||
@@ -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 - |
@@ -4,12 +4,12 @@ | |||
<parent> | |||
<groupId>org.pf4j</groupId> | |||
<artifactId>pf4j-parent</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>pf4j</artifactId> | |||
<version>2.7.0-SNAPSHOT</version> | |||
<version>3.0.0-SNAPSHOT</version> | |||
<packaging>jar</packaging> | |||
<name>PF4J</name> | |||
<description>Plugin Framework for Java</description> | |||
@@ -22,6 +22,35 @@ | |||
<configuration> | |||
<compilerArgument>-proc:none</compilerArgument> | |||
</configuration> | |||
<executions> | |||
<!-- compile everything for Java 8 except the module-info.java --> | |||
<execution> | |||
<id>default-compile</id> | |||
<goals> | |||
<goal>compile</goal> | |||
</goals> | |||
<configuration> | |||
<excludes> | |||
<exclude>module-info.java</exclude> | |||
</excludes> | |||
</configuration> | |||
</execution> | |||
<!-- compile module-info.java for Java 9+ --> | |||
<execution> | |||
<id>java9-compile</id> | |||
<goals> | |||
<goal>compile</goal> | |||
</goals> | |||
<configuration> | |||
<release>9</release> | |||
<multiReleaseOutput>true</multiReleaseOutput> | |||
<includes> | |||
<include>module-info.java</include> | |||
</includes> | |||
</configuration> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
@@ -29,7 +58,7 @@ | |||
<configuration> | |||
<archive> | |||
<manifestEntries> | |||
<Automatic-Module-Name>org.pf4j</Automatic-Module-Name> | |||
<Multi-Release>true</Multi-Release> | |||
</manifestEntries> | |||
</archive> | |||
</configuration> | |||
@@ -69,11 +98,12 @@ | |||
</dependency> | |||
<dependency> | |||
<!-- An empty artifact, required while JUnit 4 is on the classpath to override its | |||
dependency on hamcrest. | |||
<!-- | |||
An empty artifact, required while JUnit 4 is on the classpath to override its | |||
dependency on hamcrest. | |||
See http://hamcrest.org/JavaHamcrest/distributables#upgrading-from-hamcrest-1x | |||
--> | |||
See http://hamcrest.org/JavaHamcrest/distributables#upgrading-from-hamcrest-1x | |||
--> | |||
<groupId>org.hamcrest</groupId> | |||
<artifactId>hamcrest-core</artifactId> | |||
<version>${hamcrest.version}</version> | |||
@@ -81,11 +111,12 @@ | |||
</dependency> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<groupId>org.junit.jupiter</groupId> | |||
<artifactId>junit-jupiter-engine</artifactId> | |||
<version>${junit.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-core</artifactId> |
@@ -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; | |||
} |
@@ -15,6 +15,10 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.pf4j.util.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.io.Closeable; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
@@ -27,9 +31,6 @@ import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.pf4j.util.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
/** | |||
* This class implements the boilerplate plugin code that any {@link PluginManager} | |||
@@ -44,6 +45,12 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
private static final Logger log = LoggerFactory.getLogger(AbstractPluginManager.class); | |||
public static final String PLUGINS_DIR_PROPERTY_NAME = "pf4j.pluginsDir"; | |||
public static final String MODE_PROPERTY_NAME = "pf4j.mode"; | |||
public static final String DEFAULT_PLUGINS_DIR = "plugins"; | |||
public static final String DEVELOPMENT_PLUGINS_DIR = "../plugins"; | |||
private Path pluginsRoot; | |||
protected ExtensionFinder extensionFinder; | |||
@@ -180,18 +187,12 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
log.debug("Loading plugin from '{}'", pluginPath); | |||
try { | |||
PluginWrapper pluginWrapper = loadPluginFromPath(pluginPath); | |||
PluginWrapper pluginWrapper = loadPluginFromPath(pluginPath); | |||
// try to resolve the loaded plugin together with other possible plugins that depend on this plugin | |||
resolvePlugins(); | |||
// try to resolve the loaded plugin together with other possible plugins that depend on this plugin | |||
resolvePlugins(); | |||
return pluginWrapper.getDescriptor().getPluginId(); | |||
} catch (PluginException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
return null; | |||
return pluginWrapper.getDescriptor().getPluginId(); | |||
} | |||
/** | |||
@@ -221,7 +222,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
for (Path pluginPath : pluginPaths) { | |||
try { | |||
loadPluginFromPath(pluginPath); | |||
} catch (PluginException e) { | |||
} catch (PluginRuntimeException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
} | |||
@@ -229,7 +230,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
// resolve plugins | |||
try { | |||
resolvePlugins(); | |||
} catch (PluginException e) { | |||
} catch (PluginRuntimeException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
} | |||
@@ -275,7 +276,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
try { | |||
((Closeable) classLoader).close(); | |||
} catch (IOException e) { | |||
log.error("Cannot close classloader", e); | |||
throw new PluginRuntimeException(e, "Cannot close classloader"); | |||
} | |||
} | |||
} | |||
@@ -293,23 +294,24 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
checkPluginId(pluginId); | |||
PluginWrapper pluginWrapper = getPlugin(pluginId); | |||
// stop the plugin if it's started | |||
PluginState pluginState = stopPlugin(pluginId); | |||
if (PluginState.STARTED == pluginState) { | |||
log.error("Failed to stop plugin '{}' on delete", pluginId); | |||
return false; | |||
} | |||
// get an instance of plugin before the plugin is unloaded | |||
// for reason see https://github.com/pf4j/pf4j/issues/309 | |||
Plugin plugin = pluginWrapper.getPlugin(); | |||
if (!unloadPlugin(pluginId)) { | |||
log.error("Failed to unload plugin '{}' on delete", pluginId); | |||
return false; | |||
} | |||
try { | |||
pluginWrapper.getPlugin().delete(); | |||
} catch (PluginException e) { | |||
log.error(e.getMessage(), e); | |||
return false; | |||
} | |||
// notify the plugin as it's deleted | |||
plugin.delete(); | |||
Path pluginPath = pluginWrapper.getPluginPath(); | |||
@@ -369,16 +371,12 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
startPlugin(dependency.getPluginId()); | |||
} | |||
try { | |||
log.info("Start plugin '{}'", getPluginLabel(pluginDescriptor)); | |||
pluginWrapper.getPlugin().start(); | |||
pluginWrapper.setPluginState(PluginState.STARTED); | |||
startedPlugins.add(pluginWrapper); | |||
log.info("Start plugin '{}'", getPluginLabel(pluginDescriptor)); | |||
pluginWrapper.getPlugin().start(); | |||
pluginWrapper.setPluginState(PluginState.STARTED); | |||
startedPlugins.add(pluginWrapper); | |||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | |||
} catch (PluginException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | |||
return pluginWrapper.getPluginState(); | |||
} | |||
@@ -402,7 +400,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
itr.remove(); | |||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | |||
} catch (PluginException e) { | |||
} catch (PluginRuntimeException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
} | |||
@@ -443,16 +441,12 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
} | |||
} | |||
try { | |||
log.info("Stop plugin '{}'", getPluginLabel(pluginDescriptor)); | |||
pluginWrapper.getPlugin().stop(); | |||
pluginWrapper.setPluginState(PluginState.STOPPED); | |||
startedPlugins.remove(pluginWrapper); | |||
log.info("Stop plugin '{}'", getPluginLabel(pluginDescriptor)); | |||
pluginWrapper.getPlugin().stop(); | |||
pluginWrapper.setPluginState(PluginState.STOPPED); | |||
startedPlugins.remove(pluginWrapper); | |||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | |||
} catch (PluginException e) { | |||
log.error(e.getMessage(), e); | |||
} | |||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | |||
return pluginWrapper.getPluginState(); | |||
} | |||
@@ -480,10 +474,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); | |||
if (!pluginStatusProvider.disablePlugin(pluginId)) { | |||
return false; | |||
} | |||
pluginStatusProvider.disablePlugin(pluginId); | |||
log.info("Disabled plugin '{}'", getPluginLabel(pluginDescriptor)); | |||
return true; | |||
@@ -509,9 +500,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
return true; | |||
} | |||
if (!pluginStatusProvider.enablePlugin(pluginId)) { | |||
return false; | |||
} | |||
pluginStatusProvider.enablePlugin(pluginId); | |||
pluginWrapper.setPluginState(PluginState.CREATED); | |||
@@ -539,55 +528,28 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
Class<?> c = extensionWrapper.getDescriptor().extensionClass; | |||
extensionClasses.add(c); | |||
} | |||
return extensionClasses; | |||
} | |||
@SuppressWarnings("unchecked") | |||
@Override | |||
public <T> List<Class<T>> getExtensionClasses(Class<T> type) { | |||
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type); | |||
List<Class<T>> extensionClasses = new ArrayList<>(extensionsWrapper.size()); | |||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | |||
Class<T> c = (Class<T>) extensionWrapper.getDescriptor().extensionClass; | |||
extensionClasses.add(c); | |||
} | |||
return extensionClasses; | |||
public <T> List<Class<? extends T>> getExtensionClasses(Class<T> type) { | |||
return getExtensionClasses(extensionFinder.find(type)); | |||
} | |||
@SuppressWarnings("unchecked") | |||
@Override | |||
public <T> List<Class<T>> getExtensionClasses(Class<T> type, String pluginId) { | |||
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type, pluginId); | |||
List<Class<T>> extensionClasses = new ArrayList<>(extensionsWrapper.size()); | |||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | |||
Class<T> c = (Class<T>) extensionWrapper.getDescriptor().extensionClass; | |||
extensionClasses.add(c); | |||
} | |||
return extensionClasses; | |||
public <T> List<Class<? extends T>> getExtensionClasses(Class<T> type, String pluginId) { | |||
return getExtensionClasses(extensionFinder.find(type, pluginId)); | |||
} | |||
@Override | |||
public <T> List<T> getExtensions(Class<T> type) { | |||
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type); | |||
List<T> extensions = new ArrayList<>(extensionsWrapper.size()); | |||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | |||
extensions.add(extensionWrapper.getExtension()); | |||
} | |||
return extensions; | |||
return getExtensions(extensionFinder.find(type)); | |||
} | |||
@Override | |||
public <T> List<T> getExtensions(Class<T> type, String pluginId) { | |||
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type, pluginId); | |||
List<T> extensions = new ArrayList<>(extensionsWrapper.size()); | |||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | |||
extensions.add(extensionWrapper.getExtension()); | |||
} | |||
return extensions; | |||
return getExtensions(extensionFinder.find(type, pluginId)); | |||
} | |||
@Override | |||
@@ -596,7 +558,11 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
List<ExtensionWrapper> extensionsWrapper = extensionFinder.find(pluginId); | |||
List extensions = new ArrayList<>(extensionsWrapper.size()); | |||
for (ExtensionWrapper extensionWrapper : extensionsWrapper) { | |||
extensions.add(extensionWrapper.getExtension()); | |||
try { | |||
extensions.add(extensionWrapper.getExtension()); | |||
} catch (PluginRuntimeException e) { | |||
log.error("Cannot retrieve extension", e); | |||
} | |||
} | |||
return extensions; | |||
@@ -612,7 +578,6 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
return extensionFactory; | |||
} | |||
// TODO remove | |||
public PluginLoader getPluginLoader() { | |||
return pluginLoader; | |||
} | |||
@@ -625,7 +590,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
public RuntimeMode getRuntimeMode() { | |||
if (runtimeMode == null) { | |||
// retrieves the runtime mode from system | |||
String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString()); | |||
String modeAsString = System.getProperty(MODE_PROPERTY_NAME, RuntimeMode.DEPLOYMENT.toString()); | |||
runtimeMode = RuntimeMode.byName(modeAsString); | |||
} | |||
@@ -723,20 +688,16 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
/** | |||
* Add the possibility to override the plugins root. | |||
* If a {@code pf4j.pluginsDir} system property is defined than this method returns that root. | |||
* If {@link #getRuntimeMode()} returns {@link RuntimeMode#DEVELOPMENT} than {@code ../plugins} | |||
* is returned else this method returns {@code plugins}. | |||
* If a {@link #PLUGINS_DIR_PROPERTY_NAME} system property is defined than this method returns that root. | |||
* If {@link #getRuntimeMode()} returns {@link RuntimeMode#DEVELOPMENT} than {@link #DEVELOPMENT_PLUGINS_DIR} | |||
* is returned else this method returns {@link #DEFAULT_PLUGINS_DIR}. | |||
* | |||
* @return the plugins root | |||
*/ | |||
protected Path createPluginsRoot() { | |||
String pluginsDir = System.getProperty("pf4j.pluginsDir"); | |||
String pluginsDir = System.getProperty(PLUGINS_DIR_PROPERTY_NAME); | |||
if (pluginsDir == null) { | |||
if (isDevelopment()) { | |||
pluginsDir = "../plugins"; | |||
} else { | |||
pluginsDir = "plugins"; | |||
} | |||
pluginsDir = isDevelopment() ? DEVELOPMENT_PLUGINS_DIR : DEFAULT_PLUGINS_DIR; | |||
} | |||
return Paths.get(pluginsDir); | |||
@@ -771,7 +732,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
return pluginStatusProvider.isPluginDisabled(pluginId); | |||
} | |||
protected void resolvePlugins() throws PluginException { | |||
protected void resolvePlugins() { | |||
// retrieves the plugins descriptors | |||
List<PluginDescriptor> descriptors = new ArrayList<>(); | |||
for (PluginWrapper plugin : plugins.values()) { | |||
@@ -820,7 +781,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
} | |||
} | |||
protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { | |||
protected PluginWrapper loadPluginFromPath(Path pluginPath) { | |||
// Test for plugin path duplication | |||
String pluginId = idForPath(pluginPath); | |||
if (pluginId != null) { | |||
@@ -838,7 +799,7 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
pluginId = pluginDescriptor.getPluginId(); | |||
if (plugins.containsKey(pluginId)) { | |||
PluginWrapper loadedPlugin = getPlugin(pluginId); | |||
throw new PluginException("There is an already loaded plugin ({}) " | |||
throw new PluginRuntimeException("There is an already loaded plugin ({}) " | |||
+ "with the same id ({}) as the plugin at path '{}'. Simultaneous loading " | |||
+ "of plugins with the same PluginId is not currently supported.\n" | |||
+ "As a workaround you may include PluginVersion and PluginProvider " | |||
@@ -859,7 +820,6 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
log.debug("Creating wrapper for plugin '{}'", pluginPath); | |||
PluginWrapper pluginWrapper = new PluginWrapper(this, pluginDescriptor, pluginPath, pluginClassLoader); | |||
pluginWrapper.setPluginFactory(getPluginFactory()); | |||
pluginWrapper.setRuntimeMode(getRuntimeMode()); | |||
// test for disabled plugin | |||
if (isPluginDisabled(pluginDescriptor.getPluginId())) { | |||
@@ -907,23 +867,18 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
* Override this to change the validation criteria. | |||
* | |||
* @param descriptor the plugin descriptor to validate | |||
* @throws PluginException if validation fails | |||
* @throws PluginRuntimeException if validation fails | |||
*/ | |||
protected void validatePluginDescriptor(PluginDescriptor descriptor) throws PluginException { | |||
protected void validatePluginDescriptor(PluginDescriptor descriptor) { | |||
if (StringUtils.isNullOrEmpty(descriptor.getPluginId())) { | |||
throw new PluginException("Field 'id' cannot be empty"); | |||
throw new PluginRuntimeException("Field 'id' cannot be empty"); | |||
} | |||
if (descriptor.getVersion() == null) { | |||
throw new PluginException("Field 'version' cannot be empty"); | |||
throw new PluginRuntimeException("Field 'version' cannot be empty"); | |||
} | |||
} | |||
// TODO add this method in PluginManager as default method for Java 8. | |||
protected boolean isDevelopment() { | |||
return RuntimeMode.DEVELOPMENT.equals(getRuntimeMode()); | |||
} | |||
/** | |||
* @return true if exact versions in requires is allowed | |||
*/ | |||
@@ -954,4 +909,28 @@ public abstract class AbstractPluginManager implements PluginManager { | |||
return pluginDescriptor.getPluginId() + "@" + pluginDescriptor.getVersion(); | |||
} | |||
@SuppressWarnings("unchecked") | |||
private <T> List<Class<? extends T>> getExtensionClasses(List<ExtensionWrapper<T>> extensionsWrapper) { | |||
List<Class<? extends T>> extensionClasses = new ArrayList<>(extensionsWrapper.size()); | |||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | |||
Class<T> c = (Class<T>) extensionWrapper.getDescriptor().extensionClass; | |||
extensionClasses.add(c); | |||
} | |||
return extensionClasses; | |||
} | |||
private <T> List<T> getExtensions(List<ExtensionWrapper<T>> extensionsWrapper) { | |||
List<T> extensions = new ArrayList<>(extensionsWrapper.size()); | |||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | |||
try { | |||
extensions.add(extensionWrapper.getExtension()); | |||
} catch (PluginRuntimeException e) { | |||
log.error("Cannot retrieve extension", e); | |||
} | |||
} | |||
return extensions; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} | |||
} |
@@ -48,14 +48,7 @@ public class BasePluginRepository implements PluginRepository { | |||
this.filter = filter; | |||
// last modified file is first | |||
this.comparator = new Comparator<File>() { | |||
@Override | |||
public int compare(File o1, File o2) { | |||
return (int) (o2.lastModified() - o1.lastModified()); | |||
} | |||
}; | |||
this.comparator = (o1, o2) -> (int) (o2.lastModified() - o1.lastModified()); | |||
} | |||
public void setFilter(FileFilter filter) { | |||
@@ -94,13 +87,17 @@ public class BasePluginRepository implements PluginRepository { | |||
@Override | |||
public boolean deletePluginPath(Path pluginPath) { | |||
if (!filter.accept(pluginPath.toFile())) { | |||
return false; | |||
} | |||
try { | |||
FileUtils.delete(pluginPath); | |||
return true; | |||
} catch (NoSuchFileException nsf) { | |||
return false; // Return false on not found to be compatible with previous API | |||
} catch (NoSuchFileException e) { | |||
return false; // Return false on not found to be compatible with previous API (#135) | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
throw new PluginRuntimeException(e); | |||
} | |||
} | |||
@@ -57,7 +57,7 @@ public class CompoundPluginDescriptorFinder implements PluginDescriptorFinder { | |||
} | |||
@Override | |||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||
public PluginDescriptor find(Path pluginPath) { | |||
for (PluginDescriptorFinder finder : finders) { | |||
if (finder.isApplicable(pluginPath)) { | |||
log.debug("'{}' is applicable for plugin '{}'", finder, pluginPath); | |||
@@ -81,7 +81,7 @@ public class CompoundPluginDescriptorFinder implements PluginDescriptorFinder { | |||
} | |||
} | |||
throw new PluginException("No PluginDescriptorFinder for plugin '{}'", pluginPath); | |||
throw new PluginRuntimeException("No PluginDescriptorFinder for plugin '{}'", pluginPath); | |||
} | |||
} |
@@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory; | |||
import java.nio.file.Path; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.function.BooleanSupplier; | |||
/** | |||
* @author Decebal Suiu | |||
@@ -41,6 +42,21 @@ public class CompoundPluginLoader implements PluginLoader { | |||
return this; | |||
} | |||
/** | |||
* Add a {@link PluginLoader} only if the {@code condition} is satisfied. | |||
* | |||
* @param loader | |||
* @param condition | |||
* @return | |||
*/ | |||
public CompoundPluginLoader add(PluginLoader loader, BooleanSupplier condition) { | |||
if (condition.getAsBoolean()) { | |||
return add(loader); | |||
} | |||
return this; | |||
} | |||
public int size() { | |||
return loaders.size(); | |||
} |
@@ -17,7 +17,10 @@ package org.pf4j; | |||
import java.nio.file.Path; | |||
import java.util.ArrayList; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.function.BooleanSupplier; | |||
/** | |||
* @author Decebal Suiu | |||
@@ -37,14 +40,29 @@ public class CompoundPluginRepository implements PluginRepository { | |||
return this; | |||
} | |||
/** | |||
* Add a {@link PluginRepository} only if the {@code condition} is satisfied. | |||
* | |||
* @param repository | |||
* @param condition | |||
* @return | |||
*/ | |||
public CompoundPluginRepository add(PluginRepository repository, BooleanSupplier condition) { | |||
if (condition.getAsBoolean()) { | |||
return add(repository); | |||
} | |||
return this; | |||
} | |||
@Override | |||
public List<Path> getPluginPaths() { | |||
List<Path> paths = new ArrayList<>(); | |||
Set<Path> paths = new LinkedHashSet<>(); | |||
for (PluginRepository repository : repositories) { | |||
paths.addAll(repository.getPluginPaths()); | |||
} | |||
return paths; | |||
return new ArrayList<>(paths); | |||
} | |||
@Override |
@@ -29,20 +29,16 @@ public class DefaultExtensionFactory implements ExtensionFactory { | |||
private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFactory.class); | |||
/** | |||
* Creates an extension instance. If an error occurs than that error is logged and the method returns {@code null}. | |||
* @param extensionClass | |||
* @return | |||
* Creates an extension instance. | |||
*/ | |||
@Override | |||
public Object create(Class<?> extensionClass) { | |||
public <T> T create(Class<T> extensionClass) { | |||
log.debug("Create instance for extension '{}'", extensionClass.getName()); | |||
try { | |||
return extensionClass.newInstance(); | |||
} catch (Exception e) { | |||
log.error(e.getMessage(), e); | |||
throw new PluginRuntimeException(e); | |||
} | |||
return null; | |||
} | |||
} |
@@ -16,17 +16,20 @@ | |||
package org.pf4j; | |||
/** | |||
* The default values are {@code classes} and {@code lib}. | |||
* The default values are {@link #CLASSES_DIR} and {@code #LIB_DIR}. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class DefaultPluginClasspath extends PluginClasspath { | |||
public static final String CLASSES_DIR = "classes"; | |||
public static final String LIB_DIR = "lib"; | |||
public DefaultPluginClasspath() { | |||
super(); | |||
addClassesDirectories("classes"); | |||
addLibDirectories("lib"); | |||
addClassesDirectories(CLASSES_DIR); | |||
addJarsDirectories(LIB_DIR); | |||
} | |||
} |
@@ -15,72 +15,23 @@ | |||
*/ | |||
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 classpath all jar files from {@code lib} directory | |||
* and all class files from {@code classes}. | |||
* Load all information needed by a plugin from {@link DefaultPluginClasspath}. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class DefaultPluginLoader implements PluginLoader { | |||
protected PluginManager pluginManager; | |||
protected PluginClasspath pluginClasspath; | |||
public class DefaultPluginLoader extends BasePluginLoader { | |||
public DefaultPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { | |||
this.pluginManager = pluginManager; | |||
this.pluginClasspath = pluginClasspath; | |||
public DefaultPluginLoader(PluginManager pluginManager) { | |||
super(pluginManager, new DefaultPluginClasspath()); | |||
} | |||
@Override | |||
public boolean isApplicable(Path pluginPath) { | |||
return Files.exists(pluginPath) && Files.isDirectory(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 {@code classes} directories to plugin class loader. | |||
*/ | |||
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 {@code lib} directories to plugin class loader. | |||
*/ | |||
protected void loadJars(Path pluginPath, PluginClassLoader pluginClassLoader) { | |||
for (String libDirectory : pluginClasspath.getLibDirectories()) { | |||
Path file = pluginPath.resolve(libDirectory); | |||
List<File> jars = FileUtils.getJars(file); | |||
for (File jar : jars) { | |||
pluginClassLoader.addFile(jar); | |||
} | |||
} | |||
return super.isApplicable(pluginPath) && Files.isDirectory(pluginPath); | |||
} | |||
} |
@@ -19,12 +19,13 @@ import org.pf4j.util.FileUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.io.File; | |||
import java.nio.file.Path; | |||
import java.nio.file.Paths; | |||
/** | |||
* Default implementation of the {@link PluginManager} interface. | |||
* In essence it is a {@link ZipPluginManager} plus a {@link JarPluginManager}. | |||
* So, it can load plugins from jar and zip, simultaneous. | |||
* | |||
* <p>This class is not thread-safe. | |||
* | |||
@@ -34,22 +35,12 @@ public class DefaultPluginManager extends AbstractPluginManager { | |||
private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); | |||
protected PluginClasspath pluginClasspath; | |||
public static final String PLUGINS_DIR_CONFIG_PROPERTY_NAME = "pf4j.pluginsConfigDir"; | |||
public DefaultPluginManager() { | |||
super(); | |||
} | |||
/** | |||
* Use {@link DefaultPluginManager#DefaultPluginManager(Path)}. | |||
* | |||
* @param pluginsDir | |||
*/ | |||
@Deprecated | |||
public DefaultPluginManager(File pluginsDir) { | |||
this(pluginsDir.toPath()); | |||
} | |||
public DefaultPluginManager(Path pluginsRoot) { | |||
super(pluginsRoot); | |||
} | |||
@@ -81,23 +72,26 @@ public class DefaultPluginManager extends AbstractPluginManager { | |||
@Override | |||
protected PluginStatusProvider createPluginStatusProvider() { | |||
String configDir = System.getProperty("pf4j.pluginsConfigDir"); | |||
String configDir = System.getProperty(PLUGINS_DIR_CONFIG_PROPERTY_NAME); | |||
Path configPath = configDir != null ? Paths.get(configDir) : getPluginsRoot(); | |||
return new DefaultPluginStatusProvider(configPath); | |||
} | |||
@Override | |||
protected PluginRepository createPluginRepository() { | |||
return new CompoundPluginRepository() | |||
.add(new DefaultPluginRepository(getPluginsRoot(), isDevelopment())) | |||
.add(new JarPluginRepository(getPluginsRoot())); | |||
.add(new DevelopmentPluginRepository(getPluginsRoot()), this::isDevelopment) | |||
.add(new JarPluginRepository(getPluginsRoot()), this::isNotDevelopment) | |||
.add(new DefaultPluginRepository(getPluginsRoot()), this::isNotDevelopment); | |||
} | |||
@Override | |||
protected PluginLoader createPluginLoader() { | |||
return new CompoundPluginLoader() | |||
.add(new DefaultPluginLoader(this, pluginClasspath)) | |||
.add(new JarPluginLoader(this)); | |||
.add(new DevelopmentPluginLoader(this), this::isDevelopment) | |||
.add(new JarPluginLoader(this), this::isNotDevelopment) | |||
.add(new DefaultPluginLoader(this), this::isNotDevelopment); | |||
} | |||
@Override | |||
@@ -105,19 +99,8 @@ public class DefaultPluginManager extends AbstractPluginManager { | |||
return new DefaultVersionManager(); | |||
} | |||
/** | |||
* By default if {@link DefaultPluginManager#isDevelopment()} returns true | |||
* than a {@link DevelopmentPluginClasspath} is returned | |||
* else this method returns {@link DefaultPluginClasspath}. | |||
*/ | |||
protected PluginClasspath createPluginClasspath() { | |||
return isDevelopment() ? new DevelopmentPluginClasspath() : new DefaultPluginClasspath(); | |||
} | |||
@Override | |||
protected void initialize() { | |||
pluginClasspath = createPluginClasspath(); | |||
super.initialize(); | |||
if (isDevelopment()) { | |||
@@ -128,13 +111,14 @@ public class DefaultPluginManager extends AbstractPluginManager { | |||
} | |||
/** | |||
* Load a plugin from disk. If the path is a zip file, first unpack | |||
* Load a plugin from disk. If the path is a zip file, first unpack. | |||
* | |||
* @param pluginPath plugin location on disk | |||
* @return PluginWrapper for the loaded plugin or null if not loaded | |||
* @throws PluginException if problems during load | |||
* @throws PluginRuntimeException if problems during load | |||
*/ | |||
@Override | |||
protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { | |||
protected PluginWrapper loadPluginFromPath(Path pluginPath) { | |||
// First unzip any ZIP files | |||
try { | |||
pluginPath = FileUtils.expandIfZip(pluginPath); |
@@ -24,7 +24,6 @@ import org.pf4j.util.OrFileFilter; | |||
import org.pf4j.util.ZipFileFilter; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.pf4j.util.NameFileFilter; | |||
import java.io.File; | |||
import java.io.FileFilter; | |||
@@ -39,29 +38,17 @@ public class DefaultPluginRepository extends BasePluginRepository { | |||
private static final Logger log = LoggerFactory.getLogger(DefaultPluginRepository.class); | |||
public DefaultPluginRepository(Path pluginsRoot, boolean development) { | |||
public DefaultPluginRepository(Path pluginsRoot) { | |||
super(pluginsRoot); | |||
AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); | |||
pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter(development))); | |||
pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter())); | |||
setFilter(pluginsFilter); | |||
} | |||
@Override | |||
public List<Path> getPluginPaths() { | |||
// expand plugins zip files | |||
File[] pluginZips = pluginsRoot.toFile().listFiles(new ZipFileFilter()); | |||
if ((pluginZips != null) && pluginZips.length > 0) { | |||
for (File pluginZip : pluginZips) { | |||
try { | |||
FileUtils.expandIfZip(pluginZip.toPath()); | |||
} catch (IOException e) { | |||
log.error("Cannot expand plugin zip '{}'", pluginZip); | |||
log.error(e.getMessage(), e); | |||
} | |||
} | |||
} | |||
extractZipFiles(); | |||
return super.getPluginPaths(); | |||
} | |||
@@ -71,16 +58,23 @@ public class DefaultPluginRepository extends BasePluginRepository { | |||
return super.deletePluginPath(pluginPath); | |||
} | |||
protected FileFilter createHiddenPluginFilter(boolean development) { | |||
OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); | |||
protected FileFilter createHiddenPluginFilter() { | |||
return new OrFileFilter(new HiddenFilter()); | |||
} | |||
if (development) { | |||
// skip default build output folders since these will cause errors in the logs | |||
hiddenPluginFilter | |||
.addFileFilter(new NameFileFilter("target")) // MAVEN | |||
.addFileFilter(new NameFileFilter("build")); // GRADLE | |||
private void extractZipFiles() { | |||
// expand plugins zip files | |||
File[] zipFiles = pluginsRoot.toFile().listFiles(new ZipFileFilter()); | |||
if ((zipFiles != null) && zipFiles.length > 0) { | |||
for (File pluginZip : zipFiles) { | |||
try { | |||
FileUtils.expandIfZip(pluginZip.toPath()); | |||
} catch (IOException e) { | |||
log.error("Cannot expand plugin zip '{}'", pluginZip); | |||
log.error(e.getMessage(), e); | |||
} | |||
} | |||
} | |||
return hiddenPluginFilter; | |||
} | |||
} |
@@ -66,31 +66,23 @@ public class DefaultPluginStatusProvider implements PluginStatusProvider { | |||
} | |||
@Override | |||
public boolean disablePlugin(String pluginId) { | |||
if (disabledPlugins.add(pluginId)) { | |||
try { | |||
FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); | |||
} catch (IOException e) { | |||
log.error("Failed to disable plugin {}", pluginId, e); | |||
return false; | |||
} | |||
public void disablePlugin(String pluginId) { | |||
disabledPlugins.add(pluginId); | |||
try { | |||
FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); | |||
} catch (IOException e) { | |||
throw new PluginRuntimeException(e); | |||
} | |||
return true; | |||
} | |||
@Override | |||
public boolean enablePlugin(String pluginId) { | |||
if (disabledPlugins.remove(pluginId)) { | |||
try { | |||
FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); | |||
} catch (IOException e) { | |||
log.error("Failed to enable plugin {}", pluginId, e); | |||
return false; | |||
} | |||
public void enablePlugin(String pluginId) { | |||
disabledPlugins.remove(pluginId); | |||
try { | |||
FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); | |||
} catch (IOException e) { | |||
throw new PluginRuntimeException(e); | |||
} | |||
return true; | |||
} | |||
} |
@@ -272,7 +272,7 @@ public class DependencyResolver { | |||
/** | |||
* It will be thrown if a cyclic dependency is detected. | |||
*/ | |||
public static class CyclicDependencyException extends PluginException { | |||
public static class CyclicDependencyException extends PluginRuntimeException { | |||
public CyclicDependencyException() { | |||
super("Cyclic dependencies"); | |||
@@ -283,7 +283,7 @@ public class DependencyResolver { | |||
/** | |||
* Indicates that the dependencies required were not found. | |||
*/ | |||
public static class DependenciesNotFoundException extends PluginException { | |||
public static class DependenciesNotFoundException extends PluginRuntimeException { | |||
private List<String> dependencies; | |||
@@ -302,7 +302,7 @@ public class DependencyResolver { | |||
/** | |||
* Indicates that some dependencies have wrong version. | |||
*/ | |||
public static class DependenciesWrongVersionException extends PluginException { | |||
public static class DependenciesWrongVersionException extends PluginRuntimeException { | |||
private List<WrongDependencyVersion> dependencies; | |||
@@ -27,7 +27,7 @@ public class DevelopmentPluginClasspath extends PluginClasspath { | |||
* The development plugin classpath for <a href="https://maven.apache.org">Maven</a>. | |||
* The classes directory is {@code target/classes} and the lib directory is {@code target/lib}. | |||
*/ | |||
public static final PluginClasspath MAVEN = new PluginClasspath().addClassesDirectories("target/classes").addLibDirectories("target/lib"); | |||
public static final PluginClasspath MAVEN = new PluginClasspath().addClassesDirectories("target/classes").addJarsDirectories("target/lib"); | |||
/** | |||
* The development plugin classpath for <a href="https://gradle.org">Gradle</a>. | |||
@@ -46,9 +46,9 @@ public class DevelopmentPluginClasspath extends PluginClasspath { | |||
addClassesDirectories(GRADLE.getClassesDirectories()); | |||
addClassesDirectories(KOTLIN.getClassesDirectories()); | |||
addLibDirectories(MAVEN.getLibDirectories()); | |||
addLibDirectories(GRADLE.getLibDirectories()); | |||
addLibDirectories(KOTLIN.getLibDirectories()); | |||
addJarsDirectories(MAVEN.getJarsDirectories()); | |||
addJarsDirectories(GRADLE.getJarsDirectories()); | |||
addJarsDirectories(KOTLIN.getJarsDirectories()); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -20,6 +20,6 @@ package org.pf4j; | |||
*/ | |||
public interface ExtensionFactory { | |||
Object create(Class<?> extensionClass); | |||
<T> T create(Class<T> extensionClass); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -20,6 +20,7 @@ import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.InputStreamReader; | |||
import java.io.Reader; | |||
import java.net.URL; | |||
@@ -81,11 +82,14 @@ public class LegacyExtensionFinder extends AbstractExtensionFinder { | |||
Set<String> bucket = new HashSet<>(); | |||
try { | |||
Enumeration<URL> urls = ((PluginClassLoader) plugin.getPluginClassLoader()).findResources(getExtensionsResource()); | |||
if (urls.hasMoreElements()) { | |||
collectExtensions(urls, bucket); | |||
} else { | |||
log.debug("Cannot find '{}'", getExtensionsResource()); | |||
log.debug("Read '{}'", getExtensionsResource()); | |||
ClassLoader pluginClassLoader = plugin.getPluginClassLoader(); | |||
try (InputStream resourceStream = pluginClassLoader.getResourceAsStream(getExtensionsResource())) { | |||
if (resourceStream == null) { | |||
log.debug("Cannot find '{}'", getExtensionsResource()); | |||
} else { | |||
collectExtensions(resourceStream, bucket); | |||
} | |||
} | |||
debugExtensions(bucket); | |||
@@ -103,9 +107,13 @@ public class LegacyExtensionFinder extends AbstractExtensionFinder { | |||
while (urls.hasMoreElements()) { | |||
URL url = urls.nextElement(); | |||
log.debug("Read '{}'", url.getFile()); | |||
try (Reader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { | |||
LegacyExtensionStorage.read(reader, bucket); | |||
} | |||
collectExtensions(url.openStream(), bucket); | |||
} | |||
} | |||
private void collectExtensions(InputStream inputStream, Set<String> bucket) throws IOException { | |||
try (Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { | |||
LegacyExtensionStorage.read(reader, bucket); | |||
} | |||
} | |||
@@ -37,19 +37,28 @@ public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { | |||
private static final Logger log = LoggerFactory.getLogger(ManifestPluginDescriptorFinder.class); | |||
public static final String PLUGIN_ID = "Plugin-Id"; | |||
public static final String PLUGIN_DESCRIPTION = "Plugin-Description"; | |||
public static final String PLUGIN_CLASS = "Plugin-Class"; | |||
public static final String PLUGIN_VERSION = "Plugin-Version"; | |||
public static final String PLUGIN_PROVIDER = "Plugin-Provider"; | |||
public static final String PLUGIN_DEPENDENCIES = "Plugin-Dependencies"; | |||
public static final String PLUGIN_REQUIRES = "Plugin-Requires"; | |||
public static final String PLUGIN_LICENSE = "Plugin-License"; | |||
@Override | |||
public boolean isApplicable(Path pluginPath) { | |||
return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isJarFile(pluginPath)); | |||
} | |||
@Override | |||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||
public PluginDescriptor find(Path pluginPath) { | |||
Manifest manifest = readManifest(pluginPath); | |||
return createPluginDescriptor(manifest); | |||
} | |||
protected Manifest readManifest(Path pluginPath) throws PluginException { | |||
protected Manifest readManifest(Path pluginPath) { | |||
if (FileUtils.isJarFile(pluginPath)) { | |||
try (JarFile jar = new JarFile(pluginPath.toFile())) { | |||
Manifest manifest = jar.getManifest(); | |||
@@ -57,28 +66,28 @@ public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { | |||
return manifest; | |||
} | |||
} catch (IOException e) { | |||
throw new PluginException(e); | |||
throw new PluginRuntimeException(e); | |||
} | |||
} | |||
Path manifestPath = getManifestPath(pluginPath); | |||
if (manifestPath == null) { | |||
throw new PluginException("Cannot find the manifest path"); | |||
throw new PluginRuntimeException("Cannot find the manifest path"); | |||
} | |||
log.debug("Lookup plugin descriptor in '{}'", manifestPath); | |||
if (Files.notExists(manifestPath)) { | |||
throw new PluginException("Cannot find '{}' path", manifestPath); | |||
throw new PluginRuntimeException("Cannot find '{}' path", manifestPath); | |||
} | |||
try (InputStream input = Files.newInputStream(manifestPath)) { | |||
return new Manifest(input); | |||
} catch (IOException e) { | |||
throw new PluginException(e); | |||
throw new PluginRuntimeException(e); | |||
} | |||
} | |||
protected Path getManifestPath(Path pluginPath) throws PluginException { | |||
protected Path getManifestPath(Path pluginPath) { | |||
if (Files.isDirectory(pluginPath)) { | |||
// legacy (the path is something like "classes/META-INF/MANIFEST.MF") | |||
return FileUtils.findFile(pluginPath,"MANIFEST.MF"); | |||
@@ -92,37 +101,37 @@ public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { | |||
// TODO validate !!! | |||
Attributes attributes = manifest.getMainAttributes(); | |||
String id = attributes.getValue("Plugin-Id"); | |||
String id = attributes.getValue(PLUGIN_ID); | |||
pluginDescriptor.setPluginId(id); | |||
String description = attributes.getValue("Plugin-Description"); | |||
String description = attributes.getValue(PLUGIN_DESCRIPTION); | |||
if (StringUtils.isNullOrEmpty(description)) { | |||
pluginDescriptor.setPluginDescription(""); | |||
} else { | |||
pluginDescriptor.setPluginDescription(description); | |||
} | |||
String clazz = attributes.getValue("Plugin-Class"); | |||
String clazz = attributes.getValue(PLUGIN_CLASS); | |||
if (StringUtils.isNotNullOrEmpty(clazz)) { | |||
pluginDescriptor.setPluginClass(clazz); | |||
} | |||
String version = attributes.getValue("Plugin-Version"); | |||
String version = attributes.getValue(PLUGIN_VERSION); | |||
if (StringUtils.isNotNullOrEmpty(version)) { | |||
pluginDescriptor.setPluginVersion(version); | |||
} | |||
String provider = attributes.getValue("Plugin-Provider"); | |||
String provider = attributes.getValue(PLUGIN_PROVIDER); | |||
pluginDescriptor.setProvider(provider); | |||
String dependencies = attributes.getValue("Plugin-Dependencies"); | |||
String dependencies = attributes.getValue(PLUGIN_DEPENDENCIES); | |||
pluginDescriptor.setDependencies(dependencies); | |||
String requires = attributes.getValue("Plugin-Requires"); | |||
String requires = attributes.getValue(PLUGIN_REQUIRES); | |||
if (StringUtils.isNotNullOrEmpty(requires)) { | |||
pluginDescriptor.setRequires(requires); | |||
} | |||
pluginDescriptor.setLicense(attributes.getValue("Plugin-License")); | |||
pluginDescriptor.setLicense(attributes.getValue(PLUGIN_LICENSE)); | |||
return pluginDescriptor; | |||
} |
@@ -60,21 +60,21 @@ public class Plugin { | |||
* This method is called by the application when the plugin is started. | |||
* See {@link PluginManager#startPlugin(String)}. | |||
*/ | |||
public void start() throws PluginException { | |||
public void start() { | |||
} | |||
/** | |||
* This method is called by the application when the plugin is stopped. | |||
* See {@link PluginManager#stopPlugin(String)}. | |||
*/ | |||
public void stop() throws PluginException { | |||
public void stop() { | |||
} | |||
/** | |||
* This method is called by the application when the plugin is deleted. | |||
* See {@link PluginManager#deletePlugin(String)}. | |||
*/ | |||
public void delete() throws PluginException { | |||
public void delete() { | |||
} | |||
} |
@@ -20,7 +20,7 @@ import java.nio.file.Path; | |||
/** | |||
* @author Decebal Suiu | |||
*/ | |||
public class PluginAlreadyLoadedException extends PluginException { | |||
public class PluginAlreadyLoadedException extends PluginRuntimeException { | |||
private final String pluginId; | |||
private final Path pluginPath; |
@@ -22,14 +22,15 @@ import java.util.Set; | |||
/** | |||
* The classpath of the plugin. | |||
* It contains {@code classes} directories and {@code lib} directories (directories that contains jars). | |||
* It contains {@code classes} directories (directories that contain classes files) | |||
* and {@code jars} directories (directories that contain jars files). | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class PluginClasspath { | |||
private Set<String> classesDirectories = new HashSet<>(); | |||
private Set<String> libDirectories = new HashSet<>(); | |||
private Set<String> jarsDirectories = new HashSet<>(); | |||
public Set<String> getClassesDirectories() { | |||
return classesDirectories; | |||
@@ -45,16 +46,16 @@ public class PluginClasspath { | |||
return this; | |||
} | |||
public Set<String> getLibDirectories() { | |||
return libDirectories; | |||
public Set<String> getJarsDirectories() { | |||
return jarsDirectories; | |||
} | |||
public PluginClasspath addLibDirectories(String... libDirectories) { | |||
return addLibDirectories(Arrays.asList(libDirectories)); | |||
public PluginClasspath addJarsDirectories(String... jarsDirectories) { | |||
return addJarsDirectories(Arrays.asList(jarsDirectories)); | |||
} | |||
public PluginClasspath addLibDirectories(Collection<String> libDirectories) { | |||
this.libDirectories.addAll(libDirectories); | |||
public PluginClasspath addJarsDirectories(Collection<String> jarsDirectories) { | |||
this.jarsDirectories.addAll(jarsDirectories); | |||
return this; | |||
} |
@@ -29,12 +29,9 @@ public interface PluginDescriptorFinder { | |||
/** | |||
* Returns true if this finder is applicable to the given {@link Path}. | |||
* | |||
* @param pluginPath | |||
* @return | |||
*/ | |||
boolean isApplicable(Path pluginPath); | |||
PluginDescriptor find(Path pluginPath) throws PluginException; | |||
PluginDescriptor find(Path pluginPath); | |||
} |
@@ -69,8 +69,8 @@ public interface PluginManager { | |||
* Load a plugin. | |||
* | |||
* @param pluginPath the plugin location | |||
* @return the pluginId of the installed plugin as specified in | |||
* its {@linkplain PluginDescriptor metadata}; or {@code null} | |||
* @return the pluginId of the installed plugin as specified in its {@linkplain PluginDescriptor metadata} | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
String loadPlugin(Path pluginPath); | |||
@@ -83,6 +83,7 @@ public interface PluginManager { | |||
* Start the specified plugin and its dependencies. | |||
* | |||
* @return the plugin state | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
PluginState startPlugin(String pluginId); | |||
@@ -95,6 +96,7 @@ public interface PluginManager { | |||
* Stop the specified plugin and its dependencies. | |||
* | |||
* @return the plugin state | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
PluginState stopPlugin(String pluginId); | |||
@@ -103,6 +105,7 @@ public interface PluginManager { | |||
* | |||
* @param pluginId the unique plugin identifier, specified in its metadata | |||
* @return true if the plugin was unloaded | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
boolean unloadPlugin(String pluginId); | |||
@@ -111,6 +114,7 @@ public interface PluginManager { | |||
* | |||
* @param pluginId the unique plugin identifier, specified in its metadata | |||
* @return true if plugin is disabled | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
boolean disablePlugin(String pluginId); | |||
@@ -119,6 +123,7 @@ public interface PluginManager { | |||
* | |||
* @param pluginId the unique plugin identifier, specified in its metadata | |||
* @return true if plugin is enabled | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
boolean enablePlugin(String pluginId); | |||
@@ -127,6 +132,7 @@ public interface PluginManager { | |||
* | |||
* @param pluginId the unique plugin identifier, specified in its metadata | |||
* @return true if the plugin was deleted | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
boolean deletePlugin(String pluginId); | |||
@@ -134,9 +140,9 @@ public interface PluginManager { | |||
List<Class<?>> getExtensionClasses(String pluginId); | |||
<T> List<Class<T>> getExtensionClasses(Class<T> type); | |||
<T> List<Class<? extends T>> getExtensionClasses(Class<T> type); | |||
<T> List<Class<T>> getExtensionClasses(Class<T> type, String pluginId); | |||
<T> List<Class<? extends T>> getExtensionClasses(Class<T> type, String pluginId); | |||
<T> List<T> getExtensions(Class<T> type); | |||
@@ -153,6 +159,20 @@ public interface PluginManager { | |||
*/ | |||
RuntimeMode getRuntimeMode(); | |||
/** | |||
* Returns {@code true} if the runtime mode is {@code RuntimeMode.DEVELOPMENT}. | |||
*/ | |||
default boolean isDevelopment() { | |||
return RuntimeMode.DEVELOPMENT.equals(getRuntimeMode()); | |||
} | |||
/** | |||
* Returns {@code true} if the runtime mode is not {@code RuntimeMode.DEVELOPMENT}. | |||
*/ | |||
default boolean isNotDevelopment() { | |||
return !isDevelopment(); | |||
} | |||
/** | |||
* Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. | |||
*/ | |||
@@ -180,7 +200,8 @@ public interface PluginManager { | |||
String getSystemVersion(); | |||
/** | |||
* Gets the path of the folder where plugins are installed | |||
* Gets the path of the folder where plugins are installed. | |||
* | |||
* @return Path of plugins root | |||
*/ | |||
Path getPluginsRoot(); |
@@ -19,7 +19,7 @@ import java.nio.file.Path; | |||
import java.util.List; | |||
/** | |||
* Directory that contains plugins. A plugin could be a zip file. | |||
* Directory that contains plugins. A plugin could be a {@code directory}, @code zip} or {@code jar} file. | |||
* | |||
* @author Decebal Suiu | |||
* @author Mário Franco | |||
@@ -29,7 +29,7 @@ public interface PluginRepository { | |||
/** | |||
* List all plugin paths. | |||
* | |||
* @return a list of files | |||
* @return a list with paths | |||
*/ | |||
List<Path> getPluginPaths(); | |||
@@ -38,6 +38,7 @@ public interface PluginRepository { | |||
* | |||
* @param pluginPath the plugin path | |||
* @return true if deleted | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
boolean deletePluginPath(Path pluginPath); | |||
@@ -23,29 +23,25 @@ import org.pf4j.util.StringUtils; | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class PluginException extends Exception { | |||
public class PluginRuntimeException extends RuntimeException { | |||
public PluginException() { | |||
public PluginRuntimeException() { | |||
super(); | |||
} | |||
public PluginException(String message) { | |||
public PluginRuntimeException(String message) { | |||
super(message); | |||
} | |||
public PluginException(Throwable cause) { | |||
public PluginRuntimeException(Throwable cause) { | |||
super(cause); | |||
} | |||
public PluginException(String message, Throwable cause) { | |||
super(message, cause); | |||
} | |||
public PluginException(Throwable cause, String message, Object... args) { | |||
public PluginRuntimeException(Throwable cause, String message, Object... args) { | |||
super(StringUtils.format(message, args), cause); | |||
} | |||
public PluginException(String message, Object... args) { | |||
public PluginRuntimeException(String message, Object... args) { | |||
super(StringUtils.format(message, args)); | |||
} | |||
@@ -33,16 +33,16 @@ public interface PluginStatusProvider { | |||
* Disables a plugin from being loaded. | |||
* | |||
* @param pluginId the unique plugin identifier, specified in its metadata | |||
* @return true if plugin is disabled | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
boolean disablePlugin(String pluginId); | |||
void disablePlugin(String pluginId); | |||
/** | |||
* Enables a plugin that has previously been disabled. | |||
* | |||
* @param pluginId the unique plugin identifier, specified in its metadata | |||
* @return true if plugin is enabled | |||
* @throws PluginRuntimeException if something goes wrong | |||
*/ | |||
boolean enablePlugin(String pluginId); | |||
void enablePlugin(String pluginId); | |||
} |
@@ -41,6 +41,7 @@ public class PluginWrapper { | |||
this.pluginClassLoader = pluginClassLoader; | |||
pluginState = PluginState.CREATED; | |||
runtimeMode = pluginManager.getRuntimeMode(); | |||
} | |||
/** | |||
@@ -120,11 +121,9 @@ public class PluginWrapper { | |||
} | |||
PluginWrapper other = (PluginWrapper) obj; | |||
if (!descriptor.getPluginId().equals(other.descriptor.getPluginId())) { | |||
return false; | |||
} | |||
return true; | |||
return descriptor.getPluginId().equals(other.descriptor.getPluginId()); | |||
} | |||
@Override | |||
@@ -136,10 +135,6 @@ public class PluginWrapper { | |||
this.pluginState = pluginState; | |||
} | |||
void setRuntimeMode(RuntimeMode runtimeMode) { | |||
this.runtimeMode = runtimeMode; | |||
} | |||
void setPluginFactory(PluginFactory pluginFactory) { | |||
this.pluginFactory = pluginFactory; | |||
} |
@@ -36,7 +36,16 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder | |||
private static final Logger log = LoggerFactory.getLogger(PropertiesPluginDescriptorFinder.class); | |||
private static final String DEFAULT_PROPERTIES_FILE_NAME = "plugin.properties"; | |||
public static final String DEFAULT_PROPERTIES_FILE_NAME = "plugin.properties"; | |||
public static final String PLUGIN_ID = "plugin.id"; | |||
public static final String PLUGIN_DESCRIPTION = "plugin.description"; | |||
public static final String PLUGIN_CLASS = "plugin.class"; | |||
public static final String PLUGIN_VERSION = "plugin.version"; | |||
public static final String PLUGIN_PROVIDER = "plugin.provider"; | |||
public static final String PLUGIN_DEPENDENCIES = "plugin.dependencies"; | |||
public static final String PLUGIN_REQUIRES = "plugin.requires"; | |||
public static final String PLUGIN_LICENSE = "plugin.license"; | |||
protected String propertiesFileName; | |||
@@ -54,34 +63,34 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder | |||
} | |||
@Override | |||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||
public PluginDescriptor find(Path pluginPath) { | |||
Properties properties = readProperties(pluginPath); | |||
return createPluginDescriptor(properties); | |||
} | |||
protected Properties readProperties(Path pluginPath) throws PluginException { | |||
protected Properties readProperties(Path pluginPath) { | |||
Path propertiesPath = getPropertiesPath(pluginPath, propertiesFileName); | |||
if (propertiesPath == null) { | |||
throw new PluginException("Cannot find the properties path"); | |||
throw new PluginRuntimeException("Cannot find the properties path"); | |||
} | |||
log.debug("Lookup plugin descriptor in '{}'", propertiesPath); | |||
if (Files.notExists(propertiesPath)) { | |||
throw new PluginException("Cannot find '{}' path", propertiesPath); | |||
throw new PluginRuntimeException("Cannot find '{}' path", propertiesPath); | |||
} | |||
Properties properties = new Properties(); | |||
try (InputStream input = Files.newInputStream(propertiesPath)) { | |||
properties.load(input); | |||
} catch (IOException e) { | |||
throw new PluginException(e); | |||
throw new PluginRuntimeException(e); | |||
} | |||
return properties; | |||
} | |||
protected Path getPropertiesPath(Path pluginPath, String propertiesFileName) throws PluginException { | |||
protected Path getPropertiesPath(Path pluginPath, String propertiesFileName) { | |||
if (Files.isDirectory(pluginPath)) { | |||
return pluginPath.resolve(Paths.get(propertiesFileName)); | |||
} else { | |||
@@ -89,7 +98,7 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder | |||
try { | |||
return FileUtils.getPath(pluginPath, propertiesFileName); | |||
} catch (IOException e) { | |||
throw new PluginException(e); | |||
throw new PluginRuntimeException(e); | |||
} | |||
} | |||
} | |||
@@ -98,38 +107,38 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder | |||
DefaultPluginDescriptor pluginDescriptor = createPluginDescriptorInstance(); | |||
// TODO validate !!! | |||
String id = properties.getProperty("plugin.id"); | |||
String id = properties.getProperty(PLUGIN_ID); | |||
pluginDescriptor.setPluginId(id); | |||
String description = properties.getProperty("plugin.description"); | |||
String description = properties.getProperty(PLUGIN_DESCRIPTION); | |||
if (StringUtils.isNullOrEmpty(description)) { | |||
pluginDescriptor.setPluginDescription(""); | |||
} else { | |||
pluginDescriptor.setPluginDescription(description); | |||
} | |||
String clazz = properties.getProperty("plugin.class"); | |||
String clazz = properties.getProperty(PLUGIN_CLASS); | |||
if (StringUtils.isNotNullOrEmpty(clazz)) { | |||
pluginDescriptor.setPluginClass(clazz); | |||
} | |||
String version = properties.getProperty("plugin.version"); | |||
String version = properties.getProperty(PLUGIN_VERSION); | |||
if (StringUtils.isNotNullOrEmpty(version)) { | |||
pluginDescriptor.setPluginVersion(version); | |||
} | |||
String provider = properties.getProperty("plugin.provider"); | |||
String provider = properties.getProperty(PLUGIN_PROVIDER); | |||
pluginDescriptor.setProvider(provider); | |||
String dependencies = properties.getProperty("plugin.dependencies"); | |||
String dependencies = properties.getProperty(PLUGIN_DEPENDENCIES); | |||
pluginDescriptor.setDependencies(dependencies); | |||
String requires = properties.getProperty("plugin.requires"); | |||
String requires = properties.getProperty(PLUGIN_REQUIRES); | |||
if (StringUtils.isNotNullOrEmpty(requires)) { | |||
pluginDescriptor.setRequires(requires); | |||
} | |||
pluginDescriptor.setLicense(properties.getProperty("plugin.license")); | |||
pluginDescriptor.setLicense(properties.getProperty(PLUGIN_LICENSE)); | |||
return pluginDescriptor; | |||
} |
@@ -41,7 +41,7 @@ public enum RuntimeMode { | |||
} | |||
} | |||
private RuntimeMode(final String name, final String... aliases) { | |||
RuntimeMode(final String name, final String... aliases) { | |||
this.name = name; | |||
this.aliases = aliases; | |||
} |
@@ -39,13 +39,14 @@ public class SingletonExtensionFactory extends DefaultExtensionFactory { | |||
} | |||
@Override | |||
public Object create(Class<?> extensionClass) { | |||
@SuppressWarnings("unchecked") | |||
public <T> T create(Class<T> extensionClass) { | |||
String extensionClassName = extensionClass.getName(); | |||
if (cache.containsKey(extensionClassName)) { | |||
return cache.get(extensionClassName); | |||
return (T) cache.get(extensionClassName); | |||
} | |||
Object extension = super.create(extensionClass); | |||
T extension = super.create(extensionClass); | |||
if (extensionClassNames.isEmpty() || extensionClassNames.contains(extensionClassName)) { | |||
cache.put(extensionClassName, extension); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -109,11 +109,7 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor { | |||
String extension = getBinaryName(extensionElement); | |||
for (TypeElement extensionPointElement : extensionPointElements) { | |||
String extensionPoint = getBinaryName(extensionPointElement); | |||
Set<String> extensionPoints = extensions.get(extensionPoint); | |||
if (extensionPoints == null) { | |||
extensionPoints = new TreeSet<>(); | |||
extensions.put(extensionPoint, extensionPoints); | |||
} | |||
Set<String> extensionPoints = extensions.computeIfAbsent(extensionPoint, k -> new TreeSet<>()); | |||
extensionPoints.add(extension); | |||
} | |||
} |
@@ -23,11 +23,9 @@ import java.util.Collections; | |||
import java.util.List; | |||
/** | |||
* This filter providing conditional AND logic across a list of | |||
* file filters. This filter returns <code>true</code> if all filters in the | |||
* list return <code>true</code>. Otherwise, it returns <code>false</code>. | |||
* Checking of the file filter list stops when the first filter returns | |||
* <code>false</code>. | |||
* This filter providing conditional AND logic across a list of file filters. | |||
* This filter returns {@code true} if all filters in the list return {@code true}. Otherwise, it returns {@code false}. | |||
* Checking of the file filter list stops when the first filter returns {@code false}. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
@@ -37,7 +35,7 @@ public class AndFileFilter implements FileFilter { | |||
private List<FileFilter> fileFilters; | |||
public AndFileFilter() { | |||
this(new ArrayList<FileFilter>()); | |||
this(new ArrayList<>()); | |||
} | |||
public AndFileFilter(FileFilter... fileFilters) { | |||
@@ -68,7 +66,7 @@ public class AndFileFilter implements FileFilter { | |||
@Override | |||
public boolean accept(File file) { | |||
if (this.fileFilters.size() == 0) { | |||
if (this.fileFilters.isEmpty()) { | |||
return false; | |||
} | |||
@@ -44,7 +44,7 @@ public class DirectedGraph<V> { | |||
return; | |||
} | |||
neighbors.put(vertex, new ArrayList<V>()); | |||
neighbors.put(vertex, new ArrayList<>()); | |||
} | |||
/** | |||
@@ -85,7 +85,7 @@ public class DirectedGraph<V> { | |||
} | |||
public List<V> getNeighbors(V vertex) { | |||
return containsVertex(vertex) ? neighbors.get(vertex) : new ArrayList<V>(); | |||
return containsVertex(vertex) ? neighbors.get(vertex) : new ArrayList<>(); | |||
} | |||
/** | |||
@@ -101,7 +101,7 @@ public class DirectedGraph<V> { | |||
} | |||
/** | |||
* Report (as a Map) the in-degree (the number of head ends adjacent to a vertex) of each vertex. | |||
* Report (as a {@link Map}) the in-degree (the number of head ends adjacent to a vertex) of each vertex. | |||
*/ | |||
public Map<V, Integer> inDegree() { | |||
Map<V, Integer> result = new HashMap<>(); | |||
@@ -181,9 +181,9 @@ public class DirectedGraph<V> { | |||
*/ | |||
@Override | |||
public String toString() { | |||
StringBuffer sb = new StringBuffer(); | |||
StringBuilder sb = new StringBuilder(); | |||
for (V vertex : neighbors.keySet()) { | |||
sb.append("\n " + vertex + " -> " + neighbors.get(vertex)); | |||
sb.append("\n ").append(vertex).append(" -> ").append(neighbors.get(vertex)); | |||
} | |||
return sb.toString(); |
@@ -172,17 +172,10 @@ public class FileUtils { | |||
FileTime pluginZipDate = Files.getLastModifiedTime(filePath); | |||
String fileName = filePath.getFileName().toString(); | |||
Path pluginDirectory = filePath.resolveSibling(fileName.substring(0, fileName.lastIndexOf("."))); | |||
String directoryName = fileName.substring(0, fileName.lastIndexOf(".")); | |||
Path pluginDirectory = filePath.resolveSibling(directoryName); | |||
if (!Files.exists(pluginDirectory) || pluginZipDate.compareTo(Files.getLastModifiedTime(pluginDirectory)) > 0) { | |||
// do not overwrite an old version, remove it | |||
if (Files.exists(pluginDirectory)) { | |||
FileUtils.delete(pluginDirectory); | |||
} | |||
// create root for plugin | |||
Files.createDirectories(pluginDirectory); | |||
// expand '.zip' file | |||
Unzip unzip = new Unzip(); | |||
unzip.setSource(filePath.toFile()); |
@@ -23,11 +23,9 @@ import java.util.Collections; | |||
import java.util.List; | |||
/** | |||
* This filter providing conditional OR logic across a list of | |||
* file filters. This filter returns <code>true</code> if one filter in the | |||
* list return <code>true</code>. Otherwise, it returns <code>false</code>. | |||
* Checking of the file filter list stops when the first filter returns | |||
* <code>true</code>. | |||
* This filter providing conditional OR logic across a list of file filters. | |||
* This filter returns {@code true} if one filter in the list return {@code true}. Otherwise, it returns {@code false}. | |||
* Checking of the file filter list stops when the first filter returns {@code true}. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
@@ -37,7 +35,7 @@ public class OrFileFilter implements FileFilter { | |||
private List<FileFilter> fileFilters; | |||
public OrFileFilter() { | |||
this(new ArrayList<FileFilter>()); | |||
this(new ArrayList<>()); | |||
} | |||
public OrFileFilter(FileFilter... fileFilters) { | |||
@@ -68,7 +66,7 @@ public class OrFileFilter implements FileFilter { | |||
@Override | |||
public boolean accept(File file) { | |||
if (this.fileFilters.size() == 0) { | |||
if (this.fileFilters.isEmpty()) { | |||
return true; | |||
} | |||
@@ -63,10 +63,14 @@ public class Unzip { | |||
this.destination = destination; | |||
} | |||
/** | |||
* Extract the content of zip file ({@code source}) to destination directory. | |||
* If destination directory already exists it will be deleted before. | |||
*/ | |||
public void extract() throws IOException { | |||
log.debug("Extract content of '{}' to '{}'", source, destination); | |||
// delete destination file if exists | |||
// delete destination directory if exists | |||
if (destination.exists() && destination.isDirectory()) { | |||
FileUtils.delete(destination.toPath()); | |||
} |
@@ -15,9 +15,9 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.junit.jupiter.api.AfterEach; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.plugin.FailTestPlugin; | |||
import org.pf4j.plugin.TestExtensionPoint; | |||
@@ -28,7 +28,7 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.mockito.Mockito.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@@ -40,7 +40,7 @@ public class AbstractExtensionFinderTest { | |||
private PluginManager pluginManager; | |||
@Before | |||
@BeforeEach | |||
public void setUp() { | |||
PluginWrapper pluginStarted = mock(PluginWrapper.class); | |||
when(pluginStarted.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); | |||
@@ -57,7 +57,7 @@ public class AbstractExtensionFinderTest { | |||
when(pluginManager.getExtensionFactory()).thenReturn(new DefaultExtensionFactory()); | |||
} | |||
@After | |||
@AfterEach | |||
public void tearDown() { | |||
pluginManager = null; | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -15,28 +15,34 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.junit.jupiter.api.Test; | |||
import org.junit.jupiter.api.io.TempDir; | |||
import org.pf4j.plugin.PluginJar; | |||
import org.pf4j.plugin.PluginZip; | |||
import org.pf4j.plugin.TestPlugin; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStreamWriter; | |||
import java.io.Writer; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertNotNull; | |||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||
/** | |||
* @author Decebal Suiu | |||
*/ | |||
public class CompoundPluginDescriptorFinderTest { | |||
@Rule | |||
public TemporaryFolder pluginsFolder = new TemporaryFolder(); | |||
@TempDir | |||
Path pluginsPath; | |||
@Test | |||
public void add() { | |||
@@ -49,8 +55,8 @@ public class CompoundPluginDescriptorFinderTest { | |||
@Test | |||
public void find() throws Exception { | |||
Path pluginPath = pluginsFolder.newFolder("test-plugin-1").toPath(); | |||
Files.write(pluginPath.resolve("plugin.properties"), getPlugin1Properties(), StandardCharsets.UTF_8); | |||
Path pluginPath = Files.createDirectories(pluginsPath.resolve("test-plugin-1")); | |||
storePropertiesToPath(getPlugin1Properties(), pluginPath); | |||
PluginDescriptorFinder descriptorFinder = new CompoundPluginDescriptorFinder() | |||
.add(new PropertiesPluginDescriptorFinder()); | |||
@@ -64,31 +70,32 @@ public class CompoundPluginDescriptorFinderTest { | |||
@Test | |||
public void findInJar() throws Exception { | |||
PluginDescriptorFinder descriptorFinder = new CompoundPluginDescriptorFinder() | |||
.add(new PropertiesPluginDescriptorFinder()); | |||
.add(new ManifestPluginDescriptorFinder()); | |||
PluginZip pluginJar = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.jar"), "myPlugin") | |||
PluginJar pluginJar = new PluginJar.Builder(pluginsPath.resolve("my-plugin-1.2.3.jar"), "myPlugin") | |||
.pluginClass(TestPlugin.class.getName()) | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
PluginDescriptor pluginDescriptor = descriptorFinder.find(pluginJar.path()); | |||
assertNotNull(pluginDescriptor); | |||
assertEquals("myPlugin", pluginJar.pluginId()); | |||
assertEquals(TestPlugin.class.getName(), pluginJar.pluginClass()); | |||
assertEquals("1.2.3", pluginJar.pluginVersion()); | |||
} | |||
@Test(expected = PluginException.class) | |||
public void testNotFound() throws Exception { | |||
@Test | |||
public void testNotFound() { | |||
PluginDescriptorFinder descriptorFinder = new CompoundPluginDescriptorFinder(); | |||
Path pluginsPath = pluginsFolder.getRoot().toPath(); | |||
descriptorFinder.find(pluginsPath.resolve("test-plugin-3")); | |||
assertThrows(PluginRuntimeException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||
} | |||
@Test | |||
public void testSpaceCharacterInFileName() throws Exception { | |||
PluginDescriptorFinder descriptorFinder = new PropertiesPluginDescriptorFinder(); | |||
File jar = pluginsFolder.newFile("my plugin-1.2.3.jar"); | |||
PluginDescriptorFinder descriptorFinder = new CompoundPluginDescriptorFinder() | |||
.add(new ManifestPluginDescriptorFinder()); | |||
PluginZip pluginJar = new PluginZip.Builder(jar, "myPlugin") | |||
PluginJar pluginJar = new PluginJar.Builder(pluginsPath.resolve("my plugin-1.2.3.jar"), "myPlugin") | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
@@ -96,21 +103,24 @@ public class CompoundPluginDescriptorFinderTest { | |||
assertNotNull(pluginDescriptor); | |||
} | |||
private List<String> getPlugin1Properties() { | |||
String[] lines = new String[] { | |||
"plugin.id=test-plugin-1\n" | |||
+ "plugin.version=0.0.1\n" | |||
+ "plugin.description=Test Plugin 1\n" | |||
+ "plugin.provider=Decebal Suiu\n" | |||
+ "plugin.class=org.pf4j.plugin.TestPlugin\n" | |||
+ "plugin.dependencies=test-plugin-2,test-plugin-3@~1.0\n" | |||
+ "plugin.requires=>=1\n" | |||
+ "plugin.license=Apache-2.0\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private Properties getPlugin1Properties() { | |||
Map<String, String> map = new LinkedHashMap<>(7); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_ID, "test-plugin-1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_CLASS, TestPlugin.class.getName()); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, "test-plugin-2,test-plugin-3@~1.0"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, ">=1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_LICENSE, "Apache-2.0"); | |||
return PluginZip.createProperties(map); | |||
} | |||
private void storePropertiesToPath(Properties properties, Path pluginPath) throws IOException { | |||
Path path = pluginPath.resolve(PropertiesPluginDescriptorFinder.DEFAULT_PROPERTIES_FILE_NAME); | |||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(path.toFile()), StandardCharsets.UTF_8)) { | |||
properties.store(writer, ""); | |||
} | |||
} | |||
} |
@@ -15,26 +15,38 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Test; | |||
import org.pf4j.plugin.TestExtension; | |||
import org.junit.jupiter.api.AfterEach; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.plugin.FailTestExtension; | |||
import org.pf4j.plugin.TestExtension; | |||
import static org.junit.Assert.*; | |||
import static org.junit.jupiter.api.Assertions.assertNotNull; | |||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||
/** | |||
* | |||
* @author Mario Franco | |||
*/ | |||
public class DefaultExtensionFactoryTest { | |||
private ExtensionFactory extensionFactory; | |||
@BeforeEach | |||
public void setUp() { | |||
extensionFactory = new DefaultExtensionFactory(); | |||
} | |||
@AfterEach | |||
public void tearDown() { | |||
extensionFactory = null; | |||
} | |||
/** | |||
* Test of create method, of class DefaultExtensionFactory. | |||
*/ | |||
@Test | |||
public void testCreate() { | |||
DefaultExtensionFactory instance = new DefaultExtensionFactory(); | |||
Object result = instance.create(TestExtension.class); | |||
assertNotNull(result); | |||
assertNotNull(extensionFactory.create(TestExtension.class)); | |||
} | |||
/** | |||
@@ -42,9 +54,7 @@ public class DefaultExtensionFactoryTest { | |||
*/ | |||
@Test | |||
public void testCreateFailConstructor() { | |||
DefaultExtensionFactory instance = new DefaultExtensionFactory(); | |||
Object result = instance.create(FailTestExtension.class); | |||
assertNull(result); | |||
assertThrows(PluginRuntimeException.class, () -> extensionFactory.create(FailTestExtension.class)); | |||
} | |||
} |
@@ -15,15 +15,15 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Test; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.plugin.AnotherFailTestPlugin; | |||
import org.pf4j.plugin.FailTestPlugin; | |||
import org.pf4j.plugin.TestPlugin; | |||
import static org.hamcrest.CoreMatchers.instanceOf; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertNull; | |||
import static org.junit.Assert.assertThat; | |||
import static org.hamcrest.MatcherAssert.assertThat; | |||
import static org.junit.jupiter.api.Assertions.assertNotNull; | |||
import static org.junit.jupiter.api.Assertions.assertNull; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@@ -15,21 +15,21 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
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.PluginZip; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertSame; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertFalse; | |||
import static org.junit.jupiter.api.Assertions.assertSame; | |||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||
import static org.junit.jupiter.api.Assertions.assertTrue; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@@ -38,14 +38,12 @@ public class DefaultPluginManagerTest { | |||
private DefaultPluginManager pluginManager; | |||
private DefaultPluginDescriptor pluginDescriptor; | |||
private PluginWrapper pluginWrapper; | |||
private Path pluginsPath; | |||
@Rule | |||
public TemporaryFolder pluginsFolder = new TemporaryFolder(); | |||
@TempDir | |||
Path pluginsPath; | |||
@Before | |||
@BeforeEach | |||
public void setUp() throws IOException { | |||
pluginsPath = pluginsFolder.getRoot().toPath(); | |||
pluginManager = new DefaultPluginManager(pluginsPath); | |||
pluginDescriptor = new DefaultPluginDescriptor(); | |||
@@ -59,7 +57,7 @@ public class DefaultPluginManagerTest { | |||
pluginWrapper = new PluginWrapper(pluginManager, pluginDescriptor, Files.createTempDirectory("test"), getClass().getClassLoader()); | |||
} | |||
@After | |||
@AfterEach | |||
public void tearDown() { | |||
pluginManager = null; | |||
pluginDescriptor = null; | |||
@@ -67,24 +65,24 @@ public class DefaultPluginManagerTest { | |||
} | |||
@Test | |||
public void validateOK() throws PluginException { | |||
public void validateOK() { | |||
pluginManager.validatePluginDescriptor(pluginDescriptor); | |||
} | |||
@Test(expected = PluginException.class) | |||
public void validateFailsOnId() throws PluginException { | |||
@Test | |||
public void validateFailsOnId() { | |||
pluginDescriptor.setPluginId(""); | |||
pluginManager.validatePluginDescriptor(pluginDescriptor); | |||
assertThrows(PluginRuntimeException.class, () -> pluginManager.validatePluginDescriptor(pluginDescriptor)); | |||
} | |||
@Test(expected = PluginException.class) | |||
public void validateFailsOnVersion() throws PluginException { | |||
@Test | |||
public void validateFailsOnVersion() { | |||
pluginDescriptor.setPluginVersion(null); | |||
pluginManager.validatePluginDescriptor(pluginDescriptor); | |||
assertThrows(PluginRuntimeException.class, () -> pluginManager.validatePluginDescriptor(pluginDescriptor)); | |||
} | |||
@Test | |||
public void validateNoPluginClass() throws PluginException { | |||
public void validateNoPluginClass() { | |||
pluginManager.validatePluginDescriptor(pluginDescriptor); | |||
assertEquals(Plugin.class.getName(), pluginDescriptor.getPluginClass()); | |||
} | |||
@@ -132,7 +130,7 @@ public class DefaultPluginManagerTest { | |||
*/ | |||
@Test | |||
public void testPluginDisabledNoStart() throws IOException { | |||
new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") | |||
new PluginZip.Builder(pluginsPath.resolve("my-plugin-1.2.3.zip"), "myPlugin") | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
@@ -15,20 +15,18 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
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.nio.file.Paths; | |||
import java.util.List; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
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 Mario Franco | |||
@@ -36,25 +34,18 @@ import static org.junit.Assert.assertTrue; | |||
*/ | |||
public class DefaultPluginRepositoryTest { | |||
private Path pluginsPath; | |||
@TempDir | |||
Path pluginsPath; | |||
@Rule | |||
public TemporaryFolder pluginsFolder = new TemporaryFolder(); | |||
@Before | |||
@BeforeEach | |||
public void setUp() throws IOException { | |||
pluginsPath = pluginsFolder.getRoot().toPath(); | |||
pluginsFolder.newFolder("plugin-1"); | |||
Path plugin1Path = Files.createDirectory(pluginsPath.resolve("plugin-1")); | |||
// Prove that we can delete a folder with a file inside | |||
Files.createFile(Paths.get(pluginsFolder.getRoot().getAbsolutePath()).resolve("plugin-1").resolve("myfile")); | |||
Files.createFile(plugin1Path.resolve("myfile")); | |||
// Create a zip file for plugin-1 to test that it is deleted when plugin is deleted | |||
Files.createFile(Paths.get(pluginsFolder.getRoot().getAbsolutePath()).resolve("plugin-1.zip")); | |||
pluginsFolder.newFolder("plugin-2"); | |||
pluginsFolder.newFolder("plugin-3"); | |||
// standard maven/gradle bin folder - these should be skipped in development mode because the cause errors | |||
pluginsFolder.newFolder("target"); | |||
pluginsFolder.newFolder("build"); | |||
Files.createFile(pluginsPath.resolve("plugin-1.zip")); | |||
Files.createDirectory(pluginsPath.resolve("plugin-2")); | |||
Files.createDirectory(pluginsPath.resolve("plugin-3")); | |||
} | |||
/** | |||
@@ -62,29 +53,14 @@ public class DefaultPluginRepositoryTest { | |||
*/ | |||
@Test | |||
public void testGetPluginArchives() { | |||
PluginRepository repository = new DefaultPluginRepository(pluginsPath, false); | |||
PluginRepository repository = new DefaultPluginRepository(pluginsPath); | |||
List<Path> pluginPaths = repository.getPluginPaths(); | |||
assertEquals(5, pluginPaths.size()); | |||
assertEquals(3, pluginPaths.size()); | |||
assertPathExists(pluginPaths, pluginsPath.resolve("plugin-1")); | |||
assertPathExists(pluginPaths, pluginsPath.resolve("plugin-2")); | |||
assertPathExists(pluginPaths, pluginsPath.resolve("plugin-3")); | |||
// when not in development mode we will honor these folders | |||
assertPathExists(pluginPaths, pluginsPath.resolve("target")); | |||
assertPathExists(pluginPaths, pluginsPath.resolve("build")); | |||
} | |||
@Test | |||
public void testGetPluginArchivesInDevelopmentMode() { | |||
PluginRepository repository = new DefaultPluginRepository(pluginsPath, true); | |||
List<Path> pluginPaths = repository.getPluginPaths(); | |||
// target and build should be ignored | |||
assertEquals(3, pluginPaths.size()); | |||
assertPathDoesNotExists(pluginPaths, pluginsPath.resolve("target")); | |||
assertPathDoesNotExists(pluginPaths, pluginsPath.resolve("build")); | |||
} | |||
/** | |||
@@ -92,15 +68,13 @@ public class DefaultPluginRepositoryTest { | |||
*/ | |||
@Test | |||
public void testDeletePluginPath() { | |||
PluginRepository repository = new DefaultPluginRepository(pluginsPath, false); | |||
PluginRepository repository = new DefaultPluginRepository(pluginsPath); | |||
assertTrue(Files.exists(pluginsPath.resolve("plugin-1.zip"))); | |||
assertTrue(repository.deletePluginPath(pluginsPath.resolve("plugin-1"))); | |||
assertFalse(Files.exists(pluginsPath.resolve("plugin-1.zip"))); | |||
assertTrue(repository.deletePluginPath(pluginsPath.resolve("plugin-3"))); | |||
assertFalse(repository.deletePluginPath(pluginsPath.resolve("plugin-4"))); | |||
assertTrue(repository.deletePluginPath(pluginsPath.resolve("target"))); | |||
assertTrue(repository.deletePluginPath(pluginsPath.resolve("build"))); | |||
List<Path> pluginPaths = repository.getPluginPaths(); | |||
@@ -109,11 +83,7 @@ public class DefaultPluginRepositoryTest { | |||
} | |||
private void assertPathExists(List<Path> paths, Path path) { | |||
assertTrue("The directory must contain the file " + path, paths.contains(path)); | |||
} | |||
private void assertPathDoesNotExists(List<Path> paths, Path path) { | |||
assertFalse("The directory must not contain the file " + path, paths.contains(path)); | |||
assertTrue(paths.contains(path), "The directory must contain the file " + path); | |||
} | |||
} |
@@ -15,20 +15,17 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.junit.jupiter.api.Test; | |||
import org.junit.jupiter.api.io.TempDir; | |||
import org.pf4j.util.FileUtils; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.file.Path; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.jupiter.api.Assertions.assertFalse; | |||
import static org.junit.jupiter.api.Assertions.assertTrue; | |||
/** | |||
* @author Mario Franco | |||
@@ -36,15 +33,8 @@ import static org.junit.Assert.assertTrue; | |||
*/ | |||
public class DefaultPluginStatusProviderTest { | |||
private Path pluginsPath; | |||
@Rule | |||
public TemporaryFolder pluginsFolder = new TemporaryFolder(); | |||
@Before | |||
public void setUp() { | |||
pluginsPath = pluginsFolder.getRoot().toPath(); | |||
} | |||
@TempDir | |||
Path pluginsPath; | |||
@Test | |||
public void testIsPluginDisabled() throws IOException { | |||
@@ -70,58 +60,59 @@ public class DefaultPluginStatusProviderTest { | |||
} | |||
@Test | |||
public void testDisablePlugin() throws IOException { | |||
public void testDisablePlugin() throws Exception { | |||
createEnabledFile(); | |||
createDisabledFile(); | |||
PluginStatusProvider statusProvider = new DefaultPluginStatusProvider(pluginsPath); | |||
statusProvider.disablePlugin("plugin-1"); | |||
assertTrue(statusProvider.disablePlugin("plugin-1")); | |||
assertTrue(statusProvider.isPluginDisabled("plugin-1")); | |||
assertTrue(statusProvider.isPluginDisabled("plugin-2")); | |||
assertTrue(statusProvider.isPluginDisabled("plugin-3")); | |||
} | |||
@Test | |||
public void testDisablePluginWithEnableEmpty() throws IOException { | |||
public void testDisablePluginWithEnableEmpty() throws Exception { | |||
createDisabledFile(); | |||
PluginStatusProvider statusProvider = new DefaultPluginStatusProvider(pluginsPath); | |||
statusProvider.disablePlugin("plugin-1"); | |||
assertTrue(statusProvider.disablePlugin("plugin-1")); | |||
assertTrue(statusProvider.isPluginDisabled("plugin-1")); | |||
assertTrue(statusProvider.isPluginDisabled("plugin-2")); | |||
assertFalse(statusProvider.isPluginDisabled("plugin-3")); | |||
} | |||
@Test | |||
public void testEnablePlugin() throws IOException { | |||
public void testEnablePlugin() throws Exception { | |||
createEnabledFile(); | |||
PluginStatusProvider statusProvider = new DefaultPluginStatusProvider(pluginsPath); | |||
statusProvider.enablePlugin("plugin-2"); | |||
assertTrue(statusProvider.enablePlugin("plugin-2")); | |||
assertFalse(statusProvider.isPluginDisabled("plugin-1")); | |||
assertFalse(statusProvider.isPluginDisabled("plugin-2")); | |||
assertTrue(statusProvider.isPluginDisabled("plugin-3")); | |||
} | |||
@Test | |||
public void testEnablePluginWithEnableEmpty() { | |||
public void testEnablePluginWithEnableEmpty() throws Exception{ | |||
PluginStatusProvider statusProvider = new DefaultPluginStatusProvider(pluginsPath); | |||
statusProvider.enablePlugin("plugin-2"); | |||
assertTrue(statusProvider.enablePlugin("plugin-2")); | |||
assertFalse(statusProvider.isPluginDisabled("plugin-1")); | |||
assertFalse(statusProvider.isPluginDisabled("plugin-2")); | |||
assertFalse(statusProvider.isPluginDisabled("plugin-3")); | |||
} | |||
@Test | |||
public void testDisablePluginWithoutDisabledFile() { | |||
public void testDisablePluginWithoutDisabledFile() throws Exception { | |||
PluginStatusProvider statusProvider = new DefaultPluginStatusProvider(pluginsPath); | |||
assertFalse(statusProvider.isPluginDisabled("plugin-1")); | |||
assertTrue(statusProvider.disablePlugin("plugin-1")); | |||
statusProvider.disablePlugin("plugin-1"); | |||
assertTrue(statusProvider.isPluginDisabled("plugin-1")); | |||
} | |||
@@ -129,8 +120,8 @@ public class DefaultPluginStatusProviderTest { | |||
List<String> disabledPlugins = new ArrayList<>(); | |||
disabledPlugins.add("plugin-2"); | |||
File disabledFile = pluginsFolder.newFile("disabled.txt"); | |||
FileUtils.writeLines(disabledPlugins, disabledFile); | |||
Path disabledPath = pluginsPath.resolve("disabled.txt"); | |||
FileUtils.writeLines(disabledPlugins, disabledPath.toFile()); | |||
} | |||
private void createEnabledFile() throws IOException { | |||
@@ -138,8 +129,8 @@ public class DefaultPluginStatusProviderTest { | |||
enabledPlugins.add("plugin-1"); | |||
enabledPlugins.add("plugin-2"); | |||
File enabledFile = pluginsFolder.newFile("enabled.txt"); | |||
FileUtils.writeLines(enabledPlugins, enabledFile); | |||
Path enabledPath = pluginsPath.resolve("enabled.txt"); | |||
FileUtils.writeLines(enabledPlugins, enabledPath.toFile()); | |||
} | |||
} |
@@ -16,10 +16,12 @@ | |||
package org.pf4j; | |||
import com.github.zafarkhaja.semver.ParseException; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import static org.junit.Assert.*; | |||
import static org.junit.jupiter.api.Assertions.assertFalse; | |||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||
import static org.junit.jupiter.api.Assertions.assertTrue; | |||
/** | |||
* @author Decebal Suiu | |||
@@ -28,7 +30,7 @@ public class DefaultVersionManagerTest { | |||
private VersionManager versionManager; | |||
@Before | |||
@BeforeEach | |||
public void init() { | |||
versionManager = new DefaultVersionManager(); | |||
} | |||
@@ -39,14 +41,14 @@ public class DefaultVersionManagerTest { | |||
assertTrue(versionManager.checkVersionConstraint("1.4.3", ">=1.4.0 & <1.6.0")); // range | |||
} | |||
@Test(expected = IllegalArgumentException.class) | |||
@Test | |||
public void nullOrEmptyVersion() { | |||
assertFalse(versionManager.checkVersionConstraint(null, ">2.0.0")); | |||
assertThrows(IllegalArgumentException.class, () -> versionManager.checkVersionConstraint(null, ">2.0.0")); | |||
} | |||
@Test(expected = ParseException.class) | |||
@Test | |||
public void invalidVersion() { | |||
assertFalse(versionManager.checkVersionConstraint("1.0", ">2.0.0")); | |||
assertThrows(ParseException.class, () -> versionManager.checkVersionConstraint("1.0", ">2.0.0")); | |||
} | |||
@Test |
@@ -15,14 +15,16 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import static org.junit.Assert.*; | |||
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 | |||
@@ -31,7 +33,7 @@ public class DependencyResolverTest { | |||
private DependencyResolver resolver; | |||
@Before | |||
@BeforeEach | |||
public void init() { | |||
VersionManager versionManager = new DefaultVersionManager(); | |||
resolver = new DependencyResolver(versionManager); |
@@ -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); | |||
} | |||
} |
@@ -15,13 +15,13 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Test; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.processor.ExtensionAnnotationProcessor; | |||
import java.util.Set; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertTrue; | |||
/** | |||
* @author Mario Franco |
@@ -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()); | |||
} | |||
} |
@@ -15,7 +15,7 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Test; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.processor.LegacyExtensionStorage; | |||
import java.io.IOException; | |||
@@ -24,7 +24,7 @@ import java.io.StringReader; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import static org.junit.Assert.*; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
/** | |||
* @author Decebal Suiu |
@@ -15,42 +15,42 @@ | |||
*/ | |||
package org.pf4j; | |||
import static org.hamcrest.CoreMatchers.containsString; | |||
import static org.hamcrest.CoreMatchers.equalTo; | |||
import static org.hamcrest.CoreMatchers.startsWith; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertNull; | |||
import static org.junit.Assert.assertThat; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.Assert.fail; | |||
import java.io.File; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.junit.jupiter.api.io.TempDir; | |||
import org.pf4j.plugin.PluginZip; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.nio.file.Paths; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.pf4j.plugin.PluginZip; | |||
import static org.hamcrest.CoreMatchers.containsString; | |||
import static org.hamcrest.CoreMatchers.equalTo; | |||
import static org.hamcrest.CoreMatchers.startsWith; | |||
import static org.hamcrest.MatcherAssert.assertThat; | |||
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.assertNull; | |||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||
import static org.junit.jupiter.api.Assertions.assertTrue; | |||
import static org.junit.jupiter.api.Assertions.fail; | |||
public class LoadPluginsTest { | |||
private DefaultPluginManager pluginManager; | |||
@Rule | |||
public TemporaryFolder pluginsFolder = new TemporaryFolder(); | |||
@TempDir | |||
Path pluginsPath; | |||
@Before | |||
@BeforeEach | |||
public void setUp() { | |||
pluginManager = new DefaultPluginManager(pluginsFolder.getRoot().toPath()); | |||
pluginManager = new DefaultPluginManager(pluginsPath); | |||
} | |||
@Test | |||
public void load() throws Exception { | |||
PluginZip pluginZip = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") | |||
PluginZip pluginZip = new PluginZip.Builder(pluginsPath.resolve("my-plugin-1.2.3.zip"), "myPlugin") | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
@@ -64,31 +64,32 @@ public class LoadPluginsTest { | |||
assertEquals(pluginZip.pluginId(), pluginManager.idForPath(pluginZip.unzippedPath())); | |||
} | |||
@Test(expected = IllegalArgumentException.class) | |||
@Test | |||
public void loadNonExisting() { | |||
pluginManager.loadPlugin(Paths.get("nonexisting")); | |||
assertThrows(IllegalArgumentException.class, () -> pluginManager.loadPlugin(Paths.get("nonexisting"))); | |||
} | |||
@Test(expected = PluginAlreadyLoadedException.class) | |||
@Test | |||
public void loadTwiceFails() throws Exception { | |||
PluginZip pluginZip = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") | |||
PluginZip pluginZip = new PluginZip.Builder(pluginsPath.resolve("my-plugin-1.2.3.zip"), "myPlugin") | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
assertNotNull(pluginManager.loadPluginFromPath(pluginZip.path())); | |||
assertNull(pluginManager.loadPluginFromPath(pluginZip.path())); | |||
assertThrows(PluginAlreadyLoadedException.class, () -> pluginManager.loadPluginFromPath(pluginZip.path())); | |||
} | |||
@Test | |||
public void loadPluginWithSameIdDifferentPathFails() throws Exception { | |||
String pluginId = "myPlugin"; | |||
String pluginVersion = "1.2.3"; | |||
File plugin1Path = pluginsFolder.newFile("my-plugin-1.2.3.zip"); | |||
Path plugin1Path = pluginsPath.resolve("my-plugin-1.2.3.zip"); | |||
PluginZip plugin1 = new PluginZip.Builder(plugin1Path, pluginId) | |||
.pluginVersion(pluginVersion) | |||
.build(); | |||
File plugin2Path = pluginsFolder.newFile("my-plugin-1.2.3-renamed.zip"); | |||
Path plugin2Path = pluginsPath.resolve("my-plugin-1.2.3-renamed.zip"); | |||
PluginZip plugin2 = new PluginZip.Builder(plugin2Path, pluginId) | |||
.pluginVersion(pluginVersion) | |||
.build(); | |||
@@ -101,7 +102,7 @@ public class LoadPluginsTest { | |||
// Verify the second plugin is not loaded as it has the same metadata | |||
pluginManager.loadPluginFromPath(plugin2.path()); | |||
fail("Expected loadPluginFromPath to fail"); | |||
} catch (PluginException e) { | |||
} catch (PluginRuntimeException e) { | |||
// Check the path of the loaded plugin remains the same | |||
PluginWrapper loadedPlugin = pluginManager.getPlugin(pluginId); | |||
assertThat(loadedPlugin.getPluginPath(), equalTo(loadedPlugin1Path)); | |||
@@ -122,13 +123,13 @@ public class LoadPluginsTest { | |||
public void loadPluginWithSameIdDifferentVersionsFails() throws Exception { | |||
String pluginId = "myPlugin"; | |||
String plugin1Version = "1.2.3"; | |||
File plugin1Path = pluginsFolder.newFile("my-plugin-1.2.3.zip"); | |||
Path plugin1Path = pluginsPath.resolve("my-plugin-1.2.3.zip"); | |||
PluginZip plugin1 = new PluginZip.Builder(plugin1Path, pluginId) | |||
.pluginVersion(plugin1Version) | |||
.build(); | |||
String plugin2Version = "2.0.0"; | |||
File plugin2Path = pluginsFolder.newFile("my-plugin-2.0.0.zip"); | |||
Path plugin2Path = pluginsPath.resolve("my-plugin-2.0.0.zip"); | |||
PluginZip plugin2 = new PluginZip.Builder(plugin2Path, pluginId) | |||
.pluginVersion(plugin2Version) | |||
.build(); | |||
@@ -140,7 +141,7 @@ public class LoadPluginsTest { | |||
// Verify the second plugin is not loaded as it has the same pluginId | |||
pluginManager.loadPluginFromPath(plugin2.path()); | |||
fail("Expected loadPluginFromPath to fail"); | |||
} catch (PluginException e) { | |||
} catch (PluginRuntimeException e) { | |||
// Check the path and version of the loaded plugin remain the same | |||
PluginWrapper loadedPlugin = pluginManager.getPlugin(pluginId); | |||
assertThat(loadedPlugin.getPluginPath(), equalTo(loadedPlugin1Path)); | |||
@@ -150,7 +151,7 @@ public class LoadPluginsTest { | |||
@Test | |||
public void loadUnloadLoad() throws Exception { | |||
PluginZip pluginZip = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") | |||
PluginZip pluginZip = new PluginZip.Builder(pluginsPath.resolve("my-plugin-1.2.3.zip"), "myPlugin") | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
@@ -169,7 +170,7 @@ public class LoadPluginsTest { | |||
public void upgrade() throws Exception { | |||
String pluginId = "myPlugin"; | |||
new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), pluginId) | |||
new PluginZip.Builder(pluginsPath.resolve("my-plugin-1.2.3.zip"), pluginId) | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
@@ -179,7 +180,7 @@ public class LoadPluginsTest { | |||
assertEquals(1, pluginManager.getPlugins().size()); | |||
assertEquals(1, pluginManager.getStartedPlugins().size()); | |||
PluginZip pluginZip2 = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-2.0.0.ZIP"), pluginId) | |||
PluginZip pluginZip2 = new PluginZip.Builder(pluginsPath.resolve("my-plugin-2.0.0.ZIP"), pluginId) | |||
.pluginVersion("2.0.0") | |||
.build(); | |||
@@ -196,12 +197,12 @@ public class LoadPluginsTest { | |||
@Test | |||
public void getRoot() { | |||
assertEquals(pluginsFolder.getRoot().toPath(), pluginManager.getPluginsRoot()); | |||
assertEquals(pluginsPath, pluginManager.getPluginsRoot()); | |||
} | |||
@Test | |||
public void notAPlugin() throws Exception { | |||
pluginsFolder.newFile("not-a-zip"); | |||
public void notAPlugin() { | |||
pluginsPath.resolve("not-a-zip"); | |||
pluginManager.loadPlugins(); | |||
@@ -210,11 +211,11 @@ public class LoadPluginsTest { | |||
@Test | |||
public void deletePlugin() throws Exception { | |||
PluginZip pluginZip1 = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") | |||
PluginZip pluginZip1 = new PluginZip.Builder(pluginsPath.resolve("my-plugin-1.2.3.zip"), "myPlugin") | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
PluginZip pluginZip3 = new PluginZip.Builder(pluginsFolder.newFile("other-3.0.0.Zip"), "other") | |||
PluginZip pluginZip3 = new PluginZip.Builder(pluginsPath.resolve("other-3.0.0.Zip"), "other") | |||
.pluginVersion("3.0.0") | |||
.build(); | |||
@@ -15,19 +15,23 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
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 java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.nio.charset.Charset; | |||
import java.io.OutputStream; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import java.util.jar.Manifest; | |||
import static org.junit.Assert.*; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||
import static org.junit.jupiter.api.Assertions.assertTrue; | |||
/** | |||
* @author Mario Franco | |||
@@ -36,42 +40,32 @@ import static org.junit.Assert.*; | |||
public class ManifestPluginDescriptorFinderTest { | |||
private VersionManager versionManager; | |||
private Path pluginsPath; | |||
@Rule | |||
public TemporaryFolder pluginsFolder = new TemporaryFolder(); | |||
@TempDir | |||
Path pluginsPath; | |||
@Before | |||
@BeforeEach | |||
public void setUp() throws IOException { | |||
pluginsPath = pluginsFolder.getRoot().toPath(); | |||
Path pluginPath = Files.createDirectories(pluginsPath.resolve("test-plugin-1")); | |||
storeManifestToPath(getPlugin1Manifest(), pluginPath); | |||
Charset charset = Charset.forName("UTF-8"); | |||
Path pluginPath = pluginsFolder.newFolder("test-plugin-1", "classes", "META-INF").toPath(); | |||
Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); | |||
Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin1Manifest(), charset); | |||
pluginPath = pluginsFolder.newFolder("test-plugin-2", "classes", "META-INF").toPath(); | |||
Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); | |||
Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin2Manifest(), charset); | |||
pluginPath = Files.createDirectories(pluginsPath.resolve("test-plugin-2")); | |||
storeManifestToPath(getPlugin2Manifest(), pluginPath); | |||
// empty plugin | |||
pluginsFolder.newFolder("test-plugin-3"); | |||
Files.createDirectories(pluginsPath.resolve("test-plugin-3")); | |||
// no plugin class | |||
pluginPath = pluginsFolder.newFolder("test-plugin-4", "classes", "META-INF").toPath(); | |||
Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); | |||
Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin4Manifest(), charset); | |||
pluginPath = Files.createDirectories(pluginsPath.resolve("test-plugin-4")); | |||
storeManifestToPath(getPlugin4Manifest(), pluginPath); | |||
// no plugin version | |||
pluginPath = pluginsFolder.newFolder("test-plugin-5", "classes", "META-INF").toPath(); | |||
Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); | |||
Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin5Manifest(), charset); | |||
pluginPath = Files.createDirectories(pluginsPath.resolve("test-plugin-5")); | |||
storeManifestToPath(getPlugin5Manifest(), pluginPath); | |||
// no plugin id | |||
pluginPath = pluginsFolder.newFolder("test-plugin-6", "classes", "META-INF").toPath(); | |||
Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); | |||
Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin6Manifest(), charset); | |||
pluginPath = Files.createDirectories(pluginsPath.resolve("test-plugin-6")); | |||
storeManifestToPath(getPlugin6Manifest(), pluginPath); | |||
versionManager = new DefaultVersionManager(); | |||
} | |||
@@ -110,126 +104,68 @@ public class ManifestPluginDescriptorFinderTest { | |||
/** | |||
* Test of {@link ManifestPluginDescriptorFinder#find(Path)} method. | |||
*/ | |||
@Test(expected = PluginException.class) | |||
public void testFindNotFound() throws Exception { | |||
@Test | |||
public void testFindNotFound() { | |||
PluginDescriptorFinder descriptorFinder = new ManifestPluginDescriptorFinder(); | |||
descriptorFinder.find(pluginsPath.resolve("test-plugin-3")); | |||
assertThrows(PluginRuntimeException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||
} | |||
private List<String> getPlugin1Manifest() { | |||
String[] lines = new String[] { | |||
"Manifest-Version: 1.0\n" | |||
+ "Implementation-Title: Test Plugin #1\n" | |||
+ "Implementation-Version: 0.10.0-SNAPSHOT\n" | |||
+ "Archiver-Version: Plexus Archiver\n" | |||
+ "Built-By: Mario Franco\n" | |||
+ "Specification-Title: Test Plugin #1\n" | |||
+ "Implementation-Vendor-Id: org.pf4j.demo\n" | |||
+ "Plugin-Version: 0.0.1\n" | |||
+ "Plugin-Id: test-plugin-1\n" | |||
+ "Plugin-Description: Test Plugin 1\n" | |||
+ "Plugin-Provider: Decebal Suiu\n" | |||
+ "Plugin-Class: org.pf4j.plugin.TestPlugin\n" | |||
+ "Plugin-Dependencies: test-plugin-2,test-plugin-3@~1.0\n" | |||
+ "Plugin-Requires: *\n" | |||
+ "Plugin-License: Apache-2.0\n" | |||
+ "Created-By: Apache Maven 3.0.5\n" | |||
+ "Build-Jdk: 1.8.0_45\n" | |||
+ "Specification-Version: 0.10.0-SNAPSHOT\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private Manifest getPlugin1Manifest() { | |||
Map<String, String> map = new LinkedHashMap<>(8); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_ID, "test-plugin-1"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_CLASS, "org.pf4j.plugin.TestPlugin"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_DESCRIPTION, "Test Plugin 1"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_DEPENDENCIES, "test-plugin-2,test-plugin-3@~1.0"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_REQUIRES, "*"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_LICENSE, "Apache-2.0"); | |||
return PluginJar.createManifest(map); | |||
} | |||
private List<String> getPlugin2Manifest() { | |||
String[] lines = new String[] { | |||
"Manifest-Version: 1.0\n" | |||
+ "Plugin-Dependencies: \n" | |||
+ "Implementation-Title: Test Plugin #2\n" | |||
+ "Implementation-Version: 0.10.0-SNAPSHOT\n" | |||
+ "Archiver-Version: Plexus Archiver\n" | |||
+ "Built-By: Mario Franco\n" | |||
+ "Specification-Title: Test Plugin #2\n" | |||
+ "Implementation-Vendor-Id: org.pf4j.demo\n" | |||
+ "Plugin-Version: 0.0.1\n" | |||
+ "Plugin-Id: test-plugin-2\n" | |||
+ "Plugin-Provider: Decebal Suiu\n" | |||
+ "Plugin-Class: org.pf4j.plugin.TestPlugin\n" | |||
+ "Created-By: Apache Maven 3.0.5\n" | |||
+ "Build-Jdk: 1.8.0_45\n" | |||
+ "Specification-Version: 0.10.0-SNAPSHOT\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private Manifest getPlugin2Manifest() { | |||
Map<String, String> map = new LinkedHashMap<>(5); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_ID, "test-plugin-2"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_CLASS, "org.pf4j.plugin.TestPlugin"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_DEPENDENCIES, ""); | |||
return PluginJar.createManifest(map); | |||
} | |||
private List<String> getPlugin4Manifest() { | |||
String[] lines = new String[] { | |||
"Manifest-Version: 1.0\n" | |||
+ "Implementation-Title: Test Plugin #4\n" | |||
+ "Implementation-Version: 0.10.0-SNAPSHOT\n" | |||
+ "Archiver-Version: Plexus Archiver\n" | |||
+ "Built-By: Mario Franco\n" | |||
+ "Specification-Title: Test Plugin #4\n" | |||
+ "Implementation-Vendor-Id: org.pf4j.demo\n" | |||
+ "Plugin-Version: 0.0.1\n" | |||
+ "Plugin-Id: test-plugin-2\n" | |||
+ "Plugin-Provider: Decebal Suiu\n" | |||
+ "Created-By: Apache Maven 3.0.5\n" | |||
+ "Build-Jdk: 1.8.0_45\n" | |||
+ "Specification-Version: 0.10.0-SNAPSHOT\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private Manifest getPlugin4Manifest() { | |||
Map<String, String> map = new LinkedHashMap<>(3); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_ID, "test-plugin-1"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
return PluginJar.createManifest(map); | |||
} | |||
private List<String> getPlugin5Manifest() { | |||
String[] lines = new String[] { | |||
"Manifest-Version: 1.0\n" | |||
+ "Implementation-Title: Test Plugin #5\n" | |||
+ "Implementation-Version: 0.10.0-SNAPSHOT\n" | |||
+ "Archiver-Version: Plexus Archiver\n" | |||
+ "Built-By: Mario Franco\n" | |||
+ "Specification-Title: Test Plugin #5\n" | |||
+ "Implementation-Vendor-Id: org.pf4j.demo\n" | |||
+ "Plugin-Id: test-plugin-2\n" | |||
+ "Plugin-Provider: Decebal Suiu\n" | |||
+ "Plugin-Class: org.pf4j.plugin.TestPlugin\n" | |||
+ "Created-By: Apache Maven 3.0.5\n" | |||
+ "Build-Jdk: 1.8.0_45\n" | |||
+ "Specification-Version: 0.10.0-SNAPSHOT\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private Manifest getPlugin5Manifest() { | |||
Map<String, String> map = new LinkedHashMap<>(3); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_ID, "test-plugin-2"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_CLASS, "org.pf4j.plugin.TestPlugin"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
return PluginJar.createManifest(map); | |||
} | |||
private Manifest getPlugin6Manifest() { | |||
Map<String, String> map = new LinkedHashMap<>(2); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_CLASS, "org.pf4j.plugin.TestPlugin"); | |||
map.put(ManifestPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
return PluginJar.createManifest(map); | |||
} | |||
private List<String> getPlugin6Manifest() { | |||
String[] lines = new String[] { | |||
"Manifest-Version: 1.0\n" | |||
+ "Implementation-Title: Test Plugin #6\n" | |||
+ "Implementation-Version: 0.10.0-SNAPSHOT\n" | |||
+ "Archiver-Version: Plexus Archiver\n" | |||
+ "Built-By: Mario Franco\n" | |||
+ "Specification-Title: Test Plugin #6\n" | |||
+ "Implementation-Vendor-Id: org.pf4j.demo\n" | |||
+ "Plugin-Provider: Decebal Suiu\n" | |||
+ "Plugin-Class: org.pf4j.plugin.TestPlugin\n" | |||
+ "Created-By: Apache Maven 3.0.5\n" | |||
+ "Build-Jdk: 1.8.0_45\n" | |||
+ "Specification-Version: 0.10.0-SNAPSHOT\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private void storeManifestToPath(Manifest manifest, Path pluginPath) throws IOException { | |||
Path path = Files.createDirectory(pluginPath.resolve("META-INF")); | |||
try (OutputStream output = new FileOutputStream(path.resolve("MANIFEST.MF").toFile())) { | |||
manifest.write(output); | |||
} | |||
} | |||
} |
@@ -15,9 +15,12 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Test; | |||
import static org.junit.Assert.*; | |||
import org.junit.jupiter.api.Test; | |||
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 Mario Franco | |||
@@ -32,33 +35,33 @@ public class PluginDependencyTest { | |||
PluginDependency instance = new PluginDependency("test"); | |||
assertEquals("test", instance.getPluginId()); | |||
assertEquals("*", instance.getPluginVersionSupport()); | |||
assertEquals(false, instance.isOptional()); | |||
assertFalse(instance.isOptional()); | |||
instance = new PluginDependency("test@"); | |||
assertEquals("test", instance.getPluginId()); | |||
assertEquals("*", instance.getPluginVersionSupport()); | |||
assertEquals(false, instance.isOptional()); | |||
assertFalse(instance.isOptional()); | |||
instance = new PluginDependency("test?"); | |||
assertEquals("test", instance.getPluginId()); | |||
assertEquals("*", instance.getPluginVersionSupport()); | |||
assertEquals(true, instance.isOptional()); | |||
assertTrue(instance.isOptional()); | |||
instance = new PluginDependency("test?@"); | |||
assertEquals("test", instance.getPluginId()); | |||
assertEquals("*", instance.getPluginVersionSupport()); | |||
assertEquals(true, instance.isOptional()); | |||
assertTrue(instance.isOptional()); | |||
instance = new PluginDependency("test@1.0"); | |||
assertEquals("test", instance.getPluginId()); | |||
assertEquals("1.0", instance.getPluginVersionSupport()); | |||
assertEquals(false, instance.isOptional()); | |||
assertFalse(instance.isOptional()); | |||
assertEquals("PluginDependency [pluginId=test, pluginVersionSupport=1.0, optional=false]", instance.toString()); | |||
instance = new PluginDependency("test?@1.0"); | |||
assertEquals("test", instance.getPluginId()); | |||
assertEquals("1.0", instance.getPluginVersionSupport()); | |||
assertEquals(true, instance.isOptional()); | |||
assertTrue(instance.isOptional()); | |||
assertEquals("PluginDependency [pluginId=test, pluginVersionSupport=1.0, optional=true]", instance.toString()); | |||
} | |||
@@ -15,55 +15,57 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.junit.jupiter.api.io.TempDir; | |||
import org.pf4j.plugin.PluginZip; | |||
import org.pf4j.plugin.TestPlugin; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.nio.charset.Charset; | |||
import java.io.OutputStreamWriter; | |||
import java.io.Writer; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
import static org.junit.Assert.*; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertFalse; | |||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||
import static org.junit.jupiter.api.Assertions.assertTrue; | |||
public class PropertiesPluginDescriptorFinderTest { | |||
private VersionManager versionManager; | |||
private Path pluginsPath; | |||
@Rule | |||
public TemporaryFolder pluginsFolder = new TemporaryFolder(); | |||
@TempDir | |||
Path pluginsPath; | |||
@Before | |||
@BeforeEach | |||
public void setUp() throws IOException { | |||
pluginsPath = pluginsFolder.getRoot().toPath(); | |||
Path pluginPath = Files.createDirectory(pluginsPath.resolve("test-plugin-1")); | |||
storePropertiesToPath(getPlugin1Properties(), pluginPath); | |||
Charset charset = Charset.forName("UTF-8"); | |||
Path pluginPath = pluginsFolder.newFolder("test-plugin-1").toPath(); | |||
Files.write(pluginPath.resolve("plugin.properties"), getPlugin1Properties(), charset); | |||
pluginPath = pluginsFolder.newFolder("test-plugin-2").toPath(); | |||
Files.write(pluginPath.resolve("plugin.properties"), getPlugin2Properties(), charset); | |||
pluginPath = Files.createDirectory(pluginsPath.resolve("test-plugin-2")); | |||
storePropertiesToPath(getPlugin2Properties(), pluginPath); | |||
// empty plugin | |||
pluginsFolder.newFolder("test-plugin-3"); | |||
Files.createDirectories(pluginsPath.resolve("test-plugin-3")); | |||
// no plugin class | |||
pluginPath = pluginsFolder.newFolder("test-plugin-4").toPath(); | |||
Files.write(pluginPath.resolve("plugin.properties"), getPlugin4Properties(), charset); | |||
pluginPath = Files.createDirectory(pluginsPath.resolve("test-plugin-4")); | |||
storePropertiesToPath(getPlugin4Properties(), pluginPath); | |||
// no plugin version | |||
pluginPath = pluginsFolder.newFolder("test-plugin-5").toPath(); | |||
Files.write(pluginPath.resolve("plugin.properties"), getPlugin5Properties(), charset); | |||
pluginPath = Files.createDirectory(pluginsPath.resolve("test-plugin-5")); | |||
storePropertiesToPath(getPlugin5Properties(), pluginPath); | |||
// no plugin id | |||
pluginPath = pluginsFolder.newFolder("test-plugin-6").toPath(); | |||
Files.write(pluginPath.resolve("plugin.properties"), getPlugin6Properties(), charset); | |||
pluginPath = Files.createDirectory(pluginsPath.resolve("test-plugin-6")); | |||
storePropertiesToPath(getPlugin6Properties(), pluginPath); | |||
versionManager = new DefaultVersionManager(); | |||
} | |||
@@ -77,7 +79,7 @@ public class PropertiesPluginDescriptorFinderTest { | |||
assertEquals("test-plugin-1", plugin1.getPluginId()); | |||
assertEquals("Test Plugin 1", plugin1.getPluginDescription()); | |||
assertEquals("org.pf4j.plugin.TestPlugin", plugin1.getPluginClass()); | |||
assertEquals(TestPlugin.class.getName(), plugin1.getPluginClass()); | |||
assertEquals("0.0.1", plugin1.getVersion()); | |||
assertEquals("Decebal Suiu", plugin1.getProvider()); | |||
assertEquals(2, plugin1.getDependencies().size()); | |||
@@ -91,7 +93,7 @@ public class PropertiesPluginDescriptorFinderTest { | |||
assertEquals("test-plugin-2", plugin2.getPluginId()); | |||
assertEquals("", plugin2.getPluginDescription()); | |||
assertEquals("org.pf4j.plugin.TestPlugin", plugin2.getPluginClass()); | |||
assertEquals(TestPlugin.class.getName(), plugin2.getPluginClass()); | |||
assertEquals("0.0.1", plugin2.getVersion()); | |||
assertEquals("Decebal Suiu", plugin2.getProvider()); | |||
assertEquals(0, plugin2.getDependencies().size()); | |||
@@ -99,98 +101,75 @@ public class PropertiesPluginDescriptorFinderTest { | |||
assertTrue(versionManager.checkVersionConstraint("1.0.0", plugin2.getRequires())); | |||
} | |||
@Test(expected = PluginException.class) | |||
public void testNotFound() throws Exception { | |||
@Test | |||
public void testNotFound() { | |||
PluginDescriptorFinder descriptorFinder = new PropertiesPluginDescriptorFinder(); | |||
descriptorFinder.find(pluginsPath.resolve("test-plugin-3")); | |||
assertThrows(PluginRuntimeException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||
} | |||
@Test | |||
public void findInJar() throws Exception { | |||
PluginZip pluginJar = new PluginZip.Builder(pluginsFolder.newFile("my-plugin-1.2.3.jar"), "myPlugin") | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
private Properties getPlugin1Properties() { | |||
Map<String, String> map = new LinkedHashMap<>(8); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_ID, "test-plugin-1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_CLASS, TestPlugin.class.getName()); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DESCRIPTION, "Test Plugin 1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, "test-plugin-2,test-plugin-3@~1.0"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, ">=1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_LICENSE, "Apache-2.0"); | |||
return PluginZip.createProperties(map); | |||
} | |||
assertTrue(Files.exists(pluginJar.path())); | |||
private Properties getPlugin2Properties() { | |||
Map<String, String> map = new LinkedHashMap<>(5); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_ID, "test-plugin-2"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_CLASS, TestPlugin.class.getName()); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, ""); | |||
PluginDescriptorFinder descriptorFinder = new PropertiesPluginDescriptorFinder(); | |||
PluginDescriptor pluginDescriptor = descriptorFinder.find(pluginJar.path()); | |||
assertNotNull(pluginDescriptor); | |||
assertEquals("myPlugin", pluginJar.pluginId()); | |||
assertEquals("1.2.3", pluginJar.pluginVersion()); | |||
return PluginZip.createProperties(map); | |||
} | |||
private List<String> getPlugin1Properties() { | |||
String[] lines = new String[] { | |||
"plugin.id=test-plugin-1\n" | |||
+ "plugin.version=0.0.1\n" | |||
+ "plugin.description=Test Plugin 1\n" | |||
+ "plugin.provider=Decebal Suiu\n" | |||
+ "plugin.class=org.pf4j.plugin.TestPlugin\n" | |||
+ "plugin.dependencies=test-plugin-2,test-plugin-3@~1.0\n" | |||
+ "plugin.requires=>=1\n" | |||
+ "plugin.license=Apache-2.0\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
} | |||
private Properties getPlugin4Properties() { | |||
Map<String, String> map = new LinkedHashMap<>(5); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_ID, "test-plugin-2"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, ""); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, "*"); | |||
private List<String> getPlugin2Properties() { | |||
String[] lines = new String[] { | |||
"plugin.id=test-plugin-2\n" | |||
+ "plugin.version=0.0.1\n" | |||
+ "plugin.provider=Decebal Suiu\n" | |||
+ "plugin.class=org.pf4j.plugin.TestPlugin\n" | |||
+ "plugin.dependencies=\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
return PluginZip.createProperties(map); | |||
} | |||
private List<String> getPlugin4Properties() { | |||
String[] lines = new String[] { | |||
"plugin.id=test-plugin-2\n" | |||
+ "plugin.version=0.0.1\n" | |||
+ "plugin.provider=Decebal Suiu\n" | |||
+ "plugin.dependencies=\n" | |||
+ "plugin.requires=*\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private Properties getPlugin5Properties() { | |||
Map<String, String> map = new LinkedHashMap<>(5); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_ID, "test-plugin-2"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_CLASS, TestPlugin.class.getName()); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, ""); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, "*"); | |||
return PluginZip.createProperties(map); | |||
} | |||
private List<String> getPlugin5Properties() { | |||
String[] lines = new String[] { | |||
"plugin.id=test-plugin-2\n" | |||
+ "plugin.provider=Decebal Suiu\n" | |||
+ "plugin.class=org.pf4j.plugin.TestPlugin\n" | |||
+ "plugin.dependencies=\n" | |||
+ "plugin.requires=*\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private Properties getPlugin6Properties() { | |||
Map<String, String> map = new LinkedHashMap<>(5); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_CLASS, TestPlugin.class.getName()); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_VERSION, "0.0.1"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_PROVIDER, "Decebal Suiu"); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_DEPENDENCIES, ""); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_REQUIRES, "*"); | |||
return PluginZip.createProperties(map); | |||
} | |||
private List<String> getPlugin6Properties() { | |||
String[] lines = new String[] { | |||
"plugin.version=0.0.1\n" | |||
+ "plugin.provider=Decebal Suiu\n" | |||
+ "plugin.class=org.pf4j.plugin.TestPlugin\n" | |||
+ "plugin.dependencies=\n" | |||
+ "plugin.requires=*\n" | |||
+ "\n" | |||
+ "" | |||
}; | |||
return Arrays.asList(lines); | |||
private void storePropertiesToPath(Properties properties, Path pluginPath) throws IOException { | |||
Path path = pluginPath.resolve(PropertiesPluginDescriptorFinder.DEFAULT_PROPERTIES_FILE_NAME); | |||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(path.toFile()), StandardCharsets.UTF_8)) { | |||
properties.store(writer, ""); | |||
} | |||
} | |||
} |
@@ -15,11 +15,12 @@ | |||
*/ | |||
package org.pf4j; | |||
import org.junit.Test; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.plugin.FailTestExtension; | |||
import org.pf4j.plugin.TestExtension; | |||
import static org.junit.Assert.*; | |||
import static org.junit.jupiter.api.Assertions.assertNotSame; | |||
import static org.junit.jupiter.api.Assertions.assertSame; | |||
/** | |||
* @author Decebal Suiu |
@@ -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); | |||
} | |||
} | |||
} |
@@ -26,4 +26,9 @@ public class FailTestExtension implements TestExtensionPoint { | |||
public FailTestExtension(String name) { | |||
} | |||
@Override | |||
public String saySomething() { | |||
return "I am a fail test extension"; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} | |||
} |
@@ -15,44 +15,54 @@ | |||
*/ | |||
package org.pf4j.plugin; | |||
import org.pf4j.PropertiesPluginDescriptorFinder; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.nio.file.Path; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
import java.util.zip.ZipEntry; | |||
import java.util.zip.ZipOutputStream; | |||
/** | |||
* Represents a plugin zip/jar file. | |||
* The "plugin.properties" file is created on the fly from the information supplied in Builder. | |||
* Represents a plugin {@code zip} file. | |||
* The {@code plugin.properties} file is created on the fly from the information supplied in {@link Builder}. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class PluginZip { | |||
private final File file; | |||
private final Path path; | |||
private final String pluginId; | |||
private final String pluginClass; | |||
private final String pluginVersion; | |||
protected PluginZip(Builder builder) { | |||
this.file = builder.file; | |||
this.path = builder.path; | |||
this.pluginId = builder.pluginId; | |||
this.pluginClass = builder.pluginClass; | |||
this.pluginVersion = builder.pluginVersion; | |||
} | |||
public File file() { | |||
return file; | |||
public Path path() { | |||
return path; | |||
} | |||
public Path path() { | |||
return file.toPath(); | |||
public File file() { | |||
return path.toFile(); | |||
} | |||
public String pluginId() { | |||
return pluginId; | |||
} | |||
public String pluginClass() { | |||
return pluginClass; | |||
} | |||
public String pluginVersion() { | |||
return pluginVersion; | |||
} | |||
@@ -64,24 +74,59 @@ public class PluginZip { | |||
return path.getParent().resolve(fileName.substring(0, fileName.length() - 4)); // without ".zip" suffix | |||
} | |||
public static Properties createProperties(Map<String, String> map) { | |||
Properties properties = new Properties(); | |||
properties.putAll(map); | |||
return properties; | |||
} | |||
public static class Builder { | |||
private final File file; | |||
private final Path path; | |||
private final String pluginId; | |||
private String pluginClass; | |||
private String pluginVersion; | |||
private Map<String, String> properties = new LinkedHashMap<>(); | |||
public Builder(File file, String pluginId) { | |||
this.file = file; | |||
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 properties to the {@code properties} file. | |||
* As possible attribute name please see {@link PropertiesPluginDescriptorFinder}. | |||
*/ | |||
public Builder properties(Map<String, String> properties) { | |||
this.properties.putAll(properties); | |||
return this; | |||
} | |||
/** | |||
* Add extra property to the {@code properties} file. | |||
* As possible property name please see {@link PropertiesPluginDescriptorFinder}. | |||
*/ | |||
public Builder property(String name, String value) { | |||
properties.put(name, value); | |||
return this; | |||
} | |||
public PluginZip build() throws IOException { | |||
createPropertiesFile(); | |||
@@ -89,15 +134,20 @@ public class PluginZip { | |||
} | |||
protected void createPropertiesFile() throws IOException { | |||
Properties properties = new Properties(); | |||
properties.setProperty("plugin.id", pluginId); | |||
properties.setProperty("plugin.version", pluginVersion); | |||
properties.setProperty("plugin.class", "org.pf4j.plugin.TestPlugin"); | |||
ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(file)); | |||
ZipEntry propertiesFile = new ZipEntry("plugin.properties"); | |||
Map<String, String> map = new LinkedHashMap<>(); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_ID, pluginId); | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_VERSION, pluginVersion); | |||
if (pluginClass != null) { | |||
map.put(PropertiesPluginDescriptorFinder.PLUGIN_CLASS, pluginClass); | |||
} | |||
if (properties != null) { | |||
map.putAll(properties); | |||
} | |||
ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(path.toFile())); | |||
ZipEntry propertiesFile = new ZipEntry(PropertiesPluginDescriptorFinder.DEFAULT_PROPERTIES_FILE_NAME); | |||
outputStream.putNextEntry(propertiesFile); | |||
properties.store(outputStream, ""); | |||
createProperties(map).store(outputStream, ""); | |||
outputStream.closeEntry(); | |||
outputStream.close(); | |||
} |
@@ -23,4 +23,9 @@ import org.pf4j.Extension; | |||
@Extension | |||
public class TestExtension implements TestExtensionPoint { | |||
@Override | |||
public String saySomething() { | |||
return "I am a test extension"; | |||
} | |||
} |
@@ -22,4 +22,6 @@ import org.pf4j.ExtensionPoint; | |||
*/ | |||
public interface TestExtensionPoint extends ExtensionPoint { | |||
String saySomething(); | |||
} |
@@ -16,19 +16,23 @@ | |||
package org.pf4j.processor; | |||
import org.junit.Test; | |||
import org.junit.jupiter.api.Test; | |||
import javax.annotation.processing.Filer; | |||
import javax.tools.FileObject; | |||
import javax.tools.StandardLocation; | |||
import java.io.IOException; | |||
import java.io.StringReader; | |||
import java.util.*; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import static org.hamcrest.MatcherAssert.assertThat; | |||
import static org.hamcrest.core.Is.is; | |||
import static org.junit.Assert.*; | |||
import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.BDDMockito.given; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Mockito.mock; | |||
/** | |||
@@ -46,7 +50,6 @@ public class ServiceProviderExtensionStorageTest { | |||
assertThat(entries.contains("World"), is(true)); | |||
} | |||
@Test | |||
public void ensureReadingExtensionsProducesCorrectListOfExtensions() { | |||
final StringReader file = new StringReader("#hello\n World"); | |||
@@ -56,6 +59,7 @@ public class ServiceProviderExtensionStorageTest { | |||
given(processor.getExtensions()).willReturn(extensions); | |||
ServiceProviderExtensionStorage extensionStorage = new ServiceProviderExtensionStorage(processor) { | |||
@Override | |||
protected Filer getFiler() { | |||
try { | |||
@@ -72,6 +76,7 @@ public class ServiceProviderExtensionStorageTest { | |||
throw new IllegalStateException("Shouldn't have gotten here"); | |||
} | |||
} | |||
}; | |||
Map<String, Set<String>> read = extensionStorage.read(); |
@@ -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); | |||
} | |||
} |
@@ -15,24 +15,24 @@ | |||
*/ | |||
package org.pf4j.util; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.junit.jupiter.api.Test; | |||
import org.junit.jupiter.api.io.TempDir; | |||
import org.pf4j.plugin.PluginZip; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import static org.junit.Assert.*; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertTrue; | |||
public class FileUtilsTest { | |||
@Rule | |||
public TemporaryFolder testFolder = new TemporaryFolder(); | |||
@TempDir | |||
Path pluginsPath; | |||
@Test | |||
public void expandIfZip() throws Exception { | |||
PluginZip pluginZip = new PluginZip.Builder(testFolder.newFile("my-plugin-1.2.3.zip"), "myPlugin") | |||
PluginZip pluginZip = new PluginZip.Builder(pluginsPath.resolve("my-plugin-1.2.3.zip"), "myPlugin") | |||
.pluginVersion("1.2.3") | |||
.build(); | |||
@@ -41,10 +41,10 @@ public class FileUtilsTest { | |||
assertTrue(Files.exists(unzipped.resolve("plugin.properties"))); | |||
// File without .suffix | |||
Path extra = testFolder.newFile("extra").toPath(); | |||
Path extra = pluginsPath.resolve("extra"); | |||
assertEquals(extra, FileUtils.expandIfZip(extra)); | |||
// Folder | |||
Path folder = testFolder.newFile("folder").toPath(); | |||
Path folder = pluginsPath.resolve("folder"); | |||
assertEquals(folder, FileUtils.expandIfZip(folder)); | |||
} | |||