I'm working on a project that builds and deploys fine. I'm trying to add some code that uses JWebUnit, and use the following Maven code to bring it in:
<dependency>
<groupId>net.sourceforge.jwebunit</groupId>
<artifactId>jwebunit-htmlunit-plugin</artifactId>
<version>3.2</version>
<scope>test</scope>
</dependency>
Maven seems to resolve this fine and it's bringing everything in (I'm using Intellij, and it now appears under 'Dependencies' in the 'Maven Projects' tab, and also under 'External Libraries' in the Project tab).
However, when I bring this dependency in, the IDE is not able to find it (e.g. if I use import net.sourceforge.jwebunit.junit.WebTester, it can't find it).
But an even bigger issue is it actually breaks some existing code -- I have some JUnit tests that use org.apache.commons.httpclient.HttpClient, and now on Maven's install goal I get a
NoClassDefFoundError - Could not initialize class for that class.
If I remove the JWebUnit dependency, the Maven install goal exits successfully.
I'm used to seeing errors about dependency version convergence when bringing new dependencies, and I feel like chasing this 'no class def found' error could be a red herring, but I'm not sure of the general types of issues in Maven that could be causing it.
EDIT: the dependency code for pulling in HttpClient is:
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
jwebunit-htmlunit-plugin includes transitive dependencies which seem like they're conflicting with some of your dependencies (likely because they are different versions).
Maven puts classpath priority on artifacts declared earlier. Try moving jwebunit to the end of your dependencies section, or at least after where you pull in the httpclient classes. Alternatively, you can manually exclude certain transitive dependencies from being pulled in, but this can be tedious.
As for your IDE not allowing imports on the library, remember that you have this declared in the test scope. Production classes cannot see test dependencies.
Related
I have a Java 11 project embedding Tomcat:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>10.1.0</version>
</dependency>
The Tomcat-specific code is in a subproject with only two classes. When I compile using Maven 3.8.6 and Java 17 using -Xlint:all, I see the following warning for that subproject:
[WARNING] Cannot find annotation method 'value()' in type 'aQute.bnd.annotation.spi.ServiceConsumer': class file for aQute.bnd.annotation.spi.ServiceConsumer not found
Doing a bit of searching brings up similar (but not exact) things, such as Lombok Issue #2145, which hints that I may need to add some sort of extra dependency such as biz.aQute.bnd:bndlib or org.osgi:osgi.annotation. But even after adding those dependencies, the warning remains.
Where is this error coming from, and what does it mean? I don't have any #ServiceConsumer annotation in my source code, and I couldn't find any in the Tomcat classes I'm extending, either. How can I make it go away?
I filed Tomcat Bug 66299.
I discussed this on the Tomcat users mailing list users#tomcat.apache.org (thanks Mark), and here's what is happening:
Tomcat effectively has two builds:
What I call the Tomcat "primary build" uses Ant with build.xml, which compiles the source files, creates all the JARs and binaries, and publishes them to Maven Central (Nexus).
Any "secondary build" by third parties using the published JARs and POMs, using e.g. org.apache.tomcat.embed:tomcat-embed-core:10.1.0 with Maven.
The latest versions of direct dependencies are found in the Tomcat repository inside build.properties.default.
The primary build generates JPMS and OSGi metadata, so some classes are annotated with the bnd annotation aQute.bnd.annotation.spi.ServiceConsumer. Currently Tomcat gets this annotation from biz.aQute.bnd:biz.aQute.bnd:6.3.1, which is apparently some aggregate JAR; the same annotation can be found in the smaller biz.aQute.bnd:biz.aQute.bnd.annotation:6.3.1.
The aQute.bnd.annotation.spi.ServiceConsumer annotation source code uses the OSGi annotation org.osgi.annotation.bundle.Requirement. Currently this annotation can be found in org.osgi:osgi.annotation:8.1.0.
The bnd and OSGi annotations remain part of the compiled classes even though they are not used at runtime and are not technically needed in any secondary builds.
If you want to inform Maven and javac where these classes are so that they will not appear missing (if you are compiling with some variations of -Xlint), but that they nevertheless will not be needed at runtime (and technically aren't even needed at compile time in a secondary build) and should not be distributed in the resulting JAR, you can note them in your pom.xml file using the provided scope.
<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bnd.annotation</artifactId>
<version>6.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.annotation</artifactId>
<version>8.1.0</version>
<scope>provided</scope>
</dependency>
Maven will download these artifacts during your build thereby removing the warning, but they will not be included in the resulting artifacts of your build.
I have a multi-module Maven project, with modules 'app' and 'domain'. App depends on Domain. The domain module has public and private code. Public code is in the package **/domain/api/**.
In the pom of the Domain module, I added an execution for the maven jar plugin, which generates an additional project artifact with classifier 'api' (containing only the public classes).
In the App module, I added a 'runtime' scoped dependency on the Domain module (so all classes are present at runtime) and I added a compile-time scoped dependency on the classified 'API'. This is to ensure the App modules only access public api code from the domain.
Everything compiles and runs. I have checked the contents of the api artifact from the domain module and it contains only the code intended to be public.
But in IntelliJ I'm still able to use (import) domain code from private packages. IntelliJ seems to ignore the classifier part. So IntelliJ compiles when I reference private code (which is wrong IMHO) and the Maven build fails (which is good).
Is this an IntelliJ issue or am I missing something?
The relevant pom part from de App module:
<dependency>
<groupId>com.acme</groupId>
<artifactId>sbp-domain</artifactId>
<version>${project.version}</version>
<classifier>api</classifier>
</dependency>
<dependency>
<groupId>com.acme</groupId>
<artifactId>sbp-domain</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
Intellij has trouble with complex maven dependencies. Especially if you try to filter the original project.
I had the same problem with test-jar (the easy-way) as Intellij ignores the exclude. The IDEA-204719 tracks the progress.
khmrbase is right in the comments. You should create a separate project for your API. The only downside of that approach is that you have to duplicate (or further complicate your modules) for api-implementation shared test code. The solution for that could be the test-jar which as I explained doesn't work properly in Intellij.
I never worked with Maven before, but after having a lot of problems with mockito dependencies in my Netbeans module, I decided to create a new one using Maven and move everything to this new project. After some time doing so, I managed to get the module running just ok.
The problem is that when I use some functionalities that require the Netbeans API I get an exception.
My guess is that the problem is in the dependencies declared in pom.xml. In some of the imports that I use in my application, I get the following message:
Package from transitive module dependency referenced, declare a
direct dependency to fix.
When I clean and build or run mvn clean install in the root directory I get this error:
Project uses classes from transitive module
org.netbeans.api:org-netbeans-modules-projectapi:jar:RELEASE73 which
will not be accessible at runtime.
To fix the problem, add this module as direct dependency. For OSGi bundles that are supposed to be wrapped in NetBeans modules, use the
useOSGiDependencies=false parameter
In the org.codehaus.mojo plugin declaration (which was the only one that had the useOSGiDependencies tag) I tried to change useOSGiDependencies=true to useOSGiDependencies=false but that didn't work. I also tried to change RELEASE73 to RELEASE82, since I am using Netbeans 8.2 but that didn't work as well. I also tried a bunch of other possible solutions that I found on google, but none seemed to work for me.
These are the Netbeans api dependencies that I have in my pom.xml file:
<dependencies>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-netbeans-modules-project-libraries</artifactId>
<version>RELEASE73</version>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-netbeans-api-annotations-common</artifactId>
<version>RELEASE82</version>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-openide-util</artifactId>
<version>RELEASE82</version>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-openide-awt</artifactId>
<version>RELEASE82</version>
</dependency>
<dependency>
<groupId>org.netbeans.modules</groupId>
<artifactId>org-netbeans-core</artifactId>
<version>RELEASE73</version>
</dependency>
<!-- ... -->
</dependencies>
I looked everywhere but couldn't find a straightforward answer on how to declare a direct dependency.
Can anyone explain to me what am I doing wrong here?
After digging a bit more, I found that I had to add all the dependencies manually. This answer helped me a lot.
I just lack experience with maven. Basically I googled org-netbeans-modules-projectapi:jar:RELEASE73 maven entered the first link and added the needed dependency to my project.
I have tried to search web for the problem I am facing but maybe I am not asking google the right question so here I am.
I am using IntelliJ IDEA for my multi-module project. For one of my modules, one of the class file is using a static import -
import static javax.ws.rs.core.Response.Status.Family.familyOf;
Being a big project, there are a lot of dependencies downloaded from internal repo but for some reason IntelliJ refuses to use the dependency "javax.ws.rs-api-2.0" instead it is using "jersey-core-1.8". Because of this it is throwing a compilation error saying Cannot find symbol "familyof".
I looked into Response.java from both the dependencies and found that jersey dependency does not have familyof method while javax.ws.rs-api-2.0 has it but IntelliJ doesnt use this dependency. How do I fix this problem. Most of the developers in my team are using Eclipse and they do not have this problem. I am trying to get used to IntelliJ IDE but cant seem to figure a way out of this. Any help in this regard is much appreciated.
PS - This issue does not occur in Eclipse IDE.
I could resolve this issue by following the below mentioned steps -
Goto "Open Module Settings" Command+Down arrow key
Select Dependencies tab
Search for the above two dependencies in the list
Move "javax.ws.rs-api-2.0" dependency up to ensure this dependency is above "jersey-core-1.8" dependency.
I don't think this is a permanent solution but it seemed to work. if someone with in-depth knowledge of Java/Mave/IntelliJ has an answer to this question that would be great!
You may be able to get the right result every time with Maven by shuffling around the dependencies in the pom.xml, and make sure the dependency you want to take precedence is declared first in the list of dependencies. yes, the order in which the dependencies are declared in pom.xml matters !
Then, if all of you are using the same Maven version, you should have a consistent result.
How does Maven choose between two versions of a same dependency?
As explained here, Maven chooses the first met dependency that's why it works when you change the order of the dependency in the pom.
How to find Maven dependencies conflicts?
In a terminal, in the pom folder :
mvn dependency:tree -Dverbose | grep "conflict"
will give you all the dependencies in conflict in your project.
With Eclipse IDE, click on the pom and on the Dependency hierarchy tab. Then, fill the Filter field with a dependency. On the left side, you will see the conflicts (like with mvn dependendy:tree, with filtered results) and on the right side, the dependencies chosen.
With IntelliJ, the documentation of IntelliJ can help you. There is a diagram view to find the conflicts.
How to resolve Maven dependencies conflicts?
Add dependencyManagement tag in the pom to tell Maven which dependency you want
Example :
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependencies>
</dependencyManagement>
Change the order of the dependency in the pom
Add an exclusion of the dependency
Example :
<dependency>
<groupId>com.my.groupid</groupId>
<artifactId>my-artifact-id</artifactId>
<version>1.2.2</version>
<exclusions>
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</exclusion>
</exclusions>
</dependency>
Why is there a difference between IntelliJ and Eclipse?
You can add a dependency to a module without adding it to the pom as explained in the documentation of IntelliJ. It's probably possible with other IDE.
IntelliJ IDEA lets you add a Maven dependency to your project. We recommend that you specify the dependency inside your POM. Dependencies that you set up manually inside IntelliJ IDEA module settings will be discarded on the next Maven project import.
The Maven plugin in Eclipse (M2Eclipse) may load dependencies differently compared to mvn
Please, read the documentation of IntelliJ to configure the project dependencies easily.
I use the maven-enforcer-plugin to check for dependency convergence issues. A typical output would be:
[WARNING] Rule 1: org.apache.maven.plugins.enforcer.DependencyConvergence failed
with message:
Failed while enforcing releasability the error(s) are [
Dependency convergence error for junit:junit:3.8.1 paths to dependency are:
+-foo:bar:1.0-SNAPSHOT
+-ca.juliusdavies:not-yet-commons-ssl:0.3.9
+-commons-httpclient:commons-httpclient:3.0
+-junit:junit:3.8.1
and
+-foo:bar:1.0-SNAPSHOT
+-junit:junit:4.11
]
Seeing this message, I would normally "solve" it by excluding the transitive dependency, e.g.
<dependency>
<groupId>ca.juliusdavies</groupId>
<artifactId>not-yet-commons-ssl</artifactId>
<version>0.3.9</version>
<exclusions>
<!-- This artifact links to another artifact which stupidly includes
junit in compile scope -->
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
I'd like to understand whether this is truly a fix and the risks involved in excluding libraries in this fashion. As I see it:
The "fix" is normally safe, provided I'm choosing to use the newer version. This relies on the library authors maintaining backwards compatibility.
There is typically no impact on the Maven build (since the nearer definition wins), however by excluding the dependency I'm telling Maven that I know about this problem and thus appeasing the maven-enforcer-plugin.
Are my thoughts correct and is there an alternative way of handling this issue? I'm interested in answers that focus on the general case - I realise the junit example above is a little strange.
We all agree that JUnit should never be set to another scope than test. Generally speaking I don't think either that there is another solution than excluding the unwanted dependency, so we all agree that your are right to do it.
A SIMPLE CASE :
As Andreas Krueger says, there may be a risk with versions (I actually encountered it). Let say that the project's dependencies are the following:
+-foo:bar:1.0-SNAPSHOT
+-group1:projectA:2.0
+-group2:projectB:3.8.1
+-group2:projectB:4.11
Note that it is only a mere simplification of your case. Seeing this dependency tree, you would exclude the dependency projectB given by projectA :
<dependency>
<groupId>group1</groupId>
<artifactId>projectA</artifactId>
<version>2.0</version>
<exclusions>
<exclusion>
<groupId>group2</groupId>
<artifactId>projectB</artifactId>
</exclusion>
</exclusions>
</dependency>
After packaging the project with maven, the remaining dependency would be group2-someProjectB-4.11.jar, version 4.11 and not 3.8.1. Everything would be fine and the project would run without encountering any problem at all.
Then, a while after, let say that you decide to upgrade to the next version of project A, version 3.0 which adds new great features :
<dependency>
<groupId>group1</groupId>
<artifactId>projectA</artifactId>
<version>3.0</version>
<exclusions>
<exclusion>
<groupId>group2</groupId>
<artifactId>projectB</artifactId>
</exclusion>
</exclusions>
</dependency>
The problem is that you are not aware yet that projectA version 3.0 also have upgraded its dependency projectB to version 5.0 :
+-foo:bar:1.0-SNAPSHOT
+-group1:projectA:3.0
+-group2:projectB:5.0
+-group2:projectB:4.11
In that case, the exclusion you would have made a while ago excludes projectB version 5.0.
However, projectA version 3.0 needs the improvements from project B version 5.0. Because of the exclusion, after packaging the project with maven, the remaining dependency would be group2-someProjectB-4.11.jar, version 4.11 and not 5.0. At the moment you use any of projectA's new features, the program wouldn't run correctly.
WHAT WAS THE SOLUTION ?
I encountered this problem in a Java-EE project.
A team developped database services. They packaged it as projectA. Each time they updated the services, they also updated a file listing all their current dependencies and the current versions.
ProjectA was a dependency for the Java-EE project I was working on. Each time the service-team updated ProjectA, I also checked the versions' updates.
In fact, there is no harm in excluding a dependency. But each time you update a dependency where an exclusion has been set, You have to check :
if this exclusion still makes sense.
if you need to upgrade the version of the excluded dependency in your own project.
I guess maven exclusions are like kitchen knifes. It's sharp, cuts vegetables with no effort, but requires care when handling it...
If JUnit as an artifact is coming through as a dependency in compile scope, it is a bug of one of your libraries, here: ca.juliusdavies.
JUnit should always be included in test scope. Thus, it is not packed into the produced .jar, .war or .ear file, on successful build.
Generally speaking, there is no harm in excluding already included dependencies, as when library 1 and library 2 share one common dependency.
The only problem, of course, that can occur, is when library 1 and library 2 include different versions of the same dependent artifact. This can cause run-time errors, when the features of the library have changed.
Fortunately, this is not often the case, unless the difference in the version numbers is great. In general, it is advisable to include the latest dependency version and exlude the older one. This is most of the time viable.
If not, check wheter there are updates to the first-level dependencies of your project.