jart-tools is a Unix shell script to build fat-jars from scala applications It means build a single and self-contained jar file with the underlying application, all dependencies and scala runt-time libraries.
Motivation: It makes the distribution of java and scala libraries
easier as the user only needs to run $ java -jar application.jar
or
double click at the application.jar file. The developer also doesn’t
need to build any shell script to run the jar file.
Benefits:
- Inspect jar files.
- Bundle a java/scala application with all its dependencies.
- Make the deployment and distribution easy and simple.
- Run Scala application in a machine without Scala installed.
- Run a Scala/java application with a simple double click.
Just copy the script jart-tools.sh to any directory listed in the $PATH variable.
No dependencies are required. It only needs java and scala available.
Download it:
cd ~/bin
$ curl -O -L https://github.com/caiorss/jart-tools/raw/master/jar-tools.sh
$ chmod +x jar-tools.sh
$ ./jar-tools.sh
./jart-tools.sh
$ jar-tools.sh
Build fat jar. Tool like one-jar to build java fat jar.
Options:
+ Show information about Scala libraries.
* ./jar-tools.sh -scala-lib
+ Run an application compiled with Scala using its runtime.
* ./jar-tools.sh -scala-run scalaApp.jar
+ Start scala repl loading all *.jar files in classpath from ./lib
* ./jar-tools.sh -scala-repl
+ Build a fat jar for a Scala application. out/output-jar.jar. The
main-jar file contains the main class.
* ./jar-tools.sh -scala-build-jar out/output-jar.jar main-jar.jar lib/dependency1.jar lib/dependency2.jar
+ Display manifest of a jar file.
* ./jar-tools.sh -jar-mainifest file.jar
+ Display main class of a jar file.
* ./jar-tools.sh -jar-main file.jar
+ View contents of a jar file
* ./jar-tools.sh -jar-view file.jar
+ Make a self-executable jar-file.sh out of jar-file.jar
that can be run as ./jar-file.sh instead of '$ java -jar jar-file.jar.'
* ./jar-tools.sh -jar-to-sh file.jar
+ Makes a self-executable jar file with .sh extension out of a scala
compiled jar file. It assumes that scala is instaled in the target machine.
The generated file, app.sh that can be run as ./app.sh instead of '$ java -jar app.sh'
* ./jar-tools.sh -jar-to-sh2 file.jar
Note: Use the command below to enable debug.
$ env DEBUG=true ./build-fat-jar.sh
./jart-tools.sh -scala-lib
$ jart-tools.sh -scala-lib
Scala library path = /home/archbox/opt/scala-2.11.8/lib
/home/archbox/opt/scala-2.11.8/lib/akka-actor_2.11-2.3.10.jar
/home/archbox/opt/scala-2.11.8/lib/config-1.2.1.jar
/home/archbox/opt/scala-2.11.8/lib/jline-2.12.1.jar
/home/archbox/opt/scala-2.11.8/lib/scala-actors-2.11.0.jar
/home/archbox/opt/scala-2.11.8/lib/scala-actors-migration_2.11-1.1.0.jar
/home/archbox/opt/scala-2.11.8/lib/scala-compiler.jar
/home/archbox/opt/scala-2.11.8/lib/scala-continuations-library_2.11-1.0.2.jar
/home/archbox/opt/scala-2.11.8/lib/scala-continuations-plugin_2.11.8-1.0.2.jar
/home/archbox/opt/scala-2.11.8/lib/scala-library.jar
/home/archbox/opt/scala-2.11.8/lib/scalap-2.11.8.jar
/home/archbox/opt/scala-2.11.8/lib/scala-parser-combinators_2.11-1.0.4.jar
/home/archbox/opt/scala-2.11.8/lib/scala-reflect.jar
/home/archbox/opt/scala-2.11.8/lib/scala-swing_2.11-1.0.2.jar
/home/archbox/opt/scala-2.11.8/lib/scala-xml_2.11-1.0.4.jar
Example:
$ jar-tools.sh -jar-manifest canvas.jar
$ jar-tools.sh -jar-manifest canvas.jar
Manifest-Version: 1.0
Scala-Compiler-Version: 2.11.8
Main-Class: Main
$ jar-tools.sh -jar-view canvas.jar
$ jar-tools.sh -jar-view canvas.jar
75 Fri Jul 21 18:35:46 BRT 2017 META-INF/MANIFEST.MF
1773 Fri Jul 21 18:35:46 BRT 2017 Main$$anon$2$OriginXY$.class
2930 Fri Jul 21 18:35:46 BRT 2017 Main$$anon$2$OriginXY.class
626 Fri Jul 21 18:35:46 BRT 2017 Main$$anon$2$OriginType.class
1755 Fri Jul 21 18:35:46 BRT 2017 Main$$anon$2$OriginC$.class
1759 Fri Jul 21 18:35:46 BRT 2017 Main$$anon$2$OriginBL$.class
1894 Fri Jul 21 18:35:46 BRT 2017 Main$$anon$2$DrawUtils$$anonfun$withContext$1.class
1682 Fri Jul 21 18:35:46 BRT 2017 Main$$anon$2$DrawUtils$$anonfun$withColor$1$$anonfun$apply$4.class
1654 Fri Jul 21 18:35:46 BRT 2017 Main$$anon$2$DrawUtils$$anonfun$withColor$1.class
... ... ...
It generates a self-executable jar-file with ‘.sh’ extension of unix shell script from a runnable jar file named app.jar which can be executed with:
$ java -jar app.jar
The application can be executed with ./app.sh.
- Run
$ jar-tools.sh -jar-to-sh out/linuxPanel.jar
Build jar-executable out/linuxPanel.sh
Run it with ./out/linuxPanel.sh
- The command above generates linuxPanel.sh that can be executed with:
./linuxPanel.sh
This command builds a self-executable jar-file out of a scala-compiled jar file that cannot be run with ‘java -jar’ directly, but can be run with ‘scala app.jar’.
Example:
- Build the program linuxPanel.jar
$ scalac linuxPanel.scala -d linuxPanel.jar
- Build the self-executable jar, named linuxPanel.sh.
$ jar-tools.sh -jar-to-sh2 linuxPanel.jar
Input = linuxPanel.jar
Output = linuxPanel.sh
Main class = linuxPanel.Main
Build jar-executable linuxPanel.sh
Run it with ./linuxPanel.sh
- Run the application linuxPanel.sh
./linuxPanel.sh
- Optional - Check linuxPanel.sh
$ file linuxPanel.sh
linuxPanel.sh: a /usr/bin/env sh script executable (binary data)
$ head -n 15 linuxPanel.sh
#!/usr/bin/env sh
# set -x
SCALA_LIB_PATH="$(dirname $(dirname $(which scala)))"/lib
jars=""
for f in $(ls $SCALA_LIB_PATH); do
jars=$SCALA_LIB_PATH/$f:$jars
done
jars=$jars"."
java -cp $jars:$0 linuxPanel.Main
exit 0
./jart-tools.sh -scala out/output-jar.jar main-jar.jar dep1.jar dep2.jar ...
Build a fat jar for the application main-jar.jar that contains the main class packing it with the scala run-time (scala-library.jar) and the jar dependencies dep1.jar dep2.jar and so on.
It will build a fat-jar for the sample-scala program testProgram.scala
- Compile the scala program to a jar file.
$ scalac testProgram.scala -d testProgram.jar
Run it with scala:
$ scala testProgram.jar
Hello world Scala
It will display a simple GUI:
Run it with java: It first will fail because the dependency scala-library.jar is missing.
$ java -cp testProgram.jar scalaApp.Main
Exception in thread "main" java.lang.NoClassDefFoundError: scala/Predef$
at scalaApp.Main$.main(testProgram.scala:8)
at scalaApp.Main.main(testProgram.scala)
Caused by: java.lang.ClassNotFoundException: scala.Predef$
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
$ java -jar testProgram.jar
Exception in thread "main" java.lang.NoClassDefFoundError: scala/Predef$
at scalaApp.Main$.main(testProgram.scala:8)
at scalaApp.Main.main(testProgram.scala)
Caused by: java.lang.ClassNotFoundException: scala.Predef$
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
Try again. Now it works.
$ java -cp /home/archbox/opt/scala-2.11.8/lib/scala-library.jar:testProgram.jar scalaApp.Main
Hello world Scala
2. Build a fat jar.
It will create the file testProgram-fat.jar.
$ ./jar-tools.sh -scala-build-jar testProgram-fat.jar testProgram.jar
At directory /home/archbox/Documents/projects/jart-tools.sh/temp
Manifest Content META-INF/MANIFEST.MF
Manifest-Version: 1.0
Scala-Compiler-Version: 2.11.8
Main-Class: scalaApp.Main
Building fat-jar file ...
added manifest
adding: library.properties(in = 187) (out= 135)(deflated 27%)
ignoring entry META-INF/
ignoring entry META-INF/MANIFEST.MF
adding: rootdoc.txt(in = 4279) (out= 1329)(deflated 68%)
adding: scala/(in = 0) (out= 0)(stored 0%)
adding: scala/languageFeature.class(in = 2317) (out= 1471)(deflated 36%)
adding: scala/Function6.class(in = 1739) (out= 1012)(deflated 41%)
.... ... ... ... .... .... ... ... ... .... .... ... ... ... ....
adding: scala/Function1.class(in = 2600) (out= 1431)(deflated 44%)
adding: scala/Function2$mcFJI$sp.class(in = 323) (out= 195)(deflated 39%)
adding: scala/Tuple3$.class(in = 1555) (out= 720)(deflated 53%)
adding: scala/Unit.class(in = 1133) (out= 809)(deflated 28%)
adding: scala/Function21.class(in = 3610) (out= 1671)(deflated 53%)
adding: scala/Enumeration$$anonfun$scala$Enumeration$$isValDef$1$1.class(in = 1771) (out= 890)(deflated 49%)
adding: scala/Function7.class(in = 1851) (out= 1055)(deflated 43%)
adding: scala/Function18$$anonfun$tupled$1.class(in = 2393) (out= 1019)(deflated 57%)
adding: scala/Predef$StringFormat$.class(in = 2107) (out= 1010)(deflated 52%)
adding: scala/Tuple5$.class(in = 1805) (out= 768)(deflated 57%)
adding: scala/Function2$mcZJD$sp.class(in = 323) (out= 196)(deflated 39%)
adding: scala/Char.class(in = 6084) (out= 3604)(deflated 40%)
adding: scala/Float.class(in = 5382) (out= 3268)(deflated 39%)
adding: scala/Enumeration$ValueSet$$anon$2.class(in = 1673) (out= 668)(deflated 60%)
adding: scalaApp/(in = 0) (out= 0)(stored 0%)
adding: scalaApp/Main.class(in = 585) (out= 472)(deflated 19%)
adding: scalaApp/Main$.class(in = 1386) (out= 830)(deflated 40%)
--------------------------------------
Built file: testProgram-fat.jar Ok.
3. Check the generated file
$ file testProgram-fat.jar
testProgram-fat.jar: Java archive data (JAR)
# A little bit heavier, but this size is insignificant and a very
# small price for all Scala's goodness.
#
$ du -h testProgram-fat.jar
5,5M testProgram-fat.jar
5,5M total
$ jar -tf testProgram-fat.jar
META-INF/
META-INF/MANIFEST.MF
library.properties
rootdoc.txt
scala/
scala/languageFeature.class
scala/Function6.class
scala/Function2$mcIDI$sp.class
scala/Function1$mcDI$sp.class
scala/Product2.class
scala/SerialVersionUID.class
scala/Function1$mcVI$sp$class.class
scala/Function22$class.class
... ... ... ... ... ....
scala/Float.class
scala/Enumeration$ValueSet$$anon$2.class
scalaApp/
scalaApp/Main.class
scalaApp/Main$.class
4. Run it and deploy.
The fat jar can be run with a simple command java -jar
or by double
clicking it if the desktop is configured properly. It can also be
distributed to machines without Scala installed.
$ java -jar testProgram-fat.jar
Hello world Scala
Example: It will build the fat-jar file out/exrates.jar from the application bin/demoTableExrates.jar and pack it with scala-library.jar and scala-xml_2.11-1.0.4.jar.
$ jar-tools.sh -scala out/exrates.jar \
bin/demoTableExrates.jar \
bin/jswing.jar /home/archbox/opt/scala-2.11.8/lib/scala-xml_2.11-1.0.4.jar
# Script output below
At directory /home/archbox/Documents/projects/jswing.scala/out/temp
Extracting /home/archbox/Documents/projects/jswing.scala/bin/jswing.jar
Extracting /home/archbox/Documents/projects/jswing.scala/bin/jswing.jar
Manifest Content META-INF/MANIFEST.MF
Manifest-Version: 1.0
Scala-Compiler-Version: 2.11.8
Main-Class: Main
added manifest
adding: jswing/(in = 0) (out= 0)(stored 0%)
adding: jswing/Event$$anon$1.class(in = 790) (out= 459)(deflated 41%)
adding: jswing/Dialog$FileChooser$$anonfun$run$1.class(in = 1179) (out= 613)(deflated 48%)
adding: jswing/JUtils$.class(in = 1984) (out= 1071)(deflated 46%)
adding: jswing/guis/(in = 0) (out= 0)(stored 0%)
adding: jswing/guis/TextView.class(in = 5485) (out= 3114)(deflated 43%)
adding: jswing/guis/PictureFrame$.class(in = 1114) (out= 561)(deflated 49%)
adding: jswing/guis/ListView.class(in = 6373) (out= 3410)(deflated 46%)
adding: jswing/guis/PictureFrame.class(in = 4352) (out= 2510)(deflated 42%)
adding: jswing/guis/ListView$.class(in = 859) (out= 456)(deflated 46%)
adding: jswing/guis/ListView$$anon$1.class(in = 1047) (out= 584)(deflated 44%)
adding: jswing/guis/ListView$$anonfun$onSelect$1.class(in = 1398) (out= 728)(deflated 47%)
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
adding: scala/Predef$StringFormat$.class(in = 2107) (out= 1010)(deflated 52%)
adding: scala/Tuple5$.class(in = 1805) (out= 768)(deflated 57%)
adding: scala/Function2$mcZJD$sp.class(in = 323) (out= 196)(deflated 39%)
adding: scala/Char.class(in = 6084) (out= 3604)(deflated 40%)
adding: scala/Float.class(in = 5382) (out= 3268)(deflated 39%)
adding: scala/Enumeration$ValueSet$$anon$2.class(in = 1673) (out= 668)(deflated 60%)
adding: scala-xml.properties(in = 112) (out= 76)(deflated 32%)
--------------------------------------
Built file: out/exrates.jar Ok.
Run it with $ java -jar out/exrates.jar