It's kind of hard to explain my problem with words, so I took a pic, which shows exactly what my problem is:
As you can see, I have 3 relevant modules, a global, genui and web.
genui depends on global and web depends on genui.
Directly, web doesn't depend on global, but through genui it obviously does.
As you can see, if I take a look at the hibernate version of web->genui->global, it is 5.2.14, but if I look at it from genui->global, it's 5.3.0.
There is not, and there have never been any versions of either of these modules other than 1.0-SNAPSHOT.
I tried cleaning, deleting the files from .m2/repository, and even tried purging the local repo completely, nothing worked. I have no idea where maven gets the 5.2.14 number, I don't remember ever putting that in. The version is received from a property in parent module.
I figured out that the problem is caused by the pom.xml of web module, more specifically this part:
<dependencyManagement>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
</dependencyManagement>
You probably need to exclude that repeated dependency from one of the spring ones... verify all your dependencies and look which are the ones that include hibernate. Check that you also have different versions for servlet-api.
Related
Context: in a project composed of 5 different applications and a 20 some packages, i try to update spring-boot (to 2.7.4) and only spring-boot, the project uses an internal enterprise artifactory, if that may have some impact. when i indicated that i need 2.7.4 some packages begun to use mongo-driver 4.6.2. Before it was 3.11.0. I went a head and updated it as well, by indicating in those pom.xml (where it was explicitly said 3.11.0 to use 4.6.2 instead). But that caused many unwanted errors (as some syntaxes have changed between those versions) So i went back to 3.11.0 and i explicitly indicated in all pom.xml that i want to use 3.11.0
The issue is that spring-boot now calls things in my mongodb-driver that are not there yet.
So the question is: can i have spring-boot 2.7.4. with mongodb-driver 3.11.0 (and i am just missing how to tell it to use it correctly) OR that those versions are the strongly coupled with 4.6.2 so i have no choice but update that as well?
Also i did tried to find (for 2 days now), and i am sure it should exist for something so widely used as spring, but not found an explicit list that says "to upgrade from X->Y version of dependancy Z, here is the list of syntax changes that need to be made.", does it and how to find that?
Sorry for no concrete pom.xml exemples i am not on my work PC now.
You can use the <exclusions> tag for this:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<exclusions>
<exclusion>
<!-- dependency you want to exclude (with version) -->
</exclusion>
</exclusions>
</dependency>
<dependency>
<!-- The version you want as a dependency -->
</dependency>
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I do have some random issues within my application. And I suspect that the problem may arise from external dependencies. Let me elaborate.
My application is using an external jar with requests classes. Inside one of my projects, I had added the newest version of this library. But as you probably suspect, I have more dependencies than just one. And unfortunately, one of these dependencies has the same library which I added already, but with an older version. So at this moment, when I am firing mvn dependecy:tree, simplified results look like that:
+-my_project
+-request_lib.jar:1.5.0
+-required_lib.jar:1.0.0
+-random.jar:1.0.0
+-request_lib.jar:1.2.0
//more libs
//more libs
As you can see, what I need is the newest version of request_lib.jar:1.5.0, and at this moment no one can update required_lib.jar:1.0.0, and change the version or request inside of it.
How does Java handle this situation? Lets say inside of this project I am using request GetPlayerDataRequest, and in the newest version someone added a new field to this called String playerTitle. Will Java always use classes from the newest version? Or will it be mixed?
Because from what I see, sometimes users have fatal errors, where in logs we can find that method setPlayerData does not exist. I know that I can exclude the old jar in the pom. But I would like to know how it is handled by Java.
You want to use Maven's <dependencyManagement> feature. This is one of the most misunderstood and poorly documented areas of Maven and yet also one of its most powerful features.
In your pom.xml, if you have a <dependencyManagement><dependencies> section, then anything that is a child of it will apply all the way down the dependency tree, regardless of where else that dependency may be specified. (You can also include non-existing entries; if they designate a dependency that is not actually used anywhere by you or any of your transitive dependencies, then they are ignored.)
So, for example, if you want to ensure that the following dependency is used everywhere in your project no matter how it is "pulled in" or by whom:
<dependency>
<groupId>com.foo</groupId>
<artifactId>bar</artifactId>
<version>32</version>
</dependency>
…then if you add that entry to your pom.xml's <dependencyManagement><dependencies> section—even if your project itself doesn't use it, but your transitive dependencies do—so that it looks like this:
<dependencyManagement>
<dependencies>
<!-- other entries might go here -->
<!-- force version 32 of com.foo:bar no matter how it gets pulled into your project -->
<dependency>
<groupId>com.foo</groupId>
<artifactId>bar</artifactId>
<version>32</version>
</dependency>
<!-- other entries might go here -->
</dependencies>
</dependencyManagement>
…then that will do the trick.
A related thing that is not exactly documented anywhere is: dependency versions in "regular" <dependencies> entries (so <dependency> elements that do not appear as children of <dependencyManagement><dependencies> but only as children of <dependencies>) are suggestions. They are used only if an overriding <dependencyManagement> entry does not exist (which many times, of course, it does not).
Maven applies a dependency mediation mechanism, guaranteeing that only one of the two versions of the dependency gets bundled. So, in this case, Java doesn't have to handle anything really.
Relevant documentation: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
Java allows you to load different versions of the same class by using classloaders.
Using your example, you would load classes from request_lib.jar using one classloader, and required_lib.jar from another.
The topic is too extensive for a stackoverflow answer, there are many tutorials online.
This example is a hypothetical scenario based on a real-world problem. Consider a block taken from a pom.xml file as given below. In this scenario, I want to exclude artifact-b dependency from artifact-a and reimport it with a different version. Obviously, excluded artifact-b version is not equal to ${product2.version}.
<dependency>
<groupId>com.company.product1</groupId>
<artifactId>artifact-a</artifactId>
<version>${product1.version}</version>
<exclusions>
<exclusion>
<groupId>com.company.product2</groupId>
<artifactId>artifact-b</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.company.product2</groupId>
<artifactId>artifact-b</artifactId>
<version>${product2.version}</version>
</dependency>
Thus, when mvn install command is executed for the final application, I will only have the artifact-b with ${product2.version} under the folder where the application stores the jars collected from its dependencies.
This is the expected behaviour for my module, but does it also mean that the methods in artifact-a jar will now make calls to the artifact-b jar with ${product2.version}? If so (which is what I expect since target repository does not include the excluded version), what happens if the called method in artifact-b is different in the new version? Does it only check method signatures or are there other factors that can cause compilation/runtime errors?
First of all, as khmarbaise said, the exclusion is unnecessary.
Secondly, increasing the library version can cause all kinds of problems. The compile time problems are mainly method signatures or missing classes, but at runtime, anything can happen, depending on the changes made in the source code of that library.
You can just hope that the maintainers of the library tried to keep everything backwards compatible.
I'll present a simplified version of our problem, but know that we have way more than 2 projects, and so on.
So, at our job, we have 2 projects -
A
B
where B depends on A through ivy.
Recently we added configurations to the mix, mainly default and test.
The problem we ran into is that in A we have test utilities, which B needs in order to run it's own tests, so we need B to get these utilities from A.
We've thought of 2 ways to solve this:
make the test configuration public
create a new conf - test-utils - that will be public, and A will publish it's own tests under that configuration
The problem is that both solutions seem somewhat forced, and I wanted to get an idea how people do this world-wide.
Any ideas?
Note:
This answer assumes you're using a Maven repository manager. (Something
like Nexus or Artifactory).
I suggest that you upload an additional "test-utils" artifact when publishing Project A. This means the primary Maven artifact would be:
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>ProjectA</artifactId>
<version>1.0.1</version>
</dependency>
and the second artifact is:
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>ProjectA</artifactId>
<version>1.0.1</version>
<classifier>test-utils</classifier>
</dependency>
For more details on how ivy publishes and retrieves additional artifacts from a Maven repository I would suggest reading the following:
how to publish 3rdparty artifacts with ivy and nexus
ivy:install from maven with classifiers
I have a multi-module project built with maven. I need to run the project's integration tests daily. It is not possible to do this during the standard maven build cycle, because on runtime the integration tests defined within the modules have circular dependencies, which are illegal for me to declare on their poms.
Instead, I have created a separate project named Global that lists all modules jars and test-jars as its dependencies. Global has the same parent as all the modules. The idea is that using maven-ant-tasks I will be able to get a classpath of all modules jars and test-jars and go on from there. Global's pom.xml dependency section is as follows:
<dependency>
<groupId>mygroup</groupId>
<artifactId>A</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>mygroup</groupId>
<artifactId>A</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mygroup</groupId>
<artifactId>B</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>mygroup</groupId>
<artifactId>B</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
...etc
The problem is that I cannot seem to get a classpath that contains all jars and test-jars declared on Global's pom.xml (and their runtime dependencies) using the ant tasks available. I have tried (among other things):
<dependencies pathId="cp1" type="jar" usescope="runtime">
<pom file="${basedir}/pom.xml">
<profile id="DEV" />
</pom>
</dependencies>
[1] This one fetches all runtime dependencies. Nothing wrong with that.
<dependencies pathId="cp2">
<dependency groupId="mygroup" artifactId="Global" version="myVersion" scope="test" type="test-jar"/>
</dependencies>
[2] This one fetches all runtime dependencies along with Global-myversion-tests.jar, but no other test-jar.
<dependencies pathId="cp3" type="test-jar" usescope="test">
<pom file="${basedir}/pom.xml">
<profile id="DEV" />
</pom>
</dependencies>
[3] This one fetches nothing.
Obviously, declaring something like [2] once for each module will do the trick, but I am looking to create a setup that will not need to edit a gazillion files each time a new module is added or removed. BTW I am using maven-ant-task-2.1.3.
Thanks for any input.
---Edits for #yannisf accepted answer---
You should not ever have cyclic dependencies
I assume you mean for maven builds. Having cyclic dependencies on runtime is pretty common, for example:
Module A declares interface: UploadToDocumentManagementSystem
Module B implements it in : UploadToCoolDms (that way in the future, when the DMS system changes to CoolerDms module B can be replaced by a new implementation with no side-effects to the rest of the app).
Module B depends on A compile time (and, by definition, runtime as well)
Module A depends on B on runtime
Maven does not allow to declare this. The reason, that I can sympathize with, is that maven needs to complete build cycles (including tests) of multi-module projects in a specific order. Thing is, it is not really necessary to declare it if you get rid of any runtime dependecy to B for the tests of A (which is good practice and should happen anyway).
You should do things the maven way instead of resorting to ant-tasks
Fair enough, I can see how maven-ant-tasks was not made for this use.
In your global pom you are declaring dual types for the same artifact (jar, test-jar)
Is that a problem in general? For example module A contains some samples for its tests that I would like to use in the tests of module B as well. Is it wrong (by maven best practices standards) to declare that B depends on A jar (compile scope) and on A test-jar (test scope)? Won't an integration tests project justify to depend on a module as well as the same module's samples and resources used for its unit tests?
tl;dr version: I will attempt to rearrange the tests declared on the modules and create separate module(s) for integration tests (assuming I can get 20 developers to play ball). Thanks for the answer and for making me admit defeat and stop trying to make maven work with the project instead of making the project work with maven :).
You are trying to break the maven conventions in many ways. 1. You should not ever have cyclic dependencies, 2. You should do things the maven way instead of resorting to ant-tasks 3. In your global pom you are declaring dual types for the same artifact (jar, test-jar).
Although at first this might not seem to answer your question, you should take a step back and rethink your layout. Integration tests need all the dependencies and are much more demanding than unit tests. So, instead of trying to fit them into the existing projects, create a separate maven project in the same group, that will only host integration tests (under src/java/test, main will be blank) and will have as dependencies all the other projects.