[Git][java-team/libpf4j-java][upstream] New upstream version 3.13.0+dfsg
Pierre Gruet (@pgt)
gitlab at salsa.debian.org
Thu Oct 2 05:58:05 BST 2025
Pierre Gruet pushed to branch upstream at Debian Java Maintainers / libpf4j-java
Commits:
cb0a4dc8 by Pierre Gruet at 2025-10-01T23:38:21+02:00
New upstream version 3.13.0+dfsg
- - - - -
24 changed files:
- CHANGELOG.md
- README.md
- demo/maven/api/pom.xml
- demo/maven/app/pom.xml
- demo/maven/plugins/plugin1/pom.xml
- demo/maven/plugins/plugin2/pom.xml
- demo/maven/plugins/pom.xml
- demo/maven/pom.xml
- maven-archetypes/quickstart/pom.xml
- pf4j/pom.xml
- pf4j/src/main/java/org/pf4j/AbstractExtensionFinder.java
- pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
- pf4j/src/main/java/org/pf4j/PluginClassLoader.java
- pf4j/src/main/java/org/pf4j/PluginState.java
- pf4j/src/main/java/org/pf4j/PluginStateEvent.java
- pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java
- pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java
- pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java
- pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java
- pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java
- pf4j/src/test/java/org/pf4j/PluginClassLoaderTest.java
- pf4j/src/test/java/org/pf4j/SecurePluginManagerWrapperTest.java
- pf4j/src/test/java/org/pf4j/test/JavaSources.java
- pom.xml
Changes:
=====================================
CHANGELOG.md
=====================================
@@ -4,6 +4,24 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### [Unreleased][unreleased]
+#### Fixed
+- [#595]: Fix StackOverFlow error on Kotlin classes without @Extension
+
+#### Changed
+- [#594]: Always check for ClassLoaders differences in AbstractExtensionFinder
+
+#### Added
+
+#### Removed
+
+### [3.12.1] - 2024-10-12
+
+#### Fixed
+- [#586]: Plugin Extensions should be detected with any ClassLoadingStrategy
+- [#590]: Fix fire PluginStateEvent for STOPPED state
+
+### [3.12.0] - 2024-06-25
+
#### Fixed
- [#576]: Error due to incorrect dependency update after plugin deletion
@@ -14,8 +32,6 @@ This project adheres to [Semantic Versioning](http://semver.org/).
#### Added
- Add PluginClassLoader#isClosed method
-#### Removed
-
### [3.11.1] - 2024-05-19
#### Fixed
@@ -448,7 +464,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- [#41]: Added plugin archive source abstraction
- Added test for DefaultPluginRepository
-[unreleased]: https://github.com/decebals/pf4j/compare/release-3.11.1...HEAD
+[unreleased]: https://github.com/decebals/pf4j/compare/release-3.12.1...HEAD
+[3.12.1]: https://github.com/decebals/pf4j/compare/release-3.12.0...release-3.12.1
+[3.12.0]: https://github.com/decebals/pf4j/compare/release-3.11.1...release-3.12.0
[3.11.1]: https://github.com/decebals/pf4j/compare/release-3.11.0...release-3.11.1
[3.11.0]: https://github.com/decebals/pf4j/compare/release-3.10.0...release-3.11.0
[3.10.0]: https://github.com/decebals/pf4j/compare/release-3.9.0...release-3.10.0
@@ -481,6 +499,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
[0.11.0]: https://github.com/decebals/pf4j/compare/release-0.10.0...release-0.11.0
[0.10.0]: https://github.com/decebals/pf4j/compare/release-0.9.0...release-0.10.0
+[#595]: https://github.com/pf4j/pf4j/pull/595
+[#594]: https://github.com/pf4j/pf4j/issues/594
+[#590]: https://github.com/pf4j/pf4j/issues/590
+[#586]: https://github.com/pf4j/pf4j/pull/586
[#584]: https://github.com/pf4j/pf4j/pull/584
[#582]: https://github.com/pf4j/pf4j/issues/582
[#576]: https://github.com/pf4j/pf4j/issues/576
=====================================
README.md
=====================================
@@ -10,7 +10,7 @@ Plugin Framework for Java (PF4J)
A plugin is a way for a third party to extend the functionality of an application. A plugin implements extension points
declared by application or other plugins. Also, a plugin can define extension points.
-**NOTE:** Starting with version 0.9 you can define an extension directly in the application jar (you're not obligated to put the extension in a plugin - you can see this extension as a default/system extension). See [WhazzupGreeting](https://github.com/pf4j/pf4j/blob/master/demo/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java) for a real example.
+**NOTE:** Starting with version 0.9 you can define an extension directly in the application jar (you're not obligated to put the extension in a plugin - you can see this extension as a default/system extension). See [WhazzupGreeting](https://github.com/pf4j/pf4j/blob/master/demo/gradle/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java) for a real example.
Features/Benefits
-------------------
=====================================
demo/maven/api/pom.xml
=====================================
@@ -4,12 +4,12 @@
<parent>
<groupId>org.pf4j.demo</groupId>
<artifactId>pf4j-demo-parent</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-api</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<packaging>jar</packaging>
<name>Demo Api</name>
=====================================
demo/maven/app/pom.xml
=====================================
@@ -4,12 +4,12 @@
<parent>
<groupId>org.pf4j.demo</groupId>
<artifactId>pf4j-demo-parent</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-app</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<packaging>jar</packaging>
<name>Demo App</name>
=====================================
demo/maven/plugins/plugin1/pom.xml
=====================================
@@ -4,12 +4,12 @@
<parent>
<groupId>org.pf4j.demo</groupId>
<artifactId>pf4j-demo-plugins</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-plugin1</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<packaging>jar</packaging>
<name>Demo Plugin #1</name>
=====================================
demo/maven/plugins/plugin2/pom.xml
=====================================
@@ -4,12 +4,12 @@
<parent>
<groupId>org.pf4j.demo</groupId>
<artifactId>pf4j-demo-plugins</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-plugin2</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<packaging>jar</packaging>
<name>Demo Plugin #2</name>
=====================================
demo/maven/plugins/pom.xml
=====================================
@@ -4,12 +4,12 @@
<parent>
<groupId>org.pf4j.demo</groupId>
<artifactId>pf4j-demo-parent</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j-demo-plugins</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<packaging>pom</packaging>
<name>Demo Plugins Parent</name>
=====================================
demo/maven/pom.xml
=====================================
@@ -4,14 +4,14 @@
<parent>
<groupId>org.pf4j</groupId>
<artifactId>pf4j-parent</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.pf4j.demo</groupId>
<artifactId>pf4j-demo-parent</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<packaging>pom</packaging>
<name>Demo Parent</name>
=====================================
maven-archetypes/quickstart/pom.xml
=====================================
@@ -4,7 +4,7 @@
<parent>
<groupId>org.pf4j</groupId>
<artifactId>pf4j-parent</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
=====================================
pf4j/pom.xml
=====================================
@@ -4,12 +4,12 @@
<parent>
<groupId>org.pf4j</groupId>
<artifactId>pf4j-parent</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pf4j</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<packaging>jar</packaging>
<name>PF4J</name>
<description>Plugin Framework for Java</description>
@@ -192,6 +192,18 @@
<artifactId>compile-testing</artifactId>
<version>0.21.0</version>
<scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-stdlib</artifactId>
+ <version>2.0.21</version>
+ <scope>test</scope>
</dependency>
</dependencies>
=====================================
pf4j/src/main/java/org/pf4j/AbstractExtensionFinder.java
=====================================
@@ -88,7 +88,7 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
if (pluginId != null) {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
- if (PluginState.STARTED != pluginWrapper.getPluginState()) {
+ if (!pluginWrapper.getPluginState().isStarted()) {
return result;
}
@@ -107,7 +107,7 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
// If optional dependencies are used, the class loader might not be able
// to load the extension class because of missing optional dependencies.
//
- // Therefore we're extracting the extension annotation via asm, in order
+ // Therefore, we're extracting the extension annotation via asm, in order
// to extract the required plugins for an extension. Only if all required
// plugins are currently available and started, the corresponding
// extension is loaded through the class loader.
@@ -121,7 +121,7 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
List<String> missingPluginIds = new ArrayList<>();
for (String requiredPluginId : extensionInfo.getPlugins()) {
PluginWrapper requiredPlugin = pluginManager.getPlugin(requiredPluginId);
- if (requiredPlugin == null || !PluginState.STARTED.equals(requiredPlugin.getPluginState())) {
+ if (requiredPlugin == null || !requiredPlugin.getPluginState().isStarted()) {
missingPluginIds.add(requiredPluginId);
}
}
@@ -146,8 +146,8 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
log.debug("Added extension '{}' with ordinal {}", className, extensionWrapper.getOrdinal());
} else {
log.trace("'{}' is not an extension for extension point '{}'", className, type.getName());
- if (RuntimeMode.DEVELOPMENT.equals(pluginManager.getRuntimeMode())) {
- checkDifferentClassLoaders(type, extensionClass);
+ if (checkDifferentClassLoaders(type, extensionClass)) {
+ log.error("Different class loaders: '{}' (E) and '{}' (EP)", extensionClass.getClassLoader(), type.getClassLoader());
}
}
} catch (ClassNotFoundException | NoClassDefFoundError e) {
@@ -179,7 +179,7 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
if (pluginId != null) {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
- if (PluginState.STARTED != pluginWrapper.getPluginState()) {
+ if (!pluginWrapper.getPluginState().isStarted()) {
return result;
}
@@ -231,13 +231,13 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
// clear cache
entries = null;
- // By default we're assuming, that no checks for extension dependencies are necessary.
+ // By default, we're assuming, that no checks for extension dependencies are necessary.
//
// A plugin, that has an optional dependency to other plugins, might lead to unloadable
// Java classes (NoClassDefFoundError) at application runtime due to possibly missing
- // dependencies. Therefore we're enabling the check for optional extensions, if the
+ // dependencies. Therefore, we're enabling the check for optional extensions, if the
// started plugin contains at least one optional plugin dependency.
- if (checkForExtensionDependencies == null && PluginState.STARTED.equals(event.getPluginState())) {
+ if (checkForExtensionDependencies == null && event.getPluginState().isStarted()) {
for (PluginDependency dependency : event.getPlugin().getDescriptor().getDependencies()) {
if (dependency.isOptional()) {
log.debug("Enable check for extension dependencies via ASM.");
@@ -255,7 +255,7 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
* <p>
* This feature is enabled by default, if at least one available plugin makes use of
* optional plugin dependencies. Those optional plugins might not be available at runtime.
- * Therefore any extension is checked by default against available plugins before its
+ * Therefore, any extension is checked by default against available plugins before its
* instantiation.
* <p>
* Notice: This feature requires the optional <a href="https://asm.ow2.io/">ASM library</a>
@@ -274,7 +274,7 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
* <p>
* This feature is enabled by default, if at least one available plugin makes use of
* optional plugin dependencies. Those optional plugins might not be available at runtime.
- * Therefore any extension is checked by default against available plugins before its
+ * Therefore, any extension is checked by default against available plugins before its
* instantiation.
* <p>
* Notice: This feature requires the optional <a href="https://asm.ow2.io/">ASM library</a>
@@ -360,7 +360,12 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
// search recursively through all annotations
for (Annotation annotation : clazz.getAnnotations()) {
Class<? extends Annotation> annotationClass = annotation.annotationType();
- if (!annotationClass.getName().startsWith("java.lang.annotation")) {
+ if (!annotationClass.getName().startsWith("java.lang.annotation") && !annotationClass.getName().startsWith("kotlin")) {
+ // In case an annotation is annotated with itself,
+ // an example would be @Target, which is ignored by the check above
+ // however, other libraries might do similar things
+ if (annotationClass.equals(clazz)) continue;
+
Extension extensionAnnotation = findExtensionAnnotation(annotationClass);
if (extensionAnnotation != null) {
return extensionAnnotation;
@@ -371,15 +376,13 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
return null;
}
- private void checkDifferentClassLoaders(Class<?> type, Class<?> extensionClass) {
+ boolean checkDifferentClassLoaders(Class<?> type, Class<?> extensionClass) {
ClassLoader typeClassLoader = type.getClassLoader(); // class loader of extension point
ClassLoader extensionClassLoader = extensionClass.getClassLoader();
boolean match = ClassUtils.getAllInterfacesNames(extensionClass).contains(type.getSimpleName());
- if (match && !extensionClassLoader.equals(typeClassLoader)) {
- // in this scenario the method 'isAssignableFrom' returns only FALSE
- // see http://www.coderanch.com/t/557846/java/java/FWIW-FYI-isAssignableFrom-isInstance-differing
- log.error("Different class loaders: '{}' (E) and '{}' (EP)", extensionClassLoader, typeClassLoader);
- }
+ // in this scenario the method 'isAssignableFrom' returns only FALSE
+ // see http://www.coderanch.com/t/557846/java/java/FWIW-FYI-isAssignableFrom-isInstance-differing
+ return match && extensionClassLoader != typeClassLoader;
}
}
=====================================
pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
=====================================
@@ -312,7 +312,7 @@ public abstract class AbstractPluginManager implements PluginManager {
PluginState pluginState;
try {
pluginState = stopPlugin(pluginId, false);
- if (PluginState.STARTED == pluginState) {
+ if (pluginState.isStarted()) {
return false;
}
@@ -358,7 +358,7 @@ public abstract class AbstractPluginManager implements PluginManager {
PluginWrapper pluginWrapper = getPlugin(pluginId);
// stop the plugin if it's started
PluginState pluginState = stopPlugin(pluginId);
- if (PluginState.STARTED == pluginState) {
+ if (pluginState.isStarted()) {
log.error("Failed to stop plugin '{}' on delete", pluginId);
return false;
}
@@ -385,7 +385,7 @@ public abstract class AbstractPluginManager implements PluginManager {
public void startPlugins() {
for (PluginWrapper pluginWrapper : resolvedPlugins) {
PluginState pluginState = pluginWrapper.getPluginState();
- if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) {
+ if (!pluginState.isDisabled() && !pluginState.isStarted()) {
try {
log.info("Start plugin '{}'", getPluginLabel(pluginWrapper.getDescriptor()));
pluginWrapper.getPlugin().start();
@@ -413,7 +413,7 @@ public abstract class AbstractPluginManager implements PluginManager {
PluginWrapper pluginWrapper = getPlugin(pluginId);
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
PluginState pluginState = pluginWrapper.getPluginState();
- if (PluginState.STARTED == pluginState) {
+ if (pluginState.isStarted()) {
log.debug("Already started plugin '{}'", getPluginLabel(pluginDescriptor));
return PluginState.STARTED;
}
@@ -423,7 +423,7 @@ public abstract class AbstractPluginManager implements PluginManager {
return pluginState;
}
- if (PluginState.DISABLED == pluginState) {
+ if (pluginState.isDisabled()) {
// automatically enable plugin on manual plugin start
if (!enablePlugin(pluginId)) {
return pluginState;
@@ -458,7 +458,7 @@ public abstract class AbstractPluginManager implements PluginManager {
while (itr.hasNext()) {
PluginWrapper pluginWrapper = itr.next();
PluginState pluginState = pluginWrapper.getPluginState();
- if (PluginState.STARTED == pluginState) {
+ if (pluginState.isStarted()) {
try {
log.info("Stop plugin '{}'", getPluginLabel(pluginWrapper.getDescriptor()));
pluginWrapper.getPlugin().stop();
@@ -516,7 +516,7 @@ public abstract class AbstractPluginManager implements PluginManager {
pluginWrapper.setPluginState(PluginState.STOPPED);
getStartedPlugins().remove(pluginWrapper);
- firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED));
+ firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STARTED));
return PluginState.STOPPED;
}
@@ -550,11 +550,11 @@ public abstract class AbstractPluginManager implements PluginManager {
PluginWrapper pluginWrapper = getPlugin(pluginId);
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
PluginState pluginState = pluginWrapper.getPluginState();
- if (PluginState.DISABLED == pluginState) {
+ if (pluginState.isDisabled()) {
log.debug("Already disabled plugin '{}'", getPluginLabel(pluginDescriptor));
return true;
- } else if (PluginState.STARTED == pluginState) {
- if (PluginState.STOPPED != stopPlugin(pluginId)) {
+ } else if (pluginState.isStarted()) {
+ if (!stopPlugin(pluginId).isStopped()) {
log.error("Failed to stop plugin '{}' on disable", getPluginLabel(pluginDescriptor));
return false;
}
@@ -582,7 +582,7 @@ public abstract class AbstractPluginManager implements PluginManager {
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
PluginState pluginState = pluginWrapper.getPluginState();
- if (PluginState.DISABLED != pluginState) {
+ if (!pluginState.isDisabled()) {
log.debug("Plugin '{}' is not disabled", getPluginLabel(pluginDescriptor));
return true;
}
@@ -842,7 +842,7 @@ public abstract class AbstractPluginManager implements PluginManager {
PluginWrapper pluginWrapper = plugins.get(pluginId);
if (unresolvedPlugins.remove(pluginWrapper)) {
PluginState pluginState = pluginWrapper.getPluginState();
- if (pluginState != PluginState.DISABLED) {
+ if (!pluginState.isDisabled()) {
pluginWrapper.setPluginState(PluginState.RESOLVED);
}
=====================================
pf4j/src/main/java/org/pf4j/PluginClassLoader.java
=====================================
@@ -181,8 +181,9 @@ public class PluginClassLoader extends URLClassLoader {
*/
@Override
public URL getResource(String name) {
+ ClassLoadingStrategy loadingStrategy = getClassLoadingStrategy(name);
log.trace("Received request to load resource '{}'", name);
- for (ClassLoadingStrategy.Source classLoadingSource : classLoadingStrategy.getSources()) {
+ for (ClassLoadingStrategy.Source classLoadingSource : loadingStrategy.getSources()) {
URL url = null;
switch (classLoadingSource) {
case APPLICATION:
@@ -210,9 +211,9 @@ public class PluginClassLoader extends URLClassLoader {
@Override
public Enumeration<URL> getResources(String name) throws IOException {
List<URL> resources = new ArrayList<>();
-
+ ClassLoadingStrategy loadingStrategy = getClassLoadingStrategy(name);
log.trace("Received request to load resources '{}'", name);
- for (ClassLoadingStrategy.Source classLoadingSource : classLoadingStrategy.getSources()) {
+ for (ClassLoadingStrategy.Source classLoadingSource : loadingStrategy.getSources()) {
switch (classLoadingSource) {
case APPLICATION:
if (getParent() != null) {
@@ -231,6 +232,14 @@ public class PluginClassLoader extends URLClassLoader {
return Collections.enumeration(resources);
}
+ private ClassLoadingStrategy getClassLoadingStrategy(String name) {
+ ClassLoadingStrategy loadingStrategy = classLoadingStrategy;
+ if (LegacyExtensionFinder.EXTENSIONS_RESOURCE.equals(name)) {
+ loadingStrategy = ClassLoadingStrategy.PAD;
+ }
+ return loadingStrategy;
+ }
+
/**
* Closes this class loader.
* <p>
=====================================
pf4j/src/main/java/org/pf4j/PluginState.java
=====================================
@@ -72,8 +72,57 @@ public enum PluginState {
this.status = status;
}
+ /**
+ * Returns {@code true} if the value is {@link #CREATED}.
+ */
+ public boolean isCreated() {
+ return this == CREATED;
+ }
+
+ /**
+ * Returns {@code true} if the value is {@link #DISABLED}.
+ */
+ public boolean isDisabled() {
+ return this == DISABLED;
+ }
+
+ /**
+ * Returns {@code true} if the value is {@link #RESOLVED}.
+ */
+ public boolean isResolved() {
+ return this == RESOLVED;
+ }
+
+ /**
+ * Returns {@code true} if the value is {@link #STARTED}.
+ */
+ public boolean isStarted() {
+ return this == STARTED;
+ }
+
+ /**
+ * Returns {@code true} if the value is {@link #STOPPED}.
+ */
+ public boolean isStopped() {
+ return this == STOPPED;
+ }
+
+ /**
+ * Returns {@code true} if the value is {@link #FAILED}.
+ */
+ public boolean isFailed() {
+ return this == FAILED;
+ }
+
+ /**
+ * Returns {@code true} if the value is {@link #UNLOADED}.
+ */
+ public boolean isUnloaded() {
+ return this == UNLOADED;
+ }
+
public boolean equals(String status) {
- return (this.status.equalsIgnoreCase(status));
+ return this.status.equalsIgnoreCase(status);
}
@Override
=====================================
pf4j/src/main/java/org/pf4j/PluginStateEvent.java
=====================================
@@ -16,6 +16,7 @@
package org.pf4j;
import java.util.EventObject;
+import java.util.Objects;
/**
* Event object that indicates a change in the state of a plugin.
@@ -83,4 +84,17 @@ public class PluginStateEvent extends EventObject {
']';
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PluginStateEvent that = (PluginStateEvent) o;
+ return Objects.equals(plugin, that.plugin) && oldState == that.oldState;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(plugin, oldState);
+ }
+
}
=====================================
pf4j/src/main/java/org/pf4j/SingletonExtensionFactory.java
=====================================
@@ -39,7 +39,7 @@ public class SingletonExtensionFactory extends DefaultExtensionFactory {
cache = new HashMap<>();
pluginManager.addPluginStateListener(event -> {
- if (event.getPluginState() != PluginState.STARTED) {
+ if (!event.getPluginState().isStarted()) {
cache.remove(event.getPlugin().getPluginClassLoader());
}
});
=====================================
pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java
=====================================
@@ -15,6 +15,7 @@
*/
package org.pf4j;
+import kotlin.sequences.Sequence;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@@ -35,13 +36,14 @@ import java.util.Set;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Mario Franco
*/
-public class AbstractExtensionFinderTest {
+class AbstractExtensionFinderTest {
private PluginManager pluginManager;
@@ -71,7 +73,7 @@ public class AbstractExtensionFinderTest {
* Test of {@link AbstractExtensionFinder#find(Class)}.
*/
@Test
- public void testFindFailType() {
+ void testFindFailType() {
ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) {
@Override
@@ -93,7 +95,7 @@ public class AbstractExtensionFinderTest {
* Test of {@link AbstractExtensionFinder#find(Class)}.
*/
@Test
- public void testFindFromClasspath() {
+ void testFindFromClasspath() {
ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) {
@Override
@@ -122,7 +124,7 @@ public class AbstractExtensionFinderTest {
* Test of {@link AbstractExtensionFinder#find(Class, String)}.
*/
@Test
- public void testFindFromPlugin() {
+ void testFindFromPlugin() {
ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) {
@Override
@@ -161,7 +163,7 @@ public class AbstractExtensionFinderTest {
* Test of {@link AbstractExtensionFinder#findClassNames(String)}.
*/
@Test
- public void testFindClassNames() {
+ void testFindClassNames() {
ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) {
@Override
@@ -200,7 +202,7 @@ public class AbstractExtensionFinderTest {
* Test of {@link org.pf4j.AbstractExtensionFinder#find(java.lang.String)}.
*/
@Test
- public void testFindExtensionWrappersFromPluginId() {
+ void testFindExtensionWrappersFromPluginId() {
// complicate the test to show hot to deal with dynamic Java classes (generated at runtime from sources)
PluginWrapper plugin3 = mock(PluginWrapper.class);
JavaFileObject object = JavaSources.compile(DefaultExtensionFactoryTest.FailTestExtension);
@@ -251,27 +253,59 @@ public class AbstractExtensionFinderTest {
}
@Test
- public void findExtensionAnnotation() {
- List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.Greeting, JavaSources.WhazzupGreeting);
+ void findExtensionAnnotation() {
+ List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.GREETING, JavaSources.WHAZZUP_GREETING);
assertEquals(2, generatedFiles.size());
Map<String, Class<?>> loadedClasses = new JavaFileObjectClassLoader().load(generatedFiles);
- Class<?> clazz = loadedClasses.get("test.WhazzupGreeting");
+ Class<?> clazz = loadedClasses.get(JavaSources.WHAZZUP_GREETING_CLASS_NAME);
Extension extension = AbstractExtensionFinder.findExtensionAnnotation(clazz);
Assertions.assertNotNull(extension);
}
@Test
- public void findExtensionAnnotationThatMissing() {
- List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.Greeting,
+ void findExtensionAnnotationThatMissing() {
+ List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.GREETING,
ExtensionAnnotationProcessorTest.SpinnakerExtension_NoExtension,
ExtensionAnnotationProcessorTest.WhazzupGreeting_SpinnakerExtension);
assertEquals(3, generatedFiles.size());
Map<String, Class<?>> loadedClasses = new JavaFileObjectClassLoader().load(generatedFiles);
- Class<?> clazz = loadedClasses.get("test.WhazzupGreeting");
+ Class<?> clazz = loadedClasses.get(JavaSources.WHAZZUP_GREETING_CLASS_NAME);
Extension extension = AbstractExtensionFinder.findExtensionAnnotation(clazz);
Assertions.assertNull(extension);
}
+ // This is a regression test, as this caused an StackOverflowError with the previous implementation
+ @Test
+ public void runningOnNonExtensionKotlinClassDoesNotThrowException() {
+ Extension result = AbstractExtensionFinder.findExtensionAnnotation(Sequence.class);
+
+ Assertions.assertNull(result);
+ }
+
+ @Test
+ void checkDifferentClassLoaders() {
+ AbstractExtensionFinder extensionFinder = new AbstractExtensionFinder(pluginManager) {
+
+ @Override
+ public Map<String, Set<String>> readPluginsStorages() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<String, Set<String>> readClasspathStorages() {
+ return Collections.emptyMap();
+ }
+
+ };
+
+ List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.GREETING, JavaSources.WHAZZUP_GREETING);
+ assertEquals(2, generatedFiles.size());
+ Class<?> extensionPointClass = new JavaFileObjectClassLoader().load(generatedFiles).get(JavaSources.GREETING_CLASS_NAME);
+ Class<?> extensionClass = new JavaFileObjectClassLoader().load(generatedFiles).get(JavaSources.WHAZZUP_GREETING_CLASS_NAME);
+
+ assertTrue(extensionFinder.checkDifferentClassLoaders(extensionPointClass, extensionClass));
+ }
+
}
=====================================
pf4j/src/test/java/org/pf4j/AbstractPluginManagerTest.java
=====================================
@@ -138,6 +138,25 @@ public class AbstractPluginManagerTest {
verify(pluginManager, times(1)).resolveDependencies();
}
+ @Test
+ void stopPluginFirePluginStateListeners() {
+ PluginStateListener pluginStateListener = mock(PluginStateListener.class);
+ pluginManager.addPluginStateListener(pluginStateListener);
+
+ String pluginId = "plugin1";
+ PluginWrapper pluginWrapper = createPluginWrapper(pluginId);
+ pluginWrapper.setPluginState(PluginState.STARTED);
+
+ doReturn(pluginWrapper).when(pluginManager).getPlugin(pluginId);
+ doNothing().when(pluginManager).checkPluginId(pluginId);
+ doReturn(new ArrayList<>(Arrays.asList(pluginWrapper))).when(pluginManager).getStartedPlugins();
+
+ pluginManager.stopPlugin(pluginId, false);
+
+ PluginStateEvent event = new PluginStateEvent(pluginManager, pluginWrapper, PluginState.STARTED);
+ verify(pluginStateListener).pluginStateChanged(event);
+ }
+
private PluginWrapper createPluginWrapper(String pluginId, String... dependencies) {
PluginDescriptor pluginDescriptor = new DefaultPluginDescriptor()
.setPluginId(pluginId)
=====================================
pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java
=====================================
@@ -42,7 +42,6 @@ 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;
-import static org.pf4j.test.JavaSources.Greeting;
class DefaultPluginManagerTest {
@@ -203,7 +202,7 @@ class DefaultPluginManagerTest {
assertFalse(pluginZip.file().exists());
Optional<PluginStateEvent> unloadedEvent = receivedEvents.stream()
- .filter(event -> event.getPluginState() == PluginState.UNLOADED)
+ .filter(event -> event.getPluginState().isUnloaded())
.findFirst();
assertTrue(unloadedEvent.isPresent());
@@ -226,7 +225,7 @@ class DefaultPluginManagerTest {
assertFalse(pluginJar.file().exists());
Optional<PluginStateEvent> unloadedEvent = receivedEvents.stream()
- .filter(event -> event.getPluginState() == PluginState.UNLOADED)
+ .filter(event -> event.getPluginState().isUnloaded())
.findFirst();
assertTrue(unloadedEvent.isPresent());
@@ -368,7 +367,7 @@ class DefaultPluginManagerTest {
assertFalse(pluginZip.file().exists());
Optional<PluginStateEvent> unloadedEvent = receivedEvents.stream()
- .filter(event -> event.getPluginState() == PluginState.UNLOADED)
+ .filter(event -> event.getPluginState().isUnloaded())
.findFirst();
assertTrue(unloadedEvent.isPresent());
@@ -441,7 +440,7 @@ class DefaultPluginManagerTest {
@Test
void unloadPlugin() throws IOException, ClassNotFoundException {
- JavaFileObject object = JavaSources.compile(Greeting);
+ JavaFileObject object = JavaSources.compile(JavaSources.GREETING);
String pluginClassName = JavaFileObjectUtils.getClassName(object);
byte[] pluginBytes = JavaFileObjectUtils.getAllBytes(object);
=====================================
pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java
=====================================
@@ -125,7 +125,7 @@ public class ExtensionAnnotationProcessorTest {
@Test
public void options() {
Compilation compilation = compiler().withOptions("-Ab=2", "-Ac=3")
- .compile(JavaSources.Greeting, JavaSources.WhazzupGreeting);
+ .compile(JavaSources.GREETING, JavaSources.WHAZZUP_GREETING);
assertEquals(Compilation.Status.SUCCESS, compilation.status());
Map<String, String> options = new HashMap<>();
options.put("b", "2");
@@ -135,20 +135,20 @@ public class ExtensionAnnotationProcessorTest {
@Test
public void storage() {
- Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting);
+ Compilation compilation = compile(JavaSources.GREETING, JavaSources.WHAZZUP_GREETING);
assertEquals(Compilation.Status.SUCCESS, compilation.status());
assertEquals(annotationProcessor.getStorage().getClass(), LegacyExtensionStorage.class);
}
@Test
public void compileWithoutError() {
- Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting);
+ Compilation compilation = compile(JavaSources.GREETING, JavaSources.WHAZZUP_GREETING);
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compileWithError() {
- Compilation compilation = compile(JavaSources.Greeting, WhazzupGreeting_NoExtensionPoint);
+ Compilation compilation = compile(JavaSources.GREETING, WhazzupGreeting_NoExtensionPoint);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("it doesn't implement ExtensionPoint")
.inFile(WhazzupGreeting_NoExtensionPoint)
@@ -158,19 +158,19 @@ public class ExtensionAnnotationProcessorTest {
@Test
public void getExtensions() {
- Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting);
+ Compilation compilation = compile(JavaSources.GREETING, JavaSources.WHAZZUP_GREETING);
assertThat(compilation).succeededWithoutWarnings();
Map<String, Set<String>> extensions = new HashMap<>();
- extensions.put("test.Greeting", new HashSet<>(Collections.singletonList("test.WhazzupGreeting")));
+ extensions.put(JavaSources.GREETING_CLASS_NAME, new HashSet<>(Collections.singletonList(JavaSources.WHAZZUP_GREETING_CLASS_NAME)));
assertEquals(extensions, annotationProcessor.getExtensions());
}
@Test
public void compileNestedExtensionAnnotation() {
- Compilation compilation = compile(JavaSources.Greeting, SpinnakerExtension, WhazzupGreeting_SpinnakerExtension);
+ Compilation compilation = compile(JavaSources.GREETING, SpinnakerExtension, WhazzupGreeting_SpinnakerExtension);
assertThat(compilation).succeededWithoutWarnings();
Map<String, Set<String>> extensions = new HashMap<>();
- extensions.put("test.Greeting", new HashSet<>(Collections.singletonList("test.WhazzupGreeting")));
+ extensions.put(JavaSources.GREETING_CLASS_NAME, new HashSet<>(Collections.singletonList(JavaSources.WHAZZUP_GREETING_CLASS_NAME)));
assertEquals(extensions, annotationProcessor.getExtensions());
}
=====================================
pf4j/src/test/java/org/pf4j/PluginClassLoaderTest.java
=====================================
@@ -20,6 +20,7 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.pf4j.processor.LegacyExtensionStorage;
import org.pf4j.test.PluginZip;
import org.pf4j.util.FileUtils;
@@ -74,6 +75,7 @@ class PluginClassLoaderTest {
createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-dependency-and-plugin"));
createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-dependency"));
createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-plugin"));
+ createFile(parentClassPathBase.resolve(LegacyExtensionStorage.EXTENSIONS_RESOURCE));
}
private static void createFile(Path pathToFile) throws IOException {
@@ -106,6 +108,7 @@ class PluginClassLoaderTest {
.addFile(Paths.get("classes/META-INF/dependency-file"), "dependency")
.addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency-and-plugin"), "dependency")
.addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency"), "dependency")
+ .addFile(Paths.get("classes/" + LegacyExtensionStorage.EXTENSIONS_RESOURCE), "dependency")
.build();
FileUtils.expandIfZip(pluginDependencyZip.path());
@@ -148,6 +151,7 @@ class PluginClassLoaderTest {
.addFile(Paths.get("classes/META-INF/plugin-file"), "plugin")
.addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency-and-plugin"), "plugin")
.addFile(Paths.get("classes/META-INF/file-in-both-parent-and-plugin"), "plugin")
+ .addFile(Paths.get("classes/" + LegacyExtensionStorage.EXTENSIONS_RESOURCE), "plugin")
.build();
FileUtils.expandIfZip(pluginZip.path());
@@ -325,6 +329,18 @@ class PluginClassLoaderTest {
assertNumberOfResourcesAndFirstLineOfFirstElement(3, "parent", resources);
}
+ @Test
+ void parentFirstGetExtensionsIndexExistsInParentAndDependencyAndPlugin() throws URISyntaxException, IOException {
+ URL resource = parentLastPluginClassLoader.getResource(LegacyExtensionFinder.EXTENSIONS_RESOURCE);
+ assertFirstLine("plugin", resource);
+ }
+
+ @Test
+ void parentLastGetExtensionsIndexExistsInParentAndDependencyAndPlugin() throws URISyntaxException, IOException {
+ URL resource = parentLastPluginClassLoader.getResource(LegacyExtensionFinder.EXTENSIONS_RESOURCE);
+ assertFirstLine("plugin", resource);
+ }
+
@Test
void isClosed() throws IOException {
parentLastPluginClassLoader.close();
=====================================
pf4j/src/test/java/org/pf4j/SecurePluginManagerWrapperTest.java
=====================================
@@ -15,15 +15,6 @@
*/
package org.pf4j;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.List;
-
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -33,6 +24,14 @@ import org.pf4j.test.TestExtension;
import org.pf4j.test.TestExtensionPoint;
import org.pf4j.test.TestPlugin;
+import java.io.IOException;
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
public class SecurePluginManagerWrapperTest {
private static final String OTHER_PLUGIN_ID = "test-plugin-2";
=====================================
pf4j/src/test/java/org/pf4j/test/JavaSources.java
=====================================
@@ -30,7 +30,8 @@ import static com.google.testing.compile.Compiler.javac;
*/
public class JavaSources {
- public static final JavaFileObject Greeting = JavaFileObjects.forSourceLines("Greeting",
+ public static final String GREETING_CLASS_NAME = "test.Greeting";
+ public static final JavaFileObject GREETING = JavaFileObjects.forSourceLines("Greeting",
"package test;",
"import org.pf4j.ExtensionPoint;",
"",
@@ -38,7 +39,8 @@ public class JavaSources {
" String getGreeting();",
"}");
- public static final JavaFileObject WhazzupGreeting = JavaFileObjects.forSourceLines("WhazzupGreeting",
+ public static final String WHAZZUP_GREETING_CLASS_NAME = "test.WhazzupGreeting";
+ public static final JavaFileObject WHAZZUP_GREETING = JavaFileObjects.forSourceLines("WhazzupGreeting",
"package test;",
"import org.pf4j.Extension;",
"",
=====================================
pom.xml
=====================================
@@ -10,7 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.pf4j</groupId>
<artifactId>pf4j-parent</artifactId>
- <version>3.12.0</version>
+ <version>3.13.0</version>
<packaging>pom</packaging>
<name>PF4J Parent</name>
<description>Plugin Framework for Java</description>
@@ -28,7 +28,7 @@
<connection>scm:git:https://github.com/pf4j/pf4j.git</connection>
<developerConnection>scm:git:git at github.com:pf4j/pf4j.git</developerConnection>
<url>git at github.com/pf4j/pf4j.git</url>
- <tag>release-3.12.0</tag>
+ <tag>release-3.13.0</tag>
</scm>
<distributionManagement>
View it on GitLab: https://salsa.debian.org/java-team/libpf4j-java/-/commit/cb0a4dc8fc2bfcb9f0c390e57329a8d63be0ca1f
--
View it on GitLab: https://salsa.debian.org/java-team/libpf4j-java/-/commit/cb0a4dc8fc2bfcb9f0c390e57329a8d63be0ca1f
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20251002/cf814fe1/attachment.htm>
More information about the pkg-java-commits
mailing list