Problems passing system properties and parameters when running Java class via Gradle - java

I am trying to run a command-line Java app via Gradle as part of a quick integration test. I am porting my build scripts from Maven, where this was easily done via exec-maven-plugin. My two big requirements are:
Being able to pass system properties to the executable Java code
Being able to pass command-line args to the executable Java code
Please note that I am not trying to read these properties in the build script, I'm trying to read them in the Java program that the script builds and executes.
I have found two other SO posts that address Java program execution via Gradle: one with an answer that advocates using apply plugin: "application" in the build file and gradle run at the command line, and another with answers advocating that approach as well as using task execute(type:JavaExec) in the build file and gradle execute at the command line. I have tried both approaches and have not succeeded.
I have two problems:
(1) I cannot get the Java executable to read the system properties
Whether I do this:
build.gradle:
apply plugin: 'application'
mainClassName = "com.mycompany.MyMain"
Command line:
gradle run -Dmyproperty=myvalue
Or this:
build.gradle:
task execute (type:JavaExec) {
main = "com.mycompany.MyMain"
classpath = sourceSets.main.runtimeClasspath
}
Command line:
gradle execute -Dmyproperty=myvalue
In either case, myproperty does not make it through. The code that begins running from MyMain.main (...) reads the myproperty system property as null/missing.
(2) I cannot pass command line arguments
This is probably related to the first problem. In exec-maven-plugin, for example, command line args were themselves passed in via a system property. Is that the case with Gradle, or is there another way to pass command line arguments?
How do I get these variables through? Also, is it better to use apply plugin: 'application' or task execute (type:JavaExec)?

Figured it out. The main issue is that when Gradle forks a new Java process, it does not automatically pass the environment variable values along to the new environment. One has to explicitly pass these variables via the systemProperties property of the task or plugin.
The other issue was understanding how to pass command-line args; these are via the args property on the task or plugin. As with the Maven exec-maven-plugin, they should be passed in on the command line via yet another system property, as a space-delimited list that then needs to be split() before setting args, which accepts List objects. I've named the property exec.args, which is the old Maven name.
It seems both the javaExec and application plugin approach are valid. One might favor the application plugin approach if one wants to use some of its other features (automatically putting together a distribution, etc.)
Here are the solutions:
JavaExec Approach
Command Line:
gradle execute -Dmyvariable=myvalue -Dexec.args="arg1 arg2 arg3"
build.gradle:
task execute (type:JavaExec) {
main = "com.myCompany.MyMain"
classpath = sourceSets.main.runtimeClasspath
/* Can pass all the properties: */
systemProperties System.getProperties()
/* Or just each by name: */
systemProperty "myvariable", System.getProperty("myvariable")
/* Need to split the space-delimited value in the exec.args */
args System.getProperty("exec.args", "").split()
}
Application Plugin Approach
Command Line:
gradle run -Dmyvariable=myvalue -Dexec.args="arg1 arg2 arg3"
build.gradle:
apply plugin: 'application'
mainClassName = "com.mycompany.MyMain"
run {
/* Can pass all the properties: */
systemProperties System.getProperties()
/* Or just each by name: */
systemProperty "myvariable", System.getProperty("myvariable")
/* Need to split the space-delimited value in the exec.args */
args System.getProperty("exec.args", "").split()
}

For those who might not want to pollute your application's system properties by passing unrelated Gradle props, I recommend namespacing your arguments.
tasks.withType(JavaExec) {
System.properties.each { k,v->
if (k.startsWith("prefix.")) {
systemProperty k - "prefix.", v
}
}
}
java ... -Dprefix.my.prop=true will pass my.prop

