Writing Java Command Line Tools is Cumbersome... Or is it???
Tags: java, bash
Published: 2018-03-02 03:06:01

---
title: Writing Java Command Line Tools is Cumbersome... Or is it???
published: true
description: You can write simple Java tools to be run from the command line without too much hassle as long as you compromise.
tags: java, bash
---

## Simple Java Tools

The JDK comes with a lot of great command-line tools on its own that support development. But on every project I've ever done I find I need just a little more from a tool that I can't get from it which usually means I have to write my own. Every tool I write is for me and it's usually just enough to do the job.

I write a lot of command-line tools to do my extra work. Most Java developers I work with hate the idea of writing a command-line Java tool. They have to be compiled, then they have to be run. Using [Maven](https://maven.apache.org/) helps with the `exec:java` goal. Without Maven, you're writing long `javac` commands to compile, then a long `java` command to run it. But if you're careful and stick to what's in the JDK to write a small tool to get the job done, you can use [a trick with the shell I saw a few years ago](https://gist.github.com/swankjesse/4742818) to make writing Java a lot more like writing Python. 

## A Simple Bash/Java Example

[Previously, I mentioned that I'm a huge fan of Bash](https://dev.to/argherna/bash-functions-and-aliases-for-the-beginning-docker-developer-d4). All you need is a single Java source file and you can write something useful very fast. Consider this source file:

```java
/*bin/mkdir -p /tmp/.java/classes 2> /dev/null

# Compile the program.
#
javac -d /tmp/.java/classes $0

# Run the compiled program only if compilation succeeds.
#
[[ $? -eq 0 ]] && java -cp /tmp/.java/classes $(basename ${0%.*}) "$@"
exit
*/

class SimpleGreeting {
    public static void main(String... args) {
        System.out.println("Hello");
    }
}
```

Running this in the shell is as easy as:

```bash
$ bash SimpleGreeting.java
Hello
$
```

What happened here? Why was that so easy?

The answer is in the comment at the top. Shell scripts often start with a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) interpreter directive. A Java source file with this comment at the top runs the commands in bash.

## Breaking down the Interpreter Directive

It's easy to see what's going on here:

- Create a subdirectory under `/tmp` redirecting output to `/dev/null` if the directory is already there (we don't care about an error message).
- Compile the source file and write the `.class` file in the target directory we just made.
- Run the `class` file from the directory it was just written to, stripping off the suffix of the source file. But only run it if it compiled.
- Exit the process with the return code of the `java` run.

The best part about writing tools this way is that you can make changes to the code and "run" it again without worrying if compilation needs to happen. If you have an error, the compiler will tell you where it is and it won't attempt to run your program.

## Tips

Developing tools this efficiently means you should:

- Use as few dependencies as possible outside the JDK (ideally none)
- Keep your tools SIMPLE, short and in 1 source file.
- Run from the command line using `bash SourceFile.java <args>`.
- Avoid too much logic in the interpreter directive; but don't be afraid to use logic if you have to.

## Sample Tools

I've written some tools that have helped me out tremendously:

- Serving javadoc from the JDK docs zip file and javadoc jar files in your local Maven repository
- Dumping Base64/raw secret keys from keystores
- Dumping a private key from a keystore
- Importing externally generated secret key into a keystore
- Checking LDAP/JDBC connectivity

There are tons of things you can do quickly using just the JDK. It may not have all the [batteries included](https://www.python.org/dev/peps/pep-0206/#batteries-included-philosophy) but it has all the kinds you could find without having to go to a specialty store.

## Acknowledgement

I'd like to again acknowledge [Jessie Wilson](https://github.com/swankjesse) whose gist for `Rip.java` (linked above) inspired this post. He's a smart dude who likes to get things done quickly (obviously).

My examples are in my [Dotfiles GitHub repository](https://github.com/argherna/Dotfiles). Look in the Scripts directory and you'll find my tools along with all the other things I use.