Share: Facebook icon - Twitter icon - LinkedIn icon

Scripting on the JVM?!? JBang and Kscript!

Published Wed Nov 10 2021

Tags: programming kotlin java emacs


Did you know that you can use Java and Kotlin for scripting? In this post I will introduce you to my favorite ways of doing scripting on the Java Virtual Machine (JVM), which is JBang and KScript. Some people may not enjoy Java projects as much because of all the verbosity (big project structures), so small scripts may make the languages more approachable and less intimidating. Being able to easily handle all external dependencies in the same file (no extra configuration files!) is also a big bonus. Lately I've heard from several people that they did not know about JBang, so I guess that might be true for KScript as well. Hope this post will provide you with some information on them, as well as good links to get started!

First, let's summarize some big benefits of scripting in Java and Kotlin:

The big drawback is off course the longer execution time due to having to compile and possibly fetch the dependencies first. I don't see this as a big problem, dependending on the problem you are solving with your script. Quick string replacements are probably better done with bash combined with sed or awk, but image processing or producing Excel reports (I know, but some people enjoy getting their reports these way and we comply to be cooperative) might be a better fit for something like a JVM language. It all depends on how fast the machine that is running the code is, and your time requirements for your code. Maybe you still prefer JVM scripting instead of the alternatives, in spite of the minor extra time taken? :)

JBang

Java has always known to be very big and verbose, with the need to many project structures and more to get in dependencies in a clean way. JBang makes this way easier by making it possible to run Java code like a script. Just but the shebang on the top of the file, and it will run like any other script! Java doesn't recognize the standard one, so JBang has a comment style one that still works like any other script in a Unix-like system. Let's show a simple example on how this looks:

///usr/bin/env jbang "$0" "$@" ; exit $?

public class test {

    public static void main(String... args) {
	System.out.println("HI! I am a script!");
	// Awesome stuff being done in Java
    }
}

(sorry for boring example here, we will do something more interesting below with KScript, which I use way more!)

Like you see above, just regular Java code in a script! Adding dependencies is as simple as a comment. Check the rest of the documentation for the rest of the awesome stuff you can do with it. The most important thing is off course to know how to program in Java :)

Some of my favorite features:

It would be awful if we couldn't edit our code in Emacs right? To have to use IntellIJ IDEA or Eclipse just to have auto completion and other IDE-like functionality would be a nightmare! I personally use lsp-java for Java IDE-like features in Emacs. It's nice, and provides a lot of features including popup Javadoc information while you code! Neat! Like many other Java IDEs, it works best with a project structure (example: it doesn't understand the comment style dependencies out of the box!). Fortunately, JBang can provide us with a temporary project here. Emacs setup is not documented that well on their website, so I found my own way. I use it like this:

# Emacs server started
emacsclient -n $(jbang edit --no-open myfile.java)

# Just start a new Emacs session
emacs $(jbang edit --no-open myfile.java)

Then Emacs opens up in that temporary project, and we don't need to navigate to it ourselves. Just open the Java file and start LSP, and you are all set :) You can get some issues with dependencies not being loaded, so make sure you set the project root correctly when importing projects (the top level, not just the src-folder). Try deleting the generated JBang projects in its cache (in your home-directory in .jbang/cache/projects) if you get issues, and generate the project using the commands above again. If auto complete suggestions doesn't show up at once, try writing the beginning of the name, save your file, and start the completion again. A little bit finicky, but I've only experienced this with smaller gradle projects like this. For Maven projects, I have always found lsp-java to work like a charm :)

KScript

Kotlin already has scripting functionality built-in, so why would we need something like KScript? KScript gives us a lot of extra functionality that proves quite useful, like including Maven dependencies and additional files. Let's start with a cool little example (notice the shebang at the top):

#!/usr/bin/env kscript

//DEPS com.sksamuel.scrimage:scrimage-core:4.0.22

import java.io.File
import com.sksamuel.scrimage.ImmutableImage
import com.sksamuel.scrimage.nio.PngWriter


if(2 > args.size) {
    println("Not enough arguments!")
}

val backgroundFilename = args[0]
val watermarkFilename = args[1]

val background = ImmutableImage.loader().fromFile(backgroundFilename)
val watermark = ImmutableImage.loader().fromFile(watermarkFilename)

background.overlay(watermark).output(PngWriter.MaxCompression, File("outfile.png"))

(this is off course just a simplified example, and there are many improvements you can do. These range from specifying output file, to making sure the image is the correct format to not cause overlay errors, like having them in a float format instead of integer format. Let's keep it simple for the sake of example)

In the above example we use Scrimage for image processing. We see that KScript support the same comment style dependencies as JBang does. KScript also support other directives for including files, compiler options etc. The most important thing here is still knowing the Kotlin language, because as we can see above, the code is just Kotlin with some added trinkets :)

Some cool features:

After seeing all these cool features, the most important question you have is probably: How can I edit this code in Emacs with nice auto completion and IDE-like features? I recently wrote an article about just that. To summarize, KScript has an –idea option to open IntelliJ IDEA, but we can override it by using the environment variable KSCRIPTIDEACOMMAND. Set it to emacs or "emacsclient -n" (if you have started Emacs server), and it will open in Emacs. You then get a temporary project setup with a build.gradle, that the lsp-mode with Kotlin Language Server talks nice to :)

While JBang is great, I prefer Kotlin as a language and therefore use KScript way more. Maybe you are different? Share your opinions in the comments below!




Other posts that might interest you: