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/05/27 11:07:21 UTC

(juneau) branch master updated: refactor: finalize framework-internal @Value adoption and tests

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 b832eb2ea7 refactor: finalize framework-internal @Value adoption and tests
b832eb2ea7 is described below

commit b832eb2ea7fbb4f4680a5f5e64880aa129ceb13b
Author: James Bognar <ja...@apache.org>
AuthorDate: Wed May 27 07:07:15 2026 -0400

    refactor: finalize framework-internal @Value adoption and tests
---
 .../apache/juneau/junit/bct/BctConfiguration.java  |  70 ++++++
 .../microservice/jetty/JettyServerComponent.java   |  21 +-
 .../apache/juneau/microservice/Microservice.java   |  35 ++-
 .../juneau/rest/auth/jwt/JwtTokenValidator.java    |  28 ++-
 .../java/org/apache/juneau/rest/RestContext.java   | 104 +++++++--
 .../java/org/apache/juneau/rest/RestOpContext.java |  33 +--
 .../rest/convention/BasicVersionResource.java      |  38 +++-
 .../org/apache/juneau/rest/logger/CallLogger.java  | 131 ++++-------
 .../bct/BctConfiguration_ValueAdoption_Test.java   |  79 +++++++
 .../Microservice_ValueAdoption_Test.java           |  62 ++++++
 .../rest/RestContext_ValueAdoption_Test.java       | 241 +++++++++++++++++++++
 .../jwt/JwtTokenValidator_ValueAdoption_Test.java  |  58 +++++
 .../BasicVersionResource_ValueAdoption_Test.java   |  76 +++++++
 .../rest/logger/CallLogger_ValueAdoption_Test.java | 160 ++++++++++++++
 14 files changed, 1011 insertions(+), 125 deletions(-)

diff --git a/juneau-core/juneau-bct/src/main/java/org/apache/juneau/junit/bct/BctConfiguration.java b/juneau-core/juneau-bct/src/main/java/org/apache/juneau/junit/bct/BctConfiguration.java
index e2ceae840e..33c35c226a 100644
--- a/juneau-core/juneau-bct/src/main/java/org/apache/juneau/junit/bct/BctConfiguration.java
+++ b/juneau-core/juneau-bct/src/main/java/org/apache/juneau/junit/bct/BctConfiguration.java
@@ -20,6 +20,7 @@ import static org.apache.juneau.commons.utils.AssertionUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
 
 import org.apache.juneau.commons.function.*;
+import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.commons.settings.*;
 
 /**
@@ -342,4 +343,73 @@ public class BctConfiguration {
 	static BeanConverter getConverter() {
 		return opt(BctConfiguration.CONVERTER_OVERRIDE.get()).orElseGet(CONVERTER_SUPPLIER.get());
 	}
+
+	/**
+	 * Returns the {@link Value @Value}-resolved defaults for {@link #BCT_SORT_MAPS} and
+	 * {@link #BCT_SORT_COLLECTIONS}, sourced from the active
+	 * {@link org.apache.juneau.commons.settings.Settings Settings} chain (system properties
+	 * &rarr; environment variables &rarr; registered property sources). Each invocation creates
+	 * a fresh, fully-injected instance.
+	 *
+	 * <p>
+	 * This is the canonical seam for &quot;BCT default sort flags&quot; reads — production code that
+	 * makes a per-call decision based on the resolved defaults should call
+	 * {@code BctConfiguration.defaults().isSortMaps()} / {@code .isSortCollections()} instead of
+	 * hand-rolling its own {@code System.getProperty(...)} read.
+	 *
+	 * @return A new {@link Defaults} instance, populated via {@link BeanInstantiator}.
+	 */
+	public static Defaults defaults() {
+		return BeanInstantiator.of(Defaults.class, BasicBeanStore.INSTANCE).run();
+	}
+
+	/**
+	 * Holder for {@link Value @Value}-resolved BCT default sort flags.
+	 *
+	 * <p>
+	 * Constructed by {@link #defaults()} via {@link BeanInstantiator}; both fields are populated
+	 * by {@link Value @Value} annotations whose SVL expressions inline the canonical
+	 * {@link #BCT_SORT_MAPS} and {@link #BCT_SORT_COLLECTIONS} property names as string literals
+	 * (per the project-wide rule that every {@code @Value} expression is a fully self-contained
+	 * string literal &mdash; no concatenation with Java constants). The constants remain the
+	 * canonical reference for external callers ({@link Listifiers}, {@code BctConfigExtension},
+	 * and the {@code BctConfiguration.set/get} helpers).
+	 */
+	public static class Defaults {
+
+		/**
+		 * The {@code Bct.sortMaps} default; {@code true} when the {@link #BCT_SORT_MAPS} property is
+		 * set to a truthy value, {@code false} otherwise.
+		 */
+		@Value("${Bct.sortMaps:false}")
+		boolean sortMaps;
+
+		/**
+		 * The {@code Bct.sortCollections} default; {@code true} when the {@link #BCT_SORT_COLLECTIONS}
+		 * property is set to a truthy value, {@code false} otherwise.
+		 */
+		@Value("${Bct.sortCollections:false}")
+		boolean sortCollections;
+
+		/** Constructor &mdash; instantiated by {@link BeanInstantiator} via {@link #defaults()}. */
+		protected Defaults() {}
+
+		/**
+		 * Returns the resolved {@code Bct.sortMaps} default.
+		 *
+		 * @return {@code true} when the {@link #BCT_SORT_MAPS} property is set to a truthy value, {@code false} otherwise.
+		 */
+		public boolean isSortMaps() {
+			return sortMaps;
+		}
+
+		/**
+		 * Returns the resolved {@code Bct.sortCollections} default.
+		 *
+		 * @return {@code true} when the {@link #BCT_SORT_COLLECTIONS} property is set to a truthy value, {@code false} otherwise.
+		 */
+		public boolean isSortCollections() {
+			return sortCollections;
+		}
+	}
 }
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 3ee73234f9..56c950274f 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
@@ -31,6 +31,8 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.logging.*;
 
 import org.apache.juneau.commons.inject.BeanStore;
+import org.apache.juneau.commons.inject.Value;
+import org.apache.juneau.commons.reflect.ClassInfo;
 import org.apache.juneau.config.event.*;
 import org.apache.juneau.cp.*;
 import org.apache.juneau.microservice.*;
