diff --git a/.travis.yml b/.travis.yml index 90b2770..b2e4fb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/demo/api/pom.xml b/demo/api/pom.xml index 723e602..9a5f33f 100644 --- a/demo/api/pom.xml +++ b/demo/api/pom.xml @@ -4,12 +4,12 @@ org.pf4j.demo pf4j-demo-parent - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 pf4j-demo-api - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT jar Demo Api diff --git a/demo/app/pom.xml b/demo/app/pom.xml index 3335fbe..4cb624c 100644 --- a/demo/app/pom.xml +++ b/demo/app/pom.xml @@ -4,12 +4,12 @@ org.pf4j.demo pf4j-demo-parent - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 pf4j-demo-app - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT jar Demo App diff --git a/demo/app/src/main/java/org/pf4j/demo/Boot.java b/demo/app/src/main/java/org/pf4j/demo/Boot.java index 562f8f8..066a207 100644 --- a/demo/app/src/main/java/org/pf4j/demo/Boot.java +++ b/demo/app/src/main/java/org/pf4j/demo/Boot.java @@ -73,8 +73,8 @@ public class Boot { } System.out.println("Extension classes by classpath:"); - List> greetingsClasses = pluginManager.getExtensionClasses(Greeting.class); - for (Class greeting : greetingsClasses) { + List> greetingsClasses = pluginManager.getExtensionClasses(Greeting.class); + for (Class greeting : greetingsClasses) { System.out.println(" Class: " + greeting.getCanonicalName()); } diff --git a/demo/plugins/plugin1/pom.xml b/demo/plugins/plugin1/pom.xml index 4695489..51e4ff9 100644 --- a/demo/plugins/plugin1/pom.xml +++ b/demo/plugins/plugin1/pom.xml @@ -4,12 +4,12 @@ org.pf4j.demo pf4j-demo-plugins - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 pf4j-demo-plugin1 - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT jar Demo Plugin #1 diff --git a/demo/plugins/plugin2/pom.xml b/demo/plugins/plugin2/pom.xml index 40e19b1..db67c4b 100644 --- a/demo/plugins/plugin2/pom.xml +++ b/demo/plugins/plugin2/pom.xml @@ -4,12 +4,12 @@ org.pf4j.demo pf4j-demo-plugins - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 pf4j-demo-plugin2 - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT jar Demo Plugin #2 diff --git a/demo/plugins/pom.xml b/demo/plugins/pom.xml index 4460964..589951a 100644 --- a/demo/plugins/pom.xml +++ b/demo/plugins/pom.xml @@ -4,19 +4,18 @@ org.pf4j.demo pf4j-demo-parent - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 pf4j-demo-plugins - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT pom Demo Plugins Parent UTF-8 UTF-8 - 1.7 @@ -28,14 +27,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - ${java.version} - ${java.version} - - org.apache.maven.plugins maven-assembly-plugin diff --git a/demo/pom.xml b/demo/pom.xml index 2d046b5..fc182e5 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -4,13 +4,13 @@ org.pf4j pf4j-parent - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 org.pf4j.demo pf4j-demo-parent - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT pom Demo Parent diff --git a/maven-archetypes/quickstart/pom.xml b/maven-archetypes/quickstart/pom.xml new file mode 100755 index 0000000..783bed1 --- /dev/null +++ b/maven-archetypes/quickstart/pom.xml @@ -0,0 +1,68 @@ + + + + + org.pf4j + pf4j-parent + 3.0.0-SNAPSHOT + ../../pom.xml + + + 4.0.0 + pf4j-quickstart + maven-archetype + Quickstart Archetype + + + + + + true + src/main/resources + + archetype-resources/**/pom.xml + + + + + false + src/main/resources + + archetype-resources/**/pom.xml + + + + + + + org.apache.maven.archetype + archetype-packaging + 2.3 + + + + + + + org.apache.maven.plugins + maven-archetype-plugin + 2.3 + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + {{*}} + + false + + + + + + + diff --git a/maven-archetypes/quickstart/src/main/resources/META-INF/maven/archetype-metadata.xml b/maven-archetypes/quickstart/src/main/resources/META-INF/maven/archetype-metadata.xml new file mode 100644 index 0000000..a6f40bd --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -0,0 +1,86 @@ + + + + + + + + run.sh + + + + + + + + + src/main/java + + **/*.java + + + + src/main/resources + + **/*.properties + + + + src/main/assembly + + **/*.xml + + + + + + + + + + + enabled.txt + disabled.txt + + + + + + + + + src/main/java + + **/*.java + + + + + + plugin.properties + + + + + + + + + src/main/java + + **/*.java + + + + + + plugin.properties + + + + + + + + + diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/pom.xml b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/pom.xml new file mode 100644 index 0000000..986c0a3 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/pom.xml @@ -0,0 +1,86 @@ + + + + + ${groupId} + ${rootArtifactId} + ${version} + + + 4.0.0 + ${artifactId} + ${version} + jar + App + + + ${package}.Boot + + + + + + maven-assembly-plugin + 2.3 + + + src/main/assembly/assembly.xml + + false + + + + make-assembly + package + + attached + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.3.1 + + + + true + lib/ + ${main.class} + + + + + + + + + + org.pf4j + pf4j + ${pf4j.version} + + + + + log4j + log4j + 1.2.16 + + + + org.slf4j + slf4j-log4j12 + ${slf4j.version} + + + + commons-lang + commons-lang + 2.4 + + + + diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/assembly/assembly.xml b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/assembly/assembly.xml new file mode 100755 index 0000000..b88a245 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/assembly/assembly.xml @@ -0,0 +1,30 @@ + + app + + dir + zip + + false + + + false + lib + + *:jar:* + + + + + + ${project.build.directory} + + + *.jar + + + *-javadoc.jar + *-sources.jar + + + + diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Boot.java b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Boot.java new file mode 100755 index 0000000..2853b08 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Boot.java @@ -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 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 extensionClassNames = pluginManager.getExtensionClassNames(null); + for (String extension : extensionClassNames) { + System.out.println(" " + extension); + } + + System.out.println("Extension classes by classpath:"); + List> greetingsClasses = pluginManager.getExtensionClasses(Greeting.class); + for (Class greeting : greetingsClasses) { + System.out.println(" Class: " + greeting.getCanonicalName()); + } + + // print extensions ids for each started plugin + List 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 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(); + } + + }); + */ + } + +} diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Greeting.java b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Greeting.java new file mode 100755 index 0000000..d64d5fe --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/Greeting.java @@ -0,0 +1,9 @@ +package ${package}; + +import org.pf4j.ExtensionPoint; + +public interface Greeting extends ExtensionPoint { + + String getGreeting(); + +} diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/WhazzupGreeting.java b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/WhazzupGreeting.java new file mode 100755 index 0000000..bb747f5 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/java/WhazzupGreeting.java @@ -0,0 +1,13 @@ +package ${package}; + +import org.pf4j.Extension; + +@Extension +public class WhazzupGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Whazzup"; + } + +} diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/resources/log4j.properties b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/resources/log4j.properties new file mode 100755 index 0000000..a05d9eb --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/app/src/main/resources/log4j.properties @@ -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 diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/disabled.txt b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/disabled.txt new file mode 100755 index 0000000..45f1801 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/disabled.txt @@ -0,0 +1,6 @@ +######################################## +# - load all plugins except these +# - add one plugin id on each line +# - put this file in plugins folder +######################################## +#welcome-plugin diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/enabled.txt b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/enabled.txt new file mode 100755 index 0000000..3d76b2f --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/enabled.txt @@ -0,0 +1,6 @@ +######################################## +# - load only these plugins +# - add one plugin id on each line +# - put this file in plugins folder +######################################## +#welcome-plugin diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/plugin.properties b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/plugin.properties new file mode 100755 index 0000000..9ed49b1 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/plugin.properties @@ -0,0 +1,5 @@ +plugin.id=hello-plugin +plugin.class=${package}.hello.HelloPlugin +plugin.version=${version} +plugin.provider= +plugin.dependencies= diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/pom.xml b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/pom.xml new file mode 100755 index 0000000..10e8262 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/pom.xml @@ -0,0 +1,24 @@ + + + + + ${groupId} + ${rootArtifactId}-plugins + ${version} + + + 4.0.0 + hello-plugin + ${version} + jar + Hello Plugin + + + hello-plugin + ${package}.hello.HelloPlugin + ${version} + + + + + diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/src/main/java/hello/HelloPlugin.java b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/src/main/java/hello/HelloPlugin.java new file mode 100755 index 0000000..48604d6 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/hello/src/main/java/hello/HelloPlugin.java @@ -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"; + } + + } + +} diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/pom.xml b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/pom.xml new file mode 100755 index 0000000..b144ca0 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/pom.xml @@ -0,0 +1,86 @@ + + + + + ${groupId} + ${rootArtifactId} + ${version} + + + 4.0.0 + ${artifactId} + ${version} + pom + Plugins Parent + + + UTF-8 + UTF-8 + + + + + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + jar-with-dependencies + + ${project.artifactId}-${project.version}-all + false + false + + + true + true + + + ${plugin.id} + ${plugin.version} + ${plugin.provider} + ${plugin.class} + ${plugin.dependencies} + + + + + + make-assembly + package + + single + + + + + + + + + + org.pf4j + pf4j + ${pf4j.version} + + provided + + + + ${groupId} + ${rootArtifactId}-app + ${version} + + provided + + + + diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/plugin.properties b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/plugin.properties new file mode 100755 index 0000000..80edaab --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/plugin.properties @@ -0,0 +1,5 @@ +plugin.id=welcome-plugin +plugin.class=${package}.welcome.WelcomePlugin +plugin.version=${version} +plugin.provider= +plugin.dependencies= diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/pom.xml b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/pom.xml new file mode 100755 index 0000000..9101abb --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/pom.xml @@ -0,0 +1,32 @@ + + + + + ${groupId} + ${rootArtifactId}-plugins + ${version} + + + 4.0.0 + welcome-plugin + ${version} + jar + Welcom Plugin + + + welcome-plugin + ${package}.welcome.WelcomePlugin + ${version} + + + + + + + commons-lang + commons-lang + 2.6 + + + + diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/src/main/java/welcome/WelcomePlugin.java b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/src/main/java/welcome/WelcomePlugin.java new file mode 100755 index 0000000..a82d13c --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/plugins/welcome/src/main/java/welcome/WelcomePlugin.java @@ -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"; + } + + } + +} diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/pom.xml b/maven-archetypes/quickstart/src/main/resources/archetype-resources/pom.xml new file mode 100755 index 0000000..13ed127 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + ${groupId} + ${artifactId} + ${version} + pom + PF4J Quickstart + + + + sonatype-nexus-snapshots + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + + + UTF-8 + 8 + + {{project.version}} + 1.7.7 + + + + + + false + src/main/java + + **/*.java + + + + + src/main/resources + + + + + diff --git a/maven-archetypes/quickstart/src/main/resources/archetype-resources/run.sh b/maven-archetypes/quickstart/src/main/resources/archetype-resources/run.sh new file mode 100755 index 0000000..6214823 --- /dev/null +++ b/maven-archetypes/quickstart/src/main/resources/archetype-resources/run.sh @@ -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 - diff --git a/pf4j/pom.xml b/pf4j/pom.xml index 061412d..c38eae8 100644 --- a/pf4j/pom.xml +++ b/pf4j/pom.xml @@ -4,12 +4,12 @@ org.pf4j pf4j-parent - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 pf4j - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT jar PF4J Plugin Framework for Java @@ -22,6 +22,35 @@ -proc:none + + + + default-compile + + compile + + + + module-info.java + + + + + + + java9-compile + + compile + + + 9 + true + + module-info.java + + + + org.apache.maven.plugins @@ -29,7 +58,7 @@ - org.pf4j + true @@ -69,11 +98,12 @@ - + See http://hamcrest.org/JavaHamcrest/distributables#upgrading-from-hamcrest-1x + --> org.hamcrest hamcrest-core ${hamcrest.version} @@ -81,11 +111,12 @@ - junit - junit + org.junit.jupiter + junit-jupiter-engine ${junit.version} test + org.mockito mockito-core diff --git a/pf4j/src/main/java/module-info.java b/pf4j/src/main/java/module-info.java new file mode 100644 index 0000000..754b9b8 --- /dev/null +++ b/pf4j/src/main/java/module-info.java @@ -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; +} diff --git a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java index c17fdab..6bd6170 100644 --- a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java +++ b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java @@ -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 List> getExtensionClasses(Class type) { - List> extensionsWrapper = extensionFinder.find(type); - List> extensionClasses = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - Class c = (Class) extensionWrapper.getDescriptor().extensionClass; - extensionClasses.add(c); - } - - return extensionClasses; + public List> getExtensionClasses(Class type) { + return getExtensionClasses(extensionFinder.find(type)); } - @SuppressWarnings("unchecked") @Override - public List> getExtensionClasses(Class type, String pluginId) { - List> extensionsWrapper = extensionFinder.find(type, pluginId); - List> extensionClasses = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - Class c = (Class) extensionWrapper.getDescriptor().extensionClass; - extensionClasses.add(c); - } - - return extensionClasses; + public List> getExtensionClasses(Class type, String pluginId) { + return getExtensionClasses(extensionFinder.find(type, pluginId)); } @Override public List getExtensions(Class type) { - List> extensionsWrapper = extensionFinder.find(type); - List extensions = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getExtension()); - } - - return extensions; + return getExtensions(extensionFinder.find(type)); } @Override public List getExtensions(Class type, String pluginId) { - List> extensionsWrapper = extensionFinder.find(type, pluginId); - List extensions = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper 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 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 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 List> getExtensionClasses(List> extensionsWrapper) { + List> extensionClasses = new ArrayList<>(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + Class c = (Class) extensionWrapper.getDescriptor().extensionClass; + extensionClasses.add(c); + } + + return extensionClasses; + } + + private List getExtensions(List> extensionsWrapper) { + List extensions = new ArrayList<>(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + try { + extensions.add(extensionWrapper.getExtension()); + } catch (PluginRuntimeException e) { + log.error("Cannot retrieve extension", e); + } + } + + return extensions; + } + } diff --git a/pf4j/src/main/java/org/pf4j/BasePluginLoader.java b/pf4j/src/main/java/org/pf4j/BasePluginLoader.java new file mode 100644 index 0000000..e4f22f3 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/BasePluginLoader.java @@ -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 jars = FileUtils.getJars(file); + for (File jar : jars) { + pluginClassLoader.addFile(jar); + } + } + } + +} diff --git a/pf4j/src/main/java/org/pf4j/BasePluginRepository.java b/pf4j/src/main/java/org/pf4j/BasePluginRepository.java index a353756..8c856f8 100644 --- a/pf4j/src/main/java/org/pf4j/BasePluginRepository.java +++ b/pf4j/src/main/java/org/pf4j/BasePluginRepository.java @@ -48,14 +48,7 @@ public class BasePluginRepository implements PluginRepository { this.filter = filter; // last modified file is first - this.comparator = new Comparator() { - - @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); } } diff --git a/pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java index 751ad5e..6fe1ff2 100644 --- a/pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java +++ b/pf4j/src/main/java/org/pf4j/CompoundPluginDescriptorFinder.java @@ -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); } } diff --git a/pf4j/src/main/java/org/pf4j/CompoundPluginLoader.java b/pf4j/src/main/java/org/pf4j/CompoundPluginLoader.java index fe10d68..c20e4bb 100644 --- a/pf4j/src/main/java/org/pf4j/CompoundPluginLoader.java +++ b/pf4j/src/main/java/org/pf4j/CompoundPluginLoader.java @@ -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(); } diff --git a/pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java b/pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java index 09bc5ce..a36b00a 100644 --- a/pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java +++ b/pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java @@ -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 getPluginPaths() { - List paths = new ArrayList<>(); + Set paths = new LinkedHashSet<>(); for (PluginRepository repository : repositories) { paths.addAll(repository.getPluginPaths()); } - return paths; + return new ArrayList<>(paths); } @Override diff --git a/pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java b/pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java index bd67032..66e5054 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java +++ b/pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java @@ -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 create(Class 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; } } diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java b/pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java index a78bfd3..2d64e1e 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java @@ -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); } } diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java b/pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java index 60d63be..1ee8349 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java @@ -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 jars = FileUtils.getJars(file); - for (File jar : jars) { - pluginClassLoader.addFile(jar); - } - } + return super.isApplicable(pluginPath) && Files.isDirectory(pluginPath); } } diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java index 96a4104..9cad187 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java @@ -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. * *

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); diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java b/pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java index 78ea43c..2fafbc7 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java @@ -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 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; } + } diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java b/pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java index ca85407..0b88fe8 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java @@ -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; } } diff --git a/pf4j/src/main/java/org/pf4j/DependencyResolver.java b/pf4j/src/main/java/org/pf4j/DependencyResolver.java index a3aa9f6..45005e2 100644 --- a/pf4j/src/main/java/org/pf4j/DependencyResolver.java +++ b/pf4j/src/main/java/org/pf4j/DependencyResolver.java @@ -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 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 dependencies; diff --git a/pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java b/pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java index 15da659..4d7727d 100644 --- a/pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java +++ b/pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java @@ -27,7 +27,7 @@ public class DevelopmentPluginClasspath extends PluginClasspath { * The development plugin classpath for Maven. * 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 Gradle. @@ -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()); } } diff --git a/pf4j/src/main/java/org/pf4j/DevelopmentPluginLoader.java b/pf4j/src/main/java/org/pf4j/DevelopmentPluginLoader.java new file mode 100644 index 0000000..58c9473 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DevelopmentPluginLoader.java @@ -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()); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DevelopmentPluginRepository.java b/pf4j/src/main/java/org/pf4j/DevelopmentPluginRepository.java new file mode 100644 index 0000000..e8f2c69 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DevelopmentPluginRepository.java @@ -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; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionFactory.java b/pf4j/src/main/java/org/pf4j/ExtensionFactory.java index 177d489..176c6ff 100644 --- a/pf4j/src/main/java/org/pf4j/ExtensionFactory.java +++ b/pf4j/src/main/java/org/pf4j/ExtensionFactory.java @@ -20,6 +20,6 @@ package org.pf4j; */ public interface ExtensionFactory { - Object create(Class extensionClass); + T create(Class extensionClass); } diff --git a/pf4j/src/main/java/org/pf4j/JarPluginManager.java b/pf4j/src/main/java/org/pf4j/JarPluginManager.java new file mode 100644 index 0000000..f280466 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/JarPluginManager.java @@ -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); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java b/pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java index 99e1313..33adc27 100644 --- a/pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java +++ b/pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java @@ -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 bucket = new HashSet<>(); try { - Enumeration 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 bucket) throws IOException { + try (Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { + LegacyExtensionStorage.read(reader, bucket); } } diff --git a/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java index 74cd0f3..650b2d7 100644 --- a/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java +++ b/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java @@ -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; } diff --git a/pf4j/src/main/java/org/pf4j/Plugin.java b/pf4j/src/main/java/org/pf4j/Plugin.java index b648b64..e3dd449 100644 --- a/pf4j/src/main/java/org/pf4j/Plugin.java +++ b/pf4j/src/main/java/org/pf4j/Plugin.java @@ -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() { } } diff --git a/pf4j/src/main/java/org/pf4j/PluginAlreadyLoadedException.java b/pf4j/src/main/java/org/pf4j/PluginAlreadyLoadedException.java index adfd90b..411756c 100644 --- a/pf4j/src/main/java/org/pf4j/PluginAlreadyLoadedException.java +++ b/pf4j/src/main/java/org/pf4j/PluginAlreadyLoadedException.java @@ -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; diff --git a/pf4j/src/main/java/org/pf4j/PluginClasspath.java b/pf4j/src/main/java/org/pf4j/PluginClasspath.java index 57ff9bf..757740c 100644 --- a/pf4j/src/main/java/org/pf4j/PluginClasspath.java +++ b/pf4j/src/main/java/org/pf4j/PluginClasspath.java @@ -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 classesDirectories = new HashSet<>(); - private Set libDirectories = new HashSet<>(); + private Set jarsDirectories = new HashSet<>(); public Set getClassesDirectories() { return classesDirectories; @@ -45,16 +46,16 @@ public class PluginClasspath { return this; } - public Set getLibDirectories() { - return libDirectories; + public Set 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 libDirectories) { - this.libDirectories.addAll(libDirectories); + public PluginClasspath addJarsDirectories(Collection jarsDirectories) { + this.jarsDirectories.addAll(jarsDirectories); return this; } diff --git a/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java index 3b9f600..35185af 100644 --- a/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java +++ b/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java @@ -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); } diff --git a/pf4j/src/main/java/org/pf4j/PluginManager.java b/pf4j/src/main/java/org/pf4j/PluginManager.java index 6bb6085..a937f22 100644 --- a/pf4j/src/main/java/org/pf4j/PluginManager.java +++ b/pf4j/src/main/java/org/pf4j/PluginManager.java @@ -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> getExtensionClasses(String pluginId); - List> getExtensionClasses(Class type); + List> getExtensionClasses(Class type); - List> getExtensionClasses(Class type, String pluginId); + List> getExtensionClasses(Class type, String pluginId); List getExtensions(Class 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(); diff --git a/pf4j/src/main/java/org/pf4j/PluginRepository.java b/pf4j/src/main/java/org/pf4j/PluginRepository.java index f3617cd..6bc2356 100644 --- a/pf4j/src/main/java/org/pf4j/PluginRepository.java +++ b/pf4j/src/main/java/org/pf4j/PluginRepository.java @@ -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 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); diff --git a/pf4j/src/main/java/org/pf4j/PluginException.java b/pf4j/src/main/java/org/pf4j/PluginRuntimeException.java similarity index 72% rename from pf4j/src/main/java/org/pf4j/PluginException.java rename to pf4j/src/main/java/org/pf4j/PluginRuntimeException.java index d1ba6f2..5d5ede9 100644 --- a/pf4j/src/main/java/org/pf4j/PluginException.java +++ b/pf4j/src/main/java/org/pf4j/PluginRuntimeException.java @@ -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)); } diff --git a/pf4j/src/main/java/org/pf4j/PluginStatusProvider.java b/pf4j/src/main/java/org/pf4j/PluginStatusProvider.java index dda5f82..46967b2 100644 --- a/pf4j/src/main/java/org/pf4j/PluginStatusProvider.java +++ b/pf4j/src/main/java/org/pf4j/PluginStatusProvider.java @@ -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); } diff --git a/pf4j/src/main/java/org/pf4j/PluginWrapper.java b/pf4j/src/main/java/org/pf4j/PluginWrapper.java index c7d93ca..a9aa147 100644 --- a/pf4j/src/main/java/org/pf4j/PluginWrapper.java +++ b/pf4j/src/main/java/org/pf4j/PluginWrapper.java @@ -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; } diff --git a/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java index 3e9e1c1..6789a5f 100644 --- a/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java +++ b/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java @@ -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; } diff --git a/pf4j/src/main/java/org/pf4j/RuntimeMode.java b/pf4j/src/main/java/org/pf4j/RuntimeMode.java index 1fa9cbd..909cc38 100644 --- a/pf4j/src/main/java/org/pf4j/RuntimeMode.java +++ b/pf4j/src/main/java/org/pf4j/RuntimeMode.java @@ -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; } diff --git a/pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java b/pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java index d339921..725b9a7 100644 --- a/pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java +++ b/pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java @@ -39,13 +39,14 @@ public class SingletonExtensionFactory extends DefaultExtensionFactory { } @Override - public Object create(Class extensionClass) { + @SuppressWarnings("unchecked") + public T create(Class 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); } diff --git a/pf4j/src/main/java/org/pf4j/ZipPluginManager.java b/pf4j/src/main/java/org/pf4j/ZipPluginManager.java new file mode 100644 index 0000000..a32d156 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ZipPluginManager.java @@ -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: + *

    + *
  • {@code lib} directory that contains all dependencies (as jar files); it's optional (no dependencies) + *
  • {@code classes} directory that contains all plugin's classes + *
