Git Product home page Git Product logo

pf4j-spring's Introduction

Plugin Framework for Java (PF4J)

Join the chat at https://gitter.im/decebals/pf4j GitHub Actions Status Coverage Maven Central

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 for a real example.

Features/Benefits

With PF4J you can easily transform a monolithic java application in a modular application.
PF4J is an open source (Apache license) lightweight (around 100 KB) plugin framework for java, with minimal dependencies (only slf4j-api) and very extensible (see PluginDescriptorFinder and ExtensionFinder).

Practically, PF4J is a microframework that aims to keep the core simple but extensible. We also have a community-driven ecosystem of extensions. For now are available these extensions:

No XML, only Java.

You can mark any interface or abstract class as an extension point (with marker interface ExtensionPoint) and you specified that an class is an extension with @Extension annotation.

Components

  • Plugin is the base class for all plugins types. Each plugin is loaded into a separate class loader to avoid conflicts.
  • PluginManager is used for all aspects of plugins management (loading, starting, stopping). You can use a built-in implementation as JarPluginManager, ZipPluginManager, DefaultPluginManager (it's a JarPluginManager + ZipPluginManager) or you can implement a custom plugin manager starting from AbstractPluginManager (implement only factory methods).
  • PluginLoader loads all information (classes) needed by a plugin.
  • ExtensionPoint is a point in the application where custom code can be invoked. It's a java interface marker.
    Any java interface or abstract class can be marked as an extension point (implements ExtensionPoint interface).
  • Extension is an implementation of an extension point. It's a java annotation on a class.

PLUGIN = a container for EXTENSION POINTS and EXTENSIONS + lifecycle methods (start, stop, delete)

A PLUGIN is similar with a MODULE from other systems. If you don't need lifecycle methods (hook methods for start, stop, delete) you are not forced to supply a plugin class (the PluginClass property from the plugin descriptor is optional). You only need to supply some description of plugin (id, version, author, ...) for a good tracking (your application wants to know who supplied the extensions or extensions points).

How to use

It's very simple to add pf4j in your application.

Define an extension point in your application/plugin using ExtensionPoint interface marker:

public interface Greeting extends ExtensionPoint {

    String getGreeting();

}

Create an extension using @Extension annotation:

@Extension
public class WelcomeGreeting implements Greeting {

    public String getGreeting() {
        return "Welcome";
    }

}

Create (it's optional) a Plugin class if you are interested in plugin's lifecycle events (start, stop, ...):

public class WelcomePlugin extends Plugin {

    @Override
    public void start() {
        System.out.println("WelcomePlugin.start()");
    }

    @Override
    public void stop() {
        System.out.println("WelcomePlugin.stop()");
    }
    
    @Override
    public void delete() {
        System.out.println("WelcomePlugin.delete()");
    }
    
}

In above code we've created a plugin (welcome) that comes with one extension for the Greeting extension point.

You can distribute your plugin as a jar file (the simple solution). In this case add the plugin's metadata in MANIFEST.MF file of jar:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: decebal
Build-Jdk: 1.6.0_17
Plugin-Class: org.pf4j.demo.welcome.WelcomePlugin
Plugin-Dependencies: x, y, z
Plugin-Id: welcome-plugin
Plugin-Provider: Decebal Suiu
Plugin-Version: 0.0.1

In the manifest above, we've described a plugin with the id of welcome-plugin (mandatory attribute). We've also defined a class org.pf4j.demo.welcome.WelcomePlugin (optional attribute), with version 0.0.1 (mandatory attribute) and with dependencies to plugins x, y, z (optional attribute).

Now you can play with plugins and extensions in your code:

public static void main(String[] args) {
    ...

    // create the plugin manager
    PluginManager pluginManager = new JarPluginManager(); // or "new ZipPluginManager() / new DefaultPluginManager()"
    
    // start and load all plugins of application
    pluginManager.loadPlugins();
    pluginManager.startPlugins();

    // retrieve all extensions for "Greeting" extension point
    List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
    for (Greeting greeting : greetings) {
        System.out.println(">>> " + greeting.getGreeting());
    }
    
    // stop and unload all plugins
    pluginManager.stopPlugins();
    pluginManager.unloadPlugins();
    
    ...
}

The output is:

>>> Welcome

PF4J is very customizable and comes with a lot of goodies. Please read the documentation to discover yourself the power of this library.

Documentation

Documentation is available on pf4j.org

Demo

Demo applications are available in demo folder

Quickstart (call to action)

  1. Read this file to have an overview about what this project does
  2. Read Getting started section of documentation to understand the basic concepts
  3. Read Quickstart section of documentation to create your first PF4J-based modular application

pf4j-spring's People

Contributors

3fong avatar decebals avatar lhanda avatar m-schroeer avatar michaelruocco avatar srbala avatar vishu184 avatar workingdevel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pf4j-spring's Issues

Add Dependency in Plugin application

Hi Decebals,
I am facing the same issue, can you please help on this how to add a dependency for third-party jar..

Also added dependency in assambly.xml file:

<includes>
    <include>org.pf4j:*</include>
    <include>com.googlecode.json-simple:*</include>
</includes>

Originally posted by @MohankumarD in #19 (comment)

SpringPlugin start/stop/start results in Exception

I tested the following calls:

pluginManager.startPlugin(PLUGIN_ID);
pluginManager.getExtensions(PLUGIN_ID);
pluginManager.stopPlugin(PLUGIN_ID);

pluginManager.startPlugin(PLUGIN_ID);
pluginManager.getExtensions(PLUGIN_ID);
pluginManager.stopPlugin(PLUGIN_ID);

The secons call to getExtensions results in an Exception: java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@4a6facb0 has been closed already

I figured out the following workaround in SpringPlugin

    @Override
    public void stop() {
        // close applicationContext
        if ((applicationContext != null) && (applicationContext instanceof ConfigurableApplicationContext)) {
            ((ConfigurableApplicationContext) applicationContext).close();
            **applicationContext = null;**
        }
    }

By adding applicationContext = null a new application context is created when restarting the plugin. It should work but I am not 100% sure that there are no side effects so maybe you could have a quick check?

Loading plugin as spring boot executable jar

I've tested loading a plugin developed with spring boot.

Normally, a spring boot project (it could be the plugin) is built with spring-boot-maven-plugin that create a spring boot executable jar, that is a fat jar that contains classes + libs.

The structure of a spring boot executable jar is different than a standard jar, look at this doc page: https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-executable-jar-format.html#executable-jar-jar-file-structure

So plugin classes (and extension.idx) are placed, into the jar, at this folder: BOOT-INF/classes.
Is there a way for pf4j to search classes not in the root of jar but in a subfolder of it?

Third party Dependency in Plugin

i am not aware of how to add dependency in plugin!

i am facing issue while adding third party jar "apache poi" in plugin, i have added maven dependency in pom file, i am able to build plugin jar file but getting ClassnotFound exception at run time

Plugin Extension As Controller On Host

Please help me apply this logic so that pf4j can be used for mvc,

so that the extension can be accessed as a controller in the application host, then the extension must be registered into ((ConfigurableBeanFactory) beanFactory) .registerSingleton (extension.getClass (). getName (), extension);

i successfully accessed the controller extension into the application host, but failed to call the service or context brought by the extension, so the extension I injected into the host could not process the database in the plugin.

my api

public interface ControllerInterface extends ExtensionPoint {
    List<Object> controller();
}

my plugin

public class PayrollPlugin extends SpringPlugin {

    public RestPlugin(PluginWrapper wrapper) {
        super(wrapper);
    }

    @Override
    .......
	.......
    @Override
    protected ApplicationContext createApplicationContext() {
        ApplicationContext rootContext = ((SpringPluginManager) getWrapper().getPluginManager()).getApplicationContext();
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
        applicationContext.setParent(rootContext); /* with this i try to hope to be able to access the bean application host datasource via the ApplicationContext host from the ApplicationContext.getParent () plugin */
        applicationContext.register(PluginConfiguration.class);
        applicationContext.refresh();
        return applicationContext;
    }
}

my controller

@Slf4j
@RestController
@RequestMapping("/plugin.id.payroll")
public class PayrollExtensionController {

    @Autowired
	private ApplicationContext pluginContext; /* i try to access the ApplicationContext plugin but return null */
	
    @GetMapping("/generate")
    public ResponseEntity<String> generate(){
		/* i can't execute the service that I defined in this plugin */
        return ResponseEntity.ok().body("Hallow from generate");
    }
}

my extension

@Slf4j
@Extension
public class ControllerPlugin implements ControllerInterface {
    @Override
    public List<Object> controller() {
        return Arrays.asList(new PayrollExtensionController(), new PieceExtensionController());
    }
}

my plugin config on host application

@Slf4j
public class ControllerBean {

    @Autowired(required = false)
    private ControllerInterface controllerInterface;

    public void printController() {
        log.info("Found {} extensions for extension '{}'", controllerInterface.controller().size(), ControllerInterface.class.getName());
    }

}
@Bean
@DependsOn("pluginManager")
public ControllerBean controllerBean() {
	addController(pluginManager());
	return new ControllerBean();
}

private void addController(PluginManager pluginManager) {
	pluginManager.getExtensions(ControllerInterface.class)
		.forEach(controllerInterface -> {
			log.info("Extension {} loaded", controllerInterface.getClass().getName());
			controllerInterface.controller().forEach(object -> {
				log.info("Registered controller {}", object.getClass().getName());
				((ConfigurableBeanFactory)beanFactory).registerSingleton(object.getClass().getName(), object);
			});
		});
}

is there a solution to my problem? or pf4j can be modified to create an @ControllerExtention annotation

pf4j-spring compatibility with Spring Boot

Hello, i hope i can get some help.

I have tested successfully in my Spring Boot web application, loading plugins and getting my extensions from the plugin packaged in a jar like in your documentation shows in this way:

FileSystem sistemaFicheros = FileSystems.getDefault();
	   
PluginManager pluginManager = new SpringPluginManager(sistemaFicheros.getPath("/home/vbravo/Escritorio/plugins"));
pluginManager.loadPlugins();
	
List<Converter> converters = pluginManager.getExtensions(Converter.class);
for (Converter zipToPdfConversion : converters) {
    zipToPdfConversion.getConversionType();
}

But i don't see any example of loading this plugin via @Autowired (in Spring Boot) like you say in the documentation:

Ready, your extension is available in your application via PluginManager or Spring Autowire.

I don't understand very well what you mean exactly with Autowired, Do you mean i could load the plugin in this way as attribute of a class in a Spring Boot context?

@Autowired
Converter zipToPdfConversion;

Where Converter zipToPdfConversion is implemented in the same form of your documentation:

public class HelloPlugin extends SpringPlugin {

    public HelloPlugin(PluginWrapper wrapper) {
        super(wrapper);
    }

    @Override
    public void start() {
        System.out.println("HelloPlugin.start()");
    }

    @Override
    public void stop() {
        System.out.println("HelloPlugin.stop()");
        super.stop(); // to close applicationContext
    }

    @Override
    protected ApplicationContext createApplicationContext() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
        applicationContext.register(SpringConfiguration.class);
        applicationContext.refresh();

        return applicationContext;
    }

    @Extension
    public static class HelloGreeting implements Greeting {

        @Autowired
        private MessageProvider messageProvider;

        @Override
        public String getGreeting() {
//            return "Hello";
            // complicate a little bit the code
           return messageProvider.getMessage();
        }

    }

}

Can you show me some example to see how to do that Autowired of a plugin class (extending SpringPlugin) thru Spring Boot?

The idea is to load the plugin without package the plugin in a jar, i have just the plugin in the source code. I want the two ways, load from a jar and load without packaged in jar (is this possible?).

Thank you, greetings.

Trying to run the Spring demo-app but got error creating bean with name 'greetings

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'greetings': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.List org.pf4j.demo.Greetings.greetings; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.pf4j.demo.api.Greeting] found for dependency [collection of org.pf4j.demo.api.Greeting]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
	at org.pf4j.demo.Boot.main(Boot.java:35)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.List org.pf4j.demo.Greetings.greetings; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.pf4j.demo.api.Greeting] found for dependency [collection of org.pf4j.demo.api.Greeting]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
	... 12 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.pf4j.demo.api.Greeting] found for dependency [collection of org.pf4j.demo.api.Greeting]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1103)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:915)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
	... 14 more

