CliWrapper4J

A library to quickly build Java-friendly APIs to call command line applications.

View on GitHub

CliWrapper4J

A library to quickly build Java-friendly APIs to call command line applications.

Get it from Maven Central:

<dependency>
  <groupId>io.github.scambon</groupId>
  <artifactId>io.github.scambon.cliwrapper4j</artifactId>
  <version>1.1.0</version>
</dependency>

Maven Central JavaDoc.io Built on Java8 Tested on Java 8, 9, 11, 12, 13 Travis CI SonarCloud Maintainability GitHub (Badges can take up to a day or 2 to show latest information; sometimes, these external resources even fail to load)

Example

Here is an example of wrapping the java executable:

@Executable("java")
public interface IJavaCommandLine extends IExecutable {

  @Switch("--help")
  @ExecuteNow
  int help();

  @Switch("-classpath")
  IJavaCommandLine classpath(
      @Converter(FilesWithPathSeparatorParameterConverter.class) File... classpath);

  @Switch("-classpath")
  IJavaCommandLine classpath(
      @Converter(FilesWithPathSeparatorParameterConverter.class) Collection<File> classpath);

  @Switch("-D")
  @Aggregator("")
  @Flattener("=")
  IJavaCommandLine systemProperty(String property, Object value);

  @Switch("-D")
  @Aggregator("")
  @Flattener("=")
  IJavaCommandLine systemPropertyAsStringLength(String property,
      @Converter(StringLengthConverter.class) Object value);

  @Switch("")
  @ExecuteNow
  Result main(String mainQualifiedName);

  @Switch("-version")
  @ExecuteNow
  @Converter(VersionResultConverter.class)
  Version version();

  @Switch("-version")
  @ExecuteNow
  @ReturnCode({})
  @Converter(VersionResultConverter.class)
  Version versionWithoutReturnCodeCheck();

  @Switch("-version")
  @ExecuteNow
  @ReturnCode(1)
  @Converter(VersionResultConverter.class)
  Version versionWithCustomReturnCodeCheck();

  @Switch("-version")
  @ExecuteNow
  VirtualMachineType getVirtualMachineTypeAndConvertReflectively();

  default int getMajorVersion() {
    return version().getMajorVersion();
  }
}

To use this executable wrapper, use the following code:

  IExecutableFactory<IJavaCommandLine> javaFactory = new ReflectiveExecutableFactory<>(IJavaCommandLine.class);
  IJavaCommandLine java = javaFactory.create();
  Version version = java.version();

There is more in this library than this example. See below for further details.

Concepts

@Executable and IExecutable

The base interface that defines the command line.

Your sub-interface must be annotated with @Executable to provide the executable name. Then, define your methods and annotate them with @Switch; some of them will be executable using an additional @ExecuteNow or @ExecuteLater.

There is no need to implement this interface yourself, use a IExecutableFactory instead. You can still define and implement static methods, default methods or even private methods (JDK9+) in your sub-interface.

@Switch

An annotation that describes a switch, i.e. a tag and values to be added to the command line.

Parameters

The method can have parameters:

Parameters then need to be processed with:

Return type

If annotated with @ExecuteNow, then you can add an @Converter to convert the Result into something else. If no @Converter is provided, the default implicit behavior is to use a ResultConverter, which returns Result components or reflectively creates an instance (see JavaDoc for details).

If the method is not annotated with @ExecuteNow, the method must return its interface type for call chaining.

Execution

To trigger execution, use @ExecuteNow or @ExecuteLater in addition to this annotation.

@Converter and IConverter

An annotation that describes what IConverter is to be used to transform from one type to another. This must be placed only on @Switch-annotated methods.

Depending on case, expectations differ:

Case Location Input type Output type Default if not set Comments
Convert execution result Method Result (anything) ResultConverter (returns Result components or reflectively creates an instance, see JavaDoc for details) Only on @ExecuteNow- or @ExecuteLater-annotated methods
Convert parameter to be passed to the command line Parameter (anything) String StringQuotedIfNeededConverter Not compatible with @Extra

Some implementations:

@Flattener and IFlattener

An annotation that describes how to flatten a method parameter values, using the #flattener() class configured with the #value() as its parameter. This should only defined along with a @Switch annotation.

@Aggregator and IAggregator

An annotation that describes how to aggregate a switch and its flattened parameter values, using the #aggregator() class configured with the #value() as its parameter. This should only defined along with a @Switch annotation.

Pre-Processors

You can modify the command line before it is called by providing implementations of ICommandLinePreProcessor to the executable annotation: @Executable(preProcessors={...}).

Pre-processors are chained, i.e. the result of pre-processor 1 is passed to pre-processor 2… The final command line elements are then executed by the IExecutor.

Predefined pre-processors are:

Execution

@ExecuteNow

An annotation that makes a @Switch-annotated method run the execution of the command line now.

Execution

The execution is handled as defined by the @Executor on the method. If none is specified, a ProcessExecutor is implicitly used.

Command line return code

The return code is checked against the expectations defined by the @ReturnCode on the method. If none is specified, 0 is implicitly expected.

Return value

The return type must be compatible with the @Converter on the method. If none is specified, a ResultConverter is implicitly used.

@ExecuteLater

An annotation that makes a @Switch-annotated method run the execution of the command line when the IExecutable#execute() method is called. This is used when the method is not the last segment of the command line.

The semantics (and rules) around this annotation are the same as with @ExecuteNow, but:

@Executor and IExecutor

An annotation for @Switch methods that defines the executor to use to run the command line. By default, a ProcessExecutor is used, which is suitable for non-interactive, short running command lines. Interactive command lines can be executed using subclasses of AbstractInteractiveProcessExecutor or even custom implementations of IExecutor.

@ReturnCode

An annotation that checks that an @ExecuteNow or @ExecuteLater execution return codes are as expected.

By default, the return code is checked to be 0. This is also the case if this annotation is not explicitly used. You can customize the expected returns code with #value(). You can also disable the checking by setting #value() to {}.

@Extra

An annotation for @Switch method parameters that are to be passed to the framework instead of directly to the command line.

Usage include:

The meaning of the parameter values passed as @Extra arguments is left to the implementation of the above interfaces.

This annotation is not compatible with @Converter: values are passed without conversion.

Instantiation

Instantiation of annotation-defined classes such as converters is handled by an IInstantiator. The default instantiator uses reflection and 0-arg public constructors, and its results are cached. You can use your own instantiator by calling new ReflectiveExecutableFactory<>(Class, IInstantiator) instead of new ReflectiveExecutableFactory<>(Class).

Licensing

This project is distributed under the Apache Software License 2.0.

Contributing

Contributions are welcome! See Contributing.