args4j
A small Java class library that makes it easy to parse command line options/arguments.
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'
}