Creating an executable jar


#1

Until now I run my scala ‘program’ with ‘scala Mancala.scala’, because when running the generated jar file I get a problem.

When I run ‘sbt package’ I see created:
target/scala-2.12/mancala_2.12-0.1.0-SNAPSHOT.jar

When calling it without parameters (which generates an error message) it works OK. But when called with --play I get:
ava.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: scala/runtime/java8/JFunction1$mcVI$sp
at Mancala$.initBoard(Mancala.scala:112)
at Mancala$.play(Mancala.scala:218)
at Mancala$.main(Mancala.scala:52)
at Mancala.main(Mancala.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at scala.reflect.internal.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:70)
at scala.reflect.internal.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:101)
at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:70)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
at scala.tools.nsc.JarRunner$.run(MainGenericRunner.scala:13)
at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
at scala.tools.nsc.JarRunner$.runJar(MainGenericRunner.scala:25)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:69)
at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
Caused by: java.lang.NoClassDefFoundError: scala/runtime/java8/JFunction1$mcVI$sp
… 22 more
Caused by: java.lang.ClassNotFoundException: scala.runtime.java8.JFunction1$mcVI$sp
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
… 22 more

When calling the scala file with:
scala Mancala.scala --play

It just works.

The code can be found on: https://github.com/CecilWesterhof/Mancala.

What should I change?


#2

Did you use the sbt-assembly plugin?


#3

I understood that that is to create a fat jar. One you can use with java. But I want to call the jar just with scala.


#4

Added it and it works, thank you.


#5

Ok, you probably tried to run the jar file, compiled with Scala 2.12, with the scala command which uses Scala 2.11. That won’t work. I suggest just creating fat jars with sbt and running them with java -jar. That way you can freely choose any Scala version you like in your sbt build.


#6

The fat jar 5 MB while the normal jar is 10 KB. But for the moment I need to live with it I think.


#7

Likely, yeah. When you factor in the Scala standard library and stuff, things tend to get pretty bulky, since there isn’t much by way of whole-program size optimization in the JVM environment, the way there is in the JS one.

More generally, note that you don’t usually need to generate the JAR unless you’re doing deployment – I don’t usually bother during development. I usually boot up sbt, and then inside that do tight loops of compile / run / debug / modify code / repeat. For development, that’s usually the efficient workflow. (And the compile step is optional – run will do it automatically if needed.)


#8

Yes, it looks that especially the boot is time expensive. So I could do it like you do.
And when I have a bit of time I could look into how to compile with the right version of Scala. But at the moment there are more important things to spend time on.


#9

If you need to deploy a runnable jar file that contains your complete application, then you have to create a big executable self-contained jar file. If the target environment does not contain the libraries you require, then you have no choice. Unless you use docker or some other type of deployment container that already has the various components you need. I notice that most environments these days are using some Docker style environment. I guess that means that the jars you need will already be there and the deployment tool whatever it is will set up the proper class path … In that case you would not have a normal big fat executable jar file; just your classes.


#10

For distributing, except for a docker container, it probably would be best to create a fat jar, yes. But for running on my own system it is a bit of overkill. But again not important at the moment.

By the way I could also have the scala version in the jar name. Someone using that version of scala, or a newer version, could use the scala jar and the others should use the fat jar.


#11

Oh, sure. But my point was that, during development, I usually find that I don’t need to deploy (at least up until integration testing) – I can just run/test directly inside sbt. And for non-production software, I can often work entirely that way – for example, for this project (which I spent a year of spare-time development on), never required deployment anywhere, I simply ran it directly in the development environment…