How to put two differents version of a dependency? - Maven - Java - Spring - java

I'm gonna make a hypothetical case, we suppose that we have the following Maven's dependency:
<dependency>
<groupId>org.group1</groupId>
<artifactId>dependency1</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.group2</groupId>
<artifactId>dependency2</artifactId>
<!-- This dependency needs the version 2.0 of the dependency1 -->
</dependency>
What happen here? We have two dependencies, one "dependency1" has the version 1.0, and "dependency2" need the version 2.0 of the first depedency internally. You could say, that I could remove remove the "dependency1" and keep the dependency2, because it already has that dependency. But if I use just the "dependency2", some methods fail.
So in conclussion: I need to use the version 1.0 of the "dependency1" in whole my project, but, when the project uses the "dependency2" I want load the version 2.0 of the "dependency1".
I'dont know if I explained it well, but, any suggestion?

Related

Upgrading spring-boot without upgrading mongodb driver in maven

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>

Can I force a maven dependency A to use a specific version of a dependency B, whilst using a different version of dependency B along-side it?

I have two maven projects, lets call them master and aux. Master has a dependence on aux, and also on a specific version of org.apache.httpcomponents.httpclient. Aux has a dependence on a later version of org.apache.httpcomponents.httpclient.
e.g.
<project...>
<artifactId>master</artifactId>
<groupId>com.my-company</groupId>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>earlier version</version>
</dependency>
<!--<uses later version of http client>-->
<dependency>
<groupId>com.my-company</groupId>
<artifactId>aux</artifactId>
</dependency>
...
</dependencies>
...
</project>
However, aux depends on classes only found in the newer version of httpclient, and master's dependencies on httpclient aren't forwards compatible, so whichever version I exclude, REST calls fail in the expected places.
Is there a way to require aux to use the newer dependency, and master to use the older one?
I know that I can reconcile aux and master by patching them to be able to use the same dependency, but this would be far from ideal.
You can't have multiple versions of the same library/classes on the classpath at the same time. You would have to separate 'master' and 'aux' into separate jars, embedding the needed version of httpclient, and load each jar with its own classloader.
There is a framework called OSGi that does exactly this. It could be overkill for you application, but if you'd like to get started you could take a look at OSGi enRoute.
You can write a custom classloader to load specified version of class, because the default classloader will just pick the first one on the classpath it can find.

Maven build - force the use of a newer version