Extensions.idx files get not generated in Elipse although annotation processing is activated

I tried to run your demo application with the development mode (-Dpf4j.mode=development) in Eclipse. I realized that the extension files get not generated in the "target/classes/META-INF" folder although I have activated on the plugin projects the Java compiler option "Enable Annotation Processing".

Selection_001

If I run the Maven build to produce the jar files then the extension files are packed inside the jar files. So I assume it is a problem of the Eclipse processing? I am using Eclipse v2019-03 and Java 1.8 and version 0.5.0 of your project

Please see the log statements when I start your demo "boot" application
log.txt

Problems with Extension and Scopes

Hey,
I want to extend an class that needs a other scope then the singleton. How can I specify the bean scope for the @Extension annotated class?

I want to use your library with Spring and Vaadin 8. All works fine! But now I want to test to get SpringView annotated classes loaded out of the plugin. And there I have problems with the scopes of the bean that will be created by your library. The SpringView cannot be a singleton.

Is there any option to set the singleton explicitly?

Greetings!

getting a single extension fails when multiple extensions exist even using Primary

I've got a more representative demo Spring PF4J demo here:
https://github.com/claymccoy/Pf4jSpringGreetingDemo

It works fine, even when I uncomment the single greeting endpoint.
https://github.com/claymccoy/Pf4jSpringGreetingDemo/blob/master/src/main/java/org/pf4j/demo/GreetingsController.java#L25

