Magnus
Magnus

Reputation: 3751

Assembling a jar containing only the provided dependencies

Is there a way to download and package all provided dependencies in sbt in one jar that I can add to the classpath of my application when I start it?

I'm using sbt-assembly but the jar generated is waaay too big for my tastes: 16 Mb including both Scala-lang, Akka and Spray. I'm planning on building about 10-15 small independent services, each as a jar and don't want to keep uploading 16 Mb files to s3 (currently my repo) whenever I create a new version deploy.

When I exclude the Scala language and the other jars mentioned above, my assembly jar goes down to about 1 Mb. So what I want to achieve is to have a way to provide a fat jar with only the provided dependencies and the scala language so I can add that to the classpath of my services when I start them.

I could grab each library from my ivy cache, deploy them manually and add them to the classpath, but basically I want to be able to build a new assembled jar whenever I change a version and upload that.

I thought of having a separate project that the other project depends on, containing all my provided jars, but is there a simpler way?


Further clarification as requested:

My larger objective is an automated deploy into a cloud server. I first build locally with sbt-assembly, for example producing service-a.jar, service-b.jar etc. Then I sync these to s3, so if only service-a.jar is updated then only that file will be uploaded to s3. Next I instruct my ec2 server to download changed files and relaunch any new services.

When using sbt-assembly I get a fat jar (just like onejar) with all dependencies. However the uploading of that jar to s3 takes a long time and 15 Mb out of 16Mb of those files are just common dependencies. So my idea was to put all those shared dependencies into a separate jar that rarely changes and just provide those on the classpath when launching java -cp "deps.jar;service-a.jar" org.myapp.StartApp.

I just don't want to manage those common jars myself manually. I want to leverage my existing build and use the sbt/ivy dependency management system for downloading the correct files for me and preparing a package that I can distribute separately with my stand alone services.

All solutions I come up with so far seem overly complicated so far, especially when I still need to be able to run tests etc (so no "provided" in sbt build files). Other suggestions are welcome.

Upvotes: 3

Views: 1817

Answers (1)

Magnus
Magnus

Reputation: 3751

I found my answer when looking in the source code of the sbt-assembly plugin:

lazy val assembly          = TaskKey[File]("assembly", "Builds a single-file deployable jar.")
lazy val packageScala      = TaskKey[File]("assembly-package-scala", "Produces the scala artifact.")
lazy val packageDependency = TaskKey[File]("assembly-package-dependency", "Produces the dependency artifact.")

So with the following config + commands I can get all the artifacts I need:

// Produces a jar without dependencies and scala language jar included
lazy val assemblyMicroService = assemblySettings ++
  Seq( 
    assembleArtifact in packageScala := false,
    assembleArtifact in packageDependency := false
  )

Then run the commands in turn: assembly, assembly-package-scala, assembly-package-dependency

Then I will have three jars, one for my service, one for the scala language and one for the dependencies.

Upvotes: 7

Related Questions