I am trying to build protege-server (https://github.com/protegeproject/org.protege.owl.server) from source. I downloaded the source code. Using "mvm -X package" yields the following error:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.
0:compile (default-compile) on project org.protege.owl.server: Compilation failu
re
[ERROR] /c:/Users/user/Programs/webprotege/org.protege.owl.server-master/src/mai
n/java/org/protege/owl/server/connect/local/OSGiLocalTransport.java:[11,32] type
org.osgi.framework.ServiceRegistration does not take parameters
Based on a previous question, an OSGI blogpost explains that the problem was fixed in a later (4.3.1) version of the library.
I tried to refer a newer version of this library in the POM.xml file:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>core</artifactId>
<version>6.0.0</version>
<scope>system</scope>
<systemPath>/c:/Users/user/Downloads/osgi.core-6.0.0.jar</systemPath>
</dependency>
and even downloaded the newer version to specifically target it.
The error still occurs. Is there any way to solve it?
EDIT:
Attempting the solution suggested by #Balazs Zsoldos didn't help and I received the same error message. I noted an import of this package (org.osgi.framework) referring version 1:
<Bundle-Activator>org.protege.owl.server.Activator</Bundle-Activator>
<Bundle-SymbolicName>org.protege.owl.server</Bundle-SymbolicName>
<Bundle-Vendor>The Protege Development Team</Bundle-Vendor>
<Embed-Dependency>antlr, antlr-runtime, stringtemplate</Embed-Dependency>
<Export-Package>org.protege.owl.server*;version=2.0.6-SNAPSHOT</Export-Package>
<Import-Package>!org.antlr.stringtemplate,
!org.apache.commons.cli,
org.osgi.framework;version="1",
*</Import-Package>
An attempt to remove this line did not help either, as it appears in another dependency down stream. I could not find out how to override the downstream import-package instruction.
The effective pom.xml, as generated by eclipse, is attached as a link: https://docs.google.com/document/d/1eHFalUHVZ45ejLes_eqaXLw6ttjcTryphbGr_CKbhRk/edit?usp=sharing
The issue is that older versions of osgi.core are still on the classpath of the as they are imported with different group and artifact ids. Drag and drop the pom.xml to your eclipse and see the Dependency Hierarchy tab of the pom editor to get more information.
The following two are imported by dependencies:
org.osgi:org.osgi.core (by org.apache.felix.log)
org.apache.felix:org.osgi.core (by owlapi distribution)
To solve the problem, you should add the following dependency:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
And as this does not override the org.apache.felix:org.osgi.core dependency, exclude that one:
<dependency>
<groupId>net.sourceforge.owlapi</groupId>
<artifactId>owlapi-distribution</artifactId>
<version>3.4.5</version>
<exclusions>
<exclusion>
<groupId>org.apache.felix</groupId>
<artifactId>org.osgi.core</artifactId>
</exclusion>
</exclusions>
</dependency>
(and remove the dependency with system scope as you do not need it and its artifactId is different from the standard anyway).
Edit
Just realized that the old osgi.core package is also inside org.apache.felix:org.apache.felix.framework that is pulled transitively by ProtegeLauncher via org.apache.felix:org.apache.felix.main:4.0.3. This means that you should either
Increment the version of org.apache.felix:org.apache.felix.main to the newest (or to one that at least implements osgi 4.3). In this case you do not need osgi.core at all
exclude org.apache.felix:org.apache.felix.main from edu.stanford.protege:ProtegeLauncher (and keep version 4.3.1 or higher of osgi.core)
I tried the second one and another issue comes that surfire plugin cannot be downloaded from maven central (or something similar, you will see).
Notes
The developer of this protege library was clearly not familiar how maven dependency management works and what should have been imported as a dependency. The project imports an OSGi runtime environment transitively that should never happen. For compilation only API should be imported and if the target runtime surely contains that API, it should be imported with provided scope. I would recommend to
not use this library or
clean it out (at least the maven dependency part) and send a pull request so the library can have an acceptable quality

Need to configure Maven project dependency tree, but *not for me*--it's only for those using my projects via Maven Central

I have a new library, called Codelet. It depends on two other libraries I've created, called Template Featherweight and XBN-Java. But the primary library--the one I'm trying to get other people to use!--is Codelet. The other two are secondary. Both are required, but they are only directly used when advanced features are needed.
I build all aspects of all three of these projects with Ant. I use Maven for one reason only: To sign the jars
codelet-0.1.0.jar
codelet-0.1.0-sources.jar
codelet-0.1.0-javadoc.jar
and push them to Maven Central. After shedding significant blood, sweat, tears, and soul in the past week, as evidenced by these four questions
How to use Maven to only sign three jars and push them to Maven Central?
Followup questions: Using Maven to only sign and deploy jars to Maven Central. Build and compilation is done entirely with Ant
Followup part 2 -- Using Maven to only sign and deploy jars to Maven Central. Build and compilation is done entirely with Ant
Why am I getting a "401 Unauthorized" error in Maven?
I have finally gotten the jars to upload to Maven Central via mvn deploy, at least once anyway, so that's big progress.
I'm realizing, however, that in order for other people to actually use Codelet, they need the entire dependency tree to be mapped out in Codelet's POM. I believe this is correct.
I am concerned that mapping this three-project dependency tree, in their three POMs, will essentially require that I duplicate much of my Ant build process in Maven. I am hoping with all hope that I don't need to do this, as Ant works well for me, and Maven and I do not get along.
Here are the dependency trees:
Dependencies for compilation of core library classes and example code, only
All items are listed by Maven groupId / artifactId / version.
XBN-Java 0.1.3 depends on
org.apache.commons / commons-collections4 / 4.0
org.apache.commons / commons-io / 2.4
org.apache.commons / commons-lang3 / 3.3.2
com.google.guava / guava / 16.0
Template Featherweight 0.1.0 depends on
com.github.aliteralmind / xbnjava / 0.1.3 (and its dependencies)
Codelet 0.1.0 depends on
com.github.aliteralmind / templatefeather / 0.1.0 (and its dependencies)
${java.home}/../lib/tools.jar (This is considered "provided", since it's part of the JDK and not on Maven Central)
Dependencies for compilation and execution of unit tests, only
These are in addition to those needed for core-compilation.
For all projects: junit / junit / 4.11 (and its dependency: hamcrest core)
For compilation of "Codelets" (which are used only by javadoc.exe), and execution of javadoc.exe
These are in addition to those needed for core-compilation.
For all projects: com.github.aliteralmind / codelet / 0.1.0 (and all its "core-compilation" dependencies)
(Some background: Codelet automates the insertion of example code into JavaDoc, using inline taglets like
{#.codelet.and.out com.github.mylibrary.examples.AGoodExample}
These optional "Codelet classes", called "customizers", are compiled before running javadoc.exe. They are used to customize how example code is displayed. Once compiled, Codelet is executed automatically, as are all inline taglets, via javadoc.exe.)
For core-compilation it's pretty much linear:
XBN-Java is the root
Template Feather depends on XBN-Java, and
Codelet depends on Template Feather
But for "javadoc", all three projects depend on Codelet. So even XBN-Java depends on Codelet...which depends on Template Feather...which depends on XBN-Java.
The POMs in all three projects are working, although this is only as far as it concerns signing and pushing the jars to Maven Central.
After reading Maven's dependency Mechanism documentation, it seems that all three projects could have the same flat dependency tree
<dependencies>
<dependency>
<groupId>com.github.aliteralmind</groupId>
<artifactId>templatefeather</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>com.github.aliteralmind</groupId>
<artifactId>codelet</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
But the right way is to have a parent project, which is inherited by the other two. It seems that XBN-Java should be the parent, but given the recursive nature of the dependencies, I'm not sure.
I am not getting the difference between dependencies and dependencyManagement (why some dependencies blocks can go right into the project proper, and others are sub-blocks in dependencyManagement...although it seems related to parent-child), and I also don't understand how "javadoc" fits into the "scope" attribute. While compile and test are explicitely listed, the word "doc" doesn't even exist on the page.
I would appreciate some advice. Thank you.
If your code needs something, but not for compile-time (just for run-time) then declare the dependency, but add a runtime scope.
Now if you want to provide a "starter parent" pom.xml, then release a "starter parent" pom.xml but don't use it in your actual build chain.

Maven 2 - different dependency versions in test and compile

I have project that depends on commons-httpclient [2.0] (compile).
I would like to write some jbehave tests - jbehave-core 3.4.5 (test).
Both this dependencies depend on commons-lang but in different versions - 1.0.1 and 2.5.
When I execute mvn package I get [BUID FAILURE] in tests section.
There is an exception for my testcase in surefire-plugin output:
java.lang.NoSuchMethodError: org.apache.commons.lang.StringUtils.substringBeforeLast(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
As I looked in source code - in commons-lang 1.0.1 - indeed, there is no StringUtils.substringBeforeLast(...) method.
Why maven uses commons-lang from commons-httpclient (compile) and not from jbehave-core in testing?
I can't afford to exclude this conflicting dependency in commons-httpclient so it must stay in compile time.
So how can this be resolved - commons-lang 2.5 version in testing and 1.0.1 in compile time?
Maven 3:
Maven 3 will attempt to obtain the nearest dependency, effectively ensuring that only one of the compile or test scoped dependency is used for both the compile and test phases.
(Thanks Vineet Reynolds)
Maven 2 (OLD):
try to define 2 different <dependency> tags with different versions and scopes. Use tag <scope>test</scope> inside dependency for tests and <scope>compile</scope> for compilation.
In Maven 3, you can trick maven by adding a dot after groupId
<dependency>
<groupId>groupId.</groupId>
<artifactId>artifactId</artifactId>
<version>version1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>groupId</groupId>
<artifactId>artifactId</artifactId>
<version>version2</version>
<scope>compile</scope>
</dependency>
The sequence matters here. need to have test first and then compile.
<dependency>
<groupId>groupId.</groupId>
<artifactId>artifactId</artifactId>
<version>version1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>groupId</groupId>
<artifactId>artifactId</artifactId>
<version>version2</version>
<scope>compile</scope>
</dependency>
Adding a dot doesnt work in pom.xml as . is converted to slash which in return generated incorrect URL.
Is thr any other way to do this
It's a really bad idea to have two different versions for compile and test dependency:
Your non-test code might rely on behavior of the newer JAR and fail when using classes of the older JAR.
When you use the older JAR in your tests, the non-test code would fail with the old JAR.
Otherwise you could have used the older JAR anywhere, with the same version...
If you get both JAR versions into your classpath, you cannot know which one gets picked up when running your tests. That's also a bad idea.
Hence you should get non-test and test to the same JAR version dependency.

Categories

Resources