args4j

A small Java class library that makes it easy to parse command line options/arguments.

javaclicommand-lineannotationargument-parsing

Framework

args4j

Overview

args4j is a small Java class library that makes it easy to parse command line options/arguments. Developed by Kohsuke Kawaguchi, it provides a simple annotation-based API. Lightweight and easy to use, it has been beloved as a standard library for command-line argument parsing in Java projects for many years.

Why args4j is chosen:

  • Simplicity: Lightweight with low learning cost
  • Annotation-based: Intuitive and declarative API
  • Maturity: Stable track record over many years
  • Lightweight: Minimal dependencies
  • Compatibility: Works with a wide range of Java versions

Details

History and Evolution

args4j development began around 2005 by Kohsuke Kawaguchi and has grown as a simple and user-friendly command-line parsing library for Java developers. The same author who created Hudson (now Jenkins) has implemented a practical and robust design. It continues to be used in many projects as a stable library.

Position in the Ecosystem

In the Java CLI library ecosystem, it has the following characteristics:

  • Legacy projects: Standard choice for older Java projects
  • Simple tools: Adoption in non-complex CLI tools
  • Educational purposes: Entry-level library for Java learners
  • Lightweight preference: Choice when minimal features are sufficient

Latest Trends (2024-2025)

  • Maintenance mode: No major feature additions, but bug fixes continue
  • Stability focus: Maintains compatibility of existing APIs
  • Modern Java support: Guaranteed operation on Java 11+
  • Maven Central: Continuous support and releases
  • Community: Small but stable user base

Key Features

Core Functionality

  • @Option: Define command-line options
  • @Argument: Define positional arguments
  • Type conversion: Automatic conversion to basic Java types
  • Help generation: Automatic generation of basic usage information
  • Validation: Simple input value validation

Supported Types

  • Basic types: int, boolean, String, File
  • Collections: List, Set, arrays
  • Enum: Enumeration type support
  • Custom types: Extension via custom converters

Design Philosophy

  • Lightweight: Focus on minimal functionality
  • Simplicity: Intuitive API avoiding complex configuration
  • Practicality: Design intended for use in real projects
  • Stability: Stable API over long periods

Pros and Cons

Pros

  • Low learning cost: Simple and intuitive API
  • Lightweight: Small footprint and minimal dependencies
  • Stability: Track record and stable API over many years
  • Compatibility: Works with older Java versions
  • Sufficient functionality: Adequate for basic CLI parsing
  • Maturity: High reliability with few bugs

Cons

  • Limited functionality: Restrictions on advanced features and customization
  • No subcommand support: Not suitable for complex command structures
  • Limited help functionality: Simple automatically generated help
  • Not modern: Slow adoption of new Java features
  • Small community: Limited active development and support

Key Links

Usage Examples

Basic Usage

import org.kohsuke.args4j.*;

public class SampleMain {
    @Option(name = "-v", aliases = "--verbose", usage = "Enable verbose output")
    private boolean verbose = false;

    @Option(name = "-o", aliases = "--output", usage = "Output file name")
    private String outputFile = "output.txt";

    @Option(name = "-n", aliases = "--number", usage = "Numeric parameter")
    private int number = 1;

    @Argument(usage = "Input file name (required)", metaVar = "INPUT")
    private String inputFile;

    public static void main(String[] args) {
        new SampleMain().doMain(args);
    }

    public void doMain(String[] args) {
        CmdLineParser parser = new CmdLineParser(this);

        try {
            parser.parseArgument(args);

            if (inputFile == null) {
                throw new CmdLineException(parser, "Input file is not specified");
            }

        } catch (CmdLineException e) {
            System.err.println(e.getMessage());
            System.err.println("java SampleMain [options...] INPUT");
            parser.printUsage(System.err);
            System.err.println();
            return;
        }

        // Actual processing
        System.out.println("Input file: " + inputFile);
        System.out.println("Output file: " + outputFile);
        System.out.println("Number: " + number);
        if (verbose) {
            System.out.println("Verbose mode is enabled");
        }
    }
}

Collection Types Example

import org.kohsuke.args4j.*;
import java.util.ArrayList;
import java.util.List;

