Why does Eclipse turn a maven runtime dependency into a compile dependency? - eclipse

I have a maven project imported into Eclipse Oxygen. Eclipse reports no compile issues (Alt + F5). When I run maven from the command line I get
[ERROR] /home/dean/src/TAP3UIs/TAP3Desktop/src/main/java/com/ms/tap3/controller/RequestAccessController.java:[8,30] package com.google.common.base does not exist
That package does exist in my .m2/repository in guava-15.0.jar. I can also see it in Eclipse mvn dependencies. When I check the mvn dependency:tree for the project I see
[INFO] | | | +- com.google.guava:guava:jar:15.0:runtime
It is a runtime transitive dependency on the command line, which explains why it doesn't compile on the command line. Somehow Eclipse has turned a transitive dependency from runtime to compile.
Does anyone know why this happens and how I make Eclispe m2e respect the scope of the transitive dependencies?

Currently, neither JDT nor m2e support multiple classpaths per project which is required to support different scopes.
See: Eclipse bug 486035 - Different classpath containers for different scopes
Update:
Since Eclipse Photon (4.8) which was released in June 2018 this is now supported. See Eclipse bug 526858 and my video showing this in action.

The main point is this: If you import external classes in your source code, you must set them as compile dependencies, and never trust the fact that they might already be transitive dependencies (because, since they are transitive, you have not direct control over them, so in a future version they might diseappear as well).
What is happening is this:
You need some classes from com.google.common.base package, so you need to set com.google.guava:guava:jar:15.0 as a dependency.
Instead, you didn't because you realised it was already a transitive dependency, but you missed the fact that is a runtime dependency.
Eclipse M2 does not distinguish the different Maven's standard classpaths, so it treats all dependencies as if they were of "compile" scope. So Eclipse includes guava-15.0.jar in the compilation and the project is compiled OK.
Maven, instead, won't include a runtime dependency in the compilation phase, so a compile error is raised.
In brief: You should include guava-15.0 (as well as any other artifact your code needs) as a direct dependency (with compile scope) in your pom file.

Related

Understanding eclipse maven dependency hierarchy

I want to understand the dependencies for a multi-module maven project and for that referred to eclipse dependency hierarchy.
I did understand fairly, however some of the things I am not able to understand at all.
Below is the screen shot.
The things which I didn't understand are:
--> managed from 1.0.2 [Compile}
--> managed from 1.0.2 (omitted for conflict with 1.0.0) [Compile]
I did search online but I got information in traces. Can anyone help me understand what they mean in easy to understand?
Thanks.
Maven builds a flat classpath from the dependency tree each for compiling ([compile]), for testing, and for running.
In a flat classpath, unlike OSGi, a dependency can only exist in one version. In your cropped screenshot, there is on the second level among other things:
kafka-streams 1.0.2 and
kafka-clients 1.0.0.
kafka-streams 1.0.2 requires kafka-clients 1.0.2 which conflicts to kafka-clients 1.0.0. Therefore kafka-streams 1.0.2 is omitted for conflicts with 1.0.0 even if the version 1.0.2 is required here ("managed from 1.0.2").
More detailed:The classpath which is used to compile or run a plain Java application is flat: all required libraries are globally specified as an ordered list. It is not possible to use a library of a specific version for one package and for another package the same library in a different version.In Maven dependencies builds a tree: each dependency might have its own dependencies. Maven maps the tree of dependencies to the classpath, an ordered list of libraries. If in the Maven dependencies tree the same library exists in different versions, it is not possible to create a flat classpath. This is a conflict.This conflict is resolved by picking one version and omitting all other versions. At the place where the picked version is used instead of the required version, (managed from <required but not picked version>) and (omitted for conflict with <picked version to use instead>) is displayed.In addition, Maven can create different classpaths to compile, to test or to run a Java application via so-called scopes. The [compile] scope is the default scope for using a library in all tasks: compiling, testing and running.
Make sure that the versions specified in the pom.xml file are compatible with each other (which is not yet the case in your screenshot): you have to upgrade kafka-clients from 1.0.0 to 1.0.2 (or downgrade the other libraries).

Intellij sbt won't resolve dependencies' dependencies

I have a Intellij Scala project, that depends on another lib, which in turn depends on some other libs (gson, log4j...), but somehow Intellij won't add those transitive dependencies to my project, so when I run my main method, jvm always complains no class found. I also tried sbt update and sbt update-classifier, they won't resolve down the dependency path as well. How can I have sbt resolve all levels of dependencies?
If all works in SBT command lines but not in Idea then remove the .idea folder and reload the SBT project in Idea.
I should warn that you will loose the project configs (such as run configs) by doing that.
I finally figure out what was wrong, it was the sbt-s3-resolver plugin I was using couldn't resolve maven style dependencies, I used an another plugin which supports maven libs and it worked. But I still couldn't figure out why it worked before.

How to find unused sbt dependencies?