But with the single greeting endpoint uncommented, if I add more greeting extensions via plugins it fails. That is not surprising. However, I would expect it to work if one of the extensions is marked with the Primary Spring annotation. But I still get this:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field greeting in org.pf4j.demo.GreetingsController required a single bean, but 2 were found:
	- org.pf4j.demo.WhazzupGreeting: a programmatically registered singleton	- com.claymccoy.pf4jdemo.bjj.BjjGreeting: a programmatically registered singleton

Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

> Task :bootRun FAILED

Not able to find spring components created in plugin zip

When we register the configuration files in plugin - only the beans created in config files are being eligble for autowiring but not the components declared in the plugin application - is there any way to register all the components created as well?

pf4j-update and pf4j spring issue

Hi,

I try to use pf4j update with pf4j spring in my app ....
as describe in previous issue https://github.com/pf4j/pf4j-spring/issues/31, i build locally the latest pf4j spring dependency.
I work with:

  • pf4j-spring 0.6.0-SNAPSHOT (that is in my local maven repo)
  • pf4j-update 2.1.0 (availble on official maven repo)
  • pf4j 3.0.1

When i try to get an updateManager using
UpdateManager updateManager = new UpdateManager(pluginManager); and i got an class not found execption as pluginException is not found. But all dependencies are correctly added. May be i forget something, but in previous issue, someone encountered a classNotFoundException (other class) and i would like to know if you are able to reproduce this issue.

Here logs about this exception:

2019-07-24 19:21:43.518 INFO 9740 --- [ main] com..nx_plugin_in.InPlugin : in plugin class successfully started
2019-07-24 19:21:43.694 INFO 9740 --- [ main] com.
.demo.NxAgentApplication : Started NxAgentApplication in 1.565 seconds (JVM running for 2.369)
2019-07-24 19:21:43.695 INFO 9740 --- [ main] com.
.demo.NxAgentApplication : in custom start ....
2019-07-24 19:21:43.735 INFO 9740 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-07-24 19:21:43.741 ERROR 9740 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.NoClassDefFoundError: org/pf4j/PluginException
at com.
.demo.NxAgentApplication$1.run(NxAgentApplication.java:84) ~[classes!/:0.0.1-SNAPSHOT]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:770) [spring-boot-2.1.6.RELEASE.jar!/:2.1.6.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760) [spring-boot-2.1.6.RELEASE.jar!/:2.1.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) [spring-boot-2.1.6.RELEASE.jar!/:2.1.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213) [spring-boot-2.1.6.RELEASE.jar!/:2.1.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202) [spring-boot-2.1.6.RELEASE.jar!/:2.1.6.RELEASE]
at com.
********.demo.NxAgentApplication.main(NxAgentApplication.java:35) [classes!/:0.0.1-SNAPSHOT]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_211]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_211]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_211]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_211]
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:47) [nx-agent-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:86) [nx-agent-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) [nx-agent-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51) [nx-agent-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: java.lang.ClassNotFoundException: org.pf4j.PluginException
at java.net.URLClassLoader.findClass(Unknown Source) ~[na:1.8.0_211]
at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_211]
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:92) ~[nx-agent-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_211]
... 15 common frames omitted

Spring application with spring enabled plugins

Hi,

I have been playing with this framework and managed to get an initial project working which is a spring application which makes use of plugins through the pf4j framework. The git repo is here: https://github.com/michaelruocco/spring-boot-plugin-example.

What I would now like to do is to make use of spring within the plugins themselves. I have had a go at adding this following this element of the demo: https://github.com/pf4j/pf4j-spring/tree/master/demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello, however I am having some issues.

The changes are pushed to a branch here: https://github.com/michaelruocco/spring-boot-plugin-example/tree/spring-enabled-plugin. I can see that the problem is that the ChannelIdProvider bean is not being injected into the extension but I am not sure why. I am also confused as to why the .log message from BidvAliasLoaderPlugin are not being printed out on startup. I suspect that I am doing something wrong but I am not sure what. Any help you can provide would be much appreciated.

Thanks.

PluginClassLoader issue with loading duplicate spring classes

I have three projects consisting of:

  • A Shared-API project with interfaces for ExtensionPoints, only depending on PF4J (compile scope)
  • A Main-Application using Spring, PF4J and PF4J-Spring, and the Shared-API project above
  • A Plugin-Project that implements a SpringPlugin and tries to start it's own application context, with its own dependencies on Spring, PF4J (compile scope), PF4J-Spring (compile scope) and the shared-api (compile scope)

The Plugin-Project is built as a fat jar (exlcuding the shared API and PF4J classes), and is loaded by the Main-Application. The SpringPluginManager starts, but when it tries to execute the start() method of the plugin (which triggers the application context start, as per the examples) I get the following Exception:

