Browse Source

Merge pull request #324 from pf4j/pf4j_3

PF4J 3
tags/release-3.0.0
Decebal Suiu 4 years ago
parent
commit
ee3b5f2276
No account linked to committer's email address
100 changed files with 2654 additions and 935 deletions
  1. 0
    4
      .travis.yml
  2. 2
    2
      demo/api/pom.xml
  3. 2
    2
      demo/app/pom.xml
  4. 2
    2
      demo/app/src/main/java/org/pf4j/demo/Boot.java
  5. 2
    2
      demo/plugins/plugin1/pom.xml
  6. 2
    2
      demo/plugins/plugin2/pom.xml
  7. 2
    11
      demo/plugins/pom.xml
  8. 2
    2
      demo/pom.xml
  9. 68
    0
      maven-archetypes/quickstart/pom.xml
  10. 86
    0
      maven-archetypes/quickstart/src/main/resources/META-INF/maven/archetype-metadata.xml
  11. 86
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/app/pom.xml
  12. 30
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/assembly/assembly.xml
  13. 102
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Boot.java
  14. 9
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Greeting.java
  15. 13
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/WhazzupGreeting.java
  16. 20
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/resources/log4j.properties
  17. 6
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/disabled.txt
  18. 6
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/enabled.txt
  19. 5
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/plugin.properties
  20. 24
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/pom.xml
  21. 37
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/src/main/java/hello/HelloPlugin.java
  22. 86
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/pom.xml
  23. 5
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/plugin.properties
  24. 32
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/pom.xml
  25. 41
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/src/main/java/welcome/WelcomePlugin.java
  26. 48
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/pom.xml
  27. 23
    0
      maven-archetypes/quickstart/src/main/resources/archetype-resources/run.sh
  28. 40
    9
      pf4j/pom.xml
  29. 47
    0
      pf4j/src/main/java/module-info.java
  30. 86
    107
      pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
  31. 88
    0
      pf4j/src/main/java/org/pf4j/BasePluginLoader.java
  32. 8
    11
      pf4j/src/main/java/org/pf4j/BasePluginRepository.java
  33. 2
    2
      pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java
  34. 16
    0
      pf4j/src/main/java/org/pf4j/CompoundPluginLoader.java
  35. 20
    2
      pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java
  36. 3
    7
      pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java
  37. 6
    3
      pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java
  38. 5
    54
      pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java
  39. 15
    31
      pf4j/src/main/java/org/pf4j/DefaultPluginManager.java
  40. 19
    25
      pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java
  41. 12
    20
      pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java
  42. 3
    3
      pf4j/src/main/java/org/pf4j/DependencyResolver.java
  43. 4
    4
      pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java
  44. 29
    0
      pf4j/src/main/java/org/pf4j/DevelopmentPluginLoader.java
  45. 55
    0
      pf4j/src/main/java/org/pf4j/DevelopmentPluginRepository.java
  46. 1
    1
      pf4j/src/main/java/org/pf4j/ExtensionFactory.java
  47. 56
    0
      pf4j/src/main/java/org/pf4j/JarPluginManager.java
  48. 16
    8
      pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java
  49. 24
    15
      pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java
  50. 3
    3
      pf4j/src/main/java/org/pf4j/Plugin.java
  51. 1
    1
      pf4j/src/main/java/org/pf4j/PluginAlreadyLoadedException.java
  52. 9
    8
      pf4j/src/main/java/org/pf4j/PluginClasspath.java
  53. 1
    4
      pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java
  54. 26
    5
      pf4j/src/main/java/org/pf4j/PluginManager.java
  55. 3
    2
      pf4j/src/main/java/org/pf4j/PluginRepository.java
  56. 6
    10
      pf4j/src/main/java/org/pf4j/PluginRuntimeException.java
  57. 4
    4
      pf4j/src/main/java/org/pf4j/PluginStatusProvider.java
  58. 3
    8
      pf4j/src/main/java/org/pf4j/PluginWrapper.java
  59. 25
    16
      pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java
  60. 1
    1
      pf4j/src/main/java/org/pf4j/RuntimeMode.java
  61. 4
    3
      pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java
  62. 49
    0
      pf4j/src/main/java/org/pf4j/ZipPluginManager.java
  63. 1
    5
      pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java
  64. 5
    7
      pf4j/src/main/java/org/pf4j/util/AndFileFilter.java
  65. 5
    5
      pf4j/src/main/java/org/pf4j/util/DirectedGraph.java
  66. 2
    9
      pf4j/src/main/java/org/pf4j/util/FileUtils.java
  67. 5
    7
      pf4j/src/main/java/org/pf4j/util/OrFileFilter.java
  68. 5
    1
      pf4j/src/main/java/org/pf4j/util/Unzip.java
  69. 6
    6
      pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java
  70. 49
    0
      pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java
  71. 46
    36
      pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java
  72. 20
    10
      pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java
  73. 4
    4
      pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java
  74. 22
    24
      pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java
  75. 18
    48
      pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java
  76. 21
    30
      pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java
  77. 10
    8
      pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java
  78. 6
    4
      pf4j/src/test/java/org/pf4j/DependencyResolverTest.java
  79. 61
    0
      pf4j/src/test/java/org/pf4j/DevelopmentPluginRepositoryTest.java
  80. 3
    3
      pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java
  81. 96
    0
      pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java
  82. 58
    0
      pf4j/src/test/java/org/pf4j/JarPluginRepositoryTest.java
  83. 73
    0
      pf4j/src/test/java/org/pf4j/LegacyExtensionFinderTest.java
  84. 2
    2
      pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java
  85. 42
    41
      pf4j/src/test/java/org/pf4j/LoadPluginsTest.java
  86. 77
    141
      pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java
  87. 11
    8
      pf4j/src/test/java/org/pf4j/PluginDependencyTest.java
  88. 86
    107
      pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java
  89. 3
    2
      pf4j/src/test/java/org/pf4j/SingletonExtensionFactoryTest.java
  90. 33
    0
      pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java
  91. 55
    0
      pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java
  92. 5
    0
      pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java
  93. 200
    0
      pf4j/src/test/java/org/pf4j/plugin/PluginJar.java
  94. 69
    19
      pf4j/src/test/java/org/pf4j/plugin/PluginZip.java
  95. 5
    0
      pf4j/src/test/java/org/pf4j/plugin/TestExtension.java
  96. 2
    0
      pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java
  97. 10
    5
      pf4j/src/test/java/org/pf4j/processor/ServiceProviderExtensionStorageTest.java
  98. 98
    0
      pf4j/src/test/java/org/pf4j/util/DirectedGraphTest.java
  99. 9
    9
      pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java
  100. 0
    0
      pom.xml

