Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

PluginClassLoader issue with loading duplicate spring classes #52

Open
jamesmmchugh opened this issue Aug 21, 2020 · 6 comments
Open

PluginClassLoader issue with loading duplicate spring classes #52

jamesmmchugh opened this issue Aug 21, 2020 · 6 comments

Comments

@jamesmmchugh
Copy link

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?

@decebals
Copy link
Member

@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 pf4j/pf4j#392.

In the latest versions of PF4J was introduced ClassLoadingStrategy. For more information see pf4j/pf4j#385.

@jamesmmchugh
Copy link
Author

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
Copy link
Member

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
Copy link
Author

jamesmmchugh commented Aug 21, 2020

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
Copy link
Author

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
Copy link
Member

decebals commented Aug 21, 2020

Take a look at #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!

@Mishin870 Mishin870 mentioned this issue Sep 30, 2020
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants