Why order of Maven dependencies matter? - spring

I thought that the order of Maven dependencies doesn't matter before and regard this as a pro of it. And this is my old pom.xml's dependencies:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.19</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.19</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.19</version>
</dependency>
</dependencies>
It works well, and today I wanna move spring dependency to the bottom so that those jersey related can be together. However then I can no longer make it working, my Jetty complains:
[ERROR] Failed to execute goal org.eclipse.jetty:jetty-maven-plugin:9.3.0.M1:run (default-cli) on project mtest: Execution default-cli of goal org.eclipse.jetty:jetty-maven-plugin:9.3.0.M1:run failed: A required class was missing while executing org.eclipse.jetty:jetty-maven-plugin:9.3.0.M1:run: org/apache/commons/logging/LogFactory
That is really confusing, so do I have to concern about dependencies order? How do I know the correct order?

The order of dependencies does matter because of how Maven resolves transitive dependencies, starting with version 2.0.9. Excerpt from the documentation:
(...) this determines what version of a dependency will be used when multiple versions of an artifact are encountered. (...) You can always guarantee a version by declaring it explicitly in your project's POM. (...) since Maven 2.0.9 it's the order in the declaration that counts: the first declaration wins.

To expand upon the other answer (which states that the declaration order affects Maven's dependency mediation for transitive dependencies), there are a few tools you can use:
mvn dependency:tree [-Dscope=[runtime|test]] will show you what dependencies will be available for the selected scope. See here for details
mvn dependency:build-classpath gives you order in which dependencies are available on your classpath (if two or more classpath entries have the same class, the earlier one wins). See here for details
I don't know much about your situation, but it's often the case that you wind up with the wrong version of 1 or more jars at compile/runtime. Declaring your own version of the library in question or locking down the version with <dependencyManagement> are options here.
Now to answer your other question - how do you know what the right order is when declaring dependencies?
My suggestion - the right declaration order is the one that gets you the versions of the dependencies you want, in the order you want them in. Use the tools above to check your dependencies, and tweak the declared order if necessary.
Note that most jars contain disjointedly-named classes, so the exact order in which jars appear on your classpath is usually not that important. The only exception I've noticed is some jars in SLF4J which intentionally shadow classes from the other logger libraries it's intended to replace.

Related

How does maven resolve the dependencies of the main dependencies on which our application is build?

I am trying to understand maven a little more. How is maven able to download the dependencies of the main dependency of the application? For example assuming my application has main dependency like this:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.0</version>
<scope>provided</scope>
</dependency>
Now, when maven downloads this jar , it downloads the dependencies for this jar as well. For example, see the screen shot below:
As can be seen, maven has not only downloaded the hadoop-hdfs-2.7.0.jar but also all it dependencies.
Now, my questions is how maven knows what are the dependencies for the "top-level" dependency, that is in this case the "top-level" dependency is hadoop-hdfs, so what all jars it has to download for this?
I see this as well in the .m2/respository for hadoop-hdfs:
I opened the .pom file, the contents are (partly):
<project>
....
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.0</version>
<description>Apache Hadoop HDFS</description>
<name>Apache Hadoop HDFS</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-auth</artifactId>
<scope>provided</scope>
</dependency>
<dependencies>
...
</project>
What is this hadoop-hdfs-2.7.0.pom ? Does this file give information to maven what are the dependencies to be downloaded for hadoop-hdfs-2.7.0.jar?
Can anyone help me clear these things?
First of all you are right, the hadoop-hdfs-2.7.0.pom tells Maven
about the libraries that hadoop depends upon. But, when using hadoop
as a dependency in your project, maven uses the below strategies to
finalize the list of dependencies in addition to using the
hadoop-hdfs-2.7.0.pom.
If a dependency is specified with groupid, artifactid and version in the current project under the dependencies tag, it takes the first
precedence. This is how hadoop-hdfs got added in your project.
Dependency Management takes the next precedence. When a dependency is specified only with group and artifact id's under dependencies tag
but at the same time, the dependency is defined under
dependencyManagement tag with version and transitively inside hadoops pom.xml also,
the one under the dependencyManagement tag will be given preference.
Dependency Mediation takes the last precedence. Dependencies are resolved using dependency mediation. Meaning, in your case the
dependencies mentioned inside hadoop-hdfs-2.7.0.pom are the transitive
dependencies (indirectly depends on these dependencies since your
dependency "hadoop-hdfs" requires it) of your project and this process continues
recursively until all child dependencies are resolved.
Note: There are other features such as excluding dependencies, marking
one optional and importing a list of dependencies. But they are used
sparsely. More information with examples can be found in the below URL
[https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management][1]

Remove extra and unnecessary dependencies from spring boot application [duplicate]

I have a large Maven project with many modules and many pom.xml files. The project has changed and I suspect the pom's contain some unnecessary dependencies. Is there is a command which removes any unused dependencies from a pom?
The Maven Dependency Plugin will help, especially the dependency:analyze goal:
dependency:analyze analyzes the dependencies of this project and determines which are: used and declared; used and undeclared; unused and declared.
Another thing that might help to do some cleanup is the Dependency Convergence report from the Maven Project Info Reports Plugin.
You can use dependency:analyze -DignoreNonCompile.
This will print a list of "used undeclared" and "unused declared" dependencies (while ignoring runtime/provided/test/system scopes for unused dependency analysis.)
But be careful while using this:
As some libraries used at runtime are considered unused!
For more details refer to this link
As others have said, you can use the dependency:analyze goal to find which dependencies are used and declared, used and undeclared, or unused and declared. You may also find dependency:analyze-dep-mgt useful to look for mismatches in your dependencyManagement section.
You can simply remove unwanted direct dependencies from your POM, but if they are introduced by third-party jars, you can use the <exclusions> tags in a dependency to exclude the third-party jars (see the section titled Dependency Exclusions for details and some discussion). Here is an example excluding commons-logging from the Spring dependency:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.5</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
Have you looked at the Maven Dependency Plugin ? That won't remove stuff for you but has tools to allow you to do the analysis yourself. I'm thinking particularly of
mvn dependency:tree
I had similar kind of problem and decided to write a script that removes dependencies for me. Using that I got over half of the dependencies away rather easily.
http://samulisiivonen.blogspot.com/2012/01/cleanin-up-maven-dependencies.html
You can use DepClean https://github.com/castor-software/depclean/
DepClean is a tool to automatically remove dependencies that are included in your Java dependency tree but are not actually used in the project's code.
You can use dependency_cleaner https://github.com/junaidbs/dependency_cleaner
This jar will help to identify and remove unwanted dependency from pom.
It will automate the process of Removing a dependency and run then check whether the dependency needful
If you are using eclipse, right-click on the jar in Maven Dependencies:
Select Maven -> Exclude Maven Artifact...

Force latest version for maven dependencies

I have the following dependency (only so far) pom.xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.6.2.RELEASE</version>
</dependency>
This dependency obviously depends on other "dependencies" via it's pom.xml... when maven finished downloading these dependencies I noticed that it didn't grab the latest version of the Spring stuff (4.0.6.RELEASE)... it grabbed a 3.2.x version.
How can I force maven to grab the latest version of the Spring stuff? Do I need to explicitly modify my pom.xml to include all the dependencies or is there some "magic" I can use for this?
Thanks.
Spring "Bill Of Materials"
Salvation may come from special "bill of materials" POMs supported by Maven and published by Spring. Quoting from Maven "Bill Of Materials" Dependency in their manual:
It is possible to accidentally mix different versions of Spring JARs when using Maven. For example, you may find that a third-party library, or another Spring project, pulls in a transitive dependency to an older release. If you forget to explicitly declare a direct dependency yourself, all sorts of unexpected issues can arise.
To overcome such problems Maven supports the concept of a "bill of materials" (BOM) dependency. You can import the spring-framework-bom in your dependencyManagement section to ensure that all spring dependencies (both direct and transitive) are at the same version.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.0.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Would this work for you?
Looking at the spring-data-jpa artifact pom file, we can see that it has a parent called spring-data-parent with current version 1.4.2.RELEASE. It's pom describes dependencies and their versions. Currently spring version is at 3.2.10.RELEASE
One way you can possibly accomplish what you want is to add explicit dependency on spring artifacts. But you would still have to define their versions.

Maven same dependency on different scopes

I have a maven (3) project with multiple submodules. One on of them, I want it to explicitly depend on a dependency, without depending on its transitive dependencies. To do this, I'm using the folloing:
<dependency>
<groupId>org.example</groupId>
<artifactId>foo</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
This works as itended. During runtime, the transitive dependencies of "foo" will already be in the classpath, so everything goes well.
However, I need "foo"'s transitive dependencies for test. I tried to also declare a dependency of "foo" with scope test, but it seems that it conflicts with the one that excludes everything. It either works as expected in tests but fails in runtime, or vice-versa.
Do you known if something like this is possible with maven?
Of course it's possible, you need to declare 2 different maven profiles holding your dependencies.
http://maven.apache.org/guides/introduction/introduction-to-profiles.html
Different dependencies for different build profiles in maven
You should check out Maven's <scope>import</scope> dependencies. Create a <profile/> for testing and define a dependency to a pom which contains these test dependencies you're interested in. (This might require some re-working of your pom.xml-s, but would be a good approach).
Check here for more details.

Change scope to provided generates compile errors

I had a dependency declared as followed:
<dependency>
<groupId>org.jboss.spec</groupId>
<artifactId>jboss-javaee-6.0</artifactId>
<version>${version.jboss.javaee6}</version>
<type>pom</type>
</dependency>
When I change the scope to provided I get compilation errors such as EJB cannot be resolved to a type. I didn't understand, the documentation says that dependencies declared as provided are still used at compile time, and discarded only at deploy time.
So can someone help me understand these compile errors?
<dependency>
<groupId>org.jboss.spec</groupId>
<artifactId>jboss-javaee-6.0</artifactId>
<version>${version.jboss.javaee6}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
I think the problem is related to the type that you have mentioned in the dependency of that artifact.
Till now whatever I have played with Maven I cannot think of any case when one would need to add pom type dependency. Generally pom type packaging is used for parent module in a project (specify common project configuration like plugin versions, common dependencies, like log4j for example, repositories, properties etc.) and for utility package module (the one that assembles the project and does some other necessary things).
So, as a suggestion remove the type tag from the dependency until you need it for any specific purpose, let it be default i.e jar.

Resources