Maven Adding dependency for specific java package - java

I have two version of the same jar file. (version 1 and 2). My problem is that i want classes in a specific package to use one version and classes in another package to use the other version.Both the packages are under the same maven project.
Tried to add both the jar files as dependencies in the POM, but the second entry overrides the first one and only one version is added as dependency.
Is there a way to achieve this in Maven.?

Think about DLL Hell. The only way you can get various versions of the same class to coexist in a single JVM is to load each using a different class loader, and you don't want to go that way.
Rewrite your code so all of it works with the newest version of the library, or rewrite it so it doesn't need whatever changes in the library require you to use v2, your choice.

Token ugly solution...
Split your project into two modules, where each module uses a different version of the dependency. To avoid the class-loader problems referenced in jwenting's excellent answer, use the Maven Shade Plugin to rename the dependency packages in one of the modules.
See Relocating Classes for an example of doing this.

Related

maven multi-module project with two versions of protobuf

We have a multi-module maven project. One of the modules has a bunch of .proto files, which we compile to java files. Pretty much every other module depends on this module. Most of them use Protobuf 2.4, but one needs to use 2.5.
Is there any nice way to do this? (The not nice way is to edit the pom file to say "2.5", build a jar, manually copy that jar to wherever we need it, and then change the pom file back to 2.4.)
Never used protobuf, but, as I understand it's a plugin that generate stuff.
So I'm gonna give you generic pointer hoping it will help.
I think you should either try to make 2 jar with different classifier from a single module, see https://maven.apache.org/plugins/maven-jar-plugin/examples/attached-jar.html
For example classifier proto2.4 and proto2.5
then you can add the classifier when you define the dependency to that module.
Other option I see is having 2 modules, the real one, you have now, and another one for 2.5
Generate a zip from the main one and the second module would be empty but have a dependency on the generated zip, unzip it and then compile with the plugin config for 2.5
Slower at execution, a bit dirtier imho, but can be needed if for example you need more customization than just the version.
I finally got things to work more or less how I wanted. I created a new module with JUST a pom file; that pom file refers to the proto files of the original module, but compiles them with the proto2.5 compiler and puts the result into its own directory.

How java/maven resolves dependency conflicts at run time