I'm new to gradle so I needed this and what is working for me with gradle 4.6 seems a little easier for the command line. Instead of parsing 1 arg string you can pass an array of args, and I found a way to pass in all property with one line as well. Combined below:
apply plugin: 'java'
apply plugin: 'org.springframework.boot' <- for my project
task runApp(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'testit.TomcatApp'
// arguments to pass to the application
// args 'myarg1 -rest' <- came in as 1 string
args = ["--myarg1 with spaces even", "--myarg2"]
// and to pass in all -D system property args:
systemProperties = System.properties
}
gradle run -Dwhatever=xxx -Dmyarg2=hey
// Java reading them:
public static void main(String[] args) {
for ( int i = 0; i < args.length; i++ )
{
logger.info( "** args [" + i + "] =" + args[i] + "=" );
}
logger.info( "** -Dwhatever =" + System.getProperty("whatever") + "=" );
logger.info( "** -Dmyarg2 =" + System.getProperty("myarg2") + "=" );
[main] INFO testit.TomcatApp - ** args [0] =--myarg1 with spaces even=
[main] INFO testit.TomcatApp - ** args [1] =--myarg2=
[main] INFO testit.TomcatApp - ** -Dwhatever =xxx=
[main] INFO testit.TomcatApp - ** -Dmyarg2 =hey=

Maybe I am late for the party, but has anyone tried with "set the prop before executing gradle"? I have tested and this works too, apparently.
myVar=myVal gradle test
For example, you can set the active profile like:
SPRING_PROFILES_ACTIVE=dev gradle test
These also work, apparently:(tested)
set myVar=myVal && gradle test # for windows
export myVar=myVal && gradle test # for linux and mac
Be wary that myVar cannot be period-separated; or else only the part before the first period will be taken as key.

Related

Gradle - Run Task for Multiple java programs within a project

Need: To create a run task each, for multiple programs within the same project
Based on the solution suggested in this LINK. I tried as shown below.
Working Code:
task runCustom1(type: JavaExec) {
group = 'Z_Custom_Run'
description = 'Testing for Gradle Run'
classpath sourceSets.main.runtimeClasspath
main = "pkg01.TestGradleRun"
}
task runCustom2(type: JavaExec) {
group = 'Z_Custom_Run'
description = 'Testing for Gradle Run'
classpath sourceSets.main.runtimeClasspath
main = "pkg01.TestGradleRun2"
}
But above method is cumbersome, as I have to generate for many programs & hence tried the below, to see if I can keep the code compact. But it gives an error as shown below.
Trial Code:
def customRunTask(String className, String packagePath){
return tasks.create("run${className}", JavaExec){
group = 'zCustomRun'
description = 'Testing for Gradle Run'
classpath sourceSets.main.runtimeClasspath
main = packagePath
}
}
artifacts {
archives customRunTask("Test1","pkg01.TestGradleRun"),
customRunTask("Test2","pkg01.TestGradleRun2")
}
Error:
A problem occurred evaluating root project 'testJavaFeatures'.
> Cannot convert the provided notation to an object of type ConfigurablePublishArtifact: task ':runTest1'.
The following types/formats are supported:
- Instances of ConfigurablePublishArtifact.
- Instances of PublishArtifact.
- Instances of AbstractArchiveTask, for example jar.
- Instances of Provider<RegularFile>.
- Instances of Provider<Directory>.
- Instances of Provider<File>.
- Instances of RegularFile.
- Instances of Directory.
- Instances of File.
- Maps with 'file' key
Since I am not too conversant with Gradle, seek guidance from the experts on how to fix the error & get it working
you were almost there ... the below should work
def customRunTask(String className, String packagePath){
return tasks.create("run${className}", JavaExec){
group = 'zCustomRun'
description = 'run ${packagePath}.${className}'
classpath sourceSets.main.runtimeClasspath
main = packagePath + '.' + className
}
}
customRunTask('ClassA', 'com.pkg1')
customRunTask('ClassB', 'com.pkg2')
(and remove the artifacts section, from your file)
Not sure what you try to do, but you can generate similar tasks en masse very easily:
List mainClassNames = [ 'pkg01.TestGradleRun', 'pkg01.TestGradleRun2' ]
mainClassNames.each{ name ->
task "runCustom-$name"(type: JavaExec) {
group = 'Z_Custom_Run'
description = "Testing for Gradle Run for $name"
classpath sourceSets.main.runtimeClasspath
main = name
}
}
artifacts {
archives mainClassNames.collect{ ":runCustom-$it" }
}

How to pass multiple parameters in command line when running gradle task?

I've got a java and groovy classes that are being run by gradle task. I have managed to make it work but I do not like the way I have to pass the parameters in command line. Here is how I do it currently via command line: gradle runTask -Pmode"['doStuff','username','password']"
my build.gradle code which takes these parameters looks like this:
if (project.hasProperty("mode")) {
args Eval.me(mode)}
and then I use my arguments/parameters in my java code as follows:
String action = args[0]; //"doStuff"
String name = args[1]; .. //"username"
I was wondering is there a way to pass the parameters in a better way such as:
gradle runTask -Pmode=doStuff -Puser=username -Ppass=password
and how to use them in my java classes.
JavaExec may be the way to go. Just declare a task and pass project parameters to java app:
task myExecTask(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'com.project.MyApplicationMainClass'
args project.getProperty('userName') + ' ' + project.getProperty('password');
}
To run it, simply write gradle myExecTask -PuserName=john -Ppassword=secret
This is working for me:
task myExecTask(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'com.project.MyApplicationMainClass'
args(user, pass); // no need to access user and pass via project.getProperty()
}
args needs to be built as a List of Strings for java main to use.
args now should be in the form of : ['myusername', 'mypassword']
this is the task I had to create for passing arguments through gradle
task
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
systemProperties = [
usr: project.getProperty('usr'),
pwd: project.getProperty('pwd')
]
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty', '--glue', 'location to step def', 'location to feature files']
}
}
}
in order to execute test
$ gradle cucumber -Pusr=<username> -Ppwd=<password>
to access args in your code System.getProperty("usr"), System.getProperty("pwd")

