Skip to content
New issue

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

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

Already on GitHub? Sign in 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants