March 17, 2017

Spring Sweets: Access Application Arguments With ApplicationArguments Bean

When we start a Spring Boot application and pass arguments to the application, Spring Boot will capture those arguments and creates a Spring bean of type ApplicationArguments and puts it in the application context. We can use this Spring bean to access the arguments passed to the application. We could for example auto wire the bean in another bean and use the provided argument values. The ApplicationArguments interface has methods to get arguments values that are options and plain argument values. An option argument is prefixed with --, for example --format=xml is a valid option argument. If the argument value is not prefixed with -- it is a plain argument.

In the following example application we have a Spring Boot application with a Spring bean that implements CommandLineRunner. When we define the CommandLineRunner implementation we use the ApplicationArguments bean that is filled by Spring Boot:

package mrhaki.springboot;

import org.jooq.DSLContext;
import org.jooq.Record1;
import org.jooq.Result;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.List;

import static mrhaki.springboot.sql.Tables.USERS;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application, let Spring Boot
     * decide the exit code and use the
     * exit code value for {@code System.exit()} method.
     */
    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }

    /**
     * Find users based on user input given via application arguments.
     * Example usage: {@code java -jar sample.jar --format=csv mrhaki}
     * 
     * @param dslContext JOOQ context to access data.
     * @param applicationArguments Bean filled by Spring Boot 
     *                             with arguments used to start the application
     * @return Bean to run at startup
     */
    @Bean
    CommandLineRunner showUsers(
            final DSLContext dslContext, 
            final ApplicationArguments applicationArguments) {
        
        return (String... arguments) -> {
            // Get arguments passed to the application  that are
            // not option arguments from ApplicationArguments bean.
            // In the following example: java -jar sample.jar mrhaki
            // mrhaki is the non option argument.
            final String name = applicationArguments.getNonOptionArgs().get(0);
            final Result<Record1<String>> result =
                    dslContext.select(USERS.NAME)
                              .from(USERS)
                              .where(USERS.NAME.likeIgnoreCase(name))
                              .fetch();

            // Check if option argument --format is used.
            // In the following example: java -jar sample.jar --format=xml --format=csv
            // format is the option argument with two values: xml, csv.
            if (applicationArguments.containsOption("format")) {
                // Get values for format option argument.
                final List<String> format = applicationArguments.getOptionValues("format");
                if (format.contains("xml")) {
                    result.formatXML(System.out);
                }
                if (format.contains("json")) {
                    result.formatJSON(System.out);
                }
                if (format.contains("html")) {
                    result.formatHTML(System.out);
                }
                if (format.contains("html")) {
                    result.formatCSV(System.out);
                }
            } else {
                result.format(System.out);
            }
        };
    }

}

We have an alternative if we want to use the ApplicationArguments bean in a CommandlineRunner implementation. Spring Boot offers the ApplicationRunner interface. The interface has the method run(ApplicationArguments) we need to implement and gives us directly access to the ApplicationArguments bean. In the next example we refactor the application and use ApplicationRunner:

package mrhaki.springboot;

import org.jooq.DSLContext;
import org.jooq.Record1;
import org.jooq.Result;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.List;

import static mrhaki.springboot.sql.Tables.USEaRS;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application, let Spring Boot
     * decide the exit code and use the
     * exit code value for {@code System.exit()} method.
     */
    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }

    /**
     * Find users based on user input given via application arguments.
     * Example usage: {@code java -jar sample.jar --format=csv mrhaki}
     *
     * @param dslContext JOOQ context to access data.
     * @return Bean to run at startup
     */
    @Bean
    ApplicationRunner showUsers(final DSLContext dslContext) {
        return (ApplicationArguments arguments) -> {
            // Get arguments passed to the application that are
            // not option arguments. 
            // In the following example: java -jar sample.jar mrhaki
            // mrhaki is the non option argument.
            final String name = arguments.getNonOptionArgs().get(0);
            final Result<Record1<String>> result =
                    dslContext.select(USERS.NAME)
                              .from(USERS)
                              .where(USERS.NAME.likeIgnoreCase(name))
                              .fetch();

            // Check if option argument --format is used.
            // In the following example: java -jar sample.jar --format=xml --format=csv
            // format is the option argument with two values: xml, csv.
            if (arguments.containsOption("format")) {
                // Get values for format option argument.
                final List<String> format = arguments.getOptionValues("format");
                if (format.contains("xml")) {
                    result.formatXML(System.out);
                } 
                if (format.contains("json")) {
                    result.formatJSON(System.out);
                }
                if (format.contains("html")) {
                    result.formatHTML(System.out);
                }
                if (format.contains("html")) {
                    result.formatCSV(System.out);
                }
            } else {
                result.format(System.out);
            }
        };
    }

}

Written with Spring Boot 1.5.2.RELEASE.