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 04a150935f feat: add SVL dotenv and env-file variable support
04a150935f is described below
commit 04a150935fab0cf738e30bec2de6d40220ac7c0f
Author: James Bognar <[email protected]>
AuthorDate: Fri May 15 07:16:47 2026 -0400
feat: add SVL dotenv and env-file variable support
---
.../org/apache/juneau/commons/svl/VarList.java | 4 ++
.../org/apache/juneau/commons/svl/VarResolver.java | 4 ++
.../apache/juneau/commons/svl/vars/DotenvVar.java | 72 ++++++++++++++++++++++
.../apache/juneau/commons/svl/vars/EnvFileVar.java | 69 +++++++++++++++++++++
.../juneau/commons/svl/vars/PropertyVars_Test.java | 51 +++++++++++++++
todo/FINISHED-22-svl-external-config-vars.md | 22 +++++++
todo/TODO.md | 2 -
7 files changed, 222 insertions(+), 2 deletions(-)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarList.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarList.java
index 78d2935c63..d6e1ba9078 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarList.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarList.java
@@ -88,6 +88,8 @@ public class VarList extends ArrayList<Object> {
* <li>{@link PropertyVar}
* <li>{@link SystemPropertiesVar}
* <li>{@link EnvVariablesVar}
+ * <li>{@link EnvFileVar}
+ * <li>{@link DotenvVar}
* <li>{@link ArgsVar}
* <li>{@link ManifestFileVar}
* <li>{@link SwitchVar}
@@ -111,6 +113,8 @@ public class VarList extends ArrayList<Object> {
PropertyVar.class,
SystemPropertiesVar.class,
EnvVariablesVar.class,
+ EnvFileVar.class,
+ DotenvVar.class,
ManifestFileVar.class,
ArgsVar.class,
SwitchVar.class,
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarResolver.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarResolver.java
index a06e54975d..142fdfbaac 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarResolver.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarResolver.java
@@ -134,6 +134,8 @@ public class VarResolver {
* <ul>
* <li>{@link SystemPropertiesVar}
* <li>{@link EnvVariablesVar}
+ * <li>{@link EnvFileVar}
+ * <li>{@link DotenvVar}
* <li>{@link ArgsVar}
* <li>{@link ManifestFileVar}
* <li>{@link SwitchVar}
@@ -213,6 +215,8 @@ public class VarResolver {
* <ul>
* <li><c>$S{key[,default]}</c> - {@link SystemPropertiesVar}
* <li><c>$E{key[,default]}</c> - {@link EnvVariablesVar}
+ * <li><c>$EF{key[,default]}</c> - {@link EnvFileVar}
+ * <li><c>$DE{key[,default]}</c> - {@link DotenvVar}
* <li><c>$A{key[,default]}</c> - {@link ArgsVar}
* <li><c>$MF{key[,default]}</c> - {@link ManifestFileVar}
*
<li><c>$SW{stringArg,pattern:thenValue[,pattern:thenValue...]}</c> - {@link
SwitchVar}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/vars/DotenvVar.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/vars/DotenvVar.java
new file mode 100644
index 0000000000..8564c750b7
--- /dev/null
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/vars/DotenvVar.java
@@ -0,0 +1,72 @@
+/*
+ * 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.commons.svl.vars;
+
+import java.nio.file.*;
+
+import org.apache.juneau.commons.settings.*;
+import org.apache.juneau.commons.svl.*;
+
+/**
+ * Dotenv file variable resolver.
+ *
+ * <p>
+ * The format for this var is <js>"$DE{key[,default]}"</js>.
+ *
+ * <p>
+ * Values are resolved from {@link DotenvPropertySource} using either:
+ * <ul class='spaced-list'>
+ * <li>The default dotenv path resolution when using the no-arg
constructor.
+ * <li>The path provided via {@link #create(Path)}.
+ * </ul>
+ */
+public class DotenvVar extends DefaultingVar {
+
+ /** The name of this variable. */
+ public static final String NAME = "DE";
+
+ /**
+ * Creates a {@link DotenvVar} bound to a specific dotenv file path.
+ *
+ * @param path The dotenv file path.
+ * @return A new {@link DotenvVar} instance.
+ */
+ public static DotenvVar create(Path path) {
+ return new DotenvVar(path);
+ }
+
+ private final DotenvPropertySource source;
+
+ /**
+ * Constructor.
+ */
+ public DotenvVar() {
+ super(NAME);
+ this.source = new DotenvPropertySource();
+ }
+
+ private DotenvVar(Path path) {
+ super(NAME);
+ this.source = new DotenvPropertySource(path);
+ }
+
+ @Override /* Overridden from Var */
+ public String resolve(VarResolverSession session, String key) {
+ var result = source.get(key);
+ return result.isPresent() ? result.value().orElse(null) : null;
+ }
+}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/vars/EnvFileVar.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/vars/EnvFileVar.java
new file mode 100644
index 0000000000..2976802f42
--- /dev/null
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/vars/EnvFileVar.java
@@ -0,0 +1,69 @@
+/*
+ * 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.commons.svl.vars;
+
+import java.nio.file.*;
+
+import org.apache.juneau.commons.settings.*;
+import org.apache.juneau.commons.svl.*;
+
+/**
+ * Environment-file variable resolver.
+ *
+ * <p>
+ * The format for this var is <js>"$EF{key[,default]}"</js>.
+ *
+ * <p>
+ * This variable is functionally identical to {@link DotenvVar}, but provides a
+ * generic env-file naming option for callers that do not want dotenv-specific
naming.
+ */
+public class EnvFileVar extends DefaultingVar {
+
+ /** The name of this variable. */
+ public static final String NAME = "EF";
+
+ /**
+ * Creates an {@link EnvFileVar} bound to a specific env file path.
+ *
+ * @param path The env file path.
+ * @return A new {@link EnvFileVar} instance.
+ */
+ public static EnvFileVar create(Path path) {
+ return new EnvFileVar(path);
+ }
+
+ private final DotenvPropertySource source;
+
+ /**
+ * Constructor.
+ */
+ public EnvFileVar() {
+ super(NAME);
+ this.source = new DotenvPropertySource();
+ }
+
+ private EnvFileVar(Path path) {
+ super(NAME);
+ this.source = new DotenvPropertySource(path);
+ }
+
+ @Override /* Overridden from Var */
+ public String resolve(VarResolverSession session, String key) {
+ var result = source.get(key);
+ return result.isPresent() ? result.value().orElse(null) : null;
+ }
+}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/svl/vars/PropertyVars_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/svl/vars/PropertyVars_Test.java
index f55fe419c0..4a414ac158 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/svl/vars/PropertyVars_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/svl/vars/PropertyVars_Test.java
@@ -18,6 +18,8 @@ package org.apache.juneau.commons.svl.vars;
import static org.junit.jupiter.api.Assertions.*;
+import java.io.*;
+import java.nio.file.*;
import java.util.jar.*;
import org.apache.juneau.*;
@@ -137,4 +139,53 @@ class PropertyVars_Test extends TestBase {
Settings.get().unsetGlobal("PropertyVars_Test.c03");
}
}
+
+
//====================================================================================================
+ // DotenvVar / EnvFileVar
+
//====================================================================================================
+
+ @Test
+ void d01_dotenvVar_create_resolvesKeyAndDefault() throws IOException {
+ var tmp = Files.createTempFile("juneau-svl-dotenv-", ".env");
+ Files.writeString(tmp, "API_KEY=abc123\n");
+ try {
+ var vr =
VarResolver.create().vars(DotenvVar.create(tmp)).build();
+ assertEquals("abc123", vr.resolve("$DE{API_KEY}"));
+ assertEquals("fallback",
vr.resolve("$DE{MISSING,fallback}"));
+ } finally {
+ Files.deleteIfExists(tmp);
+ }
+ }
+
+ @Test
+ void d02_envFileVar_create_resolvesKeyAndDefault() throws IOException {
+ var tmp = Files.createTempFile("juneau-svl-envfile-", ".env");
+ Files.writeString(tmp, "REGION=us-east-1\n");
+ try {
+ var vr =
VarResolver.create().vars(EnvFileVar.create(tmp)).build();
+ assertEquals("us-east-1", vr.resolve("$EF{REGION}"));
+ assertEquals("us-west-2",
vr.resolve("$EF{MISSING,us-west-2}"));
+ } finally {
+ Files.deleteIfExists(tmp);
+ }
+ }
+
+ @Test
+ void d03_defaultVars_includeDotenvAndEnvFile() throws IOException {
+ var tmp = Files.createTempFile("juneau-svl-default-vars-",
".env");
+ Files.writeString(tmp, "D_KEY=dot\nE_KEY=env\n");
+ var oldPath = System.getProperty("juneau.dotenv.path");
+ System.setProperty("juneau.dotenv.path", tmp.toString());
+ try {
+ var vr = VarResolver.create().defaultVars().build();
+ assertEquals("dot", vr.resolve("$DE{D_KEY}"));
+ assertEquals("env", vr.resolve("$EF{E_KEY}"));
+ } finally {
+ if (oldPath == null)
+ System.clearProperty("juneau.dotenv.path");
+ else
+ System.setProperty("juneau.dotenv.path",
oldPath);
+ Files.deleteIfExists(tmp);
+ }
+ }
}
diff --git a/todo/FINISHED-22-svl-external-config-vars.md
b/todo/FINISHED-22-svl-external-config-vars.md
new file mode 100644
index 0000000000..f73fa52643
--- /dev/null
+++ b/todo/FINISHED-22-svl-external-config-vars.md
@@ -0,0 +1,22 @@
+# FINISHED-22: SVL external config vars
+
+Implemented additional SVL vars for `.env`-style external configuration
sources in `juneau-commons`.
+
+## Shipped
+
+- Added `DotenvVar` (`$DE{key[,default]}`) backed by `DotenvPropertySource`.
+- Added `EnvFileVar` (`$EF{key[,default]}`) as a generic env-file alias, also
backed by `DotenvPropertySource`.
+- Added both vars to `VarList.addDefault()`, so they are included in
`VarResolver.DEFAULT` / `defaultVars()`.
+- Updated `VarResolver` and `VarList` Javadocs to include the new vars.
+- Added unit tests in `PropertyVars_Test` for:
+ - Direct var usage (`DotenvVar.create(path)` and `EnvFileVar.create(path)`).
+ - Default fallback behavior.
+ - Inclusion in `defaultVars()`.
+
+## Notes
+
+- `DotenvPropertySource` path resolution behavior is reused as-is:
+ - `juneau.dotenv.path` system property
+ - `JUNEAU_DOTENV_PATH` environment variable
+ - fallback `.env`
+- File-glob and JSON-pointer vars remain future enhancements if needed.
diff --git a/todo/TODO.md b/todo/TODO.md
index f00c1abc4c..750d8b1e7f 100644
--- a/todo/TODO.md
+++ b/todo/TODO.md
@@ -27,7 +27,5 @@
- [TODO-20] Rethink how debugging works in RestServlet. Can we come up with a
simpler system?
-- [TODO-22] Add additional SVL vars for common external configuration sources
(e.g. `EnvFileVar` / `DotenvVar` for `.env` files, possibly file-glob and
JSON-pointer vars). Should land after TODO-14 places SVL in `juneau-commons`.
-
- [TODO-30] Investigate moving `ClassMeta` and related non-marshalling type
metadata from `juneau-marshall` into `juneau-commons` (analysis/feasibility
pass). See `todo/TODO-30-classmeta-to-commons.md`.