Gradle task - pass arguments to Java application

I have a Java application that runs with a custom gradle task and the application requires some arguments upon being invoked. These are:
programName ( string | -f filename | -d key | -h)
Options:
string Message to be used.
-d key Use default messages, key must be s[hort], m[edium] or l[ong].
-f filename Use specified file as input.
-h Help dialog.
Gradle task looks like:
task run (type: JavaExec){
description = "Secure algorythm testing"
main = 'main.Test'
classpath = sourceSets.main.runtimeClasspath
}
I've tried running gradle run -h and it does not work.
Gradle 4.9+
gradle run --args='arg1 arg2'
This assumes your build.gradle is configured with the Application plugin. Your build.gradle should look similar to this:
plugins {
// Implicitly applies Java plugin
id: 'application'
}
application {
// URI of your main class/application's entry point (required)
mainClassName = 'org.gradle.sample.Main'
}
Pre-Gradle 4.9
Include the following in your build.gradle:
run {
if (project.hasProperty("appArgs")) {
args Eval.me(appArgs)
}
}
Then to run: gradle run -PappArgs="['arg1', 'args2']"
Since Gradle 4.9, the command line arguments can be passed with --args. For example, if you want to launch the application with command line arguments foo --bar, you can use
gradle run --args='foo --bar'
See Also Gradle Application Plugin
How to upgrade Gradle wrapper
If you want to use the same set of arguments all the time, the following is all you need.
run {
args = ["--myarg1", "--myarg2"]
}
Sorry for answering so late.
I figured an answer alike to #xlm 's:
task run (type: JavaExec, dependsOn: classes){
if(project.hasProperty('myargs')){
args(myargs.split(','))
}
description = "Secure algorythm testing"
main = "main.Test"
classpath = sourceSets.main.runtimeClasspath
}
And invoke like:
gradle run -Pmyargs=-d,s
You can find the solution in Problems passing system properties and parameters when running Java class via Gradle . Both involve the use of the args property
Also you should read the difference between passing with -D or with -P that is explained in the Gradle documentation
Of course the answers above all do the job, but still i would like to use something like
gradle run path1 path2
well this can't be done, but what if we can:
gralde run --- path1 path2
If you think it is more elegant, then you can do it, the trick is to process the command line and modify it before gradle does, this can be done by using init scripts
The init script below:
Process the command line and remove --- and all other arguments following '---'
Add property 'appArgs' to gradle.ext
So in your run task (or JavaExec, Exec) you can:
if (project.gradle.hasProperty("appArgs")) {
List<String> appArgs = project.gradle.appArgs;
args appArgs
}
The init script is:
import org.gradle.api.invocation.Gradle
Gradle aGradle = gradle
StartParameter startParameter = aGradle.startParameter
List tasks = startParameter.getTaskRequests();
List<String> appArgs = new ArrayList<>()
tasks.forEach {
List<String> args = it.getArgs();
Iterator<String> argsI = args.iterator();
while (argsI.hasNext()) {
String arg = argsI.next();
// remove '---' and all that follow
if (arg == "---") {
argsI.remove();
while (argsI.hasNext()) {
arg = argsI.next();
// and add it to appArgs
appArgs.add(arg);
argsI.remove();
}
}
}
}
aGradle.ext.appArgs = appArgs
Limitations:
I was forced to use '---' and not '--'
You have to add some global init script
If you don't like global init script, you can specify it in command line
gradle -I init.gradle run --- f:/temp/x.xml
Or better add an alias to your shell:
gradleapp run --- f:/temp/x.xml
You need to pass them as args to the task using project properties, something like:
args = [project.property('h')]
added to your task definition (see the dsl docs)
Then you can run it as:
gradle -Ph run

How to give System property to my test via Gradle and -D