+ 0
- 4
.travis.yml View File

@@ -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

+ 2
- 2
demo/api/pom.xml View File

@@ -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>


+ 2
- 2
demo/app/pom.xml View File

@@ -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>


+ 2
- 2
demo/app/src/main/java/org/pf4j/demo/Boot.java View File

@@ -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());
}


+ 2
- 2
demo/plugins/plugin1/pom.xml View File

@@ -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>


+ 2
- 2
demo/plugins/plugin2/pom.xml View File

@@ -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>


+ 2
- 11
demo/plugins/pom.xml View File

@@ -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>

+ 2
- 2
demo/pom.xml View File

@@ -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>


+ 68
- 0
maven-archetypes/quickstart/pom.xml View File

@@ -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>

+ 86
- 0
maven-archetypes/quickstart/src/main/resources/META-INF/maven/archetype-metadata.xml View File

@@ -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>

+ 86
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/app/pom.xml View File

@@ -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>

+ 30
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/assembly/assembly.xml View File

@@ -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>

+ 102
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Boot.java View File

@@ -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();
}

});
*/
}

}

+ 9
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Greeting.java View File

@@ -0,0 +1,9 @@
package ${package};

import org.pf4j.ExtensionPoint;

public interface Greeting extends ExtensionPoint {

String getGreeting();

}

+ 13
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/WhazzupGreeting.java View File

@@ -0,0 +1,13 @@
package ${package};

import org.pf4j.Extension;

@Extension
public class WhazzupGreeting implements Greeting {

@Override
public String getGreeting() {
return "Whazzup";
}

}

+ 20
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/resources/log4j.properties View File

@@ -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

+ 6
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/disabled.txt View File

@@ -0,0 +1,6 @@
########################################
# - load all plugins except these
# - add one plugin id on each line
# - put this file in plugins folder
########################################
#welcome-plugin

+ 6
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/enabled.txt View File

@@ -0,0 +1,6 @@
########################################
# - load only these plugins
# - add one plugin id on each line
# - put this file in plugins folder
########################################
#welcome-plugin

+ 5
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/plugin.properties View File

@@ -0,0 +1,5 @@
plugin.id=hello-plugin
plugin.class=${package}.hello.HelloPlugin
plugin.version=${version}
plugin.provider=
plugin.dependencies=

+ 24
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/pom.xml View File

@@ -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>

+ 37
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/src/main/java/hello/HelloPlugin.java View File

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

}

}

+ 86
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/pom.xml View File

@@ -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>

+ 5
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/plugin.properties View File

@@ -0,0 +1,5 @@
plugin.id=welcome-plugin
plugin.class=${package}.welcome.WelcomePlugin
plugin.version=${version}
plugin.provider=
plugin.dependencies=

+ 32
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/pom.xml View File

@@ -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>

+ 41
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/src/main/java/welcome/WelcomePlugin.java View File

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

}

}

+ 48
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/pom.xml View File

@@ -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>

+ 23
- 0
maven-archetypes/quickstart/src/main/resources/archetype-resources/run.sh View File

@@ -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 -

+ 40
- 9
pf4j/pom.xml View File

@@ -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>

+ 47
- 0
pf4j/src/main/java/module-info.java View File

@@ -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;
}

+ 86
- 107
pf4j/src/main/java/org/pf4j/AbstractPluginManager.java View File

@@ -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;
}

}

+ 88
- 0
pf4j/src/main/java/org/pf4j/BasePluginLoader.java View File

@@ -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);
}
}
}

}

+ 8
- 11
pf4j/src/main/java/org/pf4j/BasePluginRepository.java View File

@@ -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);
}
}


+ 2
- 2
pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java View File

@@ -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);
}

}

+ 16
- 0
pf4j/src/main/java/org/pf4j/CompoundPluginLoader.java View File

@@ -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();
}

+ 20
- 2
pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java View File

@@ -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

+ 3
- 7
pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java View File

@@ -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;
}

}

+ 6
- 3
pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java View File

@@ -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);
}

}

+ 5
- 54
pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java View File

@@ -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);
}

}

+ 15
- 31
pf4j/src/main/java/org/pf4j/DefaultPluginManager.java View File

@@ -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);

+ 19
- 25
pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java View File

@@ -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;
}

}

+ 12
- 20
pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java View File

@@ -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;
}

}

+ 3
- 3
pf4j/src/main/java/org/pf4j/DependencyResolver.java View File

@@ -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;


+ 4
- 4
pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java View File

@@ -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());
}

}

+ 29
- 0
pf4j/src/main/java/org/pf4j/DevelopmentPluginLoader.java View File

@@ -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());
}

}

+ 55
- 0
pf4j/src/main/java/org/pf4j/DevelopmentPluginRepository.java View File

@@ -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;
}

}

+ 1
- 1
pf4j/src/main/java/org/pf4j/ExtensionFactory.java View File

@@ -20,6 +20,6 @@ package org.pf4j;
*/
public interface ExtensionFactory {

Object create(Class<?> extensionClass);
<T> T create(Class<T> extensionClass);

}

+ 56
- 0
pf4j/src/main/java/org/pf4j/JarPluginManager.java View File

@@ -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);
}

}

+ 16
- 8
pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java View File

@@ -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);
}
}


+ 24
- 15
pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java View File

@@ -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;
}

+ 3
- 3
pf4j/src/main/java/org/pf4j/Plugin.java View File

@@ -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() {
}

}

+ 1
- 1
pf4j/src/main/java/org/pf4j/PluginAlreadyLoadedException.java View File

@@ -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;

+ 9
- 8
pf4j/src/main/java/org/pf4j/PluginClasspath.java View File

@@ -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;
}

+ 1
- 4
pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java View File

@@ -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);

}

+ 26
- 5
pf4j/src/main/java/org/pf4j/PluginManager.java View File

@@ -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();

+ 3
- 2
pf4j/src/main/java/org/pf4j/PluginRepository.java View File

@@ -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);


pf4j/src/main/java/org/pf4j/PluginException.java → pf4j/src/main/java/org/pf4j/PluginRuntimeException.java View File

@@ -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));
}


+ 4
- 4
pf4j/src/main/java/org/pf4j/PluginStatusProvider.java View File

@@ -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);

}

+ 3
- 8
pf4j/src/main/java/org/pf4j/PluginWrapper.java View File

@@ -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;
}

+ 25
- 16
pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java View File

@@ -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;
}

+ 1
- 1
pf4j/src/main/java/org/pf4j/RuntimeMode.java View File

@@ -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;
}

+ 4
- 3
pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java View File

@@ -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);
}

+ 49
- 0
pf4j/src/main/java/org/pf4j/ZipPluginManager.java View File

@@ -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);
}

}

+ 1
- 5
pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java View File

@@ -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);
}
}

+ 5
- 7
pf4j/src/main/java/org/pf4j/util/AndFileFilter.java View File

@@ -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;
}


+ 5
- 5
pf4j/src/main/java/org/pf4j/util/DirectedGraph.java View File

@@ -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();

+ 2
- 9
pf4j/src/main/java/org/pf4j/util/FileUtils.java View File

@@ -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());

+ 5
- 7
pf4j/src/main/java/org/pf4j/util/OrFileFilter.java View File

@@ -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;
}


+ 5
- 1
pf4j/src/main/java/org/pf4j/util/Unzip.java View File

@@ -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());
}

+ 6
- 6
pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java View File

@@ -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;
}

+ 49
- 0
pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java View File

@@ -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());
}

}

+ 46
- 36
pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java View File

@@ -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, "");
}
}

}

+ 20
- 10
pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java View File

@@ -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));
}

}

+ 4
- 4
pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java View File

@@ -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;


+ 22
- 24
pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java View File

@@ -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();


+ 18
- 48
pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java View File

@@ -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);
}

}

+ 21
- 30
pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java View File

@@ -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());
}

}

+ 10
- 8
pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java View File

@@ -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

+ 6
- 4
pf4j/src/test/java/org/pf4j/DependencyResolverTest.java View File

@@ -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);

+ 61
- 0
pf4j/src/test/java/org/pf4j/DevelopmentPluginRepositoryTest.java View File

@@ -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);
}

}

+ 3
- 3
pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java View File

@@ -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

+ 96
- 0
pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java View File

@@ -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());
}

}

+ 58
- 0
pf4j/src/test/java/org/pf4j/JarPluginRepositoryTest.java View File

@@ -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());
}

}

+ 73
- 0
pf4j/src/test/java/org/pf4j/LegacyExtensionFinderTest.java View File

@@ -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());
}

}

+ 2
- 2
pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java View File

@@ -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

+ 42
- 41
pf4j/src/test/java/org/pf4j/LoadPluginsTest.java View File

@@ -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();


+ 77
- 141
pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java View File

@@ -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);
}
}

}

+ 11
- 8
pf4j/src/test/java/org/pf4j/PluginDependencyTest.java View File

@@ -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());
}


+ 86
- 107
pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java View File

@@ -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, "");
}
}

}

+ 3
- 2
pf4j/src/test/java/org/pf4j/SingletonExtensionFactoryTest.java View File

@@ -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

+ 33
- 0
pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.plugin;

/**
* Defines the interface for classes that know to supply class data for a class name.
* The idea is to have the possibility to retrieve the data for a class from different sources:
* <ul>
* <li>Class path - the class is already loaded by the class loader</li>
* <li>String - the string (the source code) is compiled dynamically via {@link javax.tools.JavaCompiler}</>
* <li>Generate the source code programmatically using something like {@code https://github.com/square/javapoet}</li>
* </ul>
*
* @author Decebal Suiu
*/
public interface ClassDataProvider {

byte[] getClassData(String className);

}

+ 55
- 0
pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.plugin;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* Get class data from the class path.
*
* @author Decebal Suiu
*/
public class DefaultClassDataProvider implements ClassDataProvider {

@Override
public byte[] getClassData(String className) {
String path = className.replace('.', '/') + ".class";
InputStream classDataStream = getClass().getClassLoader().getResourceAsStream(path);
if (classDataStream == null) {
throw new RuntimeException("Cannot find class data");
}

try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
copyStream(classDataStream, outputStream);
return outputStream.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}

private void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];

int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}

}

+ 5
- 0
pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java View File

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

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

}

+ 200
- 0
pf4j/src/test/java/org/pf4j/plugin/PluginJar.java View File

@@ -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();
}
}

}

}

+ 69
- 19
pf4j/src/test/java/org/pf4j/plugin/PluginZip.java View File

@@ -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();
}

+ 5
- 0
pf4j/src/test/java/org/pf4j/plugin/TestExtension.java View File

@@ -23,4 +23,9 @@ import org.pf4j.Extension;
@Extension
public class TestExtension implements TestExtensionPoint {

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

}

+ 2
- 0
pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java View File

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

String saySomething();

}

+ 10
- 5
pf4j/src/test/java/org/pf4j/processor/ServiceProviderExtensionStorageTest.java View File

@@ -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();

+ 98
- 0
pf4j/src/test/java/org/pf4j/util/DirectedGraphTest.java View File

@@ -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);
}

}

+ 9
- 9
pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java View File

@@ -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));
}


+ 0
- 0
pom.xml View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save