+ * + * @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); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java b/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java index e0f3513..55bbbc2 100644 --- a/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java +++ b/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java @@ -109,11 +109,7 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor { String extension = getBinaryName(extensionElement); for (TypeElement extensionPointElement : extensionPointElements) { String extensionPoint = getBinaryName(extensionPointElement); - Set extensionPoints = extensions.get(extensionPoint); - if (extensionPoints == null) { - extensionPoints = new TreeSet<>(); - extensions.put(extensionPoint, extensionPoints); - } + Set extensionPoints = extensions.computeIfAbsent(extensionPoint, k -> new TreeSet<>()); extensionPoints.add(extension); } } diff --git a/pf4j/src/main/java/org/pf4j/util/AndFileFilter.java b/pf4j/src/main/java/org/pf4j/util/AndFileFilter.java index cc74da4..76a17ec 100644 --- a/pf4j/src/main/java/org/pf4j/util/AndFileFilter.java +++ b/pf4j/src/main/java/org/pf4j/util/AndFileFilter.java @@ -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 true if all filters in the - * list return true. Otherwise, it returns false. - * Checking of the file filter list stops when the first filter returns - * false. + * 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 fileFilters; public AndFileFilter() { - this(new ArrayList()); + 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; } diff --git a/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java b/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java index 076d67c..1163f91 100644 --- a/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java +++ b/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java @@ -44,7 +44,7 @@ public class DirectedGraph { return; } - neighbors.put(vertex, new ArrayList()); + neighbors.put(vertex, new ArrayList<>()); } /** @@ -85,7 +85,7 @@ public class DirectedGraph { } public List getNeighbors(V vertex) { - return containsVertex(vertex) ? neighbors.get(vertex) : new ArrayList(); + return containsVertex(vertex) ? neighbors.get(vertex) : new ArrayList<>(); } /** @@ -101,7 +101,7 @@ public class DirectedGraph { } /** - * 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 inDegree() { Map result = new HashMap<>(); @@ -181,9 +181,9 @@ public class DirectedGraph { */ @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(); diff --git a/pf4j/src/main/java/org/pf4j/util/FileUtils.java b/pf4j/src/main/java/org/pf4j/util/FileUtils.java index a3e955e..0cb8784 100644 --- a/pf4j/src/main/java/org/pf4j/util/FileUtils.java +++ b/pf4j/src/main/java/org/pf4j/util/FileUtils.java @@ -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()); diff --git a/pf4j/src/main/java/org/pf4j/util/OrFileFilter.java b/pf4j/src/main/java/org/pf4j/util/OrFileFilter.java index fccfb7b..5798249 100644 --- a/pf4j/src/main/java/org/pf4j/util/OrFileFilter.java +++ b/pf4j/src/main/java/org/pf4j/util/OrFileFilter.java @@ -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 true if one filter in the - * list return true. Otherwise, it returns false. - * Checking of the file filter list stops when the first filter returns - * true. + * 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 fileFilters; public OrFileFilter() { - this(new ArrayList()); + 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; } diff --git a/pf4j/src/main/java/org/pf4j/util/Unzip.java b/pf4j/src/main/java/org/pf4j/util/Unzip.java index 7762e47..afde37a 100644 --- a/pf4j/src/main/java/org/pf4j/util/Unzip.java +++ b/pf4j/src/main/java/org/pf4j/util/Unzip.java @@ -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()); } diff --git a/pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java b/pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java index 2acabee..6383756 100644 --- a/pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java +++ b/pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java @@ -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; } diff --git a/pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java b/pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java new file mode 100644 index 0000000..784d1f9 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java @@ -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> 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 extensions = pluginManager.getExtensions(TestExtensionPoint.class); + assertEquals(1, extensions.size()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java index a30a4ff..c16839e 100644 --- a/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java +++ b/pf4j/src/test/java/org/pf4j/CompoundPluginDescriptorFinderTest.java @@ -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 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 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, ""); + } } } diff --git a/pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java b/pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java index 71ffa93..150828f 100644 --- a/pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java +++ b/pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java @@ -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)); } } diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java index f105aa8..eccfca2 100644 --- a/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java @@ -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; diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java index 3e6fd04..8cb9b27 100644 --- a/pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java @@ -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(); diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java index 4f39f24..c564543 100644 --- a/pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java @@ -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 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 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 pluginPaths = repository.getPluginPaths(); @@ -109,11 +83,7 @@ public class DefaultPluginRepositoryTest { } private void assertPathExists(List paths, Path path) { - assertTrue("The directory must contain the file " + path, paths.contains(path)); - } - - private void assertPathDoesNotExists(List 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); } } diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java index b3f8d2b..e766735 100644 --- a/pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java @@ -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 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()); } } diff --git a/pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java b/pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java index 795d3cf..3a277db 100644 --- a/pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java +++ b/pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java @@ -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 diff --git a/pf4j/src/test/java/org/pf4j/DependencyResolverTest.java b/pf4j/src/test/java/org/pf4j/DependencyResolverTest.java index f7752d4..6e35780 100644 --- a/pf4j/src/test/java/org/pf4j/DependencyResolverTest.java +++ b/pf4j/src/test/java/org/pf4j/DependencyResolverTest.java @@ -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); diff --git a/pf4j/src/test/java/org/pf4j/DevelopmentPluginRepositoryTest.java b/pf4j/src/test/java/org/pf4j/DevelopmentPluginRepositoryTest.java new file mode 100644 index 0000000..20d9129 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/DevelopmentPluginRepositoryTest.java @@ -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 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 paths, Path path) { + assertFalse(paths.contains(path), "The directory must not contain the file " + path); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java b/pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java index 9edd6b9..47efca8 100644 --- a/pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java +++ b/pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java @@ -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 diff --git a/pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java b/pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java new file mode 100644 index 0000000..d9ca90a --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/JarPluginManagerTest.java @@ -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 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()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/JarPluginRepositoryTest.java b/pf4j/src/test/java/org/pf4j/JarPluginRepositoryTest.java new file mode 100644 index 0000000..4501223 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/JarPluginRepositoryTest.java @@ -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 pluginPaths = repository.getPluginPaths(); + assertEquals(1, pluginPaths.size()); + + assertTrue(repository.deletePluginPath(plugin1JarPath)); + + pluginPaths = repository.getPluginPaths(); + assertEquals(0, pluginPaths.size()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/LegacyExtensionFinderTest.java b/pf4j/src/test/java/org/pf4j/LegacyExtensionFinderTest.java new file mode 100644 index 0000000..8f5a727 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/LegacyExtensionFinderTest.java @@ -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> pluginsStorages = extensionFinder.readPluginsStorages(); + assertNotNull(pluginsStorages); + + pluginManager.unloadPlugin(pluginJar.pluginId()); + boolean fileDeleted = pluginJar.file().delete(); + + Set pluginStorages = pluginsStorages.get(pluginJar.pluginId()); + assertNotNull(pluginStorages); + assertEquals(1, pluginStorages.size()); + assertThat(pluginStorages, contains(TestExtension.class.getName())); + assertTrue(fileDeleted); + assertFalse(pluginJar.file().exists()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java b/pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java index 57125bd..1acc567 100644 --- a/pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java +++ b/pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java @@ -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 diff --git a/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java b/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java index c2917e8..e3dac02 100644 --- a/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java +++ b/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java @@ -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(); diff --git a/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java index bc4eb5f..468f2fb 100644 --- a/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java +++ b/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java @@ -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 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 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 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 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 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 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 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 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 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 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); + } } } diff --git a/pf4j/src/test/java/org/pf4j/PluginDependencyTest.java b/pf4j/src/test/java/org/pf4j/PluginDependencyTest.java index dbd5fd9..a351c4b 100644 --- a/pf4j/src/test/java/org/pf4j/PluginDependencyTest.java +++ b/pf4j/src/test/java/org/pf4j/PluginDependencyTest.java @@ -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()); } diff --git a/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java index 377d5ca..bc91a7f 100644 --- a/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java +++ b/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java @@ -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 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 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 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 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 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 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 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 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 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 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, ""); + } } } diff --git a/pf4j/src/test/java/org/pf4j/SingletonExtensionFactoryTest.java b/pf4j/src/test/java/org/pf4j/SingletonExtensionFactoryTest.java index 500dd84..80e7ab0 100644 --- a/pf4j/src/test/java/org/pf4j/SingletonExtensionFactoryTest.java +++ b/pf4j/src/test/java/org/pf4j/SingletonExtensionFactoryTest.java @@ -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 diff --git a/pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java b/pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java new file mode 100644 index 0000000..ff55967 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/ClassDataProvider.java @@ -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: + *
    + *
  • Class path - the class is already loaded by the class loader
  • + *
  • String - the string (the source code) is compiled dynamically via {@link javax.tools.JavaCompiler} + *
  • Generate the source code programmatically using something like {@code https://github.com/square/javapoet}
  • + *
+ * + * @author Decebal Suiu + */ +public interface ClassDataProvider { + + byte[] getClassData(String className); + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java b/pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java new file mode 100644 index 0000000..ef0eaf9 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/DefaultClassDataProvider.java @@ -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); + } + } + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java b/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java index 9cc37e0..13b51ee 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java +++ b/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java @@ -26,4 +26,9 @@ public class FailTestExtension implements TestExtensionPoint { public FailTestExtension(String name) { } + @Override + public String saySomething() { + return "I am a fail test extension"; + } + } diff --git a/pf4j/src/test/java/org/pf4j/plugin/PluginJar.java b/pf4j/src/test/java/org/pf4j/plugin/PluginJar.java new file mode 100644 index 0000000..a75b68f --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/PluginJar.java @@ -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 map) { + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); + for (Map.Entry 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 manifestAttributes = new LinkedHashMap<>(); + private Set 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 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 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(); + } + } + + } + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/PluginZip.java b/pf4j/src/test/java/org/pf4j/plugin/PluginZip.java index f069365..f34d84a 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/PluginZip.java +++ b/pf4j/src/test/java/org/pf4j/plugin/PluginZip.java @@ -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 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 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 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 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(); } diff --git a/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java b/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java index 5f48c9a..83bc0b5 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java +++ b/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java @@ -23,4 +23,9 @@ import org.pf4j.Extension; @Extension public class TestExtension implements TestExtensionPoint { + @Override + public String saySomething() { + return "I am a test extension"; + } + } diff --git a/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java b/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java index a33ac40..d29a7ab 100644 --- a/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java +++ b/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java @@ -22,4 +22,6 @@ import org.pf4j.ExtensionPoint; */ public interface TestExtensionPoint extends ExtensionPoint { + String saySomething(); + } diff --git a/pf4j/src/test/java/org/pf4j/processor/ServiceProviderExtensionStorageTest.java b/pf4j/src/test/java/org/pf4j/processor/ServiceProviderExtensionStorageTest.java index 2c450a0..ac16b23 100644 --- a/pf4j/src/test/java/org/pf4j/processor/ServiceProviderExtensionStorageTest.java +++ b/pf4j/src/test/java/org/pf4j/processor/ServiceProviderExtensionStorageTest.java @@ -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> read = extensionStorage.read(); diff --git a/pf4j/src/test/java/org/pf4j/util/DirectedGraphTest.java b/pf4j/src/test/java/org/pf4j/util/DirectedGraphTest.java new file mode 100644 index 0000000..44b8c54 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/util/DirectedGraphTest.java @@ -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 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 result = graph.reverseTopologicalSort(); + List expected = Arrays.asList('C', 'G', 'F', 'B', 'A', 'E', 'D'); + assertEquals(expected, result); + } + + @Test + public void topologicalSort() { + List result = graph.topologicalSort(); + List expected = Arrays.asList('D', 'E', 'A', 'B', 'F', 'G', 'C'); + assertEquals(expected, result); + } + + @Test + public void inDegree() { + Map result = graph.inDegree(); + Map 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 result = graph.outDegree(); + Map 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); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java b/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java index 76d0c2e..5f9a3a7 100644 --- a/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java +++ b/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java @@ -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)); } diff --git a/pom.xml b/pom.xml index d4540d7..add6723 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 4.0.0 org.pf4j pf4j-parent - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT pom PF4J Parent Plugin Framework for Java @@ -43,11 +43,12 @@ UTF-8 - 1.7 + 8 + 1.7.25 7.1 - 4.12 + 5.4.0 2.1 2.24.0 2.7 @@ -66,8 +67,6 @@ 3.8.0 true - ${java.version} - ${java.version}
@@ -161,6 +160,7 @@ pf4j demo + maven-archetypes/quickstart