How to run jar generated by package (possibly with other jars under lib)?
How can I run .jar
file which is generated by sbt's package
?
I created a really simple example with a single .scala
source:
package org.pack {
class ScalaParser(files: Array[String]) {
def doAll() = {
println("hello")
}
}
object Main {
def main(args: Array[String]): Unit = {
val sp = new ScalaParser(args)
sp.doAll()
}
}
}
After running
$ sbt
> compile
> package
.jar
is created in /target/scala-<version>
. If I try to run it, it fails giving this error:
$ java -jar package_2.9.2-0.1.jar
Exception in thread "main" java.lang.NoClassDefFoundError: scala/ScalaObject
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at org.pack.Main.main(Main.scala)
Caused by: java.lang.ClassNotFoundException: scala.ScalaObject
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 13 more
Note that no external libraries are used and sbt compile run
works fine.
I attached the whole project including generated .jar on dropbox.
What is really weird is the fact that .jar
Manifest contains right class to load, i.e. org.pack.Main
. Maybe it is caused by something else.
System info
$ java -version
java version "1.7.0_55"
OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-1ubuntu1)
OpenJDK 64-Bit Server VM (build 24.51-b03, mixed mode)
$ scala -version
Scala code runner version 2.9.2 -- Copyright 2002-2011, LAMP/EPFL
Additional question - what if I had some external .jar
s in /lib
? How can I assure that they are packed? I need one .jar
runnable on (possibly) every JVM.
Thanks for help.
Solution 1:
You can use the sbt plugin sbt-assembly:
sbt-assembly >= 0.12.0 with sbt >= 0.13.6
Since sbt-assembly is now an auto plugin, it is sufficient to add project/assembly.sbt to your sbt project:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")
sbt-assembly 0.11
Add project/assembly.sbt to your sbt project:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
Add assembly.sbt as well:
import AssemblyKeys._ // put this at the top of the file
assemblySettings
Usage
This gives you another sbt command:
sbt assembly
which produces a "fat jar" (which includes all dependencies, including the Scala libraries).
Now you can start your program
java -cp .../package-assembly.jar
so you only need a Java installation and the "fat jar".
Solution 2:
For those who do not wish to build a fat jar, all dependencies will have been downloaded as part of a successful run of sbt package
. Classpath information is stored in a file within the target
directory, which can be passed into the java
command.
Example:
sbt package
java -cp target/scala-2.11/yourproject.jar:$(cat target/streams/compile/dependencyClasspath/\$global/streams/export) Main