4

I have a maven multimodule project that has one parent pom-project and a bunch modules. One of these modules is the "main module" that has all the libraries shaded into it. All other modules depend on that module and use the provided libraries.

The main module is a Bukkit plugin that loads the other modules as extensions. These extensions are loaded all with their own classloader, but the loaded classes are shared between the loaders to be able to depend on each other. They are also able to depend on other Bukkit plugins, as their parent classloader is Bukkit's PluginClassLoader that also shares the loaded classes between plugins to allow interaction.

That's where the problems start: Different plugins may use the same library, but the classes of that library might get loaded by different classloaders which causes LinkageErrors and other problems.

My idea to solve that problem was to relocate the libraries in the main module via maven-shade-plugin. That works as expected with libraries that are only used by the main module. However relocating libraries used by the other modules causes runtime ClassNotFoundExceptions, because the modules still search for the normal package name instead of the relocated one. Then I tried to change the imports to the relocated packages, but my IDE (IntelliJ) doesn't find the classes.

Has anyone an idea on how to solve this relocation problem? Or maybe different approaches on the classloading issue?

pschichtel
  • 759
  • 7
  • 18

2 Answers2

3

5 years later in a very similar context (Bukkit -> SpongeApi) I encountered this problem again, but this time I found the (probably only satisfying) solution:

  1. The main module had its shaded version as the main artifact, so dependents could only see relocated classes and were unaware of the original classnames. This made no difference in our case, as the main module is a provided dependency anyway, but it also prevents consumers from accidentally using relocated classes directly. IntelliJ does not care for the relocations, so it was unaware of the new relocated classes. Attaching the shaded version as a secondary artifact (shadedArtifactAttached option set to true) makes the dependencies visible to the dependents again.

  2. The dependent modules have to apply the same relocation rule as the main module, so the plugin corrects the classnames to the ones available at runtime.

This way IntelliJ is not aware of the relocations but it also doesn't need to be aware. If necessary, the relocations can be configured in a parent pom for consistant rules across all projects.

pschichtel
  • 759
  • 7
  • 18
1

I had almost exactly the same problem you have/had (judging from the age of this question). Although I don't have a cleaner solution for the libraries overriding other plugins' versions, I do have a workaround for IntelliJ not recognizing relocated classes.

To stop it from complaining, I added the shaded jar (with the relocations) as IntelliJ library to the target module. You can do this like so:

  1. Go to File > Project Structure... > Modules > (target module) > Dependencies
  2. Select the shaded jar using Add (green +) > 1. Jars or directories....
  3. You should now see the shaded jar in the libaries list

screenshot detailing above explanation

Although it seems to work at first glance, this solution workaround has a few caveats:

  • Non-relocated classes are still visible to code through Maven's module dependency and if you happen to use them, you'll only see that upon compiling with Maven. (You could remove the module dependency, but it gets readded every time you reimport your pom)
  • You'd have to update the jar path every time you change your project's version if you include a version number in your jar file name (Workaround: Specify a static project.build.finalName)
  • When you add new methods or change signatures, you need to compile the library module again. (This can be worked around by creating a separate module for shading dependencies - That would actually also resolve the file name issue)
Philipp Nowak
  • 130
  • 11
  • I haven't tried to do this in a while now. By now the project has been heavily restructured. The main module mentioned in my question is now a separate maven project and the rest of the module are separated into 3 different projects (modules-main, modules-extra, modules-prototypes) and a separate shared parent without any dependency declarations. So the modules inherit their dependencies form the core dependency instead of the parent. I think relocating should be possible now. I still like the solution you mentioned for IntelliJ – pschichtel Aug 20 '14 at 09:51