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();