javamaven

Interfacing with legacy transitive dependencies


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

Solution

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