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 <[email protected]>
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 <[email protected]>
---
 .../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();

Reply via email to