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:<ol> <li>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).</li> <li>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.</li> <li>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? </li> </ol>
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.</li> <li>
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.</li> <li>
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).</li> </ol>
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...
Answer2:<ol> <li>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.</li> <li>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.</li> <li>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.</li> </ol>
Answer3:<ol> <li>An artefact will normally not contain its dependencies (however there are packaging options that do).</li> <li>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.</li> <li>Yes this can cause problems. They can only be avoided by being careful when making changes to the public API.</li> </ol>
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.</li> <li>
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.</li> <li>
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</li> </ol>