How to pass system property to Play JVM when using Play plugin / runPlayBinary

Context: I am using Gradle 5.1.1 with JVM 1.8.0_161 , Play 2.6.15, Scala 2.12.
I have the following plugins defined:

plugins {
id 'idea'
id 'scala'
id 'play'
}

Problem
I am trying to pass some specific system properties to the JVM which gradle creates to run the play application (via the runPlayBinary task). (As it happens this is in order to debug some weird behaviour which I suspect is caused by Gradle’s way of running play - that can be the topic of another post. :smile:

I have seen various conflicting recommendations about this, none of which work. The official gradle play plugin reference documentation is silent on the matter.

I have tried passing the properties via the gradle command line using -D, as in:

-Dmy.property=my.value

…however the property is not passed to the resulting JVM.

I have tried copying the parent JVM’s system properties to the Play JVM’s in a similar way to the JavaExec task, as below:

tasks.withType(PlayRun) { .
systemProperties System.properties
}

…however this fails because neither the implementing class nor any of its superclasses define a suitable setter method, as follows:

  > Exception thrown while executing model rule: copyToTaskContainer
  > Could not find method systemProperties() for arguments

It seems to me that being able to pass System Properties to the JVM is an incredibly basic requirement, so perhaps I am missing some obvious solution? Any assistance would be much appreciated.

It’s assumed to be part of the distribution feature set, specifically CreateStartScripts, so you’re not seeing it. It doesn’t help that the links are broken:

https://gradle.github.io/playframework/#adding-distribution-files

https://gradle.github.io/playframework/#distribution_tasks

https://docs.gradle.org/current/dsl/org.gradle.jvm.application.tasks.CreateStartScripts.html

You can see the setting is defaultJvmOpts from there, so the setting for distributions is:

createMainStartScripts {
  defaultJvmOpts += [
     "-Dmy.property=my.value"
  ]
}

For testing, it goes back to the same problem – it’s defined in the Testing plugin. I think the setting is jvmArgs there, from https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:jvmArgs

test {
   jvmArgs += [
     "-Dmy.property=my.value"
  ]
}

Finally, for the PlayRun task, you want forkOptions.jvmArgs:

tasks.withType(PlayRun) {
    forkOptions.jvmArgs = ["-Dmy.property=foo"]
}

Here’s a full build.gradle file for reference:

// https://github.com/playframework/play-scala-hello-world-tutorial/tree/2.6.x
plugins {
    id 'org.gradle.playframework' version '0.8'
}

repositories {
    jcenter()
    maven {
        name "lightbend-maven-releases"
        url "https://repo.lightbend.com/lightbend/maven-release"
    }
    ivy {
        name "lightbend-ivy-release"
        url "https://repo.lightbend.com/lightbend/ivy-releases"
        layout "ivy"
    }
}

def playV = "2.6.21"
def scalaV = System.getProperty("scala.binary.version", /* default = */ "2.12")

play {
    platform {
        playVersion = playV
        scalaVersion = scalaV
        javaVersion = JavaVersion.VERSION_1_8
    }

    injectedRoutesGenerator = true
}

tasks.withType(PlayRun) {
    forkOptions.jvmArgs = ["-Dmy.property=foo"]
}

tasks.withType(ScalaCompile) {
    scalaCompileOptions.additionalParameters = ["-feature", "-language:implicitConversions"]
}

tasks.withType(RoutesCompile) {
    additionalImports = ["scala.collections.JavaConverters._"]
}

dependencies {
    implementation "commons-lang:commons-lang:2.6"

    implementation "com.typesafe.play:play-guice_$scalaV:$playV"
    implementation group: 'com.typesafe.play', name: 'play-logback_2.12', version: '2.6.21'
    implementation group: 'com.typesafe.play', name: 'filters-helpers_2.12', version: '2.6.21'

    testImplementation "org.scalatestplus.play:scalatestplus-play_$scalaV:3.1.2"
}

Dear Will - thankyou for this. Problem is now that PlayRun task (note I am now using version 0.9 of the “new” Gradle Play plugin) ignores JVM settings and does not have a way to easily set HttpPort (that I can see). I am not a fan of SBT but Gradle is rather behind when it comes to Play.

Update: I have tested the below but the System property is not reaching the actual Play JVM. When I call System.getProperty from a custom play ApplicationLoader (which extends GuiceApplicationLoader), the resulting value is null.

However I can see that some transitive dependencies as resolved by Gradle have brought in some play sub-library versioning conflicts. Perhaps these are causing the unexpected behaviour.