0
votes

Is that a Gradle or Groovy bug?

I want to pass JVM parameters from Gradle to forked JVM, which is unfortunately not done automatically. This is supposed to work, build.gradle:

...
bootRun {
   jvmArgs = System.properties.iterator().findAll{it.key.startsWith('myapp')}.collect {
     "-D${it.key}=${it.value}"}
}
...

It is executed as:

 gradle bootRun -Dmyapp.port=34501 -Dmyapp.member.name=server1

The method collect always return empty collecting if string starts with -D. If it starts with anything else it returns expected two element String collection. If I put space before -D it also works however it breaks the build further downstream on :findMainClass misinterpreting -Dmyapp.port=... with main class name. It simply has to start with -D.

I also tried different string concatenation but as far as the result is a string starting with -D it doesn't work.

Is it a bug or I'm missing something. This is my first Gradle project and I'm not a Groovy developer. Should I report is bug? Where, Groovy or Gradle?

Notes: I'm running Gradle from IntelliJ IDE 2016.1.2
Using Gradle 3.5
Forked JVM runs Spring Boot application

UPDATE
Big apologies, my bad! The truth is, the JVM parameters are passed down using the formula above; the problem is with how I measured it that the weren't. I simply put printouts:

 println "jvmArgs: ${jvmArgs}"
 println "jvmArgs.size: ${jvmArgs.size}"
 println "jvmArgs.class: ${jvmArgs.class}"

..and aborting bootRun if jvmArgs.size == 0, to avoid slow application start; that is I wasn't really checking if parameters were passed or not in the application itself. And it turned out they were.

FYI the outputs were:

 jvmArgs: []
 jvmArgs.size: 0
 jvmArgs.class: java.lang.ArrayList

The class of jvmArgs is reported as a standard ArrayList, but behaves more like a input stream consumer, whatever array is jvmArgs assigned to, that array is scanned for all strings starting with "-D", those are consumed (by what?), passed to some ProcessBuilder (??) and jvmArgs is left only with remaining elements.

Take this example:

 jvmArgs = ["-Daaa=bbb", "foo", "bar"]
 jvmArgs = ["stuff", "-Dccc=ddd", "morestuff"]
 jvmArgs = ["-Deee=fff"]
 println "jvmArgs: ${jvmArgs}"

..it prints jvmArgs: [] and Spring Boot application is launched with -Daaa=bbb -Dccc=ddd -Deee=fff.

Can someone explain what causes this magic stream like property of jvmArgs, which otherwise claims to be a simple ListArray?

1
This is not a bug definitely - it'd have been discovered long time ago. How do you run gradle? From console, which shell? Or maybe from IDE.Opal
you are right, it worked all the time, please read my explanation. The weird behaviour seems to be by design.Espinosa

1 Answers

0
votes

This works for me, but I don't have an explanation for the observed behavior. Hope it helps anyway.

def array = System.properties.iterator().findAll{
   it.key.startsWith('myapp')
}.collect {
   "-D${it.key}=${it.value}"
}
jvmArgs.addAll(array)

EDIT

jvmArgs = ["value"] calls setJvmArgs which, if I haven't missed something, goes from JavaExec to JavaExecHandleBuilder and later JvmOptions. Here, some parameters get removed. Entries beginning with -D gets added to systemproperties instead.

setJvmArgs( ["-Dtest=1", "xx"])
println getJvmArgs() //[xx]
println systemProperties //[test:1]

Does your Application does't have access to that properties?

https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/java/org/gradle/process/internal/JvmOptions.java#L183

EDIT: what's happening in the background

In Groovy, a property assignment calls the setter instead, accessing it will call the getter. They are interchangeably. If you omit the setter and getter pair, it will be generated for you and will be visible in the bytecode. But you can even omit the property itself, only write the getter and setter pair and use it as a property.

class Foo {
   def setBar(String foo) {println "no thanks"}
   String getBar() {"test"}
}
f = new Foo()
f.bar="write Var" // println "no thanks"
println f.bar instanceof String // --> f.getBar() inst... true
println f.bar // 

So you never assigned a List to a variable, but called setJvmArgs(List). You can list all args with getAllJvmArgs() btw.

In combination with delegation strategies and dynamic Properties/Methods, this can be a blessing for DSL programming, but a curse to debug...

http://groovy-lang.org/style-guide.html#_getters_and_setters and google for groovy propertyMissing/groovy metaprogramming/groovy Resolve Strategies if you like to learn more about this topic.