File Organization

Multiple source files

It is possible to use multiple source files just by having them in the same source directory. To a limited extend, it also works with packages.

For example the example below works by just calling jbang Main.java:

Main.java
import model.Person;

public class Main {

    public static void main(String... args) {
        Person p = new Person(args[0]);
        System.out.println("Hello " + p.getName());
    }
}
model/Person.java
package model;

public class Person {
    String name;
    public String getName() { return name; }
    public Person(String n) { this.name = n; }
}

There are some cases where the above does not work; i.e. if two packages refer to each other - i.e. model.Person referring to util.Generator will fail. Also jbang edit does not know about multiple sources as it runs and must run before compilation occurs.

Since version 0.46 there is now support for having that all work with multiple source files. The main script file defines all the dependencies and you add more source files into the application using //SOURCES <filename>. If included source has //SOURCES that will also get included recursively. In the case of .jsh scripts, the included sources will be added in the order they are found, depth-first.

The listed file name(s) gets added to source list when compiling.

Currently there is no *.java style matching or support for .java files to declare //DEPS or other jbang configurations. That will currently only be honored by the main script/app. These will be loosened up in future based on feedback.

Adding more resources

If you want to add a META-INF/application.properties or META-INF/resource.index.html or other files to the generated jar you can use //FILES to add them.

The format is //FILES <mountpoint>[=<sourcefile>].

Example:

//FILES resource.properties
//FILES META-INF/resources/index.html=index.html

Here resource.properties will be copied as is and META-INF/resources/index.html gets its content from index.html.

All locations need to be relative to the script location.
Currently jbang edit and http(s) based script do not work with //FILES.

Extension-less/non-java files for cli-plugins

You can use jbang to write plugins for cli’s like kubectl, git, etc. They expect their plugins to be named like <cmd>-<plugin>, i.e. kubectl-myplugin.

Furthermore some of them, particularly kubectl currently require the file to start with #! otherwise you get a exec format error.

There are two ways to have that work. The first recommended way is to use jbang app install which setups an intermediate script to avoid the issue, i.e. jbang app install --name kubectl-my-plugin myplugin.java.

The second is to use a bit of auto-magic jbang has to help in case you only want a single file, no intermediate script. That is described below.

Kebab casing of filename

jbang lets you name your file without a .java or .jsh extension, such as kubectl-my-plugin or myjavascript.sh. jbang will in this case copy the file to a temporary directory using kebab-case to map the name to a proper java class name.

For example, if you make a file called kubectl-my-plugin then jbang will assume the actual class name to launch to be KubectlMyPlugin.

Note, similar is done when using jbang edit, here the symbolic link will be made so the IDE will treat it as regular camel cased java class.

If you do not follow this naming pattern you will get a compile error as javac expects both the public class and file names to be equal.

she-bang auto-stripped

For extension less scripts, you can put #!' header at the beginning to let apps recognize it is to be treated as a script. To avoid issues when compiling, `jbang will remove that line before compilation.

For now this is required for kubectl plugin but not git. Issue opened on this limitation.