Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interfacing with legacy transitive dependencies

Tags:

java

maven

I'm using Java 17 with maven 3.9.9. My maven project libfoo is used as a library by other end-user applications say myapp.jar.

libfoo has no choice but to depend on a bar:bar-core:1.2.3 whose code is out of my control (e.g. a proprietary client SDK), and 1.2.3 is the latest version. That artifact from hell is known to brings legacy transitive dependencies e.g.

  1. vulnerable version of logback and jackson-databind (evil) and
  2. common-zoo:common-zoo:0.0.1 (evil), with myapp.jar itself depends on common-zoo:common-zoo:5.9.0 (good)

My question is that, is there any technique I can use, to seal all the evilness within libfoo.jar itself as implementation details of libfoo, such that

  1. myapp sees libfoo as a single uber jar with 0 transitive dependency
  2. myapp.jar is not awared of evil classes on its runtime classpath, and links only to dependencies declared in its own pom, e.g. common-zoo:5.9.0.

Known problems with shade+relocate:

  1. bar-core is known to use Class.forName('com..XXX'), thus relocating class with maven-shade-plugin breaks the code.
  2. shading all dependencies is error-prune
like image 998
SedriX Avatar asked Mar 14 '26 06:03

SedriX


2 Answers

Not unless you do class loader magic.

Creating a library uber jar with external dependencies is usually a bad idea because you will have two versions of the same class on the classpath (from your uber jar, taken from common-zoo:common-zoo:0.0.1, and the one from common-zoo:5.9.0 ) and it is essentially random which version gets loaded and used.

The way around it is shading, but you said that breaks the code.

You can, though, set specific dependencies to scope runtime, meaning that they are not on the compile classpath for myapp.jar. But this only works if myapp.jar does not try to use a different (newer) version of the same library.

like image 155
J Fabian Meier Avatar answered Mar 16 '26 21:03

J Fabian Meier


You can have a look at how maven manages to deal with ancient plugins and their dependencies. It encapsulates those by using plexus-classworlds:

The Classworlds model does away with the hierarchy normally associated with ClassLoaders. Instead, ClassWorld provides a pool of ClassRealms which can import arbitrary packages from other ClassRealms. Effectively, Classworlds turns the old-style hierarchy into a directed graph.

In your case you would create one classrealm to contain all your normal library stuff. And you create another realm for everything of bar:bar-core:1.2.3 and the dependencies it brings along. In this scenario it might be beneficial to have a facade encapsulating bar-core.

However since you are creating a library I'm not sure how well you can hide the use of plexus-classworlds or if the consumers of your library need to adapt (at least a bit) as well.

like image 24
SpaceTrucker Avatar answered Mar 16 '26 20:03

SpaceTrucker



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!