Dec 7, 2017

Passing Command Line Arguments to a Spring Boot Application via a bootRun Task in Gradle

There currently seems to be no easy way to pass arguments to a Spring Boot application launched using the bootRun task in Gradle. There are plenty of stack overflow questions asking about this and at least one active issue for Spring Boot: https://github.com/spring-projects/spring-boot/issues/1176.

I ran into this while working on a small utility application for a previous client. In order to support the conversion of a number of mainframe applications to web applications, they needed to add LDAP network ids to an “operator” authorization DB2 table containing mainframe user ids that was used by existing stored procedures and some of the converted applications. The utility application read in an excel export of the network users and matched up records to the mainframe users, creating an entry for the ldap id with the same authorization privileges.

While developing this utility application I became frustrated that I did not have a good way to pass in the excel workbook location and worksheet name to the application the same way when running it as either an executable jar or via a bootRun task. I did not enjoy having to rebuild the jar to test changes to the application and did not want to pass that information via system properties or to hard code those values in for development (as I did not trust myself to always remember to remove them before committing changes).

In the application I was using a SimpleCommandLinePropertySource to pull out the arguments.

static void main(String[] args) {
...
    PropertySource propertySource = new SimpleCommandLinePropertySource(args)
    log.info("propertySource.properties: ${propertySource.properties}")
 
    List commandLineArgs = propertySource.getNonOptionArgs()
    String excelFilePath = commandLineArgs?.first()
    String workSheetName = commandLineArgs?.last()
....
}

This worked fine when I built the executable jar and ran it like the following:

java -jar ./build/libs/SpringBootArgsExample.jar ./src/test/resources/testData.xlsx sheet1

But I was not able to pass the arguments when I used bootRun or any of the custom bootRun tasks I had extended to set the profile to the correct environment: dev, test, prod.

After some research, I came to the solution of creating a task execution listener that would collect any arguments from the task’s project and pass them on to the task being called as long as the task was a org.springframework.boot.gradle.run.BootRunTask.

class BootRunPropertyListener implements TaskExecutionListener {
    void afterExecute(Task t, TaskState s) {}
 
    void beforeExecute(Task t) {
        if (t instanceof org.springframework.boot.gradle.run.BootRunTask) {
            if (t.project.hasProperty('args')) {
                String arguments = t.project.properties['args']
                Collection args = arguments.split('\\s+')
                t.args = args
            }
        }
    }
}

I then added that listener to the project and created custom tasks that extended org.springframework.boot.gradle.run.BootRunTask.

project.gradle.addListener(new BootRunPropertyListener())
 
task bootRunH2(type: org.springframework.boot.gradle.run.BootRunTask) {
    doFirst() {
        main = project.mainClassName
        classpath = sourceSets.main.runtimeClasspath
        systemProperty "spring.profiles.active", "H2"
    }
}
 
task bootRunDev(type: org.springframework.boot.gradle.run.BootRunTask) {
    doFirst() {
        main = project.mainClassName
        classpath = sourceSets.main.runtimeClasspath
        systemProperty "spring.profiles.active", "Dev"
    }
}

This allowed me to run my different tasks and easily pass in the desired arguments using -Pargs:

gradlew bootRunH2 -Pargs="./src/test/resources/testData.xlsx sheet1"

I know this is only one of many ways to solve this issue, but hopefully it saves someone some time trying to figure out a way to solve it.

I have posted a small example application of the solution to github.

About the Author

Rob Boler profile.

Rob Boler

Sr. Consultant

Rob has always been interested in computers, and remembers first attempting programming a hangman game on an Apple IIE in grade school. He has over 15 years of software development experience using Java / Spring, primarily in the transportation and defense industries. He prides himself as being able to solve complex software problems, while also being able to lead small teams of developers. Rob has a wide range of project experience ranging from dynamic web applications to high volume message processing, to applying Big Data technologies for data pattern analysis.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
Up to Spec: JavaScript Numeric Separators
Let's take a look at the proposal to add Numeric Separators to the JavaScript specification.
Using Conftest to Validate Configuration Files
Conftest is a utility within the Open Policy Agent ecosystem that helps simplify writing validation tests against configuration files. In a previous blog post, I wrote about using the Open Policy Agent utility directly to […]
SwiftGen with Image & Color Asset Catalogs
You might remember back in 2015 when iOS 9 was introduced, and we were finally given a way to manage all of our assets in one place with Asset Catalogs. A few years later, support […]
Tracking Original URL Through Authentication
If you read my other post about refreshing AWS tokens, then you probably have a use case for keeping track of the original requested resource while the user goes through authentication so you can route […]