Those days I’m mainly working with the Play! Framework for Java. We are in a microservices setup and thus we end up having some common code between the different services. We decided to make Play Modules that we would import in the different projects.

Creating the Module

In order to do so, we created a project with a few utility classes that we published using SBT and our own Nexus respository. The final configuration ended up being something like this:

name         := "some-module"
organization := "my.organization"
version      := "1.0.0-SNAPSHOT"
scalaVersion := "2.12.2"
lazy val root = (project in file(".")).enablePlugins(PlayJava)

libraryDependencies ++= Seq(
    guice,
    "com.rabbitmq" % "amqp-client" % "4.2.0",
    "org.mockito" % "mockito-core" % "2.10.0" % "test"
)

val nexusHost     = System.getenv("NEXUS_HOST")
val nexusUser     = System.getenv("NEXUS_USER")
val nexusPassword = System.getenv("NEXUS_PASSWORD")

publishTo := {
    val httpNexus = "http://" + nexusHost
    if (version.value.trim.endsWith("SNAPSHOT"))
        Some("snapshots" at httpNexus + "/repository/maven-snapshots")
    else
        Some("releases" at httpNexus + "/repository/maven-releases")
}

credentials += Credentials("Sonatype Nexus Repository Manager", nexusHost, nexusUser, nexusPassword)

And, then publishing becomes super easy, we only need to run sbt publish and voilà! The module is published inside our own repository.

Using it in your Project

Now comes the second part, actually using your module in your project. For this, theoretically, all you need is to add a Library Dependency:

libraryDependencies ++= Seq(
    "my.organization" % "some-module" % "1.0.0-SNAPSHOT",
    "com.rabbitmq" % "amqp-client" % "4.2.0"
)

But this won’t be enough.

First of all, you need to add a resolver to specify the new repository for your module:

resolvers += "My Snapshots" at "https://my.url/repository/maven-snapshots/"

You will also need your credentials to access it:

credentials += Credentials("Sonatype Nexus Repository Manager", "my.url", nexusUser, nexusPassword)

And now you are all excited and you type in sbt clean update… And it fails miserably.

Now, the thing is, SBT is gonna try to find your module at https://my.url/repository/maven-snapshots/my/organization/some-module/1.0.0-SNAPSHOT/.... But if you look carefully in your Nexus the module is actually at /my/organization/some-module_2.12/1.0.0-SNAPSHOT/.... Notice this 2.12? This is your Scala version, and this is actually quite important. So now, you’re left with 3 choices.

  • Remove the cross path by doing crossPaths := false (highly NOT RECOMMENDED)
  • Specify the Scala version by depending on some-module_2.12 instead of just some-module
  • Using the %% syntax to let SBT append this Scala version, which is the solution we chose

So here is now our final Library Dependencies declaration:

libraryDependencies ++= Seq(
    "my.organization" %% "some-module" % "1.0.0-SNAPSHOT",
    "com.rabbitmq" % "amqp-client" % "4.2.0"
)

And the complete file for science:

name := """my_service"""
organization := "my.organization"
version := "1.0"
packageName in Universal := "my_service"

lazy val root = (project in file(".")).enablePlugins(PlayJava)

scalaVersion := "2.12.2"

val nexusUser     = System.getenv("NEXUS_USER")
val nexusPassword = System.getenv("NEXUS_PASSWORD")

resolvers += "My Snapshots" at "https://my.url/repository/maven-snapshots/"
credentials += Credentials("Sonatype Nexus Repository Manager", "my.url", nexusUser, nexusPassword)

libraryDependencies ++= Seq(
  guice,
  "my.organization" %% "some-module" % "1.0.0-SNAPSHOT",
  "com.rabbitmq" % "amqp-client" % "4.0.2"
)

Please note that the Scala version that is going to be appended is the one minor one of your scalaVersion declaration, so here 2.12.

You’re now good to go, you can get back to your fun with Play!.