diff options
author | Decebal Suiu <decebal.suiu@gmail.com> | 2023-01-30 20:36:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-30 20:36:47 +0200 |
commit | 1f04209be1769e40c730cf2701926611e2f85435 (patch) | |
tree | d824ef5c8dd93264a4b99ed9bf3704540d73ce76 | |
parent | de63736b13a71c227c1dec95e8e39c95a3962870 (diff) | |
download | pf4j-1f04209be1769e40c730cf2701926611e2f85435.tar.gz pf4j-1f04209be1769e40c730cf2701926611e2f85435.zip |
Relax Plugin construction (remove dependency on PluginWrapper) (#512)
13 files changed, 561 insertions, 346 deletions
diff --git a/demo/api/src/main/java/org/pf4j/demo/api/DemoPlugin.java b/demo/api/src/main/java/org/pf4j/demo/api/DemoPlugin.java new file mode 100644 index 0000000..a2f76fc --- /dev/null +++ b/demo/api/src/main/java/org/pf4j/demo/api/DemoPlugin.java @@ -0,0 +1,35 @@ +/* + * 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.demo.api; + +import org.pf4j.Plugin; + +/** + * Base {@link Plugin} for all demo plugins. + * + * @author Decebal Suiu + */ +public abstract class DemoPlugin extends Plugin { + + protected final PluginContext context; + + protected DemoPlugin(PluginContext context) { + super(); + + this.context = context; + } + +} diff --git a/demo/api/src/main/java/org/pf4j/demo/api/PluginContext.java b/demo/api/src/main/java/org/pf4j/demo/api/PluginContext.java new file mode 100644 index 0000000..0a7506f --- /dev/null +++ b/demo/api/src/main/java/org/pf4j/demo/api/PluginContext.java @@ -0,0 +1,39 @@ +/* + * 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.demo.api; + +import org.pf4j.RuntimeMode; + +/** + * An instance of this class is provided to plugins in their constructor. + * It's safe for plugins to keep a reference to the instance for later use. + * This class facilitates communication with application and plugin manager. + * + * @author Decebal Suiu + */ +public class PluginContext { + + private final RuntimeMode runtimeMode; + + public PluginContext(RuntimeMode runtimeMode) { + this.runtimeMode = runtimeMode; + } + + public RuntimeMode getRuntimeMode() { + return runtimeMode; + } + +} 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 524009c..d101104 100644 --- a/demo/app/src/main/java/org/pf4j/demo/Boot.java +++ b/demo/app/src/main/java/org/pf4j/demo/Boot.java @@ -16,7 +16,6 @@ package org.pf4j.demo; import org.apache.commons.lang.StringUtils; -import org.pf4j.DefaultPluginManager; import org.pf4j.PluginManager; import org.pf4j.PluginWrapper; import org.pf4j.demo.api.Greeting; @@ -40,7 +39,7 @@ public class Boot { printLogo(); // create the plugin manager - PluginManager pluginManager = createPluginManager(); + PluginManager pluginManager = new DemoPluginManager(); // load the plugins pluginManager.loadPlugins(); @@ -129,23 +128,4 @@ public class Boot { log.info(StringUtils.repeat("#", 40)); } - private static PluginManager createPluginManager() { - return new DefaultPluginManager(); - - // use below plugin manager instance if you want to enable ServiceProviderExtensionFinder - /* - return new DefaultPluginManager() { - - @Override - protected ExtensionFinder createExtensionFinder() { - DefaultExtensionFinder extensionFinder = (DefaultExtensionFinder) super.createExtensionFinder(); - extensionFinder.addServiceProviderExtensionFinder(); // to activate "HowdyGreeting" extension - - return extensionFinder; - } - - }; - */ - } - } diff --git a/demo/app/src/main/java/org/pf4j/demo/DemoPluginFactory.java b/demo/app/src/main/java/org/pf4j/demo/DemoPluginFactory.java new file mode 100644 index 0000000..432dceb --- /dev/null +++ b/demo/app/src/main/java/org/pf4j/demo/DemoPluginFactory.java @@ -0,0 +1,40 @@ +/* + * 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.demo; + +import org.pf4j.DefaultPluginFactory; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import org.pf4j.demo.api.PluginContext; + +import java.lang.reflect.Constructor; + +class DemoPluginFactory extends DefaultPluginFactory { + + @Override + protected Plugin createInstance(Class<?> pluginClass, PluginWrapper pluginWrapper) { + PluginContext context = new PluginContext(pluginWrapper.getRuntimeMode()); + try { + Constructor<?> constructor = pluginClass.getConstructor(PluginContext.class); + return (Plugin) constructor.newInstance(context); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + return null; + } + +} diff --git a/demo/app/src/main/java/org/pf4j/demo/DemoPluginManager.java b/demo/app/src/main/java/org/pf4j/demo/DemoPluginManager.java new file mode 100644 index 0000000..2571a3e --- /dev/null +++ b/demo/app/src/main/java/org/pf4j/demo/DemoPluginManager.java @@ -0,0 +1,42 @@ +/* + * 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.demo; + +import org.pf4j.DefaultExtensionFinder; +import org.pf4j.DefaultPluginFactory; +import org.pf4j.DefaultPluginManager; +import org.pf4j.ExtensionFinder; +import org.pf4j.PluginFactory; + +class DemoPluginManager extends DefaultPluginManager { + + // Use below code if you want to enable ServiceProviderExtensionFinder + /* + @Override + protected ExtensionFinder createExtensionFinder() { + DefaultExtensionFinder extensionFinder = (DefaultExtensionFinder) super.createExtensionFinder(); + extensionFinder.addServiceProviderExtensionFinder(); // to activate "HowdyGreeting" extension + + return extensionFinder; + } + */ + + @Override + protected PluginFactory createPluginFactory() { + return new DemoPluginFactory(); + } + +} diff --git a/demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java b/demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java index a0cecab..3064902 100644 --- a/demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java +++ b/demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java @@ -16,27 +16,26 @@ package org.pf4j.demo.welcome; import org.apache.commons.lang.StringUtils; - -import org.pf4j.PluginWrapper; +import org.pf4j.Extension; import org.pf4j.RuntimeMode; +import org.pf4j.demo.api.DemoPlugin; import org.pf4j.demo.api.Greeting; -import org.pf4j.Extension; -import org.pf4j.Plugin; +import org.pf4j.demo.api.PluginContext; /** * @author Decebal Suiu */ -public class WelcomePlugin extends Plugin { +public class WelcomePlugin extends DemoPlugin { - public WelcomePlugin(PluginWrapper wrapper) { - super(wrapper); + public WelcomePlugin(PluginContext context) { + super(context); } @Override public void start() { log.info("WelcomePlugin.start()"); // for testing the development mode - if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { + if (RuntimeMode.DEVELOPMENT.equals(context.getRuntimeMode())) { log.info(StringUtils.upperCase("WelcomePlugin")); } } diff --git a/demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java b/demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java index 3b7c9ce..ed67878 100644 --- a/demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java +++ b/demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java @@ -16,19 +16,19 @@ package org.pf4j.demo.hello; import org.pf4j.Extension; -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; +import org.pf4j.demo.api.DemoPlugin; import org.pf4j.demo.api.Greeting; +import org.pf4j.demo.api.PluginContext; /** * A very simple plugin. * * @author Decebal Suiu */ -public class HelloPlugin extends Plugin { +public class HelloPlugin extends DemoPlugin { - public HelloPlugin(PluginWrapper wrapper) { - super(wrapper); + public HelloPlugin(PluginContext context) { + super(context); } @Override diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginFactory.java b/pf4j/src/main/java/org/pf4j/DefaultPluginFactory.java index 2f55cab..310c791 100644 --- a/pf4j/src/main/java/org/pf4j/DefaultPluginFactory.java +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginFactory.java @@ -23,18 +23,16 @@ import java.lang.reflect.Modifier; /** * The default implementation for {@link PluginFactory}. - * It uses {@link Class#newInstance()} method. + * It uses {@link Constructor#newInstance(Object...)} method. * * @author Decebal Suiu */ public class DefaultPluginFactory implements PluginFactory { - private static final Logger log = LoggerFactory.getLogger(DefaultPluginFactory.class); + protected static final Logger log = LoggerFactory.getLogger(DefaultPluginFactory.class); /** - * Creates a plugin instance. If an error occurs than that error is logged and the method returns null. - * @param pluginWrapper - * @return + * Creates a plugin instance. If an error occurs than that error is logged and the method returns {@code null}. */ @Override public Plugin create(final PluginWrapper pluginWrapper) { @@ -58,10 +56,26 @@ public class DefaultPluginFactory implements PluginFactory { return null; } - // create the plugin instance + return createInstance(pluginClass, pluginWrapper); + } + + protected Plugin createInstance(Class<?> pluginClass, PluginWrapper pluginWrapper) { try { Constructor<?> constructor = pluginClass.getConstructor(PluginWrapper.class); return (Plugin) constructor.newInstance(pluginWrapper); + } catch (NoSuchMethodException e) { + return createUsingNoParametersConstructor(pluginClass); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + return null; + } + + protected Plugin createUsingNoParametersConstructor(Class<?> pluginClass) { + try { + Constructor<?> constructor = pluginClass.getConstructor(); + return (Plugin) constructor.newInstance(); } catch (Exception e) { log.error(e.getMessage(), e); } diff --git a/pf4j/src/main/java/org/pf4j/Plugin.java b/pf4j/src/main/java/org/pf4j/Plugin.java index e3dd449..aaee012 100644 --- a/pf4j/src/main/java/org/pf4j/Plugin.java +++ b/pf4j/src/main/java/org/pf4j/Plugin.java @@ -33,14 +33,20 @@ public class Plugin { /** * Wrapper of the plugin. + * @deprecated Use application custom {@code PluginContext} instead of {@code PluginWrapper}. + * See demo for more details. */ + @Deprecated protected PluginWrapper wrapper; /** * Constructor to be used by plugin manager for plugin instantiation. * Your plugins have to provide constructor with this exact signature to * be successfully loaded by manager. + * @deprecated Use application custom {@code PluginContext} instead of {@code PluginWrapper}. + * See demo for more details. */ + @Deprecated public Plugin(final PluginWrapper wrapper) { if (wrapper == null) { throw new IllegalArgumentException("Wrapper cannot be null"); @@ -49,9 +55,15 @@ public class Plugin { this.wrapper = wrapper; } + public Plugin() { + } + /** * Retrieves the wrapper of this plug-in. + * @deprecated Use application custom {@code PluginContext} instead of {@code PluginWrapper}. + * See demo for more details. */ + @Deprecated public final PluginWrapper getWrapper() { return wrapper; } diff --git a/pf4j/src/main/java/org/pf4j/SecurePluginManagerWrapper.java b/pf4j/src/main/java/org/pf4j/SecurePluginManagerWrapper.java index f55b1e6..ee2d681 100644 --- a/pf4j/src/main/java/org/pf4j/SecurePluginManagerWrapper.java +++ b/pf4j/src/main/java/org/pf4j/SecurePluginManagerWrapper.java @@ -1,305 +1,308 @@ -package org.pf4j;
-
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Use this class to wrap the original plugin manager to prevent full access from within plugins.
- * Override AbstractPluginManager.createPluginWrapper to use this class
- * @author Wolfram Haussig
- *
- */
-public class SecurePluginManagerWrapper implements PluginManager {
-
- private static final String PLUGIN_PREFIX = "Plugin ";
- /**
- * the current plugin
- */
- private String currentPluginId;
- /**
- * the original plugin manager
- */
- private PluginManager original;
-
- /**
- * The registered {@link PluginStateListener}s.
- */
- protected List<PluginStateListener> pluginStateListeners = new ArrayList<>();
- /**
- * wrapper for pluginStateListeners
- */
- private PluginStateListenerWrapper listenerWrapper = new PluginStateListenerWrapper();
-
- /**
- * constructor
- * @param original the original plugin manager
- * @param currentPlugin the current pluginId
- */
- public SecurePluginManagerWrapper(PluginManager original, String currentPluginId) {
- this.original = original;
- this.currentPluginId = currentPluginId;
- }
-
- @Override
- public boolean isDevelopment() {
- return original.isDevelopment();
- }
-
- @Override
- public boolean isNotDevelopment() {
- return original.isNotDevelopment();
- }
-
- @Override
- public List<PluginWrapper> getPlugins() {
- return Arrays.asList(getPlugin(currentPluginId));
- }
-
- @Override
- public List<PluginWrapper> getPlugins(PluginState pluginState) {
- return getPlugins().stream().filter(p -> p.getPluginState() == pluginState).collect(Collectors.toList());
- }
-
- @Override
- public List<PluginWrapper> getResolvedPlugins() {
- return getPlugins().stream().filter(p -> p.getPluginState().ordinal() >= PluginState.RESOLVED.ordinal()).collect(Collectors.toList());
- }
-
- @Override
- public List<PluginWrapper> getUnresolvedPlugins() {
- return Collections.emptyList();
- }
-
- @Override
- public List<PluginWrapper> getStartedPlugins() {
- return getPlugins(PluginState.STARTED);
- }
-
- @Override
- public PluginWrapper getPlugin(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.getPlugin(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPlugin for foreign pluginId!");
- }
- }
-
- @Override
- public void loadPlugins() {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute loadPlugins!");
- }
-
- @Override
- public String loadPlugin(Path pluginPath) {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute loadPlugin!");
- }
-
- @Override
- public void startPlugins() {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute startPlugins!");
- }
-
- @Override
- public PluginState startPlugin(String pluginId) {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute startPlugin!");
- }
-
- @Override
- public void stopPlugins() {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute stopPlugins!");
- }
-
- @Override
- public PluginState stopPlugin(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.stopPlugin(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute stopPlugin for foreign pluginId!");
- }
- }
-
- @Override
- public void unloadPlugins() {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute unloadPlugins!");
- }
-
- @Override
- public boolean unloadPlugin(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.unloadPlugin(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute unloadPlugin for foreign pluginId!");
- }
- }
-
- @Override
- public boolean disablePlugin(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.disablePlugin(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute disablePlugin for foreign pluginId!");
- }
- }
-
- @Override
- public boolean enablePlugin(String pluginId) {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute enablePlugin!");
- }
-
- @Override
- public boolean deletePlugin(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.deletePlugin(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute deletePlugin for foreign pluginId!");
- }
- }
-
- @Override
- public ClassLoader getPluginClassLoader(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.getPluginClassLoader(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginClassLoader for foreign pluginId!");
- }
- }
-
- @Override
- public List<Class<?>> getExtensionClasses(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.getExtensionClasses(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClasses for foreign pluginId!");
- }
- }
-
- @Override
- public <T> List<Class<? extends T>> getExtensionClasses(Class<T> type) {
- return getExtensionClasses(type, currentPluginId);
- }
-
- @Override
- public <T> List<Class<? extends T>> getExtensionClasses(Class<T> type, String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.getExtensionClasses(type, pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClasses for foreign pluginId!");
- }
- }
-
- @Override
- public <T> List<T> getExtensions(Class<T> type) {
- return getExtensions(type, currentPluginId);
- }
-
- @Override
- public <T> List<T> getExtensions(Class<T> type, String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.getExtensions(type, pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensions for foreign pluginId!");
- }
- }
-
- @Override
- public List<?> getExtensions(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.getExtensions(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensions for foreign pluginId!");
- }
- }
-
- @Override
- public Set<String> getExtensionClassNames(String pluginId) {
- if (currentPluginId.equals(pluginId)) {
- return original.getExtensionClassNames(pluginId);
- } else {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClassNames for foreign pluginId!");
- }
- }
-
- @Override
- public ExtensionFactory getExtensionFactory() {
- return original.getExtensionFactory();
- }
-
- @Override
- public RuntimeMode getRuntimeMode() {
- return original.getRuntimeMode();
- }
-
- @Override
- public PluginWrapper whichPlugin(Class<?> clazz) {
- ClassLoader classLoader = clazz.getClassLoader();
- PluginWrapper plugin = getPlugin(currentPluginId);
- if (plugin.getPluginClassLoader() == classLoader) {
- return plugin;
- }
- return null;
- }
-
- @Override
- public void addPluginStateListener(PluginStateListener listener) {
- if (pluginStateListeners.isEmpty()) {
- this.original.addPluginStateListener(listenerWrapper);
- }
- pluginStateListeners.add(listener);
- }
-
- @Override
- public void removePluginStateListener(PluginStateListener listener) {
- pluginStateListeners.remove(listener);
- if (pluginStateListeners.isEmpty()) {
- this.original.removePluginStateListener(listenerWrapper);
- }
- }
-
- @Override
- public void setSystemVersion(String version) {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute setSystemVersion!");
- }
-
- @Override
- public String getSystemVersion() {
- return original.getSystemVersion();
- }
-
- @Override
- public Path getPluginsRoot() {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginsRoot!");
- }
-
- @Override
- public List<Path> getPluginsRoots() {
- throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginsRoots!");
- }
-
- @Override
- public VersionManager getVersionManager() {
- return original.getVersionManager();
- }
-
- /**
- * Wrapper for PluginStateListener events. will only propagate events if they match the current pluginId
- * @author Wolfram Haussig
- *
- */
- private class PluginStateListenerWrapper implements PluginStateListener {
-
- @Override
- public void pluginStateChanged(PluginStateEvent event) {
- if (event.getPlugin().getPluginId().equals(currentPluginId)) {
- for (PluginStateListener listener : pluginStateListeners) {
- listener.pluginStateChanged(event);
- }
- }
- }
-
- }
-}
+package org.pf4j; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Use this class to wrap the original plugin manager to prevent full access from within plugins. + * Override AbstractPluginManager.createPluginWrapper to use this class. + * @deprecated Use application custom {@code PluginContext} instead of {@code PluginWrapper} to communicate with {@link Plugin}. + * See demo for more details. + * + * @author Wolfram Haussig + */ +@Deprecated() +public class SecurePluginManagerWrapper implements PluginManager { + + private static final String PLUGIN_PREFIX = "Plugin "; + /** + * the current plugin + */ + private String currentPluginId; + /** + * the original plugin manager + */ + private PluginManager original; + + /** + * The registered {@link PluginStateListener}s. + */ + protected List<PluginStateListener> pluginStateListeners = new ArrayList<>(); + /** + * wrapper for pluginStateListeners + */ + private PluginStateListenerWrapper listenerWrapper = new PluginStateListenerWrapper(); + + /** + * constructor + * @param original the original plugin manager + * @param currentPluginId the current pluginId + */ + public SecurePluginManagerWrapper(PluginManager original, String currentPluginId) { + this.original = original; + this.currentPluginId = currentPluginId; + } + + @Override + public boolean isDevelopment() { + return original.isDevelopment(); + } + + @Override + public boolean isNotDevelopment() { + return original.isNotDevelopment(); + } + + @Override + public List<PluginWrapper> getPlugins() { + return Arrays.asList(getPlugin(currentPluginId)); + } + + @Override + public List<PluginWrapper> getPlugins(PluginState pluginState) { + return getPlugins().stream().filter(p -> p.getPluginState() == pluginState).collect(Collectors.toList()); + } + + @Override + public List<PluginWrapper> getResolvedPlugins() { + return getPlugins().stream().filter(p -> p.getPluginState().ordinal() >= PluginState.RESOLVED.ordinal()).collect(Collectors.toList()); + } + + @Override + public List<PluginWrapper> getUnresolvedPlugins() { + return Collections.emptyList(); + } + + @Override + public List<PluginWrapper> getStartedPlugins() { + return getPlugins(PluginState.STARTED); + } + + @Override + public PluginWrapper getPlugin(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.getPlugin(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPlugin for foreign pluginId!"); + } + } + + @Override + public void loadPlugins() { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute loadPlugins!"); + } + + @Override + public String loadPlugin(Path pluginPath) { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute loadPlugin!"); + } + + @Override + public void startPlugins() { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute startPlugins!"); + } + + @Override + public PluginState startPlugin(String pluginId) { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute startPlugin!"); + } + + @Override + public void stopPlugins() { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute stopPlugins!"); + } + + @Override + public PluginState stopPlugin(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.stopPlugin(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute stopPlugin for foreign pluginId!"); + } + } + + @Override + public void unloadPlugins() { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute unloadPlugins!"); + } + + @Override + public boolean unloadPlugin(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.unloadPlugin(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute unloadPlugin for foreign pluginId!"); + } + } + + @Override + public boolean disablePlugin(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.disablePlugin(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute disablePlugin for foreign pluginId!"); + } + } + + @Override + public boolean enablePlugin(String pluginId) { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute enablePlugin!"); + } + + @Override + public boolean deletePlugin(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.deletePlugin(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute deletePlugin for foreign pluginId!"); + } + } + + @Override + public ClassLoader getPluginClassLoader(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.getPluginClassLoader(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginClassLoader for foreign pluginId!"); + } + } + + @Override + public List<Class<?>> getExtensionClasses(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.getExtensionClasses(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClasses for foreign pluginId!"); + } + } + + @Override + public <T> List<Class<? extends T>> getExtensionClasses(Class<T> type) { + return getExtensionClasses(type, currentPluginId); + } + + @Override + public <T> List<Class<? extends T>> getExtensionClasses(Class<T> type, String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.getExtensionClasses(type, pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClasses for foreign pluginId!"); + } + } + + @Override + public <T> List<T> getExtensions(Class<T> type) { + return getExtensions(type, currentPluginId); + } + + @Override + public <T> List<T> getExtensions(Class<T> type, String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.getExtensions(type, pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensions for foreign pluginId!"); + } + } + + @Override + public List<?> getExtensions(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.getExtensions(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensions for foreign pluginId!"); + } + } + + @Override + public Set<String> getExtensionClassNames(String pluginId) { + if (currentPluginId.equals(pluginId)) { + return original.getExtensionClassNames(pluginId); + } else { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getExtensionClassNames for foreign pluginId!"); + } + } + + @Override + public ExtensionFactory getExtensionFactory() { + return original.getExtensionFactory(); + } + + @Override + public RuntimeMode getRuntimeMode() { + return original.getRuntimeMode(); + } + + @Override + public PluginWrapper whichPlugin(Class<?> clazz) { + ClassLoader classLoader = clazz.getClassLoader(); + PluginWrapper plugin = getPlugin(currentPluginId); + if (plugin.getPluginClassLoader() == classLoader) { + return plugin; + } + return null; + } + + @Override + public void addPluginStateListener(PluginStateListener listener) { + if (pluginStateListeners.isEmpty()) { + this.original.addPluginStateListener(listenerWrapper); + } + pluginStateListeners.add(listener); + } + + @Override + public void removePluginStateListener(PluginStateListener listener) { + pluginStateListeners.remove(listener); + if (pluginStateListeners.isEmpty()) { + this.original.removePluginStateListener(listenerWrapper); + } + } + + @Override + public void setSystemVersion(String version) { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute setSystemVersion!"); + } + + @Override + public String getSystemVersion() { + return original.getSystemVersion(); + } + + @Override + public Path getPluginsRoot() { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginsRoot!"); + } + + @Override + public List<Path> getPluginsRoots() { + throw new IllegalAccessError(PLUGIN_PREFIX + currentPluginId + " tried to execute getPluginsRoots!"); + } + + @Override + public VersionManager getVersionManager() { + return original.getVersionManager(); + } + + /** + * Wrapper for PluginStateListener events. will only propagate events if they match the current pluginId + * @author Wolfram Haussig + * + */ + private class PluginStateListenerWrapper implements PluginStateListener { + + @Override + public void pluginStateChanged(PluginStateEvent event) { + if (event.getPlugin().getPluginId().equals(currentPluginId)) { + for (PluginStateListener listener : pluginStateListeners) { + listener.pluginStateChanged(event); + } + } + } + + } +} diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java index 3107083..c453e38 100644 --- a/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java @@ -17,6 +17,7 @@ package org.pf4j; import org.junit.jupiter.api.Test; import org.pf4j.test.AnotherFailTestPlugin; +import org.pf4j.test.AnotherTestPlugin; import org.pf4j.test.FailTestPlugin; import org.pf4j.test.TestPlugin; @@ -49,6 +50,22 @@ public class DefaultPluginFactoryTest { } @Test + public void pluginConstructorNoParameters() { + PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); + when(pluginDescriptor.getPluginClass()).thenReturn(AnotherTestPlugin.class.getName()); + + PluginWrapper pluginWrapper = mock(PluginWrapper.class); + when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); + when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); + + PluginFactory pluginFactory = new DefaultPluginFactory(); + + Plugin result = pluginFactory.create(pluginWrapper); + assertNotNull(result); + assertThat(result, instanceOf(AnotherTestPlugin.class)); + } + + @Test public void testCreateFail() { PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); when(pluginDescriptor.getPluginClass()).thenReturn(FailTestPlugin.class.getName()); diff --git a/pf4j/src/test/java/org/pf4j/test/AnotherFailTestPlugin.java b/pf4j/src/test/java/org/pf4j/test/AnotherFailTestPlugin.java index d97b32c..79aa41b 100644 --- a/pf4j/src/test/java/org/pf4j/test/AnotherFailTestPlugin.java +++ b/pf4j/src/test/java/org/pf4j/test/AnotherFailTestPlugin.java @@ -19,7 +19,7 @@ import org.pf4j.Plugin; /** * A wrong {@link org.pf4j.Plugin}. - * It's wrong because it doesn't contain a constructor with one parameter ({@link org.pf4j.PluginWrapper} as parameter type). + * It's wrong because it calls super constructor with {@code null} for ({@link org.pf4j.PluginWrapper} parameter). * * @author Mario Franco */ diff --git a/pf4j/src/test/java/org/pf4j/test/AnotherTestPlugin.java b/pf4j/src/test/java/org/pf4j/test/AnotherTestPlugin.java new file mode 100644 index 0000000..f2e10c1 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/test/AnotherTestPlugin.java @@ -0,0 +1,34 @@ +/* + * 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.test; + +import org.pf4j.Plugin; + +/** + * A simple {@link Plugin}. + * + * In real applications you don't need to create a plugin like this if you are not interested in lifecycle events. + * {@code PF4J} will automatically create a plugin similar to this (empty / dummy) if no class plugin is specified. + * + * @author Decebal Suiu + */ +public class AnotherTestPlugin extends Plugin { + + public AnotherTestPlugin() { + super(); + } + +} |