loader org.pf4j.PluginClassLoader @7d3fb0ef wants to load interface org.springframework.context.ApplicationContext. A different interface with the same name was previously loaded by 'app'. (org.springframework.context.ApplicationContext is in unnamed module of loader 'app')

The exception makes sense, as both the Main-Application and the Plugin-Project have their own spring dependencies in their ClassPaths, but I thought the point of Plugins was that their class loading is isolated so that, for example, you could have differeing versions of the same dependency in the Plugin-Project and the Main-App without conflict? Am i doing something wrong / misunderstanding?

Not compatible with pf4j-update & pf4j-spring

Not compatible with pf4j-update, because different basics libraries (pf4j-spring uses pf4j: 2.5.0 but pf4j-update uses pf4j: 3.0.1) as a result when run error: Caused by: java.lang.ClassNotFoundException: org.pf4j .PluginRuntimeException

Dependencies of system extension inside SpringPluginManager's host not autowired

Have a problem when using SpringPluginManager in a SpringBoot project with a system extension defined. Inside the system extension I have annotated a dependency using the Spring '@Autowired' annotation like how you would define for a SpringPlugin's plugin extension. However, upon running the code, the dependency inside the system extension was not injected because the SpringExtensionFactory's create method only does so for SpringPlugin's plugin extension only.

Below is the extract of the SpringExtensionFactory create method and my suggested modifications

@Override
  public <T> T create(Class<T> extensionClass) {
      T extension = createWithoutSpring(extensionClass);
      if (autowire && extension != null) {
          // test for SpringBean
          PluginWrapper pluginWrapper = pluginManager.whichPlugin(extensionClass);
          if (pluginWrapper != null) { // is plugin extension
              Plugin plugin = pluginWrapper.getPlugin();
              if (plugin instanceof SpringPlugin) {
                  // autowire
                  ApplicationContext pluginContext = ((SpringPlugin) plugin).getApplicationContext();
                  pluginContext.getAutowireCapableBeanFactory().autowireBean(extension);
              }
          } else if (this.pluginManager instanced SpringPluginManager) { // is system extension and plugin manager is SpringPluginManager
              SpringPluginManager springPluginManager = (SpringPluginManager) this.pluginManager;
              ApplicationContext pluginContext = springPluginManager.getAppplicationContext();
              pluginContext.getAutowireCapableBeanFactory().autowireBean(extension);
          }
      }

      return extension;
  }

pf4j-spring and Spring Boot

I need a demo to use pf4j-spring together with Spring Boot. i.e. integrate pf4j-spring into a spring boot application.

Cannot build plugins from demo project

I try to build Plugin1 and Plugin 2 from pf4j-spring demo project. I use pf4j-spring ver. 0.4.0
I dropped pf4j-spring module from project, and use pf4j-spring as maven dependency from maven repository.
So, the project has structure like this:

parent
-api
-app
-plugins
    --plugin1
    --plugin2

It's stack trace of maven install

[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] error: C:\Users\salimgaraev\Desktop\demo-master\plugins\plugin1\target\classes\META-INF\extensions.idx
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.327 s
[INFO] Finished at: 2018-07-03T12:53:43+03:00
[INFO] Final Memory: 18M/74M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile) on project pf4j-spring-demo-plugin1: Compilation failure
[ERROR] error: C:\Users\salimgaraev\Desktop\demo-master\plugins\plugin1\target\classes\META-INF\extensions.idx
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

While maven try to compile module, it generates extension.idx with data inside
2018-07-03_14-29-01

I think problem is with extension, because when I comment extension method in Plugin class, module compiles without errors.

Next release version

Apologies if this is not the right avenue for this question.

The current release of pf4j-spring available on Maven central is version 0.5.0 which depends on pf4j version 2.x. I see that the latest HEAD uses 3.0.1. May I know when a new release will be pushed so we can start using it please?

Ideally we'd like to upgrade to using pf4j 3.2.0 (which is the latest as of today), but even 3.0.1 would be nice.

If there's anything blocking that needs to be fixed before a release, please let me know. We'll help if it's something we can take up.

Thanks for all your work.

JPA Entity Scanning won't pick up entities from plugins

It's not possible to define JPA entities in a plugin.

Here's the plugins entity: https://github.com/keering/keering/blob/master/plugins/widgets/chat/src/main/java/com/github/keering/plugins/widgets/chat/model/jpa/ChatHistory.java
And this is how I set entity scanning: https://github.com/keering/keering/blob/master/app/src/main/java/com/github/keering/app/configuration/PluginConfiguration.java#L29-L40

Debug log is only picking up these classes:

2019-01-03 12:07:19.740 DEBUG 13507 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : PersistenceUnitInfo [
        name: default
        persistence provider classname: null
        classloader: org.springframework.boot.loader.LaunchedURLClassLoader@5f341870
        excludeUnlistedClasses: true
        JTA datasource: null
        Non JTA datasource: HikariDataSource (HikariPool-1)
        Transaction type: RESOURCE_LOCAL
        PU root URL: file:/home/danny/workspace/keering/build/app-1.0-SNAPSHOT.jar
        Shared Cache Mode: UNSPECIFIED
        Validation Mode: AUTO
        Jar files URLs []
        Managed classes names [
                com.github.keering.app.model.jpa.UserConfiguration$UserConfigurationId
                com.github.keering.app.model.jpa.UserConfiguration
                com.github.keering.api.model.jpa.User
                com.github.keering.api.model.jpa.Role]
        Mapping files names []
        Properties []

I guess it's due to different class loaders for main app and plugins.

You can start the project by running: $ ./gradlew build && cd build && java -jar app-1.0-SNAPSHOT.jar

I use it SpringPluginManager, It seems to be a single case.

SpringPluginManager pluginManager = new SpringPluginManager(new File("plugins").toPath());
DemoImpl implA = pluginManager.getExtension(DemoImpl.class,"xxxx").get(0);
implA.setMessage("hello");

DemoImpl impB = pluginManager.getExtension(DemoImpl.class,"xxxx").get(0);
String message = implB.getMessage(); // I want an unassigned message

SpringExtensionFactory is written in extensionClass.newInstance(), But after the experiment, the message I get is still hello.I have no idea.

Application Properties in spring plugin

I was able to get the pf4j-spring configured with Spring boot and the plugins are working fine.

I am trying to get the properties configured in application.properties into one of the plugins and the properties are not getting resolved.

Is this possible? Or is there a way a do it?

Thx.

Under certain circumstances, SpringPluginManager does not identify loaded plugins

AbstractEventManager.whichPlugin() attempts to determine which plugin owns an extension class by comparing their classloaders. Under certain curcumstances (Such as running in IntelliJ) the classloader used will not contain the package but the parent classloader will.

I Overrode the whichPlugin() method in SpringPluginManager and the problem is resolved:

import org.pf4j.PluginWrapper;
import org.pf4j.spring.SpringPluginManager;

public class SpringPluginManagerExt extends SpringPluginManager {
    @Override
    public PluginWrapper whichPlugin(Class<?> clazz) {
        ClassLoader classLoader = clazz.getClassLoader();
        for (PluginWrapper plugin : getResolvedPlugins()) {
            if (plugin.getPluginClassLoader() == classLoader || plugin.getPluginClassLoader().getParent() == classLoader) {
                return plugin;
            }
        }

        return null;
    }
}

This method is probably not ideal... the PluginClassloader should properly fill out the packages map and then use that or some other mechanism to determine ownership...

Can't get working the functionality "Disable Plugin"

From AbstractPluginManager.loadPlugins() its called AbstractPluginManager.loadPluginFromPath() that recognise and marks plugins as DISABLED (I'm only adding the disabled.txt in the plugins folder). But later AbstractPluginManager.resolvePlugins() mark the same plugin as RESOLVED. Is there something I'm missing?

Questions: custom plugin or extension use

Hi Decebals,

I've questions about plugins and extensions. As describe in documentations, a plugin enable some feature for managins plugins. So, i extend plugin (in api), implement it on each jar and enable custom method that could be called by app or other plugin later (using custom Type/interface declared in api). But there are extensions and as describe in documentation, an extension is a specific behaviour of an application (or for a plugin). I think custom method that are created in my plugin could be an extension ... for me twice are possible but i didn't see pro and con of two scenario ... And i don't know if it's a good idea to add custom method or specific behaviour (like external request for example) in the plugin instead of using extensions

Loading plugin on-runtime : Ambiguous hander methods mapped & ConcurrentModificationException when unregistering routes

Hi,

I'm trying to import plugins on-runtime,
I began without on-runtime plugin loading, base on the pf4j-spring demo I manage to create plugins and if I place the plugin file into /plugins before creating the Spring ApplicationContext everything is fine, I can access each of my mapped routes.
To implement on-runtime plugin loading, when I detect a new plugin I load them and then call the following méthod:

public void registerMvcEndpoints(PluginManager pm) {
    pm.getExtensions(PluginInterface.class).stream()
                .flatMap(g -> g.mvcControllers().stream())
                .forEach(r -> {
                    if (!beanAlreadyRegistered.contains(r.getClass().getName())){
                        ((ConfigurableBeanFactory) beanFactory).registerSingleton(r.getClass().getName(), r);
                        beanAlreadyRegistered.add(r.getClass().getName());
                    }
                });
    applicationContext
                .getBeansOfType(RequestMappingHandlerMapping.class)
                .forEach((k, v) -> {
                    v.afterPropertiesSet();
                });

But when I try to access a route, even without adding a plugin I get IllegalStateException: Ambiguous handler methods mapped for ...
That is because the routes are registered a second time, so because of that I try to use unregisterMapping() to clean the mapping by adding the following code:

RequestMappingHandlerMapping requestMappingHandlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
for (RequestMappingInfo key : handlerMethods.keySet()) {
    requestMappingHandlerMapping.unregisterMapping(key);
}

But I get this exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pluginEndpoints' defined in class path resource [fr/polytech/al3/PolyVilleActive/PluginConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [int]: Factory method 'pluginEndpoints' threw exception; nested exception is java.util.ConcurrentModificationException
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:882) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) ~[spring-context-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.2.6.RELEASE.jar!/:2.2.6.RELEASE]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) ~[spring-boot-2.2.6.RELEASE.jar!/:2.2.6.RELEASE]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.2.6.RELEASE.jar!/:2.2.6.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.2.6.RELEASE.jar!/:2.2.6.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.2.6.RELEASE.jar!/:2.2.6.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.2.6.RELEASE.jar!/:2.2.6.RELEASE]
        at fr.polytech.al3.PolyVilleActive.SpringPluginContainerApplication.main(SpringPluginContainerApplication.java:29) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) ~[spring-plugin-container-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) ~[spring-plugin-container-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:51) ~[spring-plugin-container-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52) ~[spring-plugin-container-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [int]: Factory method 'pluginEndpoints' threw exception; nested exception is java.util.ConcurrentModificationException
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        ... 27 common frames omitted
Caused by: java.util.ConcurrentModificationException: null
        at java.base/java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719) ~[na:na]
        at java.base/java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:741) ~[na:na]
        at java.base/java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1047) ~[na:na]
        at fr.polytech.al3.PolyVilleActive.PluginConfig.registerMvcEndpoints(PluginConfig.java:81) ~[classes!/:0.0.1-SNAPSHOT]
        at fr.polytech.al3.PolyVilleActive.PluginConfig.pluginEndpoints(PluginConfig.java:54) ~[classes!/:0.0.1-SNAPSHOT]
        at fr.polytech.al3.PolyVilleActive.PluginConfig$$EnhancerBySpringCGLIB$$7f069b53.CGLIB$pluginEndpoints$2(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at fr.polytech.al3.PolyVilleActive.PluginConfig$$EnhancerBySpringCGLIB$$7f069b53$$FastClassBySpringCGLIB$$c0fdb9ae.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        at fr.polytech.al3.PolyVilleActive.PluginConfig$$EnhancerBySpringCGLIB$$7f069b53.pluginEndpoints(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
        ... 28 common frames omitted

Support constructor injection in extensions (implementation of ExtensionPoint)

Hi,

first of all: great framework, great possibilities to override all factory methods for custom needs.

However I encountered a "problem" using pf4j-spring. "Problem", because one could workaround this with ease, but this breaks with conventions.

So what am I talking about?

Background

Dependency injection can be achieved by

  • constructor injection
  • setter injection or
  • field injection.

These are in fact not specific to spring, they're just all ways Java gives us to deal with dependent classes.

Although they seem to be equally qualified, they're not. The convention is to use them in certian situations:

  1. Constructor injection, when dependencies are mandatory
  2. Setter injection, when dependencies are optional
  3. Field injection, not recommended at all (NPEs, testability, see: http://olivergierke.de/2013/11/why-field-injection-is-evil/)

Relation to pf4j-spring

And here we come back to pf4j-spring. Currently pf4j-spring only supports the latter two (setter and field injection) as it calls the default constructor of the extension class right before checking the autowire flag (see code below).

public <T> T create(Class<T> extensionClass) {
T extension = createWithoutSpring(extensionClass);
if (autowire && extension != null) {

protected <T> T createWithoutSpring(Class<?> extensionClass) {
try {
return (T) extensionClass.newInstance();

This missing support forces user of this framework to break with conventions and not use constructor injection for mandatory dependencies in classes that implement ExtensionPoint.

Workarounds for the users

The easy way

As a user of pf4j-spring one can avoid using constructor injection for mandatory dependencies. This will break with mentioned convention, but works.

The convention following way
As a user of pf4j-spring one can override <T> T create(Class<T> extensionClass) in anonymous or subclass to support the desired behaviour (did I mention the great extensibility possibilities?). As a down side of this approach each user has to come up with an own solution.

Anyways, it is my opinion that following the convention should be provided by this framework by default.

Anything to code for you?

No, I will provide a pull request.

Sneak peek
As a sneak peek the PR rewrites SpringExtensionFactory.java (but it retains the current overall structure and extensibility) and uses springs beanFactory.autowire(extensionClass, AUTOWIRE_CONSTRUCTOR, ...)to solve the mentioned problem.

It will also slightly change the HelloPlugin of demo plugin2 in order to demonstrate the new possibiliy.


Stay tuned for updates.

Best regards
M Schröer

Could not make the demo app work

Hi,

I have downloaded the demo app and imported it into Ecplipse. Then I executed the Boot class using the VM parameter -Dpf4j.mode=development.

The exception bellow appears when the application starts (the first line in the log):

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'greetings': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.List org.pf4j.demo.Greetings.greetings; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.pf4j.demo.api.Greeting] found for dependency [collection of org.pf4j.demo.api.Greeting]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Then I can see the two plugins being started:

WelcomePlugin.start()
WELCOMEPLUGIN
2018-03-18 23:44:40,778 DEBUG org.pf4j.AbstractPluginManager - Fire 'PluginStateEvent [plugin=welcome-plugin, newState=STARTED, oldState=CREATED]' to 'org.pf4j.DefaultExtensionFinder@731f8236'
2018-03-18 23:44:40,779 INFO org.pf4j.AbstractPluginManager - Start plugin '[email protected]'
2018-03-18 23:44:40,779 DEBUG org.pf4j.DefaultExtensionFactory - Create instance for plugin 'org.pf4j.demo.hello.HelloPlugin'
HelloPlugin.start()

And then I see log traces saying that no extensions were found, so the greeting messages are not being printed.

Do you have an idea of what I could be forgetting?

Once I get it working I'll test Service, Controller and RestController, I guess I will be able to use it with the information provided in other issues.

Thank you very much.

Communication between plugins

Hi decebals,

I'm trying to enable communication between two plugins. For example, i would like, when there is an incomming message on my plugin 1 to notify plugin2 and finally plugin2 to give a response to plugin1.
When i would like send a message to plugin2, i was stuck as i don't know or i haven't any known method of plugin2 in my plugin1.

So, may be i've to extend the plugin class to create my custom plugin and extends it on all plugin i need to enable communication by implementing two methods:

  • notifyIncomingEvent()
  • sendResponse()

This is what i try to achieve:

public class Main extends Plugin implements MainManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(Core.class);

    private SpringPluginManager pm;
    private String message;
    List<PluginWrapper> plugins;

    public Main(PluginWrapper wrapper) {
	    super(wrapper);

	    LOGGER.info("Core instanciation");
	    message = "Incoming message";
	    pm = (SpringPluginManager) wrapper.getPluginManager();
	    
    }

    @Override
    public void start() throws PluginException {
	    super.start();

	    LOGGER.info("Core succesfully started");
	    plugins =  pm.getPlugins();
	    //plugins.forEach(p->LOGGER.info("some id " + p.getPluginId()));
	    fromOut(message);
    }

    public void fromOut(String message) {
	    LOGGER.info("incoming message => " + message);
	    /*send data to in plugin*/
	    
	    plugins.forEach(p -> {
		    if (p.getPluginId().equals("in-plugin")) {
			    // HERE NOTIFY PLUGIN2
                p.notifyIncomingEvent(message);
		    }
	    });
    }

}

For you is it the good way ?

extension.idx cannot be found - Spring Boot maven plugin

When running spring boot application with spring-boot-maven-plugin the plugin manager can find the plugin but it cannot find the extension.idx. Simply running java -jar from command line works without the problem.

This causes that you are unable to use spring-boot-maven-plugin meaning that your development process is slowed down.

After loading the plug-in for pf4j spring:Caused by: java.lang.NoClassDefFoundError: com/devsecops/center/rest/Dpcrest

com/devsecops/center/rest/Dpcrest is the package that invokes Maven dependencies in the plug-in. I tried to invoke this dependency in other plug-ins and add plug-ins in the same way without any problems, which puzzled me.

java.lang.IllegalStateException: Failed to introspect Class [com.devsecops.plugin.service.impl.MainServiceImpl] from ClassLoader [org.pf4j.PluginClassLoader@44351f3f]
	at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481)
	at org.springframework.util.ReflectionUtils.doWithLocalMethods(ReflectionUtils.java:321)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.buildResourceMetadata(CommonAnnotationBeanPostProcessor.java:405)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.findResourceMetadata(CommonAnnotationBeanPostProcessor.java:363)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:332)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBean(AbstractAutowireCapableBeanFactory.java:318)
	at org.pf4j.spring.SpringExtensionFactory.create(SpringExtensionFactory.java:60)
	at org.pf4j.ExtensionWrapper.getExtension(ExtensionWrapper.java:37)
	at org.pf4j.AbstractPluginManager.getExtensions(AbstractPluginManager.java:572)
	at com.devsecops.center.utils.PluginManagerUtil.getExtensions(PluginManagerUtil.java:67)
	at com.devsecops.center.utils.PluginManagerUtil.instanceConfig(PluginManagerUtil.java:155)
	at com.devsecops.center.utils.PluginManagerUtil.installPlugin(PluginManagerUtil.java:143)
	at com.devsecops.center.api.v1.SystemAPI.uploadPlugin(SystemAPI.java:51)
	at sun.reflect.GeneratedMethodAccessor101.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1598)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoClassDefFoundError: com/devsecops/center/rest/Dpcrest
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.getDeclaredMethods(Class.java:1975)
	at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463)
	... 62 more
Caused by: java.lang.ClassNotFoundException: com.devsecops.center.rest.Dpcrest
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at org.pf4j.PluginClassLoader.loadClass(PluginClassLoader.java:126)
	... 66 more
public class WelcomePlugin extends SpringPlugin {

    public WelcomePlugin(PluginWrapper wrapper) {
        super(wrapper);
    }

    @Override
    public void stop() {
        super.stop();
    }

    @Override
    public void start() throws PluginException {
        super.start();
    }

    @Override
    protected ApplicationContext createApplicationContext() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
        applicationContext.register(PluginConfigurtion.class);
        applicationContext.refresh();

        return applicationContext;
    }
}

How to update the ‘List<Greeting>’ while some plugins unloaded and deleted?

Hi,

Could you take a look at this questions?

Question: How to update the ‘List’ bean while some plugins are unloaded/deleted?

public class Greetings {

  @Autowired
  private List<Greeting> greetings;

  public void printGreetings() {
    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.greeting());
    }
  }

}
  1. At the begining, there're 3 extensions found
Found 3 extensions for extension point 'api.Greeting'
>>> custom
>>> Plugin2Greeting
>>> Plugin1Greeting
  1. And I'd like to unload plugin-1 and try to replace it with a new plugin latter
  @GetMapping("uninstall/{pluginId}")
  public Flux<String> uninstall(@PathVariable String pluginId) {
    pluginManager.stopPlugin(pluginId);
    pluginManager.unloadPlugin(pluginId);
    return Flux.just("ok");
  }

  1. unfortunately, there're still be 3 extensions printed after the plugin-1 is unloaded.....

So how to update the bean 'List' during runtime, or any samples illustrate how to integrate with pf4j-update?

Thanks.

MessageProvider is null

This is the SpringConfiguration

@Configuration
public class SpringConfiguration {

    @Bean
    public MessageProvider messageProvider() {
        return new HelloMessageProvider();
    }
    @Bean
    public SpringPluginManager pluginManager() {
        return new SpringPluginManager();
    }

    @Bean
    @DependsOn("pluginManager")
    public Greetings greetings() {
        return new Greetings();
    }

}

This is the main method

 public static void main(String[] args) {
    // print logo
    printLogo();

    // retrieves the spring application context
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);

    MessageProvider messageProvider = applicationContext.getBean(MessageProvider.class);

    // retrieves automatically the extensions for the Greeting.class extension point
    Greetings greetings = applicationContext.getBean(Greetings.class);
    greetings.printGreetings();

    // stop plugins
    PluginManager pluginManager = applicationContext.getBean(PluginManager.class);
    /*
    // retrieves manually the extensions for the Greeting.class extension point
    List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
    System.out.println("greetings.size() = " + greetings.size());
    */
    pluginManager.stopPlugins();
}

However the MessageProvider is null 。 Help plz

Concrete bean could not be received from the application context

Hello,

I'm making a spring boot application using Wicket and PF4J. I want to use spring services in my plugins. These services are not using services outside the plugin class loader. In my plugin, I can have wicket panels. The panel class can use the Spring service.

Here the class of the plugin:

public class MyPlugin extends SpringPlugin implements MyPluginConfiguration {

	public MyPlugin(PluginWrapper wrapper) {
		super(wrapper);
	}

	@Override
	protected ApplicationContext createApplicationContext() {
		final AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
		applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
		applicationContext.register(SpringConfiguration.class);
		applicationContext.refresh();
		return applicationContext;
	}

	@Override
	public Panel getConfigurationPanel(String wicketId) {
		return new ConfigurationPanel(wicketId);
	}

}

Its interface:

public interface MyPluginConfiguration {

	Panel getConfigurationPanel(String wicketId);
}

The SpringConfiguration class:

@Configuration
public class SpringConfiguration {

	@Bean
	public LabelService getLabelService() {
		return new LabelService();
	}
}

The configuration panel:

public class ConfigurationPanel extends Panel {

	@SpringBean
	private LabelService labelService;

	public ConfigurationPanel(String id) {
		super(id);

		add(new Label("text", labelService.getLabel()));
	}

}

And the service:

@Service
public class LabelService {

	public String getLabel() {
		return "Hello World from the plugin.";
	}
}

To display the configuration, i'm using this page in my application:

public class PluginTestPage extends WebPage {

	@SpringBean
	private PluginManager pluginManager;

	public PluginTestPage() {
		final PluginWrapper pluginWrapper = pluginManager.getPlugin("example");
		if (pluginWrapper.getPlugin() instanceof MyPluginConfiguration) {
			final MyPluginConfiguration pluginConfiguration = (MyPluginConfiguration) pluginWrapper.getPlugin();
			add(pluginConfiguration.getConfigurationPanel("configuration-panel"));
		} else {
			add(new Label("configuration-panel", "Can't get the configuration panel"));
		}
	}

}

When I'm asking the PluginTestPage, I have this error:

org.apache.wicket.WicketRuntimeException: Can't instantiate page using constructor 'public com.example.pages.PluginTestPage()'. An exception has been thrown during construction!
	at org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:194) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:67) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.DefaultMapperContext.newPageInstance(DefaultMapperContext.java:90) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.core.request.handler.PageProvider$Provision.getPage(PageProvider.java:369) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.core.request.handler.PageProvider.getPageInstance(PageProvider.java:170) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.request.handler.render.PageRenderer.getPage(PageRenderer.java:78) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.request.handler.render.WebPageRenderer.respond(WebPageRenderer.java:231) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.core.request.handler.RenderPageRequestHandler.respond(RenderPageRequestHandler.java:202) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.request.cycle.RequestCycle$HandlerExecutor.respond(RequestCycle.java:917) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.request.RequestHandlerExecutor.execute(RequestHandlerExecutor.java:63) ~[wicket-request-9.0.0.jar:9.0.0]
	at org.apache.wicket.request.cycle.RequestCycle.execute(RequestCycle.java:283) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:254) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:222) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:276) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:207) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:300) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Caused by: java.lang.reflect.InvocationTargetException: null
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) ~[na:na]
	at org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:175) ~[wicket-core-9.0.0.jar:9.0.0]
	... 33 common frames omitted
Caused by: java.lang.IllegalStateException: Concrete bean could not be received from the application context for class: com.example.pf4j_wicket_spring.services.LabelService.
	at org.apache.wicket.spring.SpringBeanLocator.lookupSpringBean(SpringBeanLocator.java:277) ~[wicket-spring-9.0.0.jar:9.0.0]
	at org.apache.wicket.spring.SpringBeanLocator.locateProxyTarget(SpringBeanLocator.java:198) ~[wicket-spring-9.0.0.jar:9.0.0]
	at org.apache.wicket.spring.injection.annot.AnnotProxyFieldValueFactory.getFieldValue(AnnotProxyFieldValueFactory.java:150) ~[wicket-spring-9.0.0.jar:9.0.0]
	at org.apache.wicket.injection.Injector.inject(Injector.java:111) ~[wicket-ioc-9.0.0.jar:9.0.0]
	at org.apache.wicket.spring.injection.annot.SpringComponentInjector.inject(SpringComponentInjector.java:124) ~[wicket-spring-9.0.0.jar:9.0.0]
	at org.apache.wicket.spring.injection.annot.SpringComponentInjector.onInstantiation(SpringComponentInjector.java:130) ~[wicket-spring-9.0.0.jar:9.0.0]
	at org.apache.wicket.application.ComponentInstantiationListenerCollection$1.notify(ComponentInstantiationListenerCollection.java:38) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.application.ComponentInstantiationListenerCollection$1.notify(ComponentInstantiationListenerCollection.java:34) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.util.listener.ListenerCollection.notify(ListenerCollection.java:80) ~[wicket-util-9.0.0.jar:9.0.0]
	at org.apache.wicket.application.ComponentInstantiationListenerCollection.onInstantiation(ComponentInstantiationListenerCollection.java:33) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.Component.<init>(Component.java:682) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.MarkupContainer.<init>(MarkupContainer.java:185) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.markup.html.WebMarkupContainer.<init>(WebMarkupContainer.java:53) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.markup.html.WebMarkupContainer.<init>(WebMarkupContainer.java:45) ~[wicket-core-9.0.0.jar:9.0.0]
	at org.apache.wicket.markup.html.panel.Panel.<init>(Panel.java:67) ~[wicket-core-9.0.0.jar:9.0.0]
	at com.example.pf4j_wicket_spring.panels.ConfigurationPanel.<init>(ConfigurationPanel.java:25) ~[na:na]
	at com.example.pf4j_wicket_spring.MyPlugin.getConfigurationPanel(MyPlugin.java:40) ~[na:na]
	at com.example.pages.PluginTestPage.<init>(PluginTestPage.java:30) ~[classes/:na]
	... 38 common frames omitted

Is it a mistake with my settings ?

Regards,
Stef

Spring web application with pf4j-spring?

Relying on the demo application I created project where main module based on the Spring Boot. Main module runs plugin's extension. Extensions realised some API. Generally, my project is like a bunch of pluin1 and app from demo project. Now it works like demo project, but throught Spring Boot.

My project is a web application.
I want to realise this case:

  1. I create Spring Controller in my plugin, annote it with @RestController, map requests with @RequestMapping("/pluginControllerPath")

  2. From base module I load this plugin and start it.

  3. After running my aplication I expect to see not empty response after request to the http://hostname/pluginControllerPath. But now I get 404 Not Found.

In total, the task is sharing beans between plugin and base application. Plugin should be able to autowire base application's beans. Plugin's classes marked with @RestController must work as http controller, after they are loaded into the base application.

SpringBoot: Error when re-enabling plugin after being disabled: AnnotationConfigApplicationContext - exception

In combination with the PF4J-update project, I am using pf4j spring for Spring Boot.

I am trying to create a REST-API for plugin management in Spring Boot, where plugins can be installed, uninstalled, enabled and disabled at runtime.

I have been able to successfully load a plugin & start from the repository at runtime. For these calls, I have been using pluginManager.disablePlugin("hello-plugin") & pluginManager.enablePlugin("hello-plugin").

Before the enablePlugin & after executing disablePlugin of the "hello-plugin" I am able to call the
PluginManager.getExtensionClasses(Greeting.class) there's is no problem.

Then I try to re-enable the plugin again with the enablePlugin("hello-plugin") method. It all works, however when I want to access the "hello-plugin" with the pluginmanager.getExtensionClasses() I get the following error:

java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext@6df20ade has been closed already

With this object PluginState state = pluginManager.startPlugin(mPluginId)
I can see that my "hello-plugin" has started, but I am not able to access it via the getExtension methods from the pluginManager.

This is my SpringConfiguration:

@Bean
public SpringPluginManager pluginManager() {
    return new SpringPluginManager() {
        @Override
        protected PluginClasspath createPluginClasspath() {
            return isDevelopment() ? new GradleDevelopmentPluginClasspath() : new DefaultPluginClasspath();
        }
    };
}

@Bean
@DependsOn("pluginManager")
public UpdateManager updateManager() {
    return new UpdateManager(mPluginManager);
}

Currently, what works for me to "enable" a plugin after disabling the plugin is to uninstall the plugin and then re-install the plugin. But that maybe not be the best option as you may want to save the current state of the plugin.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.