If I include the following J2EE dependency in my application, I have access to servlet and JAX-RS classes and interfaces:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
But where are they coming from? On Maven Central I see that javax:javaee-api:7.0 has as a dependency javax:javaee-web-api:7.0, which in turn has as dependencies javax.servlet:javax.servlet-api:3.1.0 and javax.ws.rs:javax.ws.rs-api:2.0, so on the face of it, that's the answer.
But all those dependencies are marked as optional, which means that as transitive dependencies they wouldn't show up for my project unless I explicitly include them. But yet my program compiles simply with a dependency to javax:javaee-api:7.0. Why?
Here is my dependency tree; I don't see where they are coming in:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # temp-server ---
[INFO] com.example:test-server:war:1.0.0-SNAPSHOT
[INFO] +- com.google.code.findbugs:jsr305:jar:3.0.1:provided
[INFO] \- javax:javaee-api:jar:7.0:compile
[INFO] \- com.sun.mail:javax.mail:jar:1.5.0:compile
[INFO] \- javax.activation:activation:jar:1.1:compile
(This is all basic stuff; I don't know why I'm confused. I must be missing something obvious.)
As JAX-RS 2.0 is part of Java EE 7, the classes defining its API are directly included into the artifact javax:javaee-api:7.0 so it is enough to compile your program as long as you use standard classes only.
Here, you need to understand what is meant by <scope>provided</scope>. The javax.servlet:javax.servlet-api:3.1.0 and javax.ws.rs:javax.ws.rs-api:2.0 will be included with scope as provided when including the javaee-api dependency. What this means is that, ONLY while compiling your classes, these jar files will be used and NOT at runtime. Since the scope is provided, it expects that these 2 jars will be given by the runtime environment. To re-iterate, when any jar is included with scope as provided, it means this jar should be used for compilation, but for runtime, it expects to be provided by the runtime container.
Now to answer your question, if you consider any runtime environment container such as JBOSS, WAS, etc, they all come bundled with these jars. So at the time of compiling your classes, it will make use of the jars that come as transitive dependencies to javaee-api, but at run time it will make use of the jars that came bundled with the container instead. Hence you don't get any error.
Related
Should maven compile time dependency be necessarily packaged for access during runtime ?
Let's assume that there is a maven pom file for a library A.
On that, maven dependency tree command is run.
And in the output, we see the following line:
[INFO] +- commons-pool:commons-pool:jar:1.3:compile
This means that, commons-pool.jar should be available during compilation.
Does this imply that, necessarily, commons-pool.jar should be packaged alongside library A, inorder for access during runtime ?
So the answer is yes, as Alanpatchi said.
In Maven compile dependencies are also runtime dependencies.
I deliberately wanted to see if maven allows duplicate direct dependencies (i.e. non transitive dependencies) as it uses dependency mediation to resolve the conflicting transitive dependencies.
I wrote this is in my pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
I was surprised to see, it is picking 4.10 version instead of 4.11. What could be reason for this ?
I will add more details that shows it violates dependency mediation principle too. Here is output of mvn dependency:tree for only standalone junit 4.10 version VS standalone junit 4.11 version:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # PracticeMaven ---
[INFO] org.example:PracticeMaven:jar:1.0-SNAPSHOT
[INFO] \- junit:junit:jar:4.10:test
[INFO] \- org.hamcrest:hamcrest-core:jar:1.1:test
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # PracticeMaven ---
[INFO] org.example:PracticeMaven:jar:1.0-SNAPSHOT
[INFO] \- junit:junit:jar:4.11:test
[INFO] \- org.hamcrest:hamcrest-core:jar:1.3:test
If you see there is a conflict with org.hamcrest:hamcrest-core when we mention both junit in pom.xml. It should have chosen org.hamcrest:hamcrest-core:jar:1.3 as 4.11 is mentioned first. But it does not. It choses junit 4.10 and org.hamcrest:hamcrest-core:jar:1.1.
I am using latest Apache Maven 3.6.3.
Why were you surprised to see this?
It is probably always taking the last one. Avoid this if possible (the only use case I could see is to alter a dependency that was defined in a parent POM).
I'm not sure how accurate this information still is, but:
When declaring a "normal" version such as 3.8.2 for Junit, internally
this is represented as "allow any-thing, but prefer 3.8.2." This means
that when a conflict is detected, Maven is allowed to use the
conflict algorithms to choose the best version. If you specify
[3.8.2], it means that only 3.8.2 will be used andnothing else. If
somewhere else there is a dependency that specifies [3.8.1], you would
get a buildfailure telling you of the conflict. We point this out to
make you aware of the option, but use it sparingly and only when really
needed. The preferred way to resolve this is via dependencyManagement
Source: Maven: The Complete Reference, page 35
So, usually given a version without a range (using round or square brackets) is just hint what you like to use, not something Maven has to obey.
A typical Java project has 30-40 jar files. Maven resolves dependencies. Sometimes some features are no longer needed. But no one touches the JARs with the fear of breaking the code or application.
For example: jersey (JAX-RS) implementation with JAXB has many workarounds, different Maven configurations.
Different developers sometimes employ different methods to do the same thing. All are correct. I agree they should discuss out. But the problem is that unused JARs make the build package heavy.
Isn't there a plugin which can map (use reflection) and check for one time (one project life cycle) and mark unwanted or unused JARs. This will use java reflection and may get slow but this is needed.
Any solutions?
Maven helps to find out unused dependencies with dependency plugin.
mvn dependency:analyze
its displays the unused dependencies as follow
[INFO] --- maven-dependency-plugin:2.8:analyze (default-cli) # mymaven ---
[WARNING] Unused declared dependencies found:
[WARNING] org.apache.commons:commons-lang3:jar:3.0:compile
I have dependency:
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.10</version>
</dependency>
And run a test that includes testing a website for form submission. Works great. I package this up into my local repo mvn install and include it in another wider project.
Doing a mvn dependency:tree -Dverbose -Dincludes=net.sourceforge.htmlunit doesn't seem to reveal anything untoward
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # myproject ---
[INFO] com.myproject:myproject:war:1.0-SNAPSHOT
[INFO] \- com.myproject:myproject:mp:jar:1.0:compile
[INFO] \- net.sourceforge.htmlunit:htmlunit:jar:2.10:compile
[INFO] \- net.sourceforge.htmlunit:htmlunit-core-js:jar:2.10:compile
So I don't think any of the other deps are causing an issue. Yet if I copy/paste a test from the base project into the wider one, I always get errors:
java.lang.NoSuchMethodError: com.gargoylesoftware.htmlunit.html.HtmlPage.getElementById(Ljava/lang/String;)Lcom/gargoylesoftware/htmlunit/html/HtmlElement;
I've made sure my IDE isn't caching some dodgy library somewhere so as far as I can tell it's clean. Any ideas?
Thanks
Problem is you have this class coming from some other artifact, since you verified it is not directly coming from htmlunit with different version, it must be shaded in some other jar
for jvm to get pick up correct class please put htmlunit dependency on top of dependency so it will be first in classpath taking priority
How do I use maven or another tool to find which dependency of a dependency which provides a particular jar? Sometimes they're three or four dependencies deep.
If you want to find out from where a transitive dependency is coming from for a given project, then the Maven Dependency Plugin is indeed your friend. Use it with the includes parameter that allows to specify a comma-separated list of artifacts to filter the serialized dependency tree by, or null not to filter the dependency tree. The artifact syntax is defined by StrictPatternIncludesArtifactFilter.
About the syntax, the javadoc writes:
The artifact pattern syntax is of the
form
[groupId]:[artifactId]:[type]:[version]
Where each pattern segment is optional
and supports full and partial
* wildcards. An empty
pattern segment is treated as an
implicit wildcard.
For example, org.apache.* would
match all artifacts whose group id
started with org.apache., and
:::*-SNAPSHOT would match all
snapshot artifacts.
Here is an example (I want to find from where the activation artifact is coming from on a project):
$ mvn dependency:tree -Dincludes=:activation::
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Java EE 6 Demo - Petstore - Domain
[INFO] task-segment: [dependency:tree]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:tree {execution: default-cli}]
[INFO] name.thivent.samples.javaee6.domain:domain:jar:1.0-SNAPSHOT
[INFO] \- org.hibernate:hibernate-validator:jar:4.0.2.GA:runtime
[INFO] \- javax.xml.bind:jaxb-api:jar:2.1:runtime
[INFO] \- javax.activation:activation:jar:1.1:runtime
[INFO] ------------------------------------------------------------------------
...
M2Eclipse provides a nice front-end to the dependency:tree if you are using it.
For something "closer" to rpm --whatprovides (i.e. without searching for a particular project), you would have to use a repository search engine. Here is an example for activation-1.1.jar (see the This artifact is used by ... section).
I suppose you are looking for:
mvn dependency:tree
Edit: There are more options available to analyze dependencies. Have a look at the documentation
Even for direct dependencies in Java it is at least a challenge. Not all dependencies are static and could be reflected from the class file. And even for those classes there's no way to get the correct library that is needed to meet that dependancy.
We have for example the Class.forName(String className) way of adding dynamic dependencies and the class name could be written in a resource or properties file or constructed at runtime. And the named class then could introduce a dependenciy to a different library.
I doubt that any tool is capable of resolving all those dependencies.
This isn't a direct answer but it might help if you are using Eclipse with a Maven plugin.
If I want to find where a class comes from, I do the following:
Select the class name and use the F3 shortcut to load the source code into the Editor window.
Use "Show In > Package Explorer" to locate the file in (typically) the Maven Dependencies section of the build path.
Look at the JAR filename of the enclosing dependency "folder", and read off the group id, artifact id and version.
(This might not be 100% reliable in a multi-module project if you use Eclipse Maven's nested module support. But I've not encountered problems in practice.)