Search code examples
javamavenmaven-dependency

Maven Dependency Issues: Provided and Compile dependencies working together


I have inherited a maven project with dozens of "provided" scoped dependencies. I am working on inserting a new dependency that is itself dependent on one of the previously mentioned, "provided" dependencies. This new dependency is scoped with the "compile"/default tag.

The application works without the new dependency added. After successfully compiling the app with the new dependency, the application fails at run time because the new dependency cannot find the "provided" dependencies.

Is it necessary that all "provided" dependencies work with "provided" dependencies (and the same with any other scope -- they only work with their kind)? I can't think of any other explanation of why the new default/"compile" dependencies cannot work with the existing "provided" dependencies. As I've mentioned, they are clearly provided and worked before any new dependencies were introduced. Any help is appreciated! I've been working with the maven documentation on scopes and this SO post.


Solution

  • The problem isn't that your compile dependency depends on a provided dependency, but that the provided dependency is not present at run-time. I can only guess why that is so, but a likely explanation is that it was missing already, just no-one actually used that dependency at run-time before, so it didn't matter.


    Essentially, by making the dependency:

    • compile, you're saying "I need this to compile my code and I need it at run-time too"
    • provided, you're saying "I need this to compile my code, but at run-time someone else will provide it"
    • runtime, you're saying "I don't need this to compile my code, but will need it at run-time"

    compile is the default and most frequent scope, since most of the dependencies are needed at both compile time and run-time, so it makes sense to tell Maven to propagate them in both cases.

    provided is meant for those cases when someone else will provide the library at run-time (e.g. a servlet container will typically provide a servlet-api.jar as a part of the infrastructure). This scope is however also sometimes (ab)used for strange situations when you need to compile against some library, but will/may not actually use it at run-time (e.g. an optional feature). For your case, it's also important that the application will only fail on a provided dependency if the dependency is not in fact provided at run-time and an attempt is made to use it at run-time.

    runtime is quite frequent scope for when there is an API artifact and an implementation artifact, like slf4j-api and slf4j-log4j - then you only need API at compile-time, but need both API and actual implementation at run-time.