A common example is with logging config files (e.g., logback.xml). Some libraries ship with their own (or a library's dependency). It's not uncommon in a large project to wind up with several logback.xml files littered around from libraries that included their own. These are called "resources" because they aren't code but they are included with the compiled distributable and are placed on the classpath.
Of course, your project probably includes its own logback.xml in your resources directory.
Libraries are typically distributed as JAR files, so when you include their JAR on your classpath you're also including their logback.xml in that JAR.
Now, when you go to package up an uber-JAR for your project, whatever packaging tool you use has to merge all those files into one classpath. So now it's found collisions: there are multiple, different logback.xml files on your classpath but you can only have one. How does the tool pick?
So now you have to specify a "merge strategy" to make sure that only your logback.xml is merged in and the rest are discarded.
logback.xml is just one example. Typically Java classes themselves merge just fine because the packaging tool can dedup by fully-qualified class name which is almost never shared across projects. So even if two different dependencies have a Utils class, the FQCN is different. com.foo.bar.Utils is not the same as com.bar.baz.Utils, so there's no conflict.
Conflicts can also arise from libraries which include different versions of the same transitive dependency. Generally the fix here is to exclude a transitive dependency from one of your direct dependencies so as to only include one version of that library. A common culprit is Apache Commons since so many Java libraries rely on it.
Speaking of Java logging, that's one area that drives me batty. The lack of a good standard Java logging library caused a multitude of logging libraries to be written. Different libraries then of course use different logging libraries, so each logging library generally has a way to wrap all the others, and they all work in different ways. Getting consistent and controllable logging (e.g. ability to turn on or off logging for a given thing) can sometimes be almost impossible to understand.
It's a sad story. The proliferation of logging libraries was the problem that Commons Logging aimed to solve by providing a facade over all of them. But because it screws up classloading (i forget the details, but it's serious), eventually a critical mass of people needed to move off it. java.util.logging tried to fix the problems and canonise a standard interface by putting in the JDK, but it got so many things wrong that it wasn't widely adopted. SLF4J finally came along as a very well-implemented facade that has gained wide adoption. It would say it is a de facto standard.
Only then JBoss decided that all their stuff (including Hibernate) needed a facade of its own, which manages to not quite properly interact with SLF4J!
Agree, and will add that part of my frustration comes from the fact that nobody really cares about logging. You just want it to work.
OT, but I think this is my biggest complaint with Javascript. There's an explosion of invention and creativity with the language (because of its privileged position as the foundation of browser programming), but little standardization for the things I don't really care about.
Of course, your project probably includes its own logback.xml in your resources directory.
Libraries are typically distributed as JAR files, so when you include their JAR on your classpath you're also including their logback.xml in that JAR.
Now, when you go to package up an uber-JAR for your project, whatever packaging tool you use has to merge all those files into one classpath. So now it's found collisions: there are multiple, different logback.xml files on your classpath but you can only have one. How does the tool pick?
So now you have to specify a "merge strategy" to make sure that only your logback.xml is merged in and the rest are discarded.
logback.xml is just one example. Typically Java classes themselves merge just fine because the packaging tool can dedup by fully-qualified class name which is almost never shared across projects. So even if two different dependencies have a Utils class, the FQCN is different. com.foo.bar.Utils is not the same as com.bar.baz.Utils, so there's no conflict.
Conflicts can also arise from libraries which include different versions of the same transitive dependency. Generally the fix here is to exclude a transitive dependency from one of your direct dependencies so as to only include one version of that library. A common culprit is Apache Commons since so many Java libraries rely on it.