Sorry for my newbie question.
Supposedly I have a package A which declares B, C as its dependencies in its maven files for example. B, C use two different versions of log4j for logging. I have a couple of questions:
If I use maven, and declare B, C as A's dependencies. When maven pull in artifact (.jar) of B,C from mavencentral repo. Do B,C jar files contain log4j class files or just contain only their own compiled files (B,C own source, not dependency).
If I understand correctly, when build happens, at the end, there will only be one log4j class file in the build (even if B, C use different versions of log4j). Which version of log4j to be selected to build here? Does it mean that I need to declare log4j as A dependency as well (in A's maven build file) - and that version will be selected version to build.
B, C might use totally different log4j versions. There API might be completely different. It should cause problem at run time? But in reality, it's very rare? Why so?
Thanks.
jar files usually do not contain their dependencies. There is a way to do this called fat jar. What is a fat JAR? but let's assume you are using regular jar dependencies. The jars will only declare their dependencies in their own pom.xml. So for your example, B and C will contain only their own compiled source code.
It really depends on how you pack the files. In general, if you only generate a simple jar, it will not contain the dependencies, and it is the responsibility of the runner to supply the correct dependencies. In case of a war, for example, maven will throw in all dependencies. Another way as mentioned before is fat jar. One more common way is to zip all the dependencies and supply them separately.
I do not know why you have not encountered a conflict before, I have, with many other libraries, I do not remember a case with log4j though. One way to handle these kind of conflicts, as a library maintainer, is when you make a non backward compatible change, to change the package name, this way the user can safely have multiple versions in the classpath (which should be avoided anyway).
Maven has a way to avoid these kind of conflict, it will give priority to the closest defined dependency version. For example, if you have version a declared in A and version b in B, then the effective version will be A.
Also, there are some other mechanisms like dependency management. You can look here: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
This topic is a very serious and may cause a lot of hard to detect production errors. Hope this helps...
They should only contain their own classes. Not the classes of their dependencies. You can just open the jar files and see by yourself. Jar files are just zip files.
Maven will solve the conflict by picking the version that is the closest to the root of the dependency tree. If both versions are at the same depth in the dependency tree, then the first one is picked (IIRC). If A itself depends on log4j, or if you want a specific version to be used at runtime, you should specify log4j as a direct dependency of A, with the version you want. Or at least specify it in the dependencyManagement section of your build.
Because libraries as popular as Log4J strive to have a very stable API, which thus doesn't break code compiled against older versions of the library.
An artefact will normally not contain its dependencies (however there are packaging options that do).
Maven will determine only one version using some rules (that I don't remember in detail). If you have to override this for some reason you can put a dependency management section into the POM.
Yes this can cause problems. They can only be avoided by being careful when making changes to the public API.
If log4j is specified as a dependency of B and C and you do not use special plugins that create uber-jars/fat-jars, both B and C will not contain log4j class files.
One dependency with same coordinates (groupId, artifactId). As someone here already mentioned, version is usually picked by shortest path to root. So if you want to use a specific log4j-version you just can specify it in your pom.
If you use log4j the standard way, i.e. by just specifying the config files, both versions (log4j and log4j2) can usually coexist due to the fact they are using different packages and different configuration files. Just check the migration site of log4j: Migration from log4j to log4j2

Indirectly referenced from required .class files

I'm getting below error in STS:
The type org.springframework.core.env.EnvironmentCapable cannot be resolved. It is indirectly referenced from required .class files
This sounds like a transitive dependency issue. What this means is that your code relies on a jar or library to do something - evidently, you depend on Spring framework code. Well, all that Spring code also depends on libraries and jars.
Most likely, you need to add the corerctly versioned org.springframework.core jar to your classpath so that the EnvironmentCapable class can be found when your IDE attempts to build your project.
This might also be a jar collision issue as well, although that sounds less likely. When an application experiences jar collision (also known as "dll hell"), the compiler is finding multiple jars and classes with the same fully-qualified name. For example, let's say you added Spring to your classpath, along with the entire Tomcat server library. Well, those two jars may contain the same exact named classes, maybe the same version, maybe different versions. But either way, when the compiler looks for that EnvironmentCapable class, it finds two (in this contrived example) - one in the Spring jar and one in the Tomcat jar. Well, it doesn't know which one to choose, and so it throws a ClassDefNotFoundException, which would/could manifest itself as the error you experienced.
I faced same error while i work with spring security on spring-security-config.i jsut deleted that jar in maven repo and gave maven->update Project in eclipse.
it is resolved.Please try it once.
From command line, run "mvn clean install", you'll see project failed and you'll see artifacts in the logs that cause such a problem.
After that, remove artifacts from .m2/repository, then maven update from eclipse.
To avoid jar collision, make sure you declare your dependency versions under the properties tag in the aggregate pom.xml, and use the property name as a placeholder throughout the project. For example 4.2.5.RELEASE in the parent pom, and then in the child modules just use ${spring.version} instead of 4.2.5.RELEASE. This way you can avoid having two different versions of the same library on the classpath.
Also it is recommended to be consistent with the version of spring dependencies. Use the same version for spring-core, spring-web etc.
If you are using maven, then you can use the maven enforcer plugin to ensure dependency convergence, and avoid further issues with transitive dependencies.

Loading a class from single jar among several jars

I want to you use a class 'javax.xml.stream.XMLOutputFactory' in code.
The class (XMLOutputFactory) is available in more than one jars of library which got included as maven dependencies.
Problem : The class(XMLOutputFactory) is loading from the jar file while i am expecting to load from other jar.
Is there any solution to customize the loading of a class from the specific jar file.
Not on the java side (or maybe by implementing a new classloader but that is not a reasonable solution to your problem).
You should probably just exclude the dependencies (versions) you don't want in your pom file.
Since maven 2.0.9 the classpath is generated according dependencies declaration in pom.xml.
From maven site:
Note that if two dependency versions are at the same depth in the dependency tree, until Maven 2.0.8 it was not defined which one would win, but since Maven 2.0.9 it's the order in the declaration that counts: the first declaration wins.
So you can solve your problem if you take care of dependencies ordering in your pom.xml

How to examine required libraries?

I developing a web application with a lot of libraries like, Spring, Apache CXF, Hibernate, Apache Axis, Apache Common and so one. Each of these framework comes with a lot of *.jar libraries.
For development I simple take all of the delivered libraries and add them to my classpath.
For deployment not all of these libraries are required, so is there a quick way to examine all the required libraries (*.jar) which are used by my source code?
If you move your project to use Maven such things become easier:
mvn dependency:analyze
mvn dependency:tree
For your example, Maven + IDE + nice dependency diagrams could help allot.
See an example of this : it's much easier this way to figure out what happens in a project, and this way you don't need to add to your project "all delivered libraries" - just what it's required.
JDepend traverses Java class file
directories and generates design
quality metrics for each Java package.
JDepend allows you to automatically
measure the quality of a design in
terms of its extensibility,
reusability, and maintainability to
manage package dependencies
effectively.
So, as a quick, dirty, and potentially inefficient way, you can try this in Eclipse:
Create two copies of your project.
In project copy #2 remove all the jars from the classpath.
Pick a source file that now has errors because it can't resolve a class reference. Pick one of the unresolved classes and note its fully qualified class name.
Do Control-Shift-T and locate the unresolved class. You should be able to see which jar its contained in since all the jars are still in the classpath for project copy #1.
Add the jar that contains this unresolved class back into your classpath in project copy #2, then repeat steps 3 and 4 until all class references are resolved.
Unfortunately you're not done yet since the jar files themselves may also have dependencies. Two ways to deal with this:
Go read the documentation for all the third-party packages you're using. Each package should tell you what its dependencies are.
Run your application and see if you get any ClassNotFoundExceptions. If you do, then use Control-Shift-T to figure out what jar that class comes from and add it to your classpath. Repeat until your project runs without throwing any ClassNotFoundExceptions.
The problem with #2 is that you don't really know you've resolved all the dependencies since you can't simulate every possible execution path your project might take.

Categories

Resources