This article describes how I managed command line parameters in a standalone Java application powered by Spring 4.
Tested with Spring 4.x, JOptSimple 4.8.

I had to develop a standalone application (alias Command Line Interface application, or CLI for short).
I was already headed to use Spring (my first CLI application after many webapps) and, having found a nice JOptCommandLinePropertySource in the Spring documentation, I decided to use add JOptSimple to the stack of things to try.

Let’s say that my app would accept the following command parameters (besides “–help”):

java -jar mycliapp.jar –ndays 10 –minrlb 0.5 –dryrun

So I started defining the parameter constants and then the command line parser definition (using the JOptSimple documentation as reference):

public interface CommonBeanConst {
	public static final String ARG_HELP = "help";

	public static final String ARG_NDAYS = "ndays";
	public static final String DESC_NDAYS = "...";
	public static final short DEFAULT_NDAYS = (short) 20;
	public static final String PAR_NDAYS = "N";

	public static final String ARG_MINRLB = "minrlb";
	public static final String DESC_MINRLB = "...";
	public static final float DEFAULT_MINRLB = 0.0f;
	public static final String PAR_MINRLB = "R";

	public static final String ARG_DRYRUN = "dryrun";
	public static final String DESC_DRYRUN = "...";
}

public class Main implements CommonBeanConst {

	public static void main(String[] args) throws IOException {
		Main main = new Main();
		main.execute(args);
	}

	private void execute(String[] args) throws IOException {
		OptionParser parser = createParser();
		// TODO
	}

	private OptionParser createParser() {
		OptionParser parser = new OptionParser();
		parser.accepts(ARG_NDAYS, DESC_NDAYS).withRequiredArg().ofType(Short.class).defaultsTo(DEFAULT_NDAYS).describedAs(PAR_NDAYS);
		parser.accepts(ARG_MINRLB, DESC_MINRLB).withRequiredArg().ofType(Float.class).defaultsTo(DEFAULT_MINRLB).describedAs(PAR_MINRLB);
		parser.accepts(ARG_DRYRUN, DESC_DRYRUN);
		parser.accepts(ARG_HELP).forHelp();
		return parser;
	}
}

Now the important part: parse the command line and add the result to Spring environment’s property sources.

private void execute(String[] args) throws IOException {
	OptionParser parser = createParser();
	try {
		if (args.length > 0) {
			OptionSet options = parser.parse(args);

			if (options.has(ARG_HELP)) {
				parser.printHelpOn(System.out);
			} else {
				context = new AnnotationConfigApplicationContext();
				context.getEnvironment().getPropertySources().addFirst(new JOptCommandLinePropertySource(options));
				context.register(AppConf.class, DatabaseConf.class);
				context.refresh();

				// ...

				context.close();
			}
		} else {
			parser.printHelpOn(System.out);
		}
	} catch (OptionException e) {
		log.error("Parametri errati: " + e.getLocalizedMessage());
		parser.printHelpOn(System.out);
	}
}

Yes, we did it!

From now on, the application can freely access the command line parameters (like any other parameters from the defined PropertySources):

  • using Spring’s Environment, for example env.getProperty(CommonBeanConst.ARG_MINRLB, Float.class)
  • using a placeholder resolver (like this one), you can inject values, for example @Value(“${” + CommonBeanConst.ARG_NDAYS + “}”) private short numDays;

It is possible to instantiate different beans based on command line parameters (this is not the only method, there is a useful @Conditional annotation):

public class AppConf implements EnvironmentAware {

	private Environment env;

	@Override
	public void setEnvironment(Environment environment) {
		env = environment;
	}

	@Bean
	public Saver saver() {
		if (env.containsProperty(CommonBeanConst.ARG_DRYRUN)) {
			return new LogSaver();
		} else {
			return new DatabaseSaver();
		}
	}
}
Annunci