Thanks to visit codestin.com
Credit goes to bce.design

Skip to main content

Labs

Java CLI Script

Single-File Executable Java Scripts Installed on the PATH

Overview

Java CLI Script applies the BCE pattern to self-contained, single-file Java 25 scripts launched like any shell command. The shebang #!/usr/bin/env -S java --source 25 runs the file in source-file mode: no build tool, no compilation step, no .java extension. The filename is a short, lowercase command name (camelCase when needed, never dashes) and doubles as the application name in all output.

Scripts are installed by copying or symlinking the file into a PATH directory such as /usr/local/bin. They rely exclusively on java.base and standard JDK modules; when a task genuinely requires external libraries or multiple files, the Java CLI App style takes over.

Business Components

The business component is the single file. Short scripts stay flat: top-level methods, records, and enums ordered Boundary, then Control, then Entity, with main last. When a script grows beyond roughly two screens or accumulates several records and many methods, its members are grouped into three interfaces named after the BCE layers. The interfaces are namespaces for developer experience (IDE outline, per-layer folding, layer-labeled call sites), never enforcement ceremony: no constructors, no final, no explicit visibility modifiers. Past roughly 1000 lines even a grouped single file fights the medium; switch to the Java CLI App style.

Boundary

In grouped scripts, interface Boundary holds the coarse-grained facade named after the script's responsibility, output adapters such as a Log enum, and the NAME and VERSION constants as bare interface fields. main stays top-level at the very bottom and contains exactly one statement: the invocation of the boundary facade.

#!/usr/bin/env -S java --source 25

/// CLI facade: argument parsing, command dispatch and status output.
interface Boundary {

    String NAME = MethodHandles.lookup().lookupClass().getEnclosingClass().getName();
    String VERSION = "2026-09-15.1";

    static void run(String... args) throws Exception {
        Log.system(NAME + " " + VERSION);
        switch (args[0]) {
            case "-help" -> help();
            case "tables" -> printLines(Control.tableNames());
            default -> dispatch(args);
        }
    }

    static void dispatch(String... args) throws Exception { ... }

    static void help() { ... }
}

// ...Control and Entity interfaces...

void main(String... args) throws Exception {
    Boundary.run(args);
}

Control

interface Control contains stateless static functions owning all I/O and traversal, ordered coarsest first. Interface methods with bodies require the explicit static modifier. Cross-layer references are qualified, so Entity.Layer in a signature labels the layer at every call site.

/// Stateless record, index and rendering functions owning all file I/O.
interface Control {

    Path DB_DIR = Path.of(configuration().getProperty("db.dir", "."));

    static void saveRecord(String table, String key, SortedMap<String, String> record) throws Exception { ... }

    static List<String> keys(String table) throws Exception { ... }

    static List<String> tableNames() throws Exception { ... }

    static void atomicWrite(Path target, String content) throws Exception { ... }
}

Entity

interface Entity groups the records and enums that maintain state and expose behavior on that state. Entities perform no I/O. Records keep their own explicit static final fields; records are classes, not interfaces.

/// Domain values: filter terms parsed from the command line.
interface Entity {

    /// A case-insensitive substring, optionally scoped to a single field.
    record Filter(String field, String text) {

        static Filter of(String term) { ... }

        boolean matches(SortedMap<String, String> record, String key) { ... }
    }
}

Principles

Single File

Everything lives in one file: logic, records, enums, and helper methods. Deployment is copying one file to the PATH; maintenance is editing it in place, with no build.

Zero Dependencies

Only java.base and standard JDK modules. Never add classpath entries to the shebang; the sole exception is a convenience script wrapping an existing application JAR.

Source-File Mode

Java 25 runs the script directly from source. No .java extension, no --enable-preview, no compilation step; the file is the deliverable.

Layers over Ceremony

Interfaces beat grouping classes and enums as namespaces: no default constructor and no values() polluting completion. Grouping extends single-file viability considerably.

Code Style

Installation

Resources

Tools & References