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
Android Development for iOS Developers
Android development has greatly improved since the early days. Maybe you tried it out when Android development was done in Eclipse, emulators were slow and buggy, and Java was the required language. Things have changed […]
Add a custom object to your Liquibase diff
Adding a custom object to your liquibase diff is a pretty simple two step process. Create an implementation of DatabaseObject Create an implementation of SnapshotGenerator In my case I wanted to add tracking of Stored […]
Keeping Secrets Out of Terraform State
There are many instances where you will want to create resources via Terraform with secrets that you just don’t want anyone to see. These could be IAM credentials, certificates, RDS DB credentials, etc. One problem […]
Validating Terraform Plans using Open Policy Agent
When developing infrastructure as code using terraform, it can be difficult to test and validate changes without executing the code against a real environment. The feedback loop between writing a line of code and understanding […]