public class CollectionExample {
    @Option(name = "-f", aliases = "--file", usage = "List of files to process")
    private List<String> files = new ArrayList<>();

    @Option(name = "-D", usage = "Property settings (-Dkey=value)")
    private List<String> properties = new ArrayList<>();

    @Option(name = "-e", aliases = "--exclude", usage = "Exclude patterns")
    private String[] excludePatterns;

    @Argument(usage = "Directories to process")
    private List<String> directories = new ArrayList<>();

    public static void main(String[] args) {
        CollectionExample example = new CollectionExample();
        CmdLineParser parser = new CmdLineParser(example);

        try {
            parser.parseArgument(args);
        } catch (CmdLineException e) {
            System.err.println(e.getMessage());
            parser.printUsage(System.err);
            return;
        }

        System.out.println("Files: " + example.files);
        System.out.println("Properties: " + example.properties);
        System.out.println("Exclude patterns: " + java.util.Arrays.toString(example.excludePatterns));
        System.out.println("Directories: " + example.directories);
    }
}

Enum Types and Custom Types Example

import org.kohsuke.args4j.*;
import org.kohsuke.args4j.spi.OptionHandler;
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
import java.io.File;
import java.net.URL;
import java.net.MalformedURLException;

public class TypeExample {
    // Enum type
    public enum LogLevel {
        DEBUG, INFO, WARN, ERROR
    }

    @Option(name = "--log-level", usage = "Log level")
    private LogLevel logLevel = LogLevel.INFO;

    @Option(name = "-f", aliases = "--file", usage = "Configuration file")
    private File configFile;

    @Option(name = "--url", usage = "Resource URL", handler = URLOptionHandler.class)
    private URL resourceUrl;

    @Option(name = "-q", aliases = "--quiet", usage = "Quiet mode")
    private boolean quiet = false;

    // Custom OptionHandler
    public static class URLOptionHandler extends OptionHandler<URL> {
        public URLOptionHandler(CmdLineParser parser, OptionDef option, Setter<? super URL> setter) {
            super(parser, option, setter);
        }

        @Override
        public int parseArguments(Parameters params) throws CmdLineException {
            String param = params.getParameter(0);
            try {
                setter.addValue(new URL(param));
                return 1;
            } catch (MalformedURLException e) {
                throw new CmdLineException(owner, "Invalid URL: " + param);
            }
        }

        @Override
        public String getDefaultMetaVariable() {
            return "URL";
        }
    }

    public static void main(String[] args) {
        TypeExample example = new TypeExample();
        CmdLineParser parser = new CmdLineParser(example);

        try {
            parser.parseArgument(args);
        } catch (CmdLineException e) {
            System.err.println(e.getMessage());
            parser.printUsage(System.err);
            return;
        }

        System.out.println("Log level: " + example.logLevel);
        System.out.println("Config file: " + example.configFile);
        System.out.println("Resource URL: " + example.resourceUrl);
        System.out.println("Quiet mode: " + example.quiet);
    }
}

Validation Example

import org.kohsuke.args4j.*;
import java.io.File;

public class ValidationExample {
    @Option(name = "-p", aliases = "--port", usage = "Server port number", required = true)
    private int port;

    @Option(name = "-h", aliases = "--host", usage = "Host name")
    private String host = "localhost";

    @Option(name = "--config", usage = "Configuration file path")
    private File configFile;

    @Option(name = "--threads", usage = "Number of threads")
    private int threads = 1;

    @Argument(usage = "Working directory", required = true)
    private String workDir;

    public static void main(String[] args) {
        ValidationExample example = new ValidationExample();
        CmdLineParser parser = new CmdLineParser(example);

        try {
            parser.parseArgument(args);
            
            // Custom validation
            example.validate();
            
        } catch (CmdLineException e) {
            System.err.println(e.getMessage());
            System.err.println("java ValidationExample [options...] WORKDIR");
            parser.printUsage(System.err);
            return;
        }

        System.out.println("Starting server: " + example.host + ":" + example.port);
        System.out.println("Working directory: " + example.workDir);
        System.out.println("Threads: " + example.threads);
        if (example.configFile != null) {
            System.out.println("Config file: " + example.configFile);
        }
    }