I have a a Java program which reads a System property
System.getProperty("cassandra.ip");
and I have a Gradle build file that I start with
gradle test -Pcassandra.ip=192.168.33.13
or
gradle test -Dcassandra.ip=192.168.33.13
however System.getProperty will always return null.
The only way I found was to add that in my Gradle build file via
test {
systemProperty "cassandra.ip", "192.168.33.13"
}
How Do I do it via -D
The -P flag is for gradle properties, and the -D flag is for JVM properties. Because the test may be forked in a new JVM, the -D argument passed to gradle will not be propagated to the test - it sounds like that is the behavior you are seeing.
You can use the systemProperty in your test block as you have done but base it on the incoming gradle property by passing it with it -P:
test {
systemProperty "cassandra.ip", project.getProperty("cassandra.ip")
}
or alternatively, if you are passing it in via -D
test {
systemProperty "cassandra.ip", System.getProperty("cassandra.ip")
}
Came across this very much problem, except i don't want to list all properties given on the commandline in the gradle script again. Therefore i send all system properties to my test
task integrationTest(type: Test) {
useTestNG()
options {
systemProperties(System.getProperties())
}
}
I had a case where I needed to pass multiple system properties into the test JVM but not all (didn't want to pass in irrelevant ones). Based on the above answers, and by using subMap to filter the ones I needed, this worked for me:
task integrationTest(type: Test) {
// ... Do stuff here ...
systemProperties System.getProperties().subMap(['PROP1', 'PROP2'])
}
In this example, only PROP1 and PROP2 will be passed in, if they exist in gradle's JVM.
Here's a variant that passes numerous project properties to the test JVM as system properties. I prefer project properties over system properties to increase flexibility.
task intTest(type: Test) {
systemProperties project.properties.subMap(["foo", "bar"])
}
Which may be passed on the command-line:
$ gradle intTest -Pfoo=1 -Pbar=2
And retrieved in your test:
String foo = System.getProperty("foo");
Here is something that worked for me
//in build.gradle file
tasks.withType(Test) {
systemProperties = [
ip: System.getProperty('ip', '192.168.33.13'),
]
}
task integrationTests(type: Test){
useTestNG()
}
Suppose if you are using TestNG, you can add the annotation #Parameters as shown below
public class IpAddress {
#Test
#Parameters("ip")
public void printIpAddress(String ip) {
System.out.println(ip);
}
}
Now you are good to execute a gradlew command
./gradlew clean -Dip="xx.xx.xx.xx" integrationTests --tests "IpAddress"
If you want to use #DataProvider to pass the test data, you can pass it like below and execute the same above gradle command to run the test
public class IpAddress {
#DataProvider(name = "GetIP")
private static Object[][] getIp() {
return new Object[][]{
//if -Dip is not provided in command, then by default it gets the value assigned in build.gradle file i.e.'192.168.33.13'
{System.getProperty("ip")},
};
}
#Test(dataProvider = "GetIP")
public void printIpAddress(String ip) {
System.out.println(ip);
}
}
So I've stumbled on that issue today as well, and what worked for me was the following:
ext.env='prod'
test {
systemProperty 'env', System.properties['env'] ?: "${env}"
println "# test environment: " + systemProperties['env']
...
}
I'm calling my test task using -Penv=dev and I get my 'dev' value in my print, or 'prod' if I do not send any value, which is the expected behavior for me.
Value is also accessible on java side, using System.getProperty("env").
My conclusion on the matter is that input value (parameter) is actually stored under System, making it accessible through either System.properties['env'] or System.getProperty("env"), whereas output (system property) is stored in a systemProperties array, making it readable through systemProperties['env'].

How do I execute ant.java properly from gradle?

I'm trying to invoke a jar, but I don't see any output when I run the command without args, and when I do run with args, I get the following error:
[ant:java] The args attribute is deprecated. Please use nested arg elements.
[ant:java] Java Result: 1
How do I invoke ant.java in such a way that I see output and can pass arguments?
task compressJs(){
ant.java(jar:"lib/yuicompressor-2.4.6.jar",fork:true,args:['js/file.js', '-o', 'build/js/file.js'])
}
Your args should be specified like this:
ant.java(jar:"lib/yuicompressor-2.4.6.jar",fork:true) {
arg(value: "js/file.js")
arg(value: "-o")
arg(value: "build/js/file.js")
}
Pretty much it is the same as you would do with ant except using the Groovy style markup builder instead of XML.
By default your output will go to the screen. If you want to redirect it, set the 'output' property.
As I said before, it's best to use the JavaExec task. To execute a Jar, you can do:
task exec(type: JavaExec) {
main = "-jar"
args relativePath("lib/yuicompressor-2.4.6.jar")
args ... // add any other args as necessary
}
The comments in http://issues.gradle.org/browse/GRADLE-1274 also explain how to capture output from ant.java, but using JavaExec is the better solution.
To get the output set the --info flag on gradle or set the outputproperty on ant.java:
task compressJs(){
ant.java(outputproperty: 'cmdOut', jar:"lib/yuicompressor-2.4.6.jar",fork:true,args:['js/file.js', '-o', 'build/js/file.js'])
println(ant.project.properties.cmdOut)
}
The Ant task needs to be invoked in the execution phase, not the configuration phase:
task compressJs() << { // note the <<
ant.java(...)
}
You could also use Gradle's JavaExec task. See the documentation.
In Addition to Chris Dail's answer , you can also use something like this
ant.java(jar:"lib/yuicompressor-2.4.6.jar",fork:true) {
arg(line: "js/file.js -o build/js/file.js")
}
This allows one to declare all the arguments in a single line, very similar to the usage in ANT.

Categories

Resources