Dependencies With Provided Scope in Scala SBT
By: Kirill Panarin
Tags: Sbt, Maven, Scala, Maven Dependency, Provided

I always had a lot of questions and concerns about how Scala/Java/Maven dependencies with the provided scope work. The best way to understand something is to actually play with it, so I decided to create a Scala playground project and play with different combinations of dependencies.

Project Structure

The repository contains three Scala projects with extremely sophisticated names: project-a , project-b , project-c . The idea is to keep these projects as simple as possible and limit number of dependencies.

project-a depends on org.apache.avro dependency version 1.8.1 :

project-b depends on the same artifact but of version 1.10.1 :

Both project-a and project-b declare the dependency as provided. The main idea of using "provided" dependency scope is to exclude the dependency from being on the class path by default. As a result, it isn't included into a fat jar artifact.

project-a has very simple code which actually uses Apache Avro dependency. It serializes a GenericRecord to an array of bytes:

project-c doesn't explicitly declare the Apache Avro artifact as a dependency, but it depends on both project-a and project-b :

Lesson 1: Project A

Let’s get back to the project-a and see what is going on there. All three projects use sbt dependency graph plugin to easily check for dependencies.

With the Avro dependency declared as provided dependencyTree is indicating that there is no dependency on the Apache Avro artifact:

But the project compiles:

but on attempt to execute the code it fails:

It fails because JVM can’t find the Avro artifact on the class path and fails.

Obviously, removing provided fixes the issue. So the first takeaway is that dependency with provided scope lets a project depending on it to compile, but fails on execution.

Lesson 2: Project C. Show me your deps!

project-c depends on both project-a and project-b . Let's use dependencyTree again to check what transitive dependencies we have for project-c .

Well, since both project-a and project-b reference the Avro artifact as provided it isn't getting as a transitive dependency to the project-c .

project-c has the following code executing the "serializer" from project-a :

project-c compiles, but again fails to execute due to missing dependencies:

Lesson 3: Project C. Choosing a dependency.

To fix project-c we have to add a non-provided dependency on the Avro artifact to the project-c itself. As you might recall, project-a depends on version 1.8.1 and project-b on version 1.10.1 of the Avro artifact. Let's see if can pick whichever we want and whether it will work. Let's start from 1.10.1 :

After sbt for project-c is reloaded the dependencyTree is reporting that the 1.10.1 dep is available. Now is time to run the project.

Works now with 1.10.1 version of the Avro.

Switching to 1.8.1 works as well:

So the final takeaway is that a project depending on artifacts with provided transitive dependencies may put a dependency of its choice on the class pass to execute. Still, there might be some incompatibilities among different versions, so the approach doesn’t always work.

Links