diff --git a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
index a23fc6a..beac7c8 100644
--- a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
+++ b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java
@@ -38,8 +38,8 @@ import java.util.stream.Collectors;
* This class implements the boilerplate plugin code that any {@link PluginManager}
* implementation would have to support.
* It helps cut the noise out of the subclass that handles plugin management.
- *
- *
This class is not thread-safe.
+ *
+ * This class is not thread-safe.
*
* @author Decebal Suiu
*/
@@ -160,14 +160,9 @@ public abstract class AbstractPluginManager implements PluginManager {
*/
@Override
public List getPlugins(PluginState pluginState) {
- List plugins = new ArrayList<>();
- for (PluginWrapper plugin : getPlugins()) {
- if (pluginState.equals(plugin.getPluginState())) {
- plugins.add(plugin);
- }
- }
-
- return plugins;
+ return getPlugins().stream()
+ .filter(plugin -> pluginState.equals(plugin.getPluginState()))
+ .collect(Collectors.toList());
}
@Override
@@ -190,6 +185,14 @@ public abstract class AbstractPluginManager implements PluginManager {
return plugins.get(pluginId);
}
+ /**
+ * Load a plugin.
+ *
+ * @param pluginPath the plugin location
+ * @return the pluginId of the loaded plugin as specified in its {@linkplain PluginDescriptor metadata}
+ * @throws IllegalArgumentException if the plugin location does not exist
+ * @throws PluginRuntimeException if something goes wrong
+ */
@Override
public String loadPlugin(Path pluginPath) {
if ((pluginPath == null) || Files.notExists(pluginPath)) {
@@ -212,6 +215,7 @@ public abstract class AbstractPluginManager implements PluginManager {
@Override
public void loadPlugins() {
log.debug("Lookup plugins in '{}'", pluginsRoots);
+
// check for plugins roots
if (pluginsRoots.isEmpty()) {
log.warn("No plugins roots configured");
@@ -264,12 +268,22 @@ public abstract class AbstractPluginManager implements PluginManager {
/**
* Unload the specified plugin and it's dependents.
+ *
+ * @param pluginId the pluginId of the plugin to unload
+ * @return true if the plugin was unloaded, otherwise false
*/
@Override
public boolean unloadPlugin(String pluginId) {
return unloadPlugin(pluginId, true);
}
+ /**
+ * Unload the specified plugin and it's dependents.
+ *
+ * @param pluginId the pluginId of the plugin to unload
+ * @param unloadDependents if true, unload dependents
+ * @return true if the plugin was unloaded, otherwise false
+ */
protected boolean unloadPlugin(String pluginId, boolean unloadDependents) {
try {
if (unloadDependents) {
@@ -347,9 +361,7 @@ public abstract class AbstractPluginManager implements PluginManager {
// notify the plugin as it's deleted
plugin.delete();
- Path pluginPath = pluginWrapper.getPluginPath();
-
- return pluginRepository.deletePluginPath(pluginPath);
+ return pluginRepository.deletePluginPath(pluginWrapper.getPluginPath());
}
/**
@@ -405,7 +417,7 @@ public abstract class AbstractPluginManager implements PluginManager {
}
for (PluginDependency dependency : pluginDescriptor.getDependencies()) {
- // start dependency only if it marked as required (non optional) or if it optional and loaded
+ // start dependency only if it marked as required (non-optional) or if it optional and loaded
if (!dependency.isOptional() || plugins.containsKey(dependency.getPluginId())) {
startPlugin(dependency.getPluginId());
}
@@ -455,6 +467,13 @@ public abstract class AbstractPluginManager implements PluginManager {
return stopPlugin(pluginId, true);
}
+ /**
+ * Stop the specified plugin and it's dependents.
+ *
+ * @param pluginId the pluginId of the plugin to stop
+ * @param stopDependents if true, stop dependents
+ * @return the plugin state after stopping
+ */
protected PluginState stopPlugin(String pluginId, boolean stopDependents) {
checkPluginId(pluginId);
@@ -491,6 +510,12 @@ public abstract class AbstractPluginManager implements PluginManager {
return pluginWrapper.getPluginState();
}
+ /**
+ * Check if the plugin exists in the list of plugins.
+ *
+ * @param pluginId the pluginId to check
+ * @throws IllegalArgumentException if the plugin does not exist
+ */
protected void checkPluginId(String pluginId) {
if (!plugins.containsKey(pluginId)) {
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
@@ -741,6 +766,7 @@ public abstract class AbstractPluginManager implements PluginManager {
}
pluginsDir = isDevelopment() ? DEVELOPMENT_PLUGINS_DIR : DEFAULT_PLUGINS_DIR;
+
return Collections.singletonList(Paths.get(pluginsDir));
}
@@ -760,19 +786,30 @@ public abstract class AbstractPluginManager implements PluginManager {
return true;
}
- PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
log.warn("Plugin '{}' requires a minimum system version of {}, and you have {}",
- getPluginLabel(pluginDescriptor),
+ getPluginLabel(pluginWrapper.getDescriptor()),
requires,
getSystemVersion());
return false;
}
+ /**
+ * Check if the plugin is disabled.
+ *
+ * @param pluginId the pluginId to check
+ * @return true if the plugin is disabled, otherwise false
+ */
protected boolean isPluginDisabled(String pluginId) {
return pluginStatusProvider.isPluginDisabled(pluginId);
}
+ /**
+ * It resolves the plugins by checking the dependencies.
+ * It also checks for cyclic dependencies, missing dependencies and wrong versions of the dependencies.
+ *
+ * @throws PluginRuntimeException if something goes wrong
+ */
protected void resolvePlugins() {
// retrieves the plugins descriptors
List descriptors = new ArrayList<>();
@@ -815,6 +852,12 @@ public abstract class AbstractPluginManager implements PluginManager {
}
}
+ /**
+ * Fire a plugin state event.
+ * This method is called when a plugin is loaded, started, stopped, etc.
+ *
+ * @param event the plugin state event
+ */
protected synchronized void firePluginStateEvent(PluginStateEvent event) {
for (PluginStateListener listener : pluginStateListeners) {
log.trace("Fire '{}' to '{}'", event, listener);
@@ -822,6 +865,14 @@ public abstract class AbstractPluginManager implements PluginManager {
}
}
+ /**
+ * Load the plugin from the specified path.
+ *
+ * @param pluginPath the path to the plugin
+ * @return the loaded plugin
+ * @throws PluginAlreadyLoadedException if the plugin is already loaded
+ * @throws InvalidPluginDescriptorException if the plugin is invalid
+ */
protected PluginWrapper loadPluginFromPath(Path pluginPath) {
// Test for plugin path duplication
String pluginId = idForPath(pluginPath);
@@ -886,15 +937,21 @@ public abstract class AbstractPluginManager implements PluginManager {
}
/**
- * creates the plugin wrapper. override this if you want to prevent plugins having full access to the plugin manager
+ * Creates the plugin wrapper.
+ *
+ * Override this if you want to prevent plugins having full access to the plugin manager.
*
- * @return
+ * @param pluginDescriptor the plugin descriptor
+ * @param pluginPath the path to the plugin
+ * @param pluginClassLoader the class loader for the plugin
+ * @return the plugin wrapper
*/
protected PluginWrapper createPluginWrapper(PluginDescriptor pluginDescriptor, Path pluginPath, ClassLoader pluginClassLoader) {
// create the plugin wrapper
log.debug("Creating wrapper for plugin '{}'", pluginPath);
PluginWrapper pluginWrapper = new PluginWrapper(this, pluginDescriptor, pluginPath, pluginClassLoader);
pluginWrapper.setPluginFactory(getPluginFactory());
+
return pluginWrapper;
}
@@ -918,19 +975,21 @@ public abstract class AbstractPluginManager implements PluginManager {
* Override this to change the validation criteria.
*
* @param descriptor the plugin descriptor to validate
- * @throws PluginRuntimeException if validation fails
+ * @throws InvalidPluginDescriptorException if validation fails
*/
protected void validatePluginDescriptor(PluginDescriptor descriptor) {
if (StringUtils.isNullOrEmpty(descriptor.getPluginId())) {
- throw new PluginRuntimeException("Field 'id' cannot be empty");
+ throw new InvalidPluginDescriptorException("Field 'id' cannot be empty");
}
if (descriptor.getVersion() == null) {
- throw new PluginRuntimeException("Field 'version' cannot be empty");
+ throw new InvalidPluginDescriptorException("Field 'version' cannot be empty");
}
}
/**
+ * Check if the exact version in requires is allowed.
+ *
* @return true if exact versions in requires is allowed
*/
public boolean isExactVersionAllowed() {
@@ -954,7 +1013,10 @@ public abstract class AbstractPluginManager implements PluginManager {
}
/**
- * The plugin label is used in logging and it's a string in format {@code pluginId@pluginVersion}.
+ * The plugin label is used in logging, and it's a string in format {@code pluginId@pluginVersion}.
+ *
+ * @param pluginDescriptor the plugin descriptor
+ * @return the plugin label
*/
protected String getPluginLabel(PluginDescriptor pluginDescriptor) {
return pluginDescriptor.getPluginId() + "@" + pluginDescriptor.getVersion();
diff --git a/pf4j/src/main/java/org/pf4j/InvalidPluginDescriptorException.java b/pf4j/src/main/java/org/pf4j/InvalidPluginDescriptorException.java
new file mode 100644
index 0000000..44a80ef
--- /dev/null
+++ b/pf4j/src/main/java/org/pf4j/InvalidPluginDescriptorException.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * This exception signals incomplete or wrong information in the descriptor for a plugin.
+ *
+ * @see PluginDescriptor
+ * @author Decebal Suiu
+ */
+public class InvalidPluginDescriptorException extends PluginRuntimeException {
+
+ public InvalidPluginDescriptorException(String message) {
+ super(message);
+ }
+
+}