    private void validate() throws CmdLineException {
        // Port number range check
        if (port < 1 || port > 65535) {
            throw new CmdLineException(null, "Port number should be in range 1-65535: " + port);
        }

        // Thread count check
        if (threads < 1 || threads > 100) {
            throw new CmdLineException(null, "Thread count should be in range 1-100: " + threads);
        }

        // Config file existence check
        if (configFile != null && !configFile.exists()) {
            throw new CmdLineException(null, "Config file not found: " + configFile);
        }

        // Working directory check
        File workDirFile = new File(workDir);
        if (!workDirFile.exists()) {
            throw new CmdLineException(null, "Working directory not found: " + workDir);
        }
        if (!workDirFile.isDirectory()) {
            throw new CmdLineException(null, "Working directory is not a directory: " + workDir);
        }
    }
}

Advanced Example (Help and Error Handling)

import org.kohsuke.args4j.*;
import java.io.File;
import java.io.StringWriter;
import java.io.PrintWriter;

public class AdvancedExample {
    @Option(name = "-h", aliases = "--help", usage = "Show help")
    private boolean help = false;

    @Option(name = "-v", aliases = "--version", usage = "Show version")
    private boolean version = false;

    @Option(name = "-i", aliases = "--input", usage = "Input file", metaVar = "FILE")
    private File inputFile;

    @Option(name = "-o", aliases = "--output", usage = "Output file", metaVar = "FILE")
    private File outputFile;

    @Option(name = "--format", usage = "Output format")
    private String format = "text";

    @Option(name = "--verbose", usage = "Verbose output")
    private boolean verbose = false;

    private static final String VERSION = "1.0.0";
    private static final String PROGRAM_NAME = "advanced-example";

    public static void main(String[] args) {
        new AdvancedExample().run(args);
    }

    private void run(String[] args) {
        CmdLineParser parser = new CmdLineParser(this);
        
        try {
            parser.parseArgument(args);
            
            if (help) {
                printHelp(parser);
                return;
            }
            
            if (version) {
                printVersion();
                return;
            }
            
            // Check required arguments
            if (inputFile == null) {
                throw new CmdLineException(parser, "Input file is not specified");
            }
            
            execute();
            
        } catch (CmdLineException e) {
            System.err.println("Error: " + e.getMessage());
            printUsage(parser);
            System.exit(1);
        } catch (Exception e) {
            System.err.println("Execution error: " + e.getMessage());
            if (verbose) {
                e.printStackTrace();
            }
            System.exit(2);
        }
    }

    private void execute() {
        System.out.println("Processing started...");
        System.out.println("Input file: " + inputFile);
        if (outputFile != null) {
            System.out.println("Output file: " + outputFile);
        }
        System.out.println("Output format: " + format);
        
        if (verbose) {
            System.out.println("Running in verbose mode...");
        }
        
        // Actual processing goes here
        System.out.println("Processing completed");
    }

    private void printHelp(CmdLineParser parser) {
        System.out.println(PROGRAM_NAME + " v" + VERSION);
        System.out.println();
        System.out.println("Usage: java " + getClass().getSimpleName() + " [options...]");
        System.out.println();
        System.out.println("Options:");
        
        // Get usage using StringWriter
        StringWriter sw = new StringWriter();
        parser.printUsage(new PrintWriter(sw));
        System.out.print(sw.toString());
        
        System.out.println();
        System.out.println("Examples:");
        System.out.println("  java " + getClass().getSimpleName() + " -i input.txt -o output.txt");
        System.out.println("  java " + getClass().getSimpleName() + " --input data.csv --format json --verbose");
    }

    private void printVersion() {
        System.out.println(PROGRAM_NAME + " version " + VERSION);
    }

    private void printUsage(CmdLineParser parser) {
        System.err.println();
        System.err.println("Usage: java " + getClass().getSimpleName() + " [options...]");
        parser.printUsage(System.err);
        System.err.println();
        System.err.println("Use --help for more details");
    }
}

Maven Configuration Example

<dependency>
    <groupId>args4j</groupId>
    <artifactId>args4j</artifactId>
    <version>2.37</version>
</dependency>

Gradle Configuration Example

dependencies {
    implementation 'args4j:args4j:2.37'
}