My build.sbt has a lot of dependencies now. How do I know which dependencies are actually being used?
Maven seems to have dependency:analyse http://maven.apache.org/plugins/maven-dependency-plugin/
Is there something similar for sbt?
There is the sbt-explicit-dependencies plugin, which has been developed recently. It has direct commands in the SBT console to:
Enforce explicit direct declaration of dependencies, thus disallowing transitive dependencies.
Detect and remove unneeded dependencies.
you can use sbt-dependency-graph plugin. it shows dependencies in different graphical representations. also you can try to use tattletale, but it's not integrated with sbt. it'll require you to copy managed dependencies (retrieveManaged := true). this tool not only shows dependency graph, but analyzes class usage and can display unused dependencies (including transitive)

How to find the parent of a Maven transitive dependency

I am building a Java (web) application with Maven and Eclipse.
When I look inside my .war file I can see the following logging libraries there:
log4j-1.2.14.jar
log4j-1.2.17.jar
slf4j-api-1.7.5.jar
slf4j-log4j12-1.7.5.jar
I did not declared these libraries in my pom.xml, so they probably are transitive dependencies (i.e. dependencies of my dependencies).
How can I find out which of my dependencies depend on these libraries?
I tried to use the mvn dependency:tree plugin, but it does not show any of these .jars.
In Eclipse, the Java Resources > Libraries > Maven Dependencies node does not show them either. Though, curiously, it shows other transitive dependencies of my project.
If you want to rely on maven only you may want to take a closer look on the dependency plugin, here are two examples:
mvn dependency:tree -Dverbose will display more detailed information - especially for example if a artifact will be omitted for conflicting with another artifacts version (e.g. convergence issue). It will also display you the hirachy with all the transitive dependencies.
To have a specific artefact analyzed (to for example find who delivers a specific transitive dependency) you can specify like so:
mvn dependency:tree -Dincludes=com.my.group.id:my-artefact-id:jar:1.0.1 -Dverbose
(Where you obviously need to adjust the artefact, packaging type and version according to your needs)
Open pom.xml in Eclipse and go to Dependency Hierarchy tab
![enter image description here][1]
It should show you the dependency tree in the Dependency Hierarchy tab. Evgeniy Dorofeev is right.
Something similar to the screenshot attached
you would probably have to expand all.
EDIT: Refined the answer.
Thats a little weird. But here is what i found.
If you go to http://mvnrepository.com/artifact/log4j/log4j/1.2.17
it will show you which dependencies are used and which does the jar file depend on.
The springframework dependency for web-mvc 3.2.4 would download the spring-core and the spring-core dependency uses log4j.

How to get rid of org.hamcrest.core_1.1.0.v20090501071000.jar?

I have a project with unit tests. I'm using Maven and m2e. With Maven from the command line, I see:
[INFO] | +- org.springframework:spring-test:jar:3.1.3.RELEASE:test
[INFO] | \- junit:junit-dep:jar:4.10:test
[INFO] +- org.mockito:mockito-core:jar:1.9.0:test
[INFO] | +- org.hamcrest:hamcrest-core:jar:1.2.1:test
[INFO] | \- org.objenesis:objenesis:jar:1.0:test
mvn install works, all the tests run. Good.
Now I try to run the unit tests from within Eclipse using m2e 1.2.0 and I get this error:
java.lang.SecurityException: class "org.hamcrest.DiagnosingMatcher"'s signer information does not match signer information of other classes in the same package
at java.lang.ClassLoader.checkCerts(ClassLoader.java:806)
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:487)
...
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
which puzzles me. The dependency tree in m2e looks good. But when I open the properties of the process, I get a modified classpath which contains these additional entries:
.../eclipse/3.7.2/eclipse/plugins/org.junit_4.8.2.v4_8_2_v20110321-1705/junit.jar
.../eclipse/plugins/org.hamcrest.core_1.1.0.v20090501071000.jar
Since these JARs are signed, the tests fail. How can I get rid of these two entries? Or how can I make sure they are added late in the classpath (i.e. after my version of JUnit + Hamcrest)?
I found a workaround.
The reason for the error is that Eclipse's JUnit launcher adds the JUnit and Hamcrest bundle to the classpath. These two are signed Orbit bundles while the JARs from Maven are not signed.
Hamcrest 1.2 contains more classes than Eclipse's Hamcrest 1.1. If Hamcrest 1.1 is loaded first, Java will expect all classes in the package org.hamcrest to share the same cryptographic signatures. This isn't true and there is no way to fix that.
The error goes away when I manually add a dependency via "Java Build Path" or in the "Classpath" tab of the launch configuration.
In these places, I can make sure that the JARs from .m2/repository/ appear first in the classpath (before the JDT JUnit runner adds the JARs from Eclipse's plugins folder) so Java will never see / try to load the bundles from Orbit.
In the launch configuration, it looks like this:
But I'd prefer if Eclipse didn't mess with my classpath :-(
There seem to be two possibilities:
You've got junit on the classpath already. In Project Properties->Java Build Path, Libraries tab, do you have multiple junit libraries on the path, that is have you added the junit library to the project. If so, remove it and try again.
The more likely cause is your usage of hamcrest 1.2. This is not compatible with JUnit 4.10 (or 4.8 for that matter). JUnit pre-4.11 included some of the hamcrest 1.1 classes, so you can get incompatibility problems. Your best bet is to upgrade to 4.11, which doesn't include the hamcrest libraries, but has hamcrest 1.3 as a transitive dependency. If you can use hamcrest 1.3 do that, if not, 1.2 should work.