diff --git a/doc/overview.html b/doc/overview.html
index ebe9619..6a03b08 100644
--- a/doc/overview.html
+++ b/doc/overview.html
@@ -438,6 +438,34 @@
URI schema: stdin
The above example is particularly useful for passwords, similar to
how Docker handles passwords from stdin.
+
+Stdin is retrieved from {@link io.jstach.ezkv.kvs.KeyValuesEnvironment#getStandardInput()}.
+Because checking stdin will block an application one must enable reading from it which is done
+by either:
+
+- Checking resource parameter of {@value io.jstach.ezkv.kvs.KeyValuesResource#SCHEMA_STDIN_PARAM} is
true
.
+
+- Setting {@value io.jstach.ezkv.kvs.KeyValuesResource#SCHEMA_STDIN_MAIN_ARG_PARAM} to
+{@linkplain io.jstach.ezkv.kvs.KeyValuesEnvironment#getMainArgs() command line argument} to check if present.
+
+-
+Relying on the default which will check "
--
" + {@link io.jstach.ezkv.kvs.KeyValuesResource#name()}
+command line argument is present.
+
+
+
+{@snippet lang=properties :
+# will be enabled if --dbpassword is passed on the command line
+# since dbpassword is the resource name.
+_load_dbpassword=stdin:///db.password?_flag=sensitive,optional
+# specify that the "--password" command line arg is required
+_load_stdin=stdin:///db.password?_param_stdin_arg=--password&_flag=sensitive
+# alternatively one can do:
+_load_stdin=stdin:///?_p_stdin=${some_other_variable}
+# which will be enabled if some_other_variable evalutes to 'true'
+}
+
+
URI schema: profile.
Note: This schema may be renamed to profiles
in the future.
diff --git a/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/DefaultKeyValuesLoaderFinder.java b/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/DefaultKeyValuesLoaderFinder.java
index b92a564..efd40b4 100644
--- a/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/DefaultKeyValuesLoaderFinder.java
+++ b/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/DefaultKeyValuesLoaderFinder.java
@@ -12,7 +12,6 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -179,6 +178,33 @@ protected KeyValues load(LoaderContext context, KeyValuesResource resource) thro
STDIN(KeyValuesResource.SCHEMA_STDIN) {
@Override
protected KeyValues load(LoaderContext context, KeyValuesResource resource) throws IOException {
+
+ boolean enabled;
+
+ String stdinEnabled = resource.parameters().getValue(KeyValuesResource.SCHEMA_STDIN_PARAM);
+
+ if (stdinEnabled != null) {
+ enabled = Boolean.parseBoolean(stdinEnabled);
+ }
+ else {
+ String mainArgName = resource.parameters().getValue(KeyValuesResource.SCHEMA_STDIN_MAIN_ARG_PARAM);
+
+ if (mainArgName == null) {
+ mainArgName = "--" + resource.name();
+ }
+ String argName = mainArgName;
+
+ enabled = Arrays.asList(context.environment().getMainArgs())
+ .stream()
+ .filter(s -> s.equals(argName))
+ .findFirst()
+ .isPresent();
+ }
+
+ if (!enabled) {
+ throw new FileNotFoundException("stdin not enabled.");
+ }
+
var builder = KeyValues.builder(resource);
BiConsumer consumer = builder::add;
diff --git a/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesEnvironment.java b/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesEnvironment.java
index ce5218f..379c9cc 100644
--- a/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesEnvironment.java
+++ b/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesEnvironment.java
@@ -9,7 +9,10 @@
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
@@ -34,6 +37,7 @@
*/
public interface KeyValuesEnvironment {
+ // TODO remove
/**
* If the loader builder is not passed any resources this resource will be used.
* @return default resource is classpath:/boot.properties
@@ -47,7 +51,32 @@ default KeyValuesResource defaultResource() {
* @return an array of main method arguments
*/
default @NonNull String[] getMainArgs() {
- return new @NonNull String[] {};
+ var logger = getLogger();
+ logger.warn("Main Args were requested but not provided. Using fallback 'sun.java.command'");
+ String value = getSystemProperties().getProperty("sun.java.command");
+ if (value == null) {
+ throw new IllegalStateException("Cannot get main args from 'sun.java.command' as it was not provided");
+ }
+ return fallbackMainArgs(value);
+ }
+
+ private static @NonNull String[] fallbackMainArgs(String sunJavaCommandValue) {
+
+ // Use fallback by extracting from system property
+ String command = sunJavaCommandValue;
+ if (!command.isEmpty()) {
+ // Split command into components (main class/jar is the first token)
+ List components = new ArrayList<>(Arrays.asList(command.split("\\s+")));
+ // Remove the first element (main class or jar)
+ if (!components.isEmpty()) {
+ components.remove(0);
+ }
+ // Return remaining as the main args
+ return components.toArray(new @NonNull String[0]);
+ }
+
+ // Return an empty array if no arguments are available
+ return new String[0];
}
/**
@@ -356,16 +385,15 @@ public void warn(String message) {
}
-final class DefaultKeyValuesEnvironment implements KeyValuesEnvironment {
-
- // private static final ThreadLocal threadLocal = new
- // ThreadLocal<>();
- //
- // static Logger localLogger() {
- // var logger = threadLocal.get();
- // if (logger == null) {
- // return NoOpLogger.NOPLOGGER;
- // }
- // }
+record DefaultKeyValuesEnvironment(@NonNull String @Nullable [] mainArgs) implements KeyValuesEnvironment {
+
+ @Override
+ public @NonNull String[] getMainArgs() {
+ var args = mainArgs;
+ if (args == null) {
+ return KeyValuesEnvironment.super.getMainArgs();
+ }
+ return args;
+ }
}
diff --git a/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesResource.java b/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesResource.java
index 0842e06..b7fd058 100644
--- a/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesResource.java
+++ b/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesResource.java
@@ -486,17 +486,49 @@ public sealed interface KeyValuesResource extends NamedKeyValuesSource, KeyValue
*
* stdin:///
can also bind the input content, parsed as a UTF-8 string,
* to the key provided in the URI path.
+ *
+ * Stdin is retrieved from {@link KeyValuesEnvironment#getStandardInput()}.
+ * Because checking stdin will block an application one must enable reading from it which is done
+ * by either:
+ *
+ * - Checking resource parameter of {@value #SCHEMA_STDIN_PARAM} is
true
.
+ *
+ * - Setting {@value #SCHEMA_STDIN_MAIN_ARG_PARAM} to
+ * {@linkplain KeyValuesEnvironment#getMainArgs() command line argument} to check if present.
+ *
+ * -
+ * Relying on the default which will check
--
{@link KeyValuesResource#name()}
+ * command line argument is present.
+ *
+ *
*
* {@snippet lang=properties :
+ * # will be enabled if --stdin is passed on the command line
+ * # since stdin in is the resource name.
* _load_stdin=stdin:///db.password?_flag=sensitive,optional
+ * # alternatively one can do:
+ * _load_stdin=stdin:///?_p_stdin=${some_other_variable}
* }
- *
- * Stdin is retrieved from {@link KeyValuesEnvironment#getStandardInput()}.
- *
+ * @see #SCHEMA_STDIN_PARAM
+ * @see #SCHEMA_STDIN_MAIN_ARG_PARAM
+ * @see KeyValuesEnvironment#getMainArgs()
*/
// @formatter:on
public static final String SCHEMA_STDIN = "stdin";
+ /**
+ * Resource parameter to enable stdin.
+ * @see #SCHEMA_STDIN
+ */
+ public static final String SCHEMA_STDIN_PARAM = "stdin";
+
+ /**
+ * Resource parameter to decide what command line argument should be present to allow
+ * reading from stdin.
+ * @see #SCHEMA_STDIN
+ */
+ public static final String SCHEMA_STDIN_MAIN_ARG_PARAM = "stdin_arg";
+
/**
* Will load multiple resources based on a CSV of profiles where the profile name
* replaces part of the URI.
diff --git a/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesSystem.java b/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesSystem.java
index 0b9aa21..1b58b4b 100644
--- a/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesSystem.java
+++ b/ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesSystem.java
@@ -5,10 +5,12 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Function;
+import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import io.jstach.ezkv.kvs.KeyValuesServiceProvider.KeyValuesFilter;
@@ -106,13 +108,28 @@ default void close() {
/**
* Returns a default implementation of {@code KeyValuesSystem} configured with
* standard settings and a ServiceLoader based on {@link KeyValuesServiceProvider} and
- * its classloader.
+ * its classloader. If possible prefer {@link #defaults(String[])} to provide command
+ * line arguments.
* @return the default {@code KeyValuesSystem} instance
*/
public static KeyValuesSystem defaults() {
return builder().useServiceLoader().build();
}
+ /**
+ * Returns a default implementation of {@code KeyValuesSystem} configured with
+ * standard settings and a ServiceLoader based on {@link KeyValuesServiceProvider} and
+ * its classloader.
+ * @param mainArgs arguments that come from
+ * public static void main(String [] args)
.
+ * @return the default {@code KeyValuesSystem} instance
+ */
+ public static KeyValuesSystem defaults(@NonNull String[] mainArgs) {
+ Objects.requireNonNull(mainArgs);
+ var env = new DefaultKeyValuesEnvironment(mainArgs);
+ return builder().environment(env).useServiceLoader().build();
+ }
+
/**
* Creates and returns a new {@link Builder} for constructing customized
* {@code KeyValuesSystem} instances.
@@ -240,7 +257,7 @@ public Builder useServiceLoader() {
public KeyValuesSystem build() {
var environment = this.environment;
if (environment == null) {
- environment = new DefaultKeyValuesEnvironment();
+ environment = new DefaultKeyValuesEnvironment(null);
}
/*
* We copy as we are about to use the service loader to add more entries and
diff --git a/ezkv-kvs/src/test/java/io/jstach/ezkv/kvs/KeyValuesSystemTest.java b/ezkv-kvs/src/test/java/io/jstach/ezkv/kvs/KeyValuesSystemTest.java
index b990c67..d0b9d4a 100644
--- a/ezkv-kvs/src/test/java/io/jstach/ezkv/kvs/KeyValuesSystemTest.java
+++ b/ezkv-kvs/src/test/java/io/jstach/ezkv/kvs/KeyValuesSystemTest.java
@@ -3,8 +3,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
+import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintStream;
import java.net.URI;
import java.nio.file.Path;
@@ -15,6 +17,7 @@
import java.util.Properties;
import java.util.stream.Collectors;
+import org.jspecify.annotations.NonNull;
import org.junit.jupiter.api.Test;
import io.jstach.ezkv.kvs.KeyValuesEnvironment.Logger;
@@ -45,6 +48,10 @@ void testLoader() throws FileNotFoundException, IOException {
var logger = new TestLogger();
+ String stdin = """
+ stdin_password=guest
+ """;
+
var environment = new KeyValuesEnvironment() {
@Override
public Properties getSystemProperties() {
@@ -56,6 +63,16 @@ public Logger getLogger() {
return logger;
}
+ @Override
+ public InputStream getStandardInput() {
+ return new ByteArrayInputStream(stdin.getBytes());
+ }
+
+ @Override
+ public @NonNull String[] getMainArgs() {
+ return new String[] { "--passwords" };
+ }
+
};
var system = KeyValuesResource.builder(URI.create("system:///"))
.name("system")
@@ -93,6 +110,7 @@ public Logger getLogger() {
.add(system)
.add("classpath:/test-props/testLoader.properties")
.add("classpaths:/classpathstar.properties")
+ .add("stdin:///?_p_stdin_arg=--passwords&_mime=properties&_flag=sensitive")
.add("extra", KeyValues.builder().add(map.entrySet()).build())
.load();
@@ -113,6 +131,7 @@ public Logger getLogger() {
sensitive_ab=REDACTED
mypassword=REDACTED
classpathstar=ezkv
+ stdin_password=REDACTED
fromMap1=1
fromMap2=2
]
@@ -135,6 +154,7 @@ public Logger getLogger() {
sensitive_ab=ab
mypassword=1.2.3.4.5
classpathstar=ezkv
+ stdin_password=guest
fromMap1=1
fromMap2=2
""";
@@ -162,6 +182,7 @@ public Logger getLogger() {
KeyValue[key='sensitive_ab', raw='REDACTED', expanded='REDACTED', source=Source[uri=classpath:/test-props/testLoader-sensitive.properties, reference=[key='_load_luggage', in='classpath:/test-props/testLoader.properties'], index=2]]
KeyValue[key='mypassword', raw='REDACTED', expanded='REDACTED', source=Source[uri=classpath:/test-props/testLoader-sensitive.properties, reference=[key='_load_luggage', in='classpath:/test-props/testLoader.properties'], index=3]]
KeyValue[key='classpathstar', raw='ezkv', expanded='ezkv', source=Source[uri=file:{{CWD}}/target/test-classes/classpathstar.properties, reference=[key='_load_root30', in='classpaths:/classpathstar.properties'], index=1]]
+ KeyValue[key='stdin_password', raw='REDACTED', expanded='REDACTED', source=Source[uri=stdin:///, index=1]]
KeyValue[key='fromMap1', raw='1', expanded='1', source=Source[uri=null:///extra, index=0]]
KeyValue[key='fromMap2', raw='2', expanded='2', source=Source[uri=null:///extra, index=0]]
"""
@@ -204,6 +225,8 @@ public Logger getLogger() {
[INFO ] Loaded uri='classpaths:/classpathstar.properties'
[DEBUG] Loading uri='file:{{CWD}}/target/test-classes/classpathstar.properties' flags=[NO_LOAD_CHILDREN] specified with key: '_load_root30' in uri='classpaths:/classpathstar.properties'
[INFO ] Loaded uri='file:{{CWD}}/target/test-classes/classpathstar.properties' flags=[NO_LOAD_CHILDREN]
+ [DEBUG] Loading uri='stdin:///' flags=[SENSITIVE]
+ [INFO ] Loaded uri='stdin:///' flags=[SENSITIVE]
"""
.replace("{{CWD}}", cwd);
assertEquals(expected, actual);