Open jamesmmchugh opened 4 years ago
@jamesmmchugh See documenttion about plugin ClassLoader. By default, PF4J uses child (plugin) first class loading strategy. I think that in your case you need to switch to parent (application) first class loading strategy. For introduction in problem, you can take a look on https://github.com/pf4j/pf4j/issues/392.
In the latest versions of PF4J was introduced ClassLoadingStrategy
. For more information see https://github.com/pf4j/pf4j/pull/385.
How is it meant to behave if your plugin uses the same class (but a different version) internally, from one being used and loaded by the application?
Your case (error) is particular:
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')
I don't think that you want to use a different version of Spring (core/context) in app and plugins. I think that scope of the Spring library/dependency should be "provided" in plugins.
In general I say that it's normal to have different versions of the same library in multiple plugins but I don't think that this library could be Spring (core/context) because it is part of framework (pf4j-spring).
I think I see the issue, because the SpringPlugin class belongs to the application's ClassLoader, and i'm implementing the createApplicationContext
method for it, but i'm trying to start an application context with a different version of Spring (provided by the plugin's class loader).
I think for my use case I should actually avoid using pf4j-spring, as my plugin is an executable spring application also but I want the possibility for the version of spring to diverge between the plugin and the application (hence why I don't want Spring libraries to be provided by the application). I should do what spring-cloud-function-deployer does and implement the plugin as a standard plugin which starts the plugin spring application reflectively (i.e. https://github.com/spring-cloud/spring-cloud-function/blob/8dee0b94c70735b2c05731924650e89ad28eb526/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java#L253)
Thanks for the advise.
Is there any facility in PF4J already to handle spring-boot executable jars as plugins - given they are packaged differently from a standard JAR? If not perhaps this would be a nice extension in the future, and also starting the spring-boot executable plugin reflectively as per the example above.
Take a look at https://github.com/pf4j/pf4j-spring/issues/48. Unfortunately I don't use Spring Boot in my projects, so I cannot help you. Maybe you can work together with @fabioformosa to a PR. Thanks!
I have three projects consisting of:
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:
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?