pf4j / pf4j-spring

Plugin Framework for Spring (PF4J - Spring Framework integration)
Apache License 2.0
346 stars 105 forks source link

PluginClassLoader issue with loading duplicate spring classes #52

Open jamesmmchugh opened 4 years ago

jamesmmchugh commented 4 years ago

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:

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?

decebals commented 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.

jamesmmchugh commented 4 years ago

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?

decebals commented 4 years ago

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).

jamesmmchugh commented 4 years ago

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.

jamesmmchugh commented 4 years ago

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.

decebals commented 4 years ago

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!