@@ -85,6 +87,20 @@ public class JettyServerComponent implements MicroserviceListener {
 	private final AtomicReference<Server> server = new AtomicReference<>();
 	private final AtomicReference<Microservice> microservice = new AtomicReference<>();
 
+	/**
+	 * Env-driven sentinel for {@code availablePort}; {@link Optional#empty()} when unset (in which case
+	 * {@link #onStart(Microservice)} publishes the bound port back as the {@code availablePort} system property).
+	 */
+	@Value("${availablePort}")
+	Optional<String> availablePortEnv;
+
+	/**
+	 * Env-driven sentinel for {@code juneau.serverPort}; {@link Optional#empty()} when unset (in which case
+	 * {@link #onStart(Microservice)} publishes the bound port back as the {@code juneau.serverPort} system property).
+	 */
+	@Value("${juneau.serverPort}")
+	Optional<String> serverPortEnv;
+
 	private static int[] parseIntArray(String csv) {
 		if (csv == null || csv.isEmpty())
 			return new int[0];
@@ -172,6 +188,7 @@ public class JettyServerComponent implements MicroserviceListener {
 		try {
 			microservice.set(ms);
 			var store = ms.getBeanStore();
+			ClassInfo.of(this).inject(this, store);
 			var cf = ms.getConfig();
 			var mf = ms.getManifest();
 			var vr = ms.getVarResolver();
@@ -184,7 +201,7 @@ public class JettyServerComponent implements MicroserviceListener {
 					.orElseGet(() -> mf.get("Jetty-Port").map(JettyServerComponent::parseIntArray).orElseGet(() -> ints(8000)));
 			var availablePort = findOpenPort(ports);
 
-			if (env("availablePort").isEmpty())
+			if (availablePortEnv == null || availablePortEnv.isEmpty())
 				System.setProperty("availablePort", String.valueOf(availablePort));
 
 			// Prefer a @Bean-supplied Server, else build one from jetty.xml.
@@ -265,7 +282,7 @@ public class JettyServerComponent implements MicroserviceListener {
 				addServlet(servlet, pathSpecs);
 			}
 
-			if (env("juneau.serverPort").isEmpty())
+			if (serverPortEnv == null || serverPortEnv.isEmpty())
 				System.setProperty("juneau.serverPort", String.valueOf(availablePort));
 
 			server.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 b423351d1c..4dbad4aff1 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
@@ -124,7 +124,7 @@ public class Microservice implements ConfigEventListener {
 		Scanner consoleReader;
 		PrintWriter consoleWriter;
 		MicroserviceListener listener;
-		File workingDir = env("juneau.workingDir").map(File::new).orElse(null);
+		File workingDir;
 		WritableBeanStore beanStore;
 		BeanStore overridingBeanStore;
 		List<Class<?>> configurations = list();
@@ -134,6 +134,21 @@ public class Microservice implements ConfigEventListener {
 		 */
 		protected Builder() {}
 
+		/**
+		 * {@link Inject @Inject}-annotated initializer that populates {@link #workingDir} from the
+		 * {@code juneau.workingDir} property (system property &rarr; environment variable &rarr;
+		 * registered property sources) when the property is set. Has no effect if {@code workingDir}
+		 * was already set programmatically.
+		 *
+		 * @param workingDirEnv The resolved {@code juneau.workingDir} value, or {@code null}/empty
+		 * 	when not configured.
+		 */
+		@Inject
+		public void initWorkingDirFromEnv(@Value("${juneau.workingDir}") String workingDirEnv) {
+			if (workingDir == null && workingDirEnv != null && !workingDirEnv.isEmpty())
+				workingDir = new File(workingDirEnv);
+		}
+
 		/**
 		 * Copy constructor.
 		 *
@@ -621,10 +636,26 @@ public class Microservice implements ConfigEventListener {
 	/**
 	 * Creates a new builder for this object.
 	 *
+	 * <p>
+	 * Routes builder construction through {@link BeanInstantiator} so that the
+	 * {@code juneau.workingDir} env default is resolved through the active
+	 * {@link org.apache.juneau.commons.settings.Settings Settings} chain.
+	 *
 	 * @return A new microservice builder.
 	 */
 	public static Builder create() {
-		return new Builder();
+		return create(BasicBeanStore.INSTANCE);
+	}
+
+	/**
+	 * Creates a new builder for this object using the supplied {@link BeanStore} for
+	 * {@link Value @Value}-resolution and dependency injection.
+	 *
+	 * @param beanStore The bean store to use for dependency injection.
+	 * @return A new microservice builder.
+	 */
+	public static Builder create(BeanStore beanStore) {
+		return BeanInstantiator.of(Builder.class, beanStore).run();
 	}
 
 	/**
diff --git a/juneau-rest/juneau-rest-server-jwt/src/main/java/org/apache/juneau/rest/auth/jwt/JwtTokenValidator.java b/juneau-rest/juneau-rest-server-jwt/src/main/java/org/apache/juneau/rest/auth/jwt/JwtTokenValidator.java
index 738e869230..6f304c09cb 100644
--- a/juneau-rest/juneau-rest-server-jwt/src/main/java/org/apache/juneau/rest/auth/jwt/JwtTokenValidator.java
+++ b/juneau-rest/juneau-rest-server-jwt/src/main/java/org/apache/juneau/rest/auth/jwt/JwtTokenValidator.java
@@ -24,6 +24,7 @@ import java.text.*;
 import java.time.*;
 import java.util.*;
 
+import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.rest.auth.*;
 
 import com.nimbusds.jose.*;
@@ -99,10 +100,26 @@ public class JwtTokenValidator implements TokenValidator {
 	/**
 	 * Static creator.
 	 *
+	 * <p>
+	 * Routes builder construction through {@link BeanInstantiator} so the
+	 * {@link Builder#jwksCacheTtl jwksCacheTtl} default is resolved via {@link Value @Value} from the
+	 * active {@link org.apache.juneau.commons.settings.Settings Settings} chain
+	 * ({@code juneau.jwt.jwksCacheTtl} property &rarr; {@code PT5M}).
+	 *
 	 * @return A new builder.
 	 */
 	public static Builder create() {
-		return new Builder();
+		return create(BasicBeanStore.INSTANCE);
+	}
+
+	/**
+	 * Static creator with explicit bean store.
+	 *
+	 * @param beanStore The bean store to use for dependency injection.
+	 * @return A new builder.
+	 */
+	public static Builder create(BeanStore beanStore) {
+		return BeanInstantiator.of(Builder.class, beanStore).run();
 	}
 
 	/**
@@ -116,7 +133,14 @@ public class JwtTokenValidator implements TokenValidator {
 		private String audience;
 		private Set<JWSAlgorithm> algorithms = new LinkedHashSet<>(Arrays.asList(JWSAlgorithm.RS256, JWSAlgorithm.ES256));
 		private Duration clockSkew = Duration.ofSeconds(60);
-		private Duration jwksCacheTtl = Duration.ofMinutes(5);
+
+		/**
+		 * JWKS cache TTL; populated via {@link Value @Value} from
+		 * {@code juneau.jwt.jwksCacheTtl} (ISO-8601 duration, default {@code PT5M} = 5 minutes).
+		 */
+		@Value("${juneau.jwt.jwksCacheTtl:PT5M}")
+		Duration jwksCacheTtl;
+
 		private Clock clock = Clock.systemUTC();
 
 		/** Constructor. */
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 6105d8a180..c5c0ee7dff 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -871,6 +871,75 @@ public class RestContext extends Context {
 		return u(toList(s));
 	});
 
+	//---------------------------------------------------------------------------------------------
+	// @Value-injected env-driven defaults
+	//
+	// Populated by ClassInfo.of(this).inject(this, beanStore) during construction; consumed by the
+	// memoizer lambdas below (which run lazily, i.e. after injection has completed). Each field
+	// is the env-driven default that flows into mergeReplacedStringAttribute / mergeReplacedBooleanAttribute
+	// as the initial value before the @Rest annotation chain is walked.
+	//---------------------------------------------------------------------------------------------
+
+	/** Env-driven default for {@code @Rest(debugDefault)}; consumed by the {@link #debugEnablement} memoizer. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.debugDefault:}")
+	private String defaultDebugDefault;
+
+	/** Env-driven default for the call-logger debug level fallback in the {@link #debugConfig} memoizer. */
+	@org.apache.juneau.commons.inject.Value("${juneau.restLogger.level:INFO}")
+	private String defaultDebugLevel;
+
+	/** Env-driven default for {@code @Rest(allowedHeaderParams)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.allowedHeaderParams:Accept,Content-Type}")
+	private String defaultAllowedHeaderParams;
+
+	/** Env-driven default for {@code @Rest(allowedMethodHeaders)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.allowedMethodHeaders:}")
+	private String defaultAllowedMethodHeaders;
+
+	/** Env-driven default for {@code @Rest(allowedMethodParams)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.allowedMethodParams:HEAD,OPTIONS}")
+	private String defaultAllowedMethodParams;
+
+	/** Env-driven default for {@code @Rest(disableContentParam)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.disableContentParam:false}")
+	private boolean defaultDisableContentParam;
+
+	/** Env-driven default for {@code @Rest(renderResponseStackTraces)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.renderResponseStackTraces:false}")
+	private boolean defaultRenderResponseStackTraces;
+
+	/** Env-driven default for {@code @Rest(problemDetails)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.problemDetails:false}")
+	private boolean defaultProblemDetails;
+
+	/** Env-driven default for {@code @Rest(virtualThreads)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.virtualThreads:false}")
+	private boolean defaultVirtualThreads;
+
+	/** Env-driven default for {@code @Rest(eagerInit)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.eagerInit:false}")
+	private boolean defaultEagerInit;
+
+	/** Env-driven default for {@code @Rest(clientVersionHeader)}. */
+	@org.apache.juneau.commons.inject.Value("${RestContext.clientVersionHeader:Client-Version}")
+	private String defaultClientVersionHeader;
+
+	/** Env-driven default for {@code @Rest(uriRelativity)}; resolves to empty string when unset (treated as "no default"). */
+	@org.apache.juneau.commons.inject.Value("${RestContext.uriRelativity:}")
+	private String defaultUriRelativity;
+
+	/** Env-driven default for {@code @Rest(uriAuthority)}; {@link Optional#empty()} when unset (preserves null-vs-empty distinction). */
+	@org.apache.juneau.commons.inject.Value("${RestContext.uriAuthority}")
+	private Optional<String> defaultUriAuthority;
+
+	/** Env-driven default for {@code @Rest(uriContext)}; {@link Optional#empty()} when unset (preserves null-vs-empty distinction). */
+	@org.apache.juneau.commons.inject.Value("${RestContext.uriContext}")
+	private Optional<String> defaultUriContext;
+
+	/** Env-driven default for {@code @Rest(uriResolution)}; resolves to empty string when unset (treated as "no default"). */
+	@org.apache.juneau.commons.inject.Value("${RestContext.uriResolution:}")
+	private String defaultUriResolution;
+
 	/**
 	 * The {@link DebugEnablement} for this resource.
 	 *
@@ -888,7 +957,7 @@ public class RestContext extends Context {
 		// If neither a debugDefault annotation value NOR a pre-registered Enablement bean is present, fall back
 		// to the @Rest(debug=true|false) boolean flag — ALWAYS when set, NEVER otherwise.
 		var bs = beanStore();
-		String debugDefaultStr = mergeReplacedStringAttribute(PROPERTY_debugDefault, env("RestContext.debugDefault").orElse(null));
+		String debugDefaultStr = mergeReplacedStringAttribute(PROPERTY_debugDefault, defaultDebugDefault);
 		Enablement resolvedDebugDefault = null;
 		if (nn(debugDefaultStr) && !debugDefaultStr.isBlank())
 			resolvedDebugDefault = Enablement.fromString(debugDefaultStr);
@@ -923,7 +992,7 @@ public class RestContext extends Context {
 			.reduce((first, second) -> second)
 			.orElse("");
 		var format = formatType == null ? new BasicTextFormat() : BeanInstantiator.of(DebugFormat.class, bs).type(formatType).run();
-		var level = StringUtils.isNotBlank(levelStr) ? Level.parse(levelStr) : Level.parse(env(CallLogger.SP_level, "INFO"));
+		var level = StringUtils.isNotBlank(levelStr) ? Level.parse(levelStr) : Level.parse(defaultDebugLevel);
 		var mode2 = mode;
 		return new DebugConfig(bs) {
 			@Override
@@ -1939,6 +2008,11 @@ public class RestContext extends Context {
 				.addBean(AnnotationWorkList.class, annotationWork);
 			// @formatter:on
 
+			// Inject @Value-annotated env-driven defaults onto this RestContext instance so that the
+			// memoizer lambdas below (which run lazily on first .get() call) read fully resolved values.
+			// Must run BEFORE isEagerInit() since the eagerInit memoizer itself reads defaultEagerInit.
+			ClassInfo.of(this).inject(this, beanStore);
+
 			if (isEagerInit()) {
 				// Force-fire the framework-bean memoizers in dependency-friendly order so their @Rest()
 				// annotation walks (e.g. `@Rest(partParser=…)`, `@Rest(partSerializer=…)`, `@Rest(encoders=…)`,
@@ -2128,42 +2202,42 @@ public class RestContext extends Context {
 	 * default {@code "Accept,Content-Type"}.
 	 */
 	private final Memoizer<Set<String>> allowedHeaderParams = memoizer(() ->
-		Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedHeaderParams, env("RestContext.allowedHeaderParams", "Accept,Content-Type")))));
+		Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedHeaderParams, defaultAllowedHeaderParams))));
 
 	/**
 	 * HTTP method names that may be specified via a request header; resolved from {@code @Rest(allowedMethodHeaders)},
 	 * default empty.
 	 */
 	private final Memoizer<Set<String>> allowedMethodHeaders = memoizer(() ->
-		Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedMethodHeaders, env("RestContext.allowedMethodHeaders").orElse("")))));
+		Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedMethodHeaders, defaultAllowedMethodHeaders))));
 
 	/**
 	 * HTTP method names that may be specified via URL query parameter; resolved from {@code @Rest(allowedMethodParams)},
 	 * default {@code "HEAD,OPTIONS"}.
 	 */
 	private final Memoizer<Set<String>> allowedMethodParams = memoizer(() ->
-		Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedMethodParams, env("RestContext.allowedMethodParams", "HEAD,OPTIONS")))));
+		Collections.unmodifiableSet(newCaseInsensitiveSet(mergeReplacedStringAttribute(PROPERTY_allowedMethodParams, defaultAllowedMethodParams))));
 
 	/**
 	 * Whether a {@code &content=} URL parameter may override the request body; inverse of
 	 * {@code @Rest(disableContentParam)}.
 	 */
 	private final Memoizer<Boolean> allowContentParam = memoizer(() ->
-		!mergeReplacedBooleanAttribute(PROPERTY_disableContentParam, env("RestContext.disableContentParam", false)));
+		!mergeReplacedBooleanAttribute(PROPERTY_disableContentParam, defaultDisableContentParam));
 
 	/**
 	 * Whether exception stack traces are rendered in error responses; resolved from
 	 * {@code @Rest(renderResponseStackTraces)}.
 	 */
 	private final Memoizer<Boolean> renderResponseStackTraces = memoizer(() ->
-		mergeReplacedBooleanAttribute(PROPERTY_renderResponseStackTraces, env("RestContext.renderResponseStackTraces", false)));
+		mergeReplacedBooleanAttribute(PROPERTY_renderResponseStackTraces, defaultRenderResponseStackTraces));
 
 	/**
 	 * Whether the resource emits RFC 7807 {@code application/problem+json} responses; resolved from
 	 * {@code @Rest(problemDetails)}.
 	 */
 	private final Memoizer<Boolean> problemDetails = memoizer(() ->
-		mergeReplacedBooleanAttribute(PROPERTY_problemDetails, env("RestContext.problemDetails", false)));
+		mergeReplacedBooleanAttribute(PROPERTY_problemDetails, defaultProblemDetails));
 
 	/**
 	 * Whether the resource opts into per-request virtual-thread dispatch on Java 21+; resolved from
@@ -2174,7 +2248,7 @@ public class RestContext extends Context {
 	 * than Java 21 the flag is logged once and ignored — see {@link #virtualThreadExecutor}.
 	 */
 	private final Memoizer<Boolean> virtualThreadsEnabled = memoizer(() ->
-		mergeReplacedBooleanAttribute(PROPERTY_virtualThreads, env("RestContext.virtualThreads", false)));
+		mergeReplacedBooleanAttribute(PROPERTY_virtualThreads, defaultVirtualThreads));
 
 	/**
 	 * Configurable async-response timeout (milliseconds) applied by {@code AsyncResponseProcessor} to
@@ -2233,14 +2307,14 @@ public class RestContext extends Context {
 	 * resolved from {@code @Rest(eagerInit)}.
 	 */
 	private final Memoizer<Boolean> eagerInit = memoizer(() ->
-		mergeReplacedBooleanAttribute(PROPERTY_eagerInit, env("RestContext.eagerInit", false)));
+		mergeReplacedBooleanAttribute(PROPERTY_eagerInit, defaultEagerInit));
 
 	/**
 	 * The request header used for client-version matching; resolved from {@code @Rest(clientVersionHeader)},
 	 * default {@code "Client-Version"}.
 	 */
 	private final Memoizer<String> clientVersionHeader = memoizer(() ->
-		mergeReplacedStringAttribute(PROPERTY_clientVersionHeader, env("RestContext.clientVersionHeader", "Client-Version")));
+		mergeReplacedStringAttribute(PROPERTY_clientVersionHeader, defaultClientVersionHeader));
 
 	/**
 	 * The {@link UriRelativity} strategy for URI resolution in this resource.
@@ -2251,7 +2325,7 @@ public class RestContext extends Context {
 	 */
 	private final Memoizer<UriRelativity> uriRelativity = memoizer(() -> {
 		var v = new AtomicReference<>(
-			parseEnumConstant(UriRelativity.class, resolve(emptyIfNull(env("RestContext.uriRelativity").get())))
+			parseEnumConstant(UriRelativity.class, resolve(emptyIfNull(defaultUriRelativity)))
 				.orElse(UriRelativity.RESOURCE)
 		);
 		restAnnotationsForPropertySortedByRank(PROPERTY_uriRelativity).forEach(ai -> ai.getString(PROPERTY_uriRelativity).filter(StringUtils::isNotBlank).ifPresent(s ->
@@ -2268,7 +2342,7 @@ public class RestContext extends Context {
 	 * blocked by {@code noInherit}. {@code null} means no override.
 	 */
 	private final Memoizer<String> uriAuthority = memoizer(() -> {
-		String local = mergeReplacedStringAttribute(PROPERTY_uriAuthority, env("RestContext.uriAuthority").orElse(null));
+		String local = mergeReplacedStringAttribute(PROPERTY_uriAuthority, defaultUriAuthority.orElse(null));
 		if (nn(local))
 			return local;
 		var pc = parentContext();
@@ -2283,7 +2357,7 @@ public class RestContext extends Context {
 	 * blocked by {@code noInherit}. {@code null} means no override.
 	 */
 	private final Memoizer<String> uriContext = memoizer(() -> {
-		String local = mergeReplacedStringAttribute(PROPERTY_uriContext, env("RestContext.uriContext").orElse(null));
+		String local = mergeReplacedStringAttribute(PROPERTY_uriContext, defaultUriContext.orElse(null));
 		if (nn(local))
 			return local;
 		var pc = parentContext();
@@ -2299,7 +2373,7 @@ public class RestContext extends Context {
 	 */
 	private final Memoizer<UriResolution> uriResolution = memoizer(() -> {
 		var v = new AtomicReference<>(
-			parseEnumConstant(UriResolution.class, resolve(emptyIfNull(env("RestContext.uriResolution").get())))
+			parseEnumConstant(UriResolution.class, resolve(emptyIfNull(defaultUriResolution)))
 				.orElse(UriResolution.ROOT_RELATIVE)
 		);
 		restAnnotationsForPropertySortedByRank(PROPERTY_uriResolution).forEach(ai -> ai.getString(PROPERTY_uriResolution).filter(StringUtils::isNotBlank).ifPresent(s ->
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
index 96e59eba17..d33808e7b0 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
@@ -19,7 +19,6 @@ package org.apache.juneau.rest;
 import org.apache.juneau.commons.http.MediaType;
 import static org.apache.juneau.commons.reflect.AnnotationTraversal.*;
 import static org.apache.juneau.commons.utils.CollectionUtils.*;
-import static org.apache.juneau.commons.utils.IoUtils.UTF8;
 import static org.apache.juneau.commons.utils.StringUtils.*;
 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
@@ -97,17 +96,21 @@ public class RestOpContext extends Context implements Comparable<RestOpContext>
 
 	private static final AnnotationProvider AP = AnnotationProvider.INSTANCE;
 
-	private static Charset envDefaultRestCharset() {
-		return env("RestContext.defaultCharset").map(Charset::forName).orElse(UTF8);
-	}
-
-	private static long envDefaultRestMaxInput() {
-		return env("RestContext.maxInput").map(RestOpContext::parseMaxInputEnv).orElse(100_000_000L);
-	}
+	/**
+	 * Env-driven default charset for this operation, populated by {@link ClassInfo#inject(Object, BeanStore)}
+	 * before the {@link #defaultCharset} memoizer fires; resolved from {@code RestContext.defaultCharset} or
+	 * {@code "UTF-8"}.
+	 */
+	@org.apache.juneau.commons.inject.Value("${RestContext.defaultCharset:UTF-8}")
+	private String defaultCharsetName;
 
-	private static long parseMaxInputEnv(String value) {
-		return parseLongWithSuffix(value);
-	}
+	/**
+	 * Env-driven default max-input string for this operation, populated by {@link ClassInfo#inject(Object, BeanStore)}
+	 * before the {@link #maxInput} memoizer fires; resolved from {@code RestContext.maxInput} or
+	 * {@code "100000000"}.
+	 */
+	@org.apache.juneau.commons.inject.Value("${RestContext.maxInput:100000000}")
+	private String defaultMaxInputString;
 
 	/**
 	 * Internal construction-time state holder.
@@ -288,7 +291,7 @@ public class RestOpContext extends Context implements Comparable<RestOpContext>
 			if (rv != null && !rv.isEmpty())
 				return Charset.forName(rv);
 		}
-		return envDefaultRestCharset();
+		return Charset.forName(defaultCharsetName);
 	});
 
 	/**
@@ -617,7 +620,7 @@ public class RestOpContext extends Context implements Comparable<RestOpContext>
 			if (rv != null && !rv.isEmpty())
 				return parseLongWithSuffix(rv);
 		}
-		return envDefaultRestMaxInput();
+		return parseLongWithSuffix(defaultMaxInputString);
 	});
 
 	/**
@@ -1223,6 +1226,10 @@ public class RestOpContext extends Context implements Comparable<RestOpContext>
 			bs.add(HttpPartSerializer.class, getPartSerializer());
 			bs.add(SerializerSet.class, getSerializers());
 
+			// Inject @Value-annotated env-driven defaults onto this RestOpContext instance so that the
+			// memoizer lambdas (defaultCharset, maxInput) read fully resolved values when first invoked.
+			ClassInfo.of(this).inject(this, bs);
+
 			// The 6 formerly-eager scalar fields are now memoized; no eagerness needed here.
 			// Pre-warm httpMethod so it is in the memoizer cache for immediate use by compareTo/match.
 			httpMethod.get();
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/convention/BasicVersionResource.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/convention/BasicVersionResource.java
index b1d1362ebc..599ef5ac1d 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/convention/BasicVersionResource.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/convention/BasicVersionResource.java
@@ -16,12 +16,15 @@
  */
 package org.apache.juneau.rest.convention;
 
+import static org.apache.juneau.commons.utils.Utils.*;
+
 import java.io.*;
 import java.net.*;
 import java.util.*;
 import java.util.function.*;
 import java.util.jar.*;
 
+import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
@@ -140,12 +143,25 @@ public class BasicVersionResource {
 	public static final String UNKNOWN = "(unknown)";
 
 	/**
-	 * Creates a new builder.
+	 * Creates a new builder, with {@link Value @Value}-annotated fields populated through
+	 * {@link BeanInstantiator} from the active
+	 * {@link org.apache.juneau.commons.settings.Settings Settings} chain.
 	 *
 	 * @return A new builder.
 	 */
 	public static Builder create() {
-		return new Builder();
+		return create(BasicBeanStore.INSTANCE);
+	}
+
+	/**
+	 * Creates a new builder using the supplied {@link BeanStore} for {@link Value @Value}-resolution
+	 * and dependency injection.
+	 *
+	 * @param beanStore The bean store to use for dependency injection.
+	 * @return A new builder.
+	 */
+	public static Builder create(BeanStore beanStore) {
+		return BeanInstantiator.of(Builder.class, beanStore).run();
 	}
 
 	private final Map<String,String> info;
@@ -220,7 +236,21 @@ public class BasicVersionResource {
 		private final Map<String,String> entries = new LinkedHashMap<>();
 		private boolean explicit;
 
-		/** Constructor &mdash; package access for {@link BasicVersionResource#create()}. */
+		/**
+		 * Env-driven default for the {@code javaVersion} entry; resolved from the {@code java.version}
+		 * system property (defaulting to the literal {@code (unknown)}, matching
+		 * {@link BasicVersionResource#UNKNOWN}). Populated by {@link BeanInstantiator} via
+		 * {@link Value @Value} field injection.
+		 *
+		 * <p>
+		 * The default is inlined as a string literal rather than referencing {@code UNKNOWN} via
+		 * concatenation, per the project-wide rule that every {@code @Value} expression is a
+		 * fully self-contained string literal.
+		 */
+		@Value("${java.version:(unknown)}")
+		String javaVersionDefault;
+
+		/** Constructor &mdash; protected access for {@link BasicVersionResource#create()}. */
 		protected Builder() {}
 
 		/**
@@ -356,7 +386,7 @@ public class BasicVersionResource {
 		 * @return This object.
 		 */
 		public Builder fromJavaVersion() {
-			entries.putIfAbsent("javaVersion", System.getProperty("java.version", UNKNOWN));
+			entries.putIfAbsent("javaVersion", opt(javaVersionDefault).orElse(UNKNOWN));
 			explicit = true;
 			return this;
 		}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
index 06c4c5c369..e6633899bb 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
@@ -119,10 +119,19 @@ public class CallLogger {
 		ThrownStore thrownStore;
 		List<CallLoggerRule> normalRules = list();
 		List<CallLoggerRule> debugRules = list();
+
+		@Value("${juneau.restLogger.enabled:ALWAYS}")
 		Enablement enabled;
-		Predicate<HttpServletRequest> enabledTest;
+
+		Predicate<HttpServletRequest> enabledTest = x -> false;
+
+		@Value("${juneau.restLogger.requestDetail:STATUS_LINE}")
 		CallLoggingDetail requestDetail;
+
+		@Value("${juneau.restLogger.responseDetail:STATUS_LINE}")
 		CallLoggingDetail responseDetail;
+
+		@Value("${juneau.restLogger.level:OFF}")
 		Level level;
 
 		/**
@@ -131,12 +140,19 @@ public class CallLogger {
 		 * @param beanStore The bean store to use for creating beans.
 		 */
 		protected Builder(BeanStore beanStore) {
-			logger = Logger.getLogger(env(SP_logger, "global"));
-			enabled = env(SP_enabled, ALWAYS);
-			enabledTest = x -> false;
-			requestDetail = env(SP_requestDetail, STATUS_LINE);
-			responseDetail = env(SP_responseDetail, STATUS_LINE);
-			level = env(SP_level).map(Level::parse).orElse(OFF);
+		}
+
+		/**
+		 * Init method invoked by {@link BeanInstantiator} after construction; resolves the default
+		 * logger from the {@code juneau.restLogger.logger} property (defaulting to {@code "global"})
+		 * and installs it as this builder's logger.
+		 *
+		 * @param loggerName The resolved logger name. Defaulted to {@code "global"} when unset.
+		 */
+		@Inject
+		public void initLoggerFromName(@Value("${juneau.restLogger.logger:global}") String loggerName) {
+			if (logger == null)
+				logger = Logger.getLogger(loggerName);
 		}
 
 		/**
@@ -180,8 +196,8 @@ public class CallLogger {
 		 * <p>
 		 * If not specified, the setting is determined via the following:
 		 * <ul>
-		 * 	<li><js>{@link CallLogger#SP_enabled "juneau.restLogger.enabled"} system property.
-		 * 	<li><js>{@link CallLogger#SP_enabled "JUNEAU_RESTLOGGER_ENABLED"} environment variable.
+		 * 	<li><js>"juneau.restLogger.enabled"</js> system property.
+		 * 	<li><js>"JUNEAU_RESTLOGGER_ENABLED"</js> environment variable.
 		 * 	<li><js>"ALWAYS"</js>.
 		 * </ul>
 		 *
@@ -231,8 +247,8 @@ public class CallLogger {
 		 * <p>
 		 * If not specified, the setting is determined via the following:
 		 * <ul>
-		 * 	<li><js>{@link CallLogger#SP_level "juneau.restLogger.level"} system property.
-		 * 	<li><js>{@link CallLogger#SP_level "JUNEAU_RESTLOGGER_level"} environment variable.
+		 * 	<li><js>"juneau.restLogger.level"</js> system property.
+		 * 	<li><js>"JUNEAU_RESTLOGGER_LEVEL"</js> environment variable.
 		 * 	<li><js>"OFF"</js>.
 		 * </ul>
 		 *
@@ -251,8 +267,8 @@ public class CallLogger {
 		 * <p>
 		 * If not specified, the logger name is determined in the following order:
 		 * <ol>
-		 * 	<li><js>{@link CallLogger#SP_logger "juneau.restLogger.logger"} system property.
-		 * 	<li><js>{@link CallLogger#SP_logger "JUNEAU_RESTLOGGER_LOGGER"} environment variable.
+		 * 	<li><js>"juneau.restLogger.logger"</js> system property.
+		 * 	<li><js>"JUNEAU_RESTLOGGER_LOGGER"</js> environment variable.
 		 * 	<li><js>"global"</js>.
 		 * </ol>
 		 *
@@ -277,8 +293,8 @@ public class CallLogger {
 		 * <p>
 		 * If not specified, the logger name is determined in the following order:
 		 * <ol>
-		 * 	<li><js>{@link CallLogger#SP_logger "juneau.restLogger.logger"} system property.
-		 * 	<li><js>{@link CallLogger#SP_logger "JUNEAU_RESTLOGGER_LOGGER"} environment variable.
+		 * 	<li><js>"juneau.restLogger.logger"</js> system property.
+		 * 	<li><js>"JUNEAU_RESTLOGGER_LOGGER"</js> environment variable.
 		 * 	<li><js>"global"</js>.
 		 * </ol>
 		 *
@@ -329,8 +345,8 @@ public class CallLogger {
 		 * <p>
 		 * If not specified, the setting is determined via the following:
 		 * <ul>
-		 * 	<li><js>{@link CallLogger#SP_requestDetail "juneau.restLogger.requestDetail"} system property.
-		 * 	<li><js>{@link CallLogger#SP_requestDetail "JUNEAU_RESTLOGGER_requestDetail"} environment variable.
+		 * 	<li><js>"juneau.restLogger.requestDetail"</js> system property.
+		 * 	<li><js>"JUNEAU_RESTLOGGER_REQUESTDETAIL"</js> environment variable.
 		 * 	<li><js>"STATUS_LINE"</js>.
 		 * </ul>
 		 *
@@ -358,8 +374,8 @@ public class CallLogger {
 		 * <p>
 		 * If not specified, the setting is determined via the following:
 		 * <ul>
-		 * 	<li><js>{@link CallLogger#SP_responseDetail "juneau.restLogger.responseDetail"} system property.
-		 * 	<li><js>{@link CallLogger#SP_responseDetail "JUNEAU_RESTLOGGER_responseDetail"} environment variable.
+		 * 	<li><js>"juneau.restLogger.responseDetail"</js> system property.
+		 * 	<li><js>"JUNEAU_RESTLOGGER_RESPONSEDETAIL"</js> environment variable.
 		 * 	<li><js>"STATUS_LINE"</js>.
 		 * </ul>
 		 *
@@ -429,80 +445,21 @@ public class CallLogger {
 	private static final CallLoggerRule DEFAULT_RULE = CallLoggerRule.create(BasicBeanStore.INSTANCE).build();
 
 	/**
-	 * System property name for the default logger name to use for {@link CallLogger} objects.
-	 * <p>
-	 * Can also use a <c>JUNEAU_RESTLOGGER_LOGGER</c> environment variable.
-	 * <p>
-	 * If not specified, the default is <js>"global"</js>.
-	 */
-	public static final String SP_logger = "juneau.restLogger.logger";
-
-	/**
-	 * System property name for the default enablement setting for {@link CallLogger} objects.
-	 * <p>
-	 * Can also use a <c>JUNEAU_RESTLOGGER_ENABLED</c> environment variable.
-	 * <p>
-	 * The possible values are:
-	 * <ul>
-	 * 	<li>{@link Enablement#ALWAYS "ALWAYS"} (default) - Logging is enabled.
-	 * 	<li>{@link Enablement#NEVER "NEVER"} - Logging is disabled.
-	 * 	<li>{@link Enablement#CONDITIONAL "CONDITIONALLY"} - Logging is enabled if it passes the {@link Builder#enabledTest(Predicate)} test.
-	 * </ul>
-	 */
-	public static final String SP_enabled = "juneau.restLogger.enabled";
-
-	/**
-	 * System property name for the default request detail setting for {@link CallLogger} objects.
-	 * <p>
-	 * Can also use a <c>JUNEAU_RESTLOGGER_REQUESTDETAIL</c> environment variable.
-	 *
-	 * <ul class='values'>
-	 * 	<li>{@link CallLoggingDetail#STATUS_LINE "STATUS_LINE"} (default) - Log only the status line.
-	 * 	<li>{@link CallLoggingDetail#HEADER "HEADER"} - Log the status line and headers.
-	 * 	<li>{@link CallLoggingDetail#ENTITY "ENTITY"} - Log the status line and headers and content if available.
-	 * </ul>
-	 */
-	public static final String SP_requestDetail = "juneau.restLogger.requestDetail";
-
-	/**
-	 * System property name for the default response detail setting for {@link CallLogger} objects.
-	 * <p>
-	 * Can also use a <c>JUNEAU_RESTLOGGER_RESPONSEDETAIL</c> environment variable.
+	 * Static creator.
 	 *
-	 * <ul class='values'>
-	 * 	<li>{@link CallLoggingDetail#STATUS_LINE "STATUS_LINE"} (default) - Log only the status line.
-	 * 	<li>{@link CallLoggingDetail#HEADER "HEADER"} - Log the status line and headers.
-	 * 	<li>{@link CallLoggingDetail#ENTITY "ENTITY"} - Log the status line and headers and content if available.
-	 * </ul>
-	 */
-	public static final String SP_responseDetail = "juneau.restLogger.responseDetail";
-
-	/**
-	 * System property name for the logging level setting for {@link CallLogger} objects.
 	 * <p>
-	 * Can also use a <c>JUNEAU_RESTLOGGER_LEVEL</c> environment variable.
-	 *
-	 * <ul class='values'>
-	 * 	<li>{@link Level#OFF "OFF"} (default)
-	 * 	<li>{@link Level#SEVERE "SEVERE"}
-	 * 	<li>{@link Level#WARNING "WARNING"}
-	 * 	<li>{@link Level#INFO "INFO"}
-	 * 	<li>{@link Level#CONFIG "CONFIG"}
-	 * 	<li>{@link Level#FINE "FINE"}
-	 * 	<li>{@link Level#FINER "FINER"}
-	 * 	<li>{@link Level#FINEST "FINEST"}
-	 * </ul>
-	 */
-	public static final String SP_level = "juneau.restLogger.level";
-
-	/**
-	 * Static creator.
+	 * Routes builder construction through {@link BeanInstantiator} so that {@link Value @Value}-annotated
+	 * fields and the {@code @Inject}-annotated logger initializer pick up
+	 * {@code juneau.restLogger.logger}, {@code juneau.restLogger.enabled}, {@code juneau.restLogger.requestDetail},
+	 * {@code juneau.restLogger.responseDetail}, and {@code juneau.restLogger.level} from the active
+	 * {@link org.apache.juneau.commons.settings.Settings} chain (system properties &rarr; environment
+	 * variables &rarr; registered property sources).
 	 *
 	 * @param beanStore The bean store to use for creating beans.
 	 * @return A new builder for this object.
 	 */
 	public static Builder create(BeanStore beanStore) {
-		return new Builder(beanStore);
+		return BeanInstantiator.of(Builder.class, beanStore).run();
 	}
 
 	private final Logger logger;
@@ -762,7 +719,7 @@ public class CallLogger {
 	 * @return A new builder object.
 	 */
 	protected Builder init(BeanStore beanStore) {
-		return new Builder(beanStore).logger(beanStore.getBean(Logger.class).orElse(null)).thrownStore(beanStore.getBean(ThrownStore.class).orElse(null));
+		return create(beanStore).logger(beanStore.getBean(Logger.class).orElse(null)).thrownStore(beanStore.getBean(ThrownStore.class).orElse(null));
 	}
 
 	/**
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/bct/BctConfiguration_ValueAdoption_Test.java b/juneau-utest/src/test/java/org/apache/juneau/junit/bct/BctConfiguration_ValueAdoption_Test.java
new file mode 100644
index 0000000000..4b9673ebf7
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/bct/BctConfiguration_ValueAdoption_Test.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.junit.bct;
+
+import static org.apache.juneau.junit.bct.BctConfiguration.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.commons.settings.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * TODO-92 acceptance tests for {@code @Value}-driven defaults on {@link BctConfiguration.Defaults}.
+ *
+ * <p>
+ * 3-test triad per migrated field per OQA #4 — system property set, unset (default), and {@code Settings.setGlobal}.
+ */
+class BctConfiguration_ValueAdoption_Test {
+
+	@AfterEach
+	void cleanup() {
+		var s = Settings.get();
+		s.unsetGlobal(BCT_SORT_MAPS);
+		s.unsetGlobal(BCT_SORT_COLLECTIONS);
+		System.clearProperty(BCT_SORT_MAPS);
+		System.clearProperty(BCT_SORT_COLLECTIONS);
+	}
+
+	// -------------------- sortMaps --------------------
+
+	@Test
+	void a01_sortMaps_set() {
+		System.setProperty(BCT_SORT_MAPS, "true");
+		assertTrue(BctConfiguration.defaults().isSortMaps());
+	}
+
+	@Test
+	void a02_sortMaps_unset() {
+		assertFalse(BctConfiguration.defaults().isSortMaps());
+	}
+
+	@Test
+	void a03_sortMaps_setGlobal() {
+		Settings.get().setGlobal(BCT_SORT_MAPS, "true");
+		assertTrue(BctConfiguration.defaults().isSortMaps());
+	}
+
+	// -------------------- sortCollections --------------------
+
+	@Test
+	void b01_sortCollections_set() {
+		System.setProperty(BCT_SORT_COLLECTIONS, "true");
+		assertTrue(BctConfiguration.defaults().isSortCollections());
+	}
+
+	@Test
+	void b02_sortCollections_unset() {
+		assertFalse(BctConfiguration.defaults().isSortCollections());
+	}
+
+	@Test
+	void b03_sortCollections_setGlobal() {
+		Settings.get().setGlobal(BCT_SORT_COLLECTIONS, "true");
+		assertTrue(BctConfiguration.defaults().isSortCollections());
+	}
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/microservice/Microservice_ValueAdoption_Test.java b/juneau-utest/src/test/java/org/apache/juneau/microservice/Microservice_ValueAdoption_Test.java
new file mode 100644
index 0000000000..bc986ca13c
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/microservice/Microservice_ValueAdoption_Test.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.microservice;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.*;
+
+import org.apache.juneau.commons.settings.*;
+import org.apache.juneau.testing.annotations.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * TODO-92 acceptance tests for the {@code @Value}-driven {@code juneau.workingDir} default on
+ * {@link Microservice.Builder}.
+ *
+ * <p>
+ * 3-test triad per migrated field per OQA #4 — system property set, unset (default = null), and
+ * {@code Settings.setGlobal} override.
+ */
+@JettyMicroserviceTest
+class Microservice_ValueAdoption_Test {
+
+	private static final String SP = "juneau.workingDir";
+
+	@AfterEach
+	void cleanup() {
+		Settings.get().unsetGlobal(SP);
+		System.clearProperty(SP);
+	}
+
+	@Test
+	void a01_workingDir_set() {
+		System.setProperty(SP, "/tmp/todo92-set");
+		assertEquals(new File("/tmp/todo92-set"), Microservice.create().workingDir);
+	}
+
+	@Test
+	void a02_workingDir_unset() {
+		assertNull(Microservice.create().workingDir);
+	}
+
+	@Test
+	void a03_workingDir_setGlobal() {
+		Settings.get().setGlobal(SP, "/tmp/todo92-global");
+		assertEquals(new File("/tmp/todo92-global"), Microservice.create().workingDir);
+	}
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/RestContext_ValueAdoption_Test.java b/juneau-utest/src/test/java/org/apache/juneau/rest/RestContext_ValueAdoption_Test.java
new file mode 100644
index 0000000000..5978b372b5
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/RestContext_ValueAdoption_Test.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.commons.settings.*;
+import org.apache.juneau.rest.annotation.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * TODO-92 acceptance tests for the {@code @Value}-driven env-default fields on {@link RestContext}.
+ *
+ * <p>
+ * 3-test triad per migrated field per OQA #4 — system property set, unset (default), and
+ * {@code Settings.setGlobal} override. Validates the {@code @Value} field receives the resolved
+ * value at injection time. The downstream {@code mergeReplacedStringAttribute} pipeline applies
+ * its own DefaultConfig-driven precedence on top of these fields and is exercised by the existing
+ * {@code RestContext}-level tests; this class scopes coverage to the {@code @Value} seam itself.
+ */
+class RestContext_ValueAdoption_Test extends TestBase {
+
+	@Rest
+	public static class A {}
+
+	private static final List<String> PROPS = List.of(
+		"RestContext.debugDefault",
+		"RestContext.allowedHeaderParams",
+		"RestContext.allowedMethodHeaders",
+		"RestContext.allowedMethodParams",
+		"RestContext.disableContentParam",
+		"RestContext.renderResponseStackTraces",
+		"RestContext.problemDetails",
+		"RestContext.virtualThreads",
+		"RestContext.eagerInit",
+		"RestContext.clientVersionHeader",
+		"RestContext.uriRelativity",
+		"RestContext.uriAuthority",
+		"RestContext.uriContext",
+		"RestContext.uriResolution",
+		"juneau.restLogger.level"
+	);
+
+	@AfterEach
+	void cleanup() {
+		var s = Settings.get();
+		for (var k : PROPS) {
+			s.unsetGlobal(k);
+			System.clearProperty(k);
+		}
+	}
+
+	private RestContext ctx() throws Exception {
+		var resource = new A();
+		return new RestContext(new RestContext.Args(A.class, null, null, () -> resource, "", null, null, null, false))
+			.postInit().postInitChildFirst();
+	}
+
+	@SuppressWarnings("unchecked")
+	private <T> T fld(RestContext c, String name) throws Exception {
+		Field f = RestContext.class.getDeclaredField(name);
+		f.setAccessible(true);
+		return (T)f.get(c);
+	}
+
+	// -------------------- defaultAllowedHeaderParams (String) --------------------
+
+	@Test
+	void a01_allowedHeaderParams_set() throws Exception {
+		System.setProperty("RestContext.allowedHeaderParams", "X-Custom1,X-Custom2");
+		assertEquals("X-Custom1,X-Custom2", fld(ctx(), "defaultAllowedHeaderParams"));
+	}
+
+	@Test
+	void a02_allowedHeaderParams_unset() throws Exception {
+		assertEquals("Accept,Content-Type", fld(ctx(), "defaultAllowedHeaderParams"));
+	}
+
+	@Test
+	void a03_allowedHeaderParams_setGlobal() throws Exception {
+		Settings.get().setGlobal("RestContext.allowedHeaderParams", "X-Global");
+		assertEquals("X-Global", fld(ctx(), "defaultAllowedHeaderParams"));
+	}
+
+	// -------------------- defaultAllowedMethodParams (String) --------------------
+
+	@Test
+	void b01_allowedMethodParams_set() throws Exception {
+		System.setProperty("RestContext.allowedMethodParams", "GET,POST");
+		assertEquals("GET,POST", fld(ctx(), "defaultAllowedMethodParams"));
+	}
+
+	@Test
+	void b02_allowedMethodParams_unset() throws Exception {
+		assertEquals("HEAD,OPTIONS", fld(ctx(), "defaultAllowedMethodParams"));
+	}
+
+	@Test
+	void b03_allowedMethodParams_setGlobal() throws Exception {
+		Settings.get().setGlobal("RestContext.allowedMethodParams", "PUT");
+		assertEquals("PUT", fld(ctx(), "defaultAllowedMethodParams"));
+	}
+
+	// -------------------- defaultDisableContentParam (boolean) --------------------
+
+	@Test
+	void c01_disableContentParam_set() throws Exception {
+		System.setProperty("RestContext.disableContentParam", "true");
+		assertTrue(this.<Boolean>fld(ctx(), "defaultDisableContentParam"));
+	}
+
+	@Test
+	void c02_disableContentParam_unset() throws Exception {
+		assertFalse(this.<Boolean>fld(ctx(), "defaultDisableContentParam"));
+	}
+
+	@Test
+	void c03_disableContentParam_setGlobal() throws Exception {
+		Settings.get().setGlobal("RestContext.disableContentParam", "true");
+		assertTrue(this.<Boolean>fld(ctx(), "defaultDisableContentParam"));
+	}
+
+	// -------------------- defaultRenderResponseStackTraces (boolean) --------------------
+
+	@Test
+	void d01_renderResponseStackTraces_set() throws Exception {
+		System.setProperty("RestContext.renderResponseStackTraces", "true");
+		assertTrue(this.<Boolean>fld(ctx(), "defaultRenderResponseStackTraces"));
+	}
+
+	@Test
+	void d02_renderResponseStackTraces_unset() throws Exception {
+		assertFalse(this.<Boolean>fld(ctx(), "defaultRenderResponseStackTraces"));
+	}
+
+	@Test
+	void d03_renderResponseStackTraces_setGlobal() throws Exception {
+		Settings.get().setGlobal("RestContext.renderResponseStackTraces", "true");
+		assertTrue(this.<Boolean>fld(ctx(), "defaultRenderResponseStackTraces"));
+	}
+
+	// -------------------- defaultProblemDetails (boolean) --------------------
+
+	@Test
+	void e01_problemDetails_set() throws Exception {
+		System.setProperty("RestContext.problemDetails", "true");
+		assertTrue(this.<Boolean>fld(ctx(), "defaultProblemDetails"));
+	}
+
+	@Test
+	void e02_problemDetails_unset() throws Exception {
+		assertFalse(this.<Boolean>fld(ctx(), "defaultProblemDetails"));
+	}
+
+	@Test
+	void e03_problemDetails_setGlobal() throws Exception {
+		Settings.get().setGlobal("RestContext.problemDetails", "true");
+		assertTrue(this.<Boolean>fld(ctx(), "defaultProblemDetails"));
+	}
+
+	// -------------------- defaultClientVersionHeader (String) --------------------
+
+	@Test
+	void f01_clientVersionHeader_set() throws Exception {
+		System.setProperty("RestContext.clientVersionHeader", "X-API-Version");
+		assertEquals("X-API-Version", fld(ctx(), "defaultClientVersionHeader"));
+	}
+
+	@Test
+	void f02_clientVersionHeader_unset() throws Exception {
+		assertEquals("Client-Version", fld(ctx(), "defaultClientVersionHeader"));
+	}
+
+	@Test
+	void f03_clientVersionHeader_setGlobal() throws Exception {
+		Settings.get().setGlobal("RestContext.clientVersionHeader", "X-Global-Version");
+		assertEquals("X-Global-Version", fld(ctx(), "defaultClientVersionHeader"));
+	}
+
+	// -------------------- defaultUriAuthority (Optional<String>) --------------------
+
+	@Test
+	void g01_uriAuthority_set() throws Exception {
+		System.setProperty("RestContext.uriAuthority", "https://example.org");
+		Optional<String> v = fld(ctx(), "defaultUriAuthority");
+		assertEquals(Optional.of("https://example.org"), v);
+	}
+
+	@Test
+	void g02_uriAuthority_unset() throws Exception {
+		Optional<String> v = fld(ctx(), "defaultUriAuthority");
+		assertTrue(v.isEmpty());
+	}
+
+	@Test
+	void g03_uriAuthority_setGlobal() throws Exception {
+		Settings.get().setGlobal("RestContext.uriAuthority", "https://global.example.org");
+		Optional<String> v = fld(ctx(), "defaultUriAuthority");
+		assertEquals(Optional.of("https://global.example.org"), v);
+	}
+
+	// -------------------- defaultUriContext (Optional<String>) --------------------
+
+	@Test
+	void h01_uriContext_set() throws Exception {
+		System.setProperty("RestContext.uriContext", "/api");
+		Optional<String> v = fld(ctx(), "defaultUriContext");
+		assertEquals(Optional.of("/api"), v);
+	}
+
+	@Test
+	void h02_uriContext_unset() throws Exception {
+		Optional<String> v = fld(ctx(), "defaultUriContext");
+		assertTrue(v.isEmpty());
+	}
+
+	@Test
+	void h03_uriContext_setGlobal() throws Exception {
+		Settings.get().setGlobal("RestContext.uriContext", "/global");
+		Optional<String> v = fld(ctx(), "defaultUriContext");
+		assertEquals(Optional.of("/global"), v);
+	}
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/auth/jwt/JwtTokenValidator_ValueAdoption_Test.java b/juneau-utest/src/test/java/org/apache/juneau/rest/auth/jwt/JwtTokenValidator_ValueAdoption_Test.java
new file mode 100644
index 0000000000..6940add46e
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/auth/jwt/JwtTokenValidator_ValueAdoption_Test.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.auth.jwt;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.time.*;
+
+import org.apache.juneau.commons.settings.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * TODO-92 acceptance tests for {@code @Value}-driven defaults on {@link JwtTokenValidator.Builder}.
+ *
+ * <p>
+ * 3-test triad per migrated field per OQA #4 — system property set, unset (default), and {@code Settings.setGlobal}.
+ */
+class JwtTokenValidator_ValueAdoption_Test {
+
+	private static final String SP = "juneau.jwt.jwksCacheTtl";
+
+	@AfterEach
+	void cleanup() {
+		Settings.get().unsetGlobal(SP);
+		System.clearProperty(SP);
+	}
+
+	@Test
+	void a01_jwksCacheTtl_set() {
+		System.setProperty(SP, "PT10M");
+		assertEquals(Duration.ofMinutes(10), JwtTokenValidator.create().jwksCacheTtl);
+	}
+
+	@Test
+	void a02_jwksCacheTtl_unset() {
+		assertEquals(Duration.ofMinutes(5), JwtTokenValidator.create().jwksCacheTtl);
+	}
+
+	@Test
+	void a03_jwksCacheTtl_setGlobal() {
+		Settings.get().setGlobal(SP, "PT15M");
+		assertEquals(Duration.ofMinutes(15), JwtTokenValidator.create().jwksCacheTtl);
+	}
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/convention/BasicVersionResource_ValueAdoption_Test.java b/juneau-utest/src/test/java/org/apache/juneau/rest/convention/BasicVersionResource_ValueAdoption_Test.java
new file mode 100644
index 0000000000..dcb0f0c049
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/convention/BasicVersionResource_ValueAdoption_Test.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.convention;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.commons.settings.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * TODO-92 acceptance tests for the {@code @Value}-driven {@code java.version} default on
+ * {@link BasicVersionResource.Builder}.
+ *
+ * <p>
+ * 3-test triad per migrated field per OQA #4 — system property set, unset (default = JVM-resolved),
+ * and {@code Settings.setGlobal} override.
+ *
+ * <p>
+ * Note: the unset case for this builder always yields a non-blank value because {@code java.version}
+ * is set by the JVM itself; the assertion simply verifies that the JVM-supplied value is what flows
+ * through {@code @Value} resolution.
+ */
+class BasicVersionResource_ValueAdoption_Test {
+
+	private static final String SP = "java.version";
+
+	private String saved;
+
+	@BeforeEach
+	void capture() {
+		saved = System.getProperty(SP);
+	}
+
+	@AfterEach
+	void cleanup() {
+		Settings.get().unsetGlobal(SP);
+		if (saved != null)
+			System.setProperty(SP, saved);
+		else
+			System.clearProperty(SP);
+	}
+
+	@Test
+	void a01_javaVersion_set() {
+		System.setProperty(SP, "todo92-set");
+		var v = BasicVersionResource.create().fromJavaVersion().build().getInfoMap();
+		assertEquals("todo92-set", v.get("javaVersion"));
+	}
+
+	@Test
+	void a02_javaVersion_unset() {
+		var v = BasicVersionResource.create().fromJavaVersion().build().getInfoMap();
+		assertEquals(saved, v.get("javaVersion"));
+	}
+
+	@Test
+	void a03_javaVersion_setGlobal() {
+		Settings.get().setGlobal(SP, "todo92-global");
+		var v = BasicVersionResource.create().fromJavaVersion().build().getInfoMap();
+		assertEquals("todo92-global", v.get("javaVersion"));
+	}
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/logger/CallLogger_ValueAdoption_Test.java b/juneau-utest/src/test/java/org/apache/juneau/rest/logger/CallLogger_ValueAdoption_Test.java
new file mode 100644
index 0000000000..da994341de
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/logger/CallLogger_ValueAdoption_Test.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.logger;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.List;
+import java.util.logging.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.TestBase;
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.commons.settings.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * TODO-92 acceptance tests for {@code @Value}-driven defaults on {@link CallLogger.Builder}.
+ *
+ * <p>
+ * 3-test triad per migrated field per OQA #4 — system property set, unset (default), and {@code Settings.setGlobal}.
+ */
+class CallLogger_ValueAdoption_Test extends TestBase {
+
+	private static final String SP_LOGGER = "juneau.restLogger.logger";
+	private static final String SP_ENABLED = "juneau.restLogger.enabled";
+	private static final String SP_REQUEST_DETAIL = "juneau.restLogger.requestDetail";
+	private static final String SP_RESPONSE_DETAIL = "juneau.restLogger.responseDetail";
+	private static final String SP_LEVEL = "juneau.restLogger.level";
+
+	private static final List<String> ALL_PROPS = List.of(
+		SP_LOGGER, SP_ENABLED, SP_REQUEST_DETAIL, SP_RESPONSE_DETAIL, SP_LEVEL);
+
+	@AfterEach
+	void cleanup() {
+		var s = Settings.get();
+		for (var k : ALL_PROPS) {
+			s.unsetGlobal(k);
+			System.clearProperty(k);
+		}
+	}
+
+	private CallLogger.Builder build() {
+		return CallLogger.create(new BasicBeanStore(null));
+	}
+
+	// -------------------- enabled --------------------
+
+	@Test
+	void a01_enabled_set() {
+		System.setProperty(SP_ENABLED, "NEVER");
+		assertEquals(Enablement.NEVER, build().enabled);
+	}
+
+	@Test
+	void a02_enabled_unset() {
+		assertEquals(Enablement.ALWAYS, build().enabled);
+	}
+
+	@Test
+	void a03_enabled_setGlobal() {
+		Settings.get().setGlobal(SP_ENABLED, "CONDITIONAL");
+		assertEquals(Enablement.CONDITIONAL, build().enabled);
+	}
+
+	// -------------------- requestDetail --------------------
+
+	@Test
+	void b01_requestDetail_set() {
+		System.setProperty(SP_REQUEST_DETAIL, "HEADER");
+		assertEquals(CallLoggingDetail.HEADER, build().requestDetail);
+	}
+
+	@Test
+	void b02_requestDetail_unset() {
+		assertEquals(CallLoggingDetail.STATUS_LINE, build().requestDetail);
+	}
+
+	@Test
+	void b03_requestDetail_setGlobal() {
+		Settings.get().setGlobal(SP_REQUEST_DETAIL, "ENTITY");
+		assertEquals(CallLoggingDetail.ENTITY, build().requestDetail);
+	}
+
+	// -------------------- responseDetail --------------------
+
+	@Test
+	void c01_responseDetail_set() {
+		System.setProperty(SP_RESPONSE_DETAIL, "HEADER");
+		assertEquals(CallLoggingDetail.HEADER, build().responseDetail);
+	}
+
+	@Test
+	void c02_responseDetail_unset() {
+		assertEquals(CallLoggingDetail.STATUS_LINE, build().responseDetail);
+	}
+
+	@Test
+	void c03_responseDetail_setGlobal() {
+		Settings.get().setGlobal(SP_RESPONSE_DETAIL, "ENTITY");
+		assertEquals(CallLoggingDetail.ENTITY, build().responseDetail);
+	}
+
+	// -------------------- level --------------------
+
+	@Test
+	void d01_level_set() {
+		System.setProperty(SP_LEVEL, "WARNING");
+		assertEquals(Level.WARNING, build().level);
+	}
+
+	@Test
+	void d02_level_unset() {
+		assertEquals(Level.OFF, build().level);
+	}
+
+	@Test
+	void d03_level_setGlobal() {
+		Settings.get().setGlobal(SP_LEVEL, "INFO");
+		assertEquals(Level.INFO, build().level);
+	}
+
+	// -------------------- logger (name -> Logger via @Inject method) --------------------
+
+	@Test
+	void e01_logger_set() {
+		System.setProperty(SP_LOGGER, "TODO92.set");
+		var logger = build().logger;
+		assertNotNull(logger);
+		assertEquals("TODO92.set", logger.getName());
+	}
+
+	@Test
+	void e02_logger_unset() {
+		var logger = build().logger;
+		assertNotNull(logger);
+		assertEquals("global", logger.getName());
+	}
+
+	@Test
+	void e03_logger_setGlobal() {
+		Settings.get().setGlobal(SP_LOGGER, "TODO92.global");
+		var logger = build().logger;
+		assertNotNull(logger);
+		assertEquals("TODO92.global", logger.getName());
+	}
+}