From 3ba8c31ac59a47fddecc859904fe27a17670a9c0 Mon Sep 17 00:00:00 2001 From: Maciej Gajek Date: Mon, 21 Aug 2023 14:08:43 +0200 Subject: [PATCH] Add reading JAVA_OPTS and JDK_JAVA_OPTIONS --- build.sc | 5 ++ .../src/main/scala/scala/cli/ScalaCli.scala | 16 +++++- .../scala/cli/tests/SetupScalaCLITests.scala | 50 +++++++++++++++++-- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/build.sc b/build.sc index d2dac423e8..4852372dbd 100644 --- a/build.sc +++ b/build.sc @@ -846,6 +846,11 @@ trait Cli extends SbtModule with ProtoBuildModule with CliLaunchers def runClasspath = T { super.runClasspath() ++ Seq(localRepoJar()) } + + // Required by the reflection usage in modules/cli/src/test/scala/cli/tests/SetupScalaCLITests.scala + override def forkArgs: T[Seq[String]] = T { + super.forkArgs() ++ Seq("--add-opens=java.base/java.util=ALL-UNNAMED") + } } } diff --git a/modules/cli/src/main/scala/scala/cli/ScalaCli.scala b/modules/cli/src/main/scala/scala/cli/ScalaCli.scala index b2ec2c6e42..4bfd7f136a 100644 --- a/modules/cli/src/main/scala/scala/cli/ScalaCli.scala +++ b/modules/cli/src/main/scala/scala/cli/ScalaCli.scala @@ -177,7 +177,7 @@ object ScalaCli { val jvmoptsLines = jvmoptsContent.linesIterator.toSeq val (javaOpts, otherOpts) = jvmoptsLines.partition(_.startsWith("-D")) javaOpts.foreach { opt => - opt.stripPrefix("-D").split("=", 2).match { + opt.stripPrefix("-D").split("=", 2) match { case Array(key, value) => System.setProperty(key, value) case _ => System.err.println(s"Warning: Invalid java property: $opt") } @@ -192,11 +192,23 @@ object ScalaCli { properties <- configDb.get(Keys.javaProperties).getOrElse(Nil) } properties.foreach { opt => - opt.stripPrefix("-D").split("=", 2).match { + opt.stripPrefix("-D").split("=", 2) match { case Array(key, value) => System.setProperty(key, value) case _ => System.err.println(s"Warning: Invalid java property in config: $opt") } } + + // load java properties from JAVA_OPTS and JDK_JAVA_OPTIONS environment variables + val javaOpts = sys.env.get("JAVA_OPTS").toSeq ++ sys.env.get("JDK_JAVA_OPTIONS").toSeq + + javaOpts + .flatMap(_.split("\\s+")) + .foreach { opt => + opt.stripPrefix("-D").split("=", 2) match { + case Array(key, value) => System.setProperty(key, value) + case _ => // Ignore if not a Java property + } + } } private def main0(args: Array[String]): Unit = { diff --git a/modules/cli/src/test/scala/cli/tests/SetupScalaCLITests.scala b/modules/cli/src/test/scala/cli/tests/SetupScalaCLITests.scala index 307401e1c9..952bee741d 100644 --- a/modules/cli/src/test/scala/cli/tests/SetupScalaCLITests.scala +++ b/modules/cli/src/test/scala/cli/tests/SetupScalaCLITests.scala @@ -2,12 +2,12 @@ package cli.tests import com.eed3si9n.expecty.Expecty.expect -import java.util.Properties +import java.util.{Collections, Properties} import scala.build.internal.Constants import scala.build.tests.TestInputs import scala.cli.ScalaCli -import scala.util.Properties +import scala.jdk.CollectionConverters.{MapHasAsJava, MapHasAsScala} class SetupScalaCLITests extends munit.FunSuite { @@ -16,9 +16,9 @@ class SetupScalaCLITests extends munit.FunSuite { val value = "true" val inputs = TestInputs( os.rel / Constants.jvmPropertiesFileName -> - s"""-Xmx2048m - |-Xms128m - |-Xss8m + s"""-Xignored_1 + |-Xignored_2 + |-Xignored_3 |-D$key=$value |""".stripMargin ) @@ -28,8 +28,48 @@ class SetupScalaCLITests extends munit.FunSuite { ScalaCli.loadJavaProperties(root) expect(sys.props.get(key).contains(value)) + expect(sys.props.get("ignored_1").isEmpty) + expect(sys.props.get("ignored_2").isEmpty) + expect(sys.props.get("ignored_3").isEmpty) // restore original props System.setProperties(currentProps) ) } + + test(s"should read java properties from JAVA_OPTS and JDK_JAVA_OPTIONS") { + // Adapted from https://stackoverflow.com/a/496849 + def setEnvVars(newEnv: Map[String, String]): Unit = { + val classes = classOf[Collections].getDeclaredClasses + val env = System.getenv() + for (cl <- classes) + if (cl.getName.equals("java.util.Collections$UnmodifiableMap")) { + val field = cl.getDeclaredField("m") + field.setAccessible(true) + val obj = field.get(env) + val map = obj.asInstanceOf[java.util.Map[String, String]] + map.clear() + map.putAll(newEnv.asJava) + } + } + + val javaOptsValues = " -Xignored_1 -Dhttp.proxy=4.4.4.4 -Xignored_2" + val jdkJavaOptionsValues = " -Xignored_3 -Dscala-cli=true -Xignored_4" + + TestInputs().fromRoot(root => + // + val currentEnv = System.getenv().asScala.toMap + // modify environment variable of this process + setEnvVars(Map("JAVA_OPTS" -> javaOptsValues, "JDK_JAVA_OPTIONS" -> jdkJavaOptionsValues)) + ScalaCli.loadJavaProperties(root) + expect(sys.props.get("http.proxy").contains("4.4.4.4")) + expect(sys.props.get("scala-cli").contains("true")) + + expect(sys.props.get("ignored_1").isEmpty) + expect(sys.props.get("ignored_2").isEmpty) + expect(sys.props.get("ignored_3").isEmpty) + expect(sys.props.get("ignored_4").isEmpty) + // reset the env + setEnvVars(currentEnv) + ) + } }