Override library version in Spring - java

I have the spring-ws-security dependency in a Spring Boot 2.1.7 project:
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-security</artifactId>
</dependency>
Internally, the spring-ws-security pom has this dependency:
<dependency>
<groupId>org.apache.wss4j</groupId>
<artifactId>wss4j-ws-security-dom</artifactId>
<version>${wss4j.version}</version>
<exclusions>
...
</exclusions>
</dependency>
The parent of spring-ws-security is spring-ws, whose pom has a property:
<wss4j.version>2.2.0</wss4j.version>
I am trying to override this property in my pom file:
<properties>
<wss4j.version>2.2.4</wss4j.version>
...
</properties>
But it keeps taking the original 2.2.0 version:
$ mvn dependency:tree | grep wss4j
[INFO] | +- org.apache.wss4j:wss4j-ws-security-dom:jar:2.2.0:compile
[INFO] | | \- org.apache.wss4j:wss4j-ws-security-common:jar:2.2.0:compile
It takes the 2.2.4 version only if I explicitly supply the dependencies:
<dependency>
<groupId>org.apache.wss4j</groupId>
<artifactId>wss4j-ws-security-dom</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.wss4j</groupId>
<artifactId>wss4j</artifactId>
<version>2.2.4</version>
<type>pom</type>
</dependency>
Isn't overriding the property enough? Am I doing something wrong?

Here there are two points to consider:
One if you want to override the version in the properties in pom.xml, then your pom should have a parent-child relationship
second is if you want to use a particular version then we need to
declare the required version in your pom.xml explicitly and may
exclude the dependency from the third party jar and do a mvn clean install
Here is more information on dependency management and properties in maven

As I know, if you leave it blank without define any version. it will get the newest version of its library. So, you are right if you want to override the newest version to old version or certain version, by put the specific version. Have you trying to "mvn clean install" for that project?

Related

Maven dependencies wrong version for okhttp3 mockwebserver

I am trying to use okhttp3.mockwebserver with my Spring boot project and I find out that okhttp3:mockwebserver:jar:3.14.9 is included instead of 4.9.1.
I have created small 'mock' projects to reproduce the issue I have in my prod.
The project is here https://github.com/mkarasik/okhttp-test
It contains two folders:
lib
This is a simple library including mockwebserver as dependency
pom.xml dependency
<dependencies>
...
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.9.1</version>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
Maven dependencies tree
\- com.squareup.okhttp3:mockwebserver:jar:4.9.1:compile
+- com.squareup.okhttp3:okhttp:jar:3.14.9:compile
This is already wrong. Mockwebserver pom contains 4.9.1 okhttp artifact, however 3.14.9 is shown in tree
project
Simple Spring Boot app including lib project
<dependency>
<groupId>com.example</groupId>
<artifactId>lib</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
Maven dependencies tree
\- com.example:lib:jar:0.0.1-SNAPSHOT:test
\- com.squareup.okhttp3:mockwebserver:jar:3.14.9:test
\- com.squareup.okhttp3:okhttp:jar:3.14.9:test
\- com.squareup.okio:okio:jar:1.17.2:test
The same problem is here. okhttp3:mockwebserver:jar:3.14.9 is included instead of 4.9.1 as it is specified in my lib pom.xml.
Is there anything I am missing in my xml configuration?
Found it it it is described in Introducing dependencies in other projects causes Maven to downgrade okhttp3 version
<properties>
<okhttp3.version>4.9.1</okhttp3.version>
</properties>
Fixes the issue
OkHttp provides a Maven BOM you can use to ensure a consistent version
https://github.com/square/okhttp#releases
Also, we have a bill of materials (BOM) available to help you keep
OkHttp artifacts up to date and be sure about version compatibility.
This example is gradle, but you it is originally a feature from maven.
https://docs.gradle.org/6.2/userguide/platforms.html#sub:bom_import
dependencies {
// define a BOM and its version
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.3"))
// define any required OkHttp artifacts without version
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")
}

Maven : Update depencency

I am looking to update packets containing vulnerabilities.
When I change the version of one dependency, all the child dependencies of this one are not updated.
For exemple, here is the dependency tree of the dependency spring-boot-starter-logging:
+- org.springframework.boot:spring-boot-starter-logging:jar:2.6.3:compile
| +- ch.qos.logback:logback-classic:jar:1.2.5:compile
| | \- ch.qos.logback:logback-core:jar:1.2.5:compile
| +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.14.1:compile
| | \- org.apache.logging.log4j:log4j-api:jar:2.14.1:compile
| \- org.slf4j:jul-to-slf4j:jar:1.7.32:compile
But the childs dependencies are not updated as described here where ch.qos.logback:logback-core is up t 1.2.10.
I tried this :
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.6.3</version>
</dependency>
...
</dependencies>
</dependencyManagement>
But spring-boot-starter-logging have no BOM to add :
<type>pom</type>
<scope>import</scope>
When I modify the pom.xml, I use the following command :
mvn dependency:purge-local-repository
Thank
If child dependencies are not correctly updated a parent pom within your project tree must define the logback-classic and logback-core version as 1.2.5 within a dependencyManagement section.
<dependencyManagement>
...
</dependencyManagement>
The dependencyManagement section may be useful in a multi-module project to "pin" versions of commonly used artifacts for multiple child dependencies (avoiding to specify their version multiple times).
If you are using a single project pom, move spring-boot-starter-logging out of dependencyManagement and use dependencies directly instead.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.6.3</version>
</dependency>
</dependencies>

How to specify certain dependency version for one of my dependencies in Maven

I have a project which uses the latest version of Hibernate (let's say v2.0). I'm using it all around the project. But my project also uses some dependency (let's say MySQL Connector), which uses Hibernate (let's say v1.0). So in my pom.xml I would have something like:
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>Hibernate</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.mysql</groupId>
<artifactId>MySQLConnector</artifactId>
<version>3.7</version>
</dependency>
</dependencies>
In the end, when I compile my project, the version of Hibernate downloaded and used is v1.0 because MySQLConnector needs this one. Is there a way to specify some version of a dependency that will be used only by one of my dependencies and the rest of the code to use another version? So something like:
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>Hibernate</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.mysql</groupId>
<artifactId>MySQLConnector</artifactId>
<version>3.7</version>
<somemagicaltag>
<groupId>org.hibernate</groupId>
<artifactId>Hibernate</artifactId>
<version>1.0</version>
</somemagicaltag>
</dependency>
</dependencies>
Thus allowing MySQLConnector to use the older version of Hibernate if it likes it, but the rest of my code to use the newer, more updated version of Hibernate?
Is there a way to specify some version of a dependency that will be
used only by one of my dependencies and the rest of the code to use
another version?
No. There can be only one. So in your case either 1.0 or 2.0 (usually using newer version makes more sense). Which version is used depends on the order of dependencies in pom.xml which use such transitive dependency: Why order of Maven dependencies matter?
You can also define which version will be used by specifying such dependency (this overrides transitive dependency version) or by defining such dependency either in dependencyManagement tag: Differences between dependencyManagement and dependencies in Maven or by using BOM mechanism: Maven BOM [Bill Of Materials] Dependency
In all "normal" cases, the dependency that you declare wins against the ones that come transitively. So I would assume that in your setup, you get version 2 of hibernate (and nothing else). You can find out by calling mvn dependency:list.
You cannot load the same class twice in different versions, so normally, you cannot have two versions of hibernate in the same project. There are approaches around this (using the Maven shade plugin), but this should be the exception. Try to make your own code and your dependencies work with the same version of hibernate.
You can skip downloading that default artifact which is getting downloaded by Maven.
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>Hibernate</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.mysql</groupId>
<artifactId>MySQLConnector</artifactId>
<version>3.7</version>
<exclusions>
<exclusion> <!-- declare the exclusion here -->
<groupId>org.hibernate</groupId>
<artifactId>Hibernate</artifact>
</exclusion>
</exclusions>
</dependency>
</dependencies>

Optional system dependency causes "invalid, transitive dependencies (if any) will not be available"

I have a main project and a commons project which has a dependency that is not in any repository and needs to be included using <scope>system</scope>.
Because the system dependency is defined using a relative url using a maven property ${project.basedir}, I made it <optional>true</optional> so that it doesn't bother other projects and so depending projects need to redefine this dependency with the correct path.
Commons pom.xml:
<dependency>
<groupId>thirdparty-group</groupId>
<artifactId>artifact</artifactId>
<version>1.4.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/dependency.jar</systemPath>
<optional>true</optional>
</dependency>
In main project's pom:
<dependency>
<groupId>my-group</groupId>
<artifactId>commons</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>thirdparty-group</groupId>
<artifactId>artifact</artifactId>
<version>1.4.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/dependency.jar</systemPath>
</dependency>
Obviously dependency.jar is in both the commons and main project.
The commons jar installs properly without a hitch. But when used in the main project however, the result is:
[WARNING] The POM for my-group:commons:jar:1.0-SNAPSHOT is invalid, transitive dependencies (if any) will not be available: 1 problem was encountered while building the effective model for my-group:commons:1.0-SNAPSHOT
[ERROR] 'dependencies.dependency.systemPath' for thirdparty-group:artifact:jar must specify an absolute path but is ${project.basedir}/lib/dependency.jar #
The build continues, but transitive runtime dependencies are now excluded breaking the application.
Why is maven complaining about a dependency from commons that is not even relevant to the main project (as it is optional, why is it even included as transitive dependency)?
How to work around this problem?
Putting the system dependency in the repo is not an option unfortunately.
Going by #PascalThivent's excellent answer, I worked around the issue by defining a local repo inside the commons project, and then changing the scope to compile with <optional>true</optional>.
In commons pom.xml:
<dependencies>
<dependency>
<groupId>thirdparty-group</groupId>
<artifactId>artifact</artifactId>
<version>0.0.1</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
<repositories>
<repository>
<id>local-repo</id>
<url>file://${basedir}/lib/local-repo</url>
</repository>
</repositories>
To install the library:
mvn org.apache.maven.plugins:maven-install-plugin:2.3.1:install-file \
-Dfile=./lib/dependency.jar \
-DgroupId=thirdparty-group \
-DartifactId=artifact \
-Dversion=0.0.1 \
-Dpackaging=jar \
-DlocalRepositoryPath=.\lib\local-repo
I can just commit this to the VCS directly so other developers don't have to do this every time on checkout.
In the main project's pom.xml nothing changes. It can still use its own system dependency, or go this approach as well. Both approaches work for the WAR file that comes out there...

NoSuchMethodError: com.google.common.base.Stopwatch.createStarted()Lcom/google/common/base/Stopwatch

My app is throwing NoSuchMethodError: com.google.common.base.Stopwatch.createStarted()Lcom/google/common/base/Stopwatch error. Not sure why, because 16.0.1 do contain that class, I've checked. From what I have researched, it looks like this is a bug?
I also have this code for refernence, though I think this is not the issue:
FirewallRule rule = new PeriodicFirewallCounterRule(60, TimeUnit.SECONDS, new IpAddressCountingPolicy());
((PeriodicFirewallCounterRule)rule).addHandler(new RateLimitationHandler(new UniqueLimitPolicy(10)));
FirewallFilter firewallFiler = new FirewallFilter(getContext(), list(rule));
firewallFiler.setNext(ma);
My app is using Restlet APISpark:
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.apispark</artifactId>
<version>${version.restlet}</version>
</dependency>
When running and accessing the REST api of the app, it throws this
error:
[INFO] Caused by: java.lang.NoSuchMethodError: com.google.common.base.Stopwatch.createStarted()Lcom/google/common/base/Stopwatch;
[INFO] at org.restlet.ext.apispark.internal.firewall.rule.counter.PeriodicCounter.<init>(PeriodicCounter.java:65)
[INFO] at org.restlet.ext.apispark.internal.firewall.rule.PeriodicFirewallCounterRule$1.load(PeriodicFirewallCounterRule.java:86)
[INFO] at org.restlet.ext.apispark.internal.firewall.rule.PeriodicFirewallCounterRule$1.load(PeriodicFirewallCounterRule.java:84)
[INFO] at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3599)
[INFO] at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2379)
[INFO] at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2342)
[INFO] at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2257)
[INFO] ... 74 more
When using the extension org.restlet.ext.apispark, the guava dependency retrieved has the version 16.0.1.
Downloading: http://maven.restlet.com/com/google/guava/guava/16.0.1/guava-16.0.1.jar
Downloading: http://repo.maven.apache.org/maven2/com/google/guava/guava/16.0.1/guava-16.0.1.jar
Downloaded: http://repo.maven.apache.org/maven2/com/google/guava/guava/16.0.1/guava-16.0.1.jar (2176 KB at 711.7 KB/sec)
It comes within an application created from scratch with the following maven configuration:
<project (...)>
<modelVersion>4.0.0</modelVersion>
<groupId>org.restlet</groupId>
<artifactId>restlet-apispark-firewall</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<version>1.0.0-snapshot</version>
<properties>
<java-version>1.7</java-version>
<restlet-version>2.3.1</restlet-version>
</properties>
<dependencies>
<dependency>
<groupId>org.restlet.jse</groupId>
<artifactId>org.restlet</artifactId>
<version>${restlet-version}</version>
</dependency>
<dependency>
<groupId>org.restlet.jse</groupId>
<artifactId>org.restlet.ext.apispark</artifactId>
<version>${restlet-version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>maven-restlet</id>
<name>Public online Restlet repository</name>
<url>http://maven.restlet.com</url>
</repository>
</repositories>
</project>
I integrated your code and it works fine on my side. No exception is thrown...
I think that an older version of Guava comes from another dependency. If you use Maven, you should identify where this old guava version comes from and perhaps add an exclude within the corresponding dependency. I hope that it will fix your problem...
Hope that helps you,
Thierry
This is the solution that fixed the error:
First exclude old Guava dependency then:
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.apispark</artifactId>
<version>${version.restlet}</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
The class Stopwatch loaded by that ClassLoader does not contain that method, not sure if caused by multiple incompatible jars as Jens says or simply because 16.0.1 does not really have that method.
A simple check will be to parse the class with javap or a decompiler:
javap -p Stopwatch.class
And then check if that method is listed.
Edit: That method is there since 15.0, so i'd check the content of you classpath too.
Referring to NoSuchMethodError Oracle Documentation :
The NoSuchMethodError: is thrown if an application tries to call a specified method of a class (either static or instance), and that class no longer has a definition of that method.
Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed.
I think you got this Exception because you have more than one version of this jar in your classpath, and since the createStarted() method is available from the 15.0 version I whould say that you have an other old version of it, probably due to a dependency problem.
Moving to the latest version of Guava did it for me
In my case one of my Maven dependencies was picking up a newer version of Guava (16.0.1) that apparently does not have this method. When I added an exclusion to that dependency in my pom.xml, instead an older (correct) version of Guava was picked up by another of my dependencies and then it worked.
You can find this by printing your dependency tree via mvn dependency:tree and then looking at what is picking up the newer version of guava. You might need to add more than one exclusion to get it right.

Categories