You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/07/14 13:24:27 UTC

(juneau) branch master updated: Route production system-property access through Settings abstraction (TODO-234)

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 9a56dc951a Route production system-property access through Settings abstraction (TODO-234)
9a56dc951a is described below

commit 9a56dc951a881cfb6b81ce087f1554648177fec0
Author: James Bognar <ja...@apache.org>
AuthorDate: Tue Jul 14 09:24:11 2026 -0400

    Route production system-property access through Settings abstraction (TODO-234)
    
    Migrate production System.getProperty reads onto the Settings lookup chain so
    they are test-overridable via Settings.setLocal/setGlobal, and document the
    raw-access sites that must intentionally stay unabstracted (SVL $S{...} which
    reads System.getProperty directly, jetty/tomcat bound-port publication consumed
    by jetty.xml, and external logging adapters).
    
    Co-authored-by: Cursor <cu...@cursor.com>
---
 .../src/main/java/org/apache/juneau/config/Config.java    |  6 +++++-
 .../juneau/marshall/parquet/ParquetParserSession.java     | 15 ++++++++++-----
 .../juneau/microservice/jetty/JettyServerComponent.java   |  9 +++++++++
 .../juneau/microservice/tomcat/TomcatServerComponent.java |  9 +++++++++
 .../java/org/apache/juneau/microservice/Microservice.java |  8 ++++++++
 .../org/apache/juneau/rest/mock/classic/MockLogger.java   |  6 ++++++
 6 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
index b28e155717..9a7f917635 100644
--- a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
+++ b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
@@ -513,7 +513,7 @@ public class Config extends Context implements ConfigEventListener {
 			return l;
 		}
 
-		var cmd = System.getProperty("sun.java.command", "not_found").split("\\s+")[0];
+		var cmd = env("sun.java.command", "not_found").split("\\s+")[0];
 		if (cmd.endsWith(".jar") && ! co(cmd, "surefirebooter")) { // HTT - not a .jar during tests
 			cmd = cmd.replaceAll(".*?([^\\\\\\/]+)\\.jar$", "$1");
 			l.add(cmd + ".cfg");
@@ -1157,6 +1157,10 @@ public class Config extends Context implements ConfigEventListener {
 		for (var section : getSectionNames()) {
 			for (var key : getKeys(section)) {
 				var k = (section.isEmpty() ? key : section + '/' + key);
+				// Intentional real-JVM-property export: this method's documented contract is to publish config
+				// entries as process-global system properties so external/third-party code (which reads raw
+				// System.getProperty) can observe them.  Routing through Settings would break that contract, so
+				// the mutation is kept deliberately.
 				System.setProperty(k, getRaw(k));
 			}
 		}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/parquet/ParquetParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/parquet/ParquetParserSession.java
index ace665fec3..b7a95faee9 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/parquet/ParquetParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/parquet/ParquetParserSession.java
@@ -18,6 +18,7 @@ package org.apache.juneau.marshall.parquet;
 
 import static org.apache.juneau.commons.utils.AssertionUtils.*;
 import static org.apache.juneau.commons.utils.Shorts.*;
+import static org.apache.juneau.commons.utils.SystemUtils.*;
 import static org.apache.juneau.marshall.parquet.ParquetSchemaElement.*;
 
 import java.io.*;
@@ -47,8 +48,12 @@ public class ParquetParserSession extends InputStreamParserSession implements Re
 	private static final byte[] MAGIC = "PAR1".getBytes(StandardCharsets.UTF_8);
 	private static final String ARG_ctx = "ctx";
 
-	/** Enable via {@link #setDebugEnabled} or -Djuneau.parquet.debug=true. */
-	private static boolean parquetDebug = "true".equals(System.getProperty("juneau.parquet.debug"));
+	/**
+	 * Explicit override set via {@link #setDebugEnabled}.  When {@code null}, the debug state is read
+	 * from the {@code juneau.parquet.debug} setting through the settings lookup chain (via {@code env(...)}),
+	 * making it test-overridable via {@code Settings.setLocal}/{@code setGlobal}.
+	 */
+	private static Boolean parquetDebugOverride;
 
 	/**
 	 * Enable debug output for schema/column tracing.
@@ -56,16 +61,16 @@ public class ParquetParserSession extends InputStreamParserSession implements Re
 	 * @param enabled <jk>true</jk> to enable debug logging to temp file.
 	 */
 	public static void setDebugEnabled(boolean enabled) {
-		parquetDebug = enabled;
+		parquetDebugOverride = enabled;
 	}
 
 	private static boolean parquetDebug() {
-		return parquetDebug;
+		return parquetDebugOverride != null ? parquetDebugOverride : env("juneau.parquet.debug", false);
 	}
 
 	private static void parquetDebugLog(String msg) {
 		try {
-			var f = java.nio.file.Paths.get(System.getProperty("java.io.tmpdir", "/tmp"), "juneau-parquet-debug.log");
+			var f = java.nio.file.Paths.get(env("java.io.tmpdir", "/tmp"), "juneau-parquet-debug.log");
 			java.nio.file.Files.writeString(f, "[Parquet DEBUG] " + msg + "\n",
 				java.nio.file.StandardOpenOption.CREATE, java.nio.file.StandardOpenOption.APPEND);
 		} catch (@SuppressWarnings("unused") IOException ignored) {
diff --git a/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyServerComponent.java b/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyServerComponent.java
index 14bcb34ebf..c64139344f 100644
--- a/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyServerComponent.java
+++ b/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyServerComponent.java
@@ -212,6 +212,11 @@ public class JettyServerComponent implements MicroserviceListener {
 			var availablePort = findOpenPort(ports);
 
 			if (availablePortEnv.isEmpty())
+				// Intentional real-JVM-property publish: the bundled jetty.xml binds its connector to
+				// $S{availablePort,10000}, and the $S{...} SVL var resolves via raw System.getProperty (bypassing
+				// the Settings override chain).  A Settings-based write would be invisible to jetty.xml, so the
+				// bound port MUST be published as a real system property here.  The @Value-injected availablePortEnv
+				// read side is already Settings-abstracted; only this publish must stay raw.
 				System.setProperty("availablePort", String.valueOf(availablePort));
 
 			// Prefer a @Bean-supplied Server, else build one from jetty.xml.
@@ -312,6 +317,10 @@ public class JettyServerComponent implements MicroserviceListener {
 			}
 
 			if (serverPortEnv.isEmpty())
+				// Intentional real-JVM-property publish: juneau.serverPort is a documented cross-component/external
+				// publication of the bound port, consumed via raw System.getProperty (and $S{...} SVL).  A
+				// Settings-based write would not be visible to those raw readers, so this is kept.  The
+				// @Value-injected serverPortEnv read side is already Settings-abstracted.
 				System.setProperty("juneau.serverPort", String.valueOf(availablePort));
 
 			server.get().start();
diff --git a/juneau-microservice/juneau-microservice-tomcat/src/main/java/org/apache/juneau/microservice/tomcat/TomcatServerComponent.java b/juneau-microservice/juneau-microservice-tomcat/src/main/java/org/apache/juneau/microservice/tomcat/TomcatServerComponent.java
index 89fbd38ee9..5f91a3f7a2 100644
--- a/juneau-microservice/juneau-microservice-tomcat/src/main/java/org/apache/juneau/microservice/tomcat/TomcatServerComponent.java
+++ b/juneau-microservice/juneau-microservice-tomcat/src/main/java/org/apache/juneau/microservice/tomcat/TomcatServerComponent.java
@@ -215,6 +215,11 @@ public class TomcatServerComponent implements MicroserviceListener {
 			var availablePort = findOpenPort(ports);
 
 			if (availablePortEnv.isEmpty())
+				// Intentional real-JVM-property publish: the bound port is exposed as a process-global system
+				// property so external config/templates (e.g. the bundled jetty.xml's $S{availablePort}) can read
+				// it.  The $S{...} SVL var resolves via raw System.getProperty (bypassing the Settings override
+				// chain), so a Settings-based write would be invisible to those consumers.  The @Value-injected
+				// availablePortEnv read side is already Settings-abstracted; only this publish must stay raw.
 				System.setProperty("availablePort", String.valueOf(availablePort));
 
 			// Prefer a @Bean-supplied Tomcat, else build one programmatically via the factory.
@@ -269,6 +274,10 @@ public class TomcatServerComponent implements MicroserviceListener {
 			}
 
 			if (serverPortEnv.isEmpty())
+				// Intentional real-JVM-property publish: juneau.serverPort is a documented cross-component/external
+				// publication of the bound port, consumed via raw System.getProperty (and $S{...} SVL).  A
+				// Settings-based write would not be visible to those raw readers, so this is kept.  The
+				// @Value-injected serverPortEnv read side is already Settings-abstracted.
 				System.setProperty("juneau.serverPort", String.valueOf(availablePort));
 
 			tomcat.get().start();
diff --git a/juneau-microservice/juneau-microservice/src/main/java/org/apache/juneau/microservice/Microservice.java b/juneau-microservice/juneau-microservice/src/main/java/org/apache/juneau/microservice/Microservice.java
index 7789423a1e..e3f1fcec6c 100755
--- a/juneau-microservice/juneau-microservice/src/main/java/org/apache/juneau/microservice/Microservice.java
+++ b/juneau-microservice/juneau-microservice/src/main/java/org/apache/juneau/microservice/Microservice.java
@@ -1166,6 +1166,10 @@ public class Microservice implements ConfigEventListener {
 		var spKeys = config.getKeys("SystemProperties");
 		if (nn(spKeys))
 			for (var key : spKeys)
+				// Intentional real-JVM-property export: the [SystemProperties] config section is a documented
+				// feature whose whole purpose is to push config entries into process-global system properties so
+				// third-party libraries (e.g. SLF4J/logging adapters) that read raw System.getProperty pick them
+				// up.  A Settings-based write would be invisible to those external consumers, so this is kept.
 				System.setProperty(key, config.get("SystemProperties/" + key).orElse(null));
 
 		// --------------------------------------------------------------------------------
@@ -1183,6 +1187,10 @@ public class Microservice implements ConfigEventListener {
 				var logDirFile = resolveFile(logDir);
 				mkdirs(logDirFile, false);
 				logDir = logDirFile.getAbsolutePath();
+				// Intentional real-JVM-property publish: the resolved log directory is exposed as a process-global
+				// system property so external config/templates can reference it via the raw-reading $S{juneau.logDir}
+				// SVL var (SystemPropertiesVar reads System.getProperty directly, bypassing the Settings override
+				// chain).  A Settings-based write would not be visible to $S{...}, so this is kept.
 				System.setProperty("juneau.logDir", logDir);
 
 				var append = coalesce(logConfig.append, config.get("Logging/append").asBoolean().orElse(false));
diff --git a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/classic/MockLogger.java b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/classic/MockLogger.java
index 841370974a..880e854fba 100644
--- a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/classic/MockLogger.java
+++ b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/classic/MockLogger.java
@@ -225,6 +225,12 @@ public class MockLogger extends Logger {
 			synchronized (this) {
 				f = formatter.get();
 				if (f == null) { // HTT - double-checked locking; false branch requires concurrent thread
+					// Intentional, contained global-property mutation: the JDK's SimpleFormatter reads its layout
+					// exclusively from the raw java.util.logging.SimpleFormatter.format system property at
+					// construction time.  There is no constructor/API to inject the format, and the Settings
+					// abstraction cannot intercept a JDK-internal raw read, so we must set the property, construct
+					// the formatter, then restore the prior value.  The mutation is fully guarded by this lock and
+					// paired save/restore.
 					String oldFormat = System.getProperty(FORMAT_PROPERTY);
 					System.setProperty(FORMAT_PROPERTY, format.get());
 					f = new SimpleFormatter();