NoClassDefFoundError when Maven test-scoped dependency overrides transitive compile-scoped dependency - java

We have several deployables, and have extracted common code into several libraries. Deployable app A has a (default-scoped) Maven dependency on library B, and a test-scoped dependency on library C.
I made a change to library B, which used a method from, and so created a dependency on, library C. This caused app A to fail at run-time with NoClassDefFoundErrors. App A's unit tests all passed, of course, so we only found this in our staging environment. (Other apps either had no direct dependency on library C, or had a default-scoped dependency on it, and so continued to work.)
The obvious fix worked, of course: I edited A's pom.xml to make the dependency on C default scope.
My question is: Are we doing something wrong? Or is this how things are supposed to work? Is anyone aware of an automated way of detecting the potential problem at build-time and failing the build?

You are not doing something wrong. Maven made a peculiar design choice here. See also
Maven: test vs. transitive compile, especially Tunakis comment on my question.
and
Maven dependency within dependency with different scope
My advice: Eliminate C from A's pom.xml. Only add test dependencies if these dependencies are not already on the dependency tree.

Related

How does the library dependency is inherited/works in maven?

I have gradle scala project(Lets say "A") which uses the library(Lets say "X"). And i have used this Library-"X" in the build.gradle of Project-A as shown below and i'm using the Library-X classes to perform some operations.
build.gradle of Project-"A"
implementation "com.x.y.z.stone:central-dl-spark-common-integration-logging:0.4.0-SNAPSHOT"
Now Project-"A" is itself a library which is used in the Project-"B" and this Project-B is maven build. Now when i call the Project-A function which is acting as library in-turn uses Library-X function. But Project-B is complaining that class not found error as shown below. This is the Library-X class
java.lang.NoClassDefFoundError: com/x/y/z/integrationlogging/IntegrationLogging$
My question is do we even need to declare the Library-X in the pom.xml of Project-B as well?
No: if Project-B imports Project-A, and Project-A has an internal dependency on Library-X, you usually don't need to do anything and it will resolve automatically.
Some exceptions may include conflicts and libraries that exist in a private nexus. For the former, you need to resolve the conflict in the pom.xml. For the latter, you need to make sure your repo is accessible from wherever you're compiling.
No.
Dependencies are transitive "by default" unless the opposite is specificied (either with exclusions or scopes (a test scoped dependency won't be transitively propagated for instance)).
EDIT: you can use mvn dependency:tree to get a better understanding of your dependencies.
One possibility is that library X is pulled in different incompatible versions.
Thank you for the having look at my post. By adding the Library-X under tag in the Project-B pom.xml, worked.Something shown below
<artifactSet>
<include>x.y.stone:central-dl-spark-common-integration-logging</include>
</artifactSet>

maven: include indirect dependencies or not?

Let's say I have a maven project that depend on one external library libA. And libA depends on libB. In my pom.xml, I declare libA as a dependency and all is well.
Not let's assume that during dev, I start using classes from libB too. libB is already pulled in the project through libA, so everything compiles fine and work, but should I also declare libB in my pom.xml?
If I don't, mvn dependency:analyze will complain that I use undeclared dependencies.
If I declare libB, maven will load it only once to avoid duplicates, so if I declare it after, it's as if I didn't, and if I declared it before, I risk forcing a version of libB incompatible with libA.
What's the better answer?
Do what Federico suggests. It is the Maven way.

fail gradle build if transitive dependency is used directly?

im looking for a way to fail my gradle build if my code directly uses (so imports) a transitive dependency.
what i mean is if my project has a (compile) dependency on module A, and module A depends on B (so B is in my transitive dependencies and available on my runtime classpath) and my code directly imports and uses classes from B, i want my build to fail.
here's a maven plugin that does what i want - https://github.com/Scout24/illegal-transitive-dependency-check - but i cant find a gradle one?
I think the 2 plugins below may be what you're looking for.
https://github.com/wfhartford/gradle-dependency-analyze
From the README:
This plugin attempts to replicate the functionality of the maven dependency plugin's analyze goals which fail the build if dependencies are declared but not used or used but not declared.
https://github.com/nebula-plugins/gradle-lint-plugin
On the Unused Dependency Rule wiki page:
Promotes transitive dependencies that are used directly by your code to explicit first order dependencies
I have to say I haven't used either one myself, but they seem to address your concern.
I think that you want to use the java library plugin and the api/implementation configurations.
For the legacy java plugin you could do
dependencies {
compile('group:module-b:1.0') { transitive = false }
}
This would force you to explicitly declare module-b's transitive dependencies if you need to use them.
Note: You'll probably get lots of ClassNotFoundException using module-b at runtime since there's now jars missing from your classpath. So this might be better
dependencies {
compile('group:module-b:1.0') { transitive = false }
runtime 'group:module-b:1.0'
}

Different scopes of the same artifact and transitive dependencies issue

I have a project A that use library L v1.0.0 with test scope. Project A also depends on project B (with scope compile), with B transitively depending on the library L v1.0.0 (with scope compile).
Why the final scope of the library L for project A is 'test'? It causes me NotClassDefFoundError at runtime. It seems that the dependency definition of project A on library L overrides those of the transitive dependencies on L.
What's wrong here? My project A only uses L for unit tests so I define the dependency with 'test' scope. But, at the end, I want L to be on my classpath since project A depends on project B for production, and B needs (transitively) library L.
Thanks for helping me
Are you using Maven? In that case, if I remember correctly Maven will use the "nearest" definition to determine the actual scope. In this case module A specifies test and the transitive scope from B is overridden, because A is nearest since you're actually in A :)
This gets more complicated when you have multiple modules with dependencies between them.
A common cure is to define all dependencies (and scopes and versions) in a common parent Pom.xml in the <dependencyManagement> tag.
As an alternative to Peter's suggestion, just leave L out of the dependencies for A. You should be able to access it anyway, and Maven will treat it as a compile-scoped dependency.
This hides that A's tests depend on L, though.

Maven: How do you deal with dependencies that are both direct and transitive?

I'm trying to determine an approach to the following situation:
There are 3 Maven artifacts: A, B, and C.
B depends on A. (i.e. it uses some of A's code)
C depends on both A and B (i.e. it uses some of A's code and B's code).
Assume I want to use the same version of A for both B and C.
What approach should be used?
1) Declare A as a dependency in C's pom.xml.
Pro: It's clear to the developer that C depends on A.
Con: If A's version changes, it needs to be updated in multiple places. (both B and C)
2) Don't declare A as a dependency in C's pom.xml.
Pro/Con: Opposite of option 1.
I think you should have all direct dependencies declared in your pom. Transitive dependencies are just a convenience for automagically resolving your dependencies dependencies.
If you change a version of a direct dependency, the transitive dependencies will likely change along with it, and thus potentially breaking the module. The module should build as an independent unit and thus should have well defined dependencies that will not break due to external changes.
I disagree that this violates the DRY principal, as maven defines things within the confines of a single project and its pom. And within this scope there is no repetition.
Update:
The reliance on transitive dependencies existing makes the project frail on it's own, and may also lead to more complex issues like when to include it.
For example, if C has a compile dependency on A, but a runtime dependency on B, then you now have to either add the dependency (since it is no longer in your build path) or declare B as compile even though it isn't. There is a lot to be said for clarity. Explicitly define what your dependencies are and what their scope is, and expect your dependencies to do the same. For the most part, your dependency is a black box, until it causes problems and you have to open it.
1) Declare A as a dependency in C's pom.xml.
The dependency is readableDependency is flexible. If you want to remove A's dependency from B, you do not need to think of the projects that dependend on B.As suggested in other answer, that it is a good practice to write down direct dependencies in pom.xml and let maven handle it.
2) Don't declare A as a dependency in C's pom.xml.
Mostly, no developer going to see pom.xml. And if they want they can see it by using mvn dependency:tree and it will show transitive dependency.There will be single point of change when a new version of A is released. If you define dependency at more than one place, you may forget to update all the places. In that case, Maven automatically uses the latest one. But, it does sting sometimes. Some people prefer this because, mostly, this type of dependency is common knowledge (e.g. MyWebApp -> MyWebAppLib -> MySharedLib and MyWebApp -> MySharedLib) and they want to avoid added step of updating versions at multiple places on each release.
I have written down pros-and-cons, you should evaluate what suits you the best yourself.
Edit#1: tsk! I have switched my comments.
Edit#2: updated the answer after a discussion done on this answer.

Categories