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 5c564a347d Settings improvements
5c564a347d is described below
commit 5c564a347dcf5c399de5b0ab371dccecc4a3c0b8
Author: James Bognar <[email protected]>
AuthorDate: Tue Dec 16 09:46:11 2025 -0500
Settings improvements
---
.../commons/function/ResettableSupplier.java | 39 ++++
.../apache/juneau/commons/settings/Setting.java | 123 +++++++++++
.../apache/juneau/commons/settings/Settings.java | 230 +++++----------------
.../juneau/commons/settings/StringSetting.java | 223 ++++++++++++++++++++
.../juneau/commons/utils/AssertionUtils.java | 12 +-
.../juneau/commons/utils/ThrowableUtils.java | 5 +-
.../juneau/commons/settings/Settings_Test.java | 48 ++---
7 files changed, 478 insertions(+), 202 deletions(-)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/ResettableSupplier.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/ResettableSupplier.java
index 60f7e5a8ac..baffb64971 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/ResettableSupplier.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/ResettableSupplier.java
@@ -179,4 +179,43 @@ public class ResettableSupplier<T> implements
OptionalSupplier<T> {
return new ResettableSupplier<>(supplier);
return new ResettableSupplier<>(()->o.get());
}
+
+ /**
+ * If a value is present, applies the provided mapping function to it
and returns a ResettableSupplier describing the result.
+ *
+ * <p>
+ * The returned ResettableSupplier maintains its own cache, independent
of this supplier.
+ * Resetting the mapped supplier does not affect this supplier, and
vice versa.
+ *
+ * @param <U> The type of the result of the mapping function.
+ * @param mapper A mapping function to apply to the value, if present.
Must not be <jk>null</jk>.
+ * @return A ResettableSupplier describing the result of applying a
mapping function to the value of this ResettableSupplier, if a value is
present, otherwise an empty ResettableSupplier.
+ */
+ @Override
+ public <U> ResettableSupplier<U> map(Function<? super T, ? extends U>
mapper) {
+ assertArgNotNull("mapper", mapper);
+ return new ResettableSupplier<>(() -> {
+ T value = get();
+ return nn(value) ? mapper.apply(value) : null;
+ });
+ }
+
+ /**
+ * If a value is present, and the value matches the given predicate,
returns a ResettableSupplier describing the value, otherwise returns an empty
ResettableSupplier.
+ *
+ * <p>
+ * The returned ResettableSupplier maintains its own cache, independent
of this supplier.
+ * Resetting the filtered supplier does not affect this supplier, and
vice versa.
+ *
+ * @param predicate A predicate to apply to the value, if present. Must
not be <jk>null</jk>.
+ * @return A ResettableSupplier describing the value of this
ResettableSupplier if a value is present and the value matches the given
predicate, otherwise an empty ResettableSupplier.
+ */
+ @Override
+ public ResettableSupplier<T> filter(Predicate<? super T> predicate) {
+ assertArgNotNull("predicate", predicate);
+ return new ResettableSupplier<>(() -> {
+ T value = get();
+ return (nn(value) && predicate.test(value)) ? value :
null;
+ });
+ }
}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Setting.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Setting.java
new file mode 100644
index 0000000000..c4ec98d0c9
--- /dev/null
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Setting.java
@@ -0,0 +1,123 @@
+/*
+ * 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.settings;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+import static org.apache.juneau.commons.utils.Utils.*;
+
+import java.util.Optional;
+import java.util.function.*;
+
+import org.apache.juneau.commons.function.ResettableSupplier;
+
+/**
+ * A resettable supplier that provides convenience methods for type conversion.
+ *
+ * <p>
+ * This class extends {@link ResettableSupplier} to provide methods to convert
the string value
+ * to various types, similar to the {@link Settings#getInteger(String)},
{@link Settings#getBoolean(String)}, etc. methods.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * StringSetting <jv>setting</jv> =
Settings.<jsf>get</jsf>().setting(<js>"my.property"</js>);
+ * Setting<Integer> <jv>intValue</jv> = <jv>setting</jv>.asInteger();
+ * Setting<Boolean> <jv>boolValue</jv> =
<jv>setting</jv>.asBoolean();
+ * Setting<Charset> <jv>charset</jv> = <jv>setting</jv>.asCharset();
+ *
+ * <jc>// Reset the cache to force recomputation</jc>
+ * <jv>setting</jv>.reset();
+ * </p>
+ *
+ * @param <T> The type of value supplied.
+ */
+public class Setting<T> extends ResettableSupplier<T> {
+ private final Settings settings;
+
+ /**
+ * Creates a new Setting from a Settings instance and a Supplier.
+ *
+ * @param settings The Settings instance that created this setting.
Must not be <jk>null</jk>.
+ * @param supplier The supplier that provides the value. Must not be
<jk>null</jk>.
+ */
+ public Setting(Settings settings, Supplier<T> supplier) {
+ super(assertArgNotNull("supplier", supplier));
+ this.settings = assertArgNotNull("settings", settings);
+ }
+
+ /**
+ * Returns the Settings instance that created this setting.
+ *
+ * @return The Settings instance.
+ */
+ public Settings getSettings() {
+ return settings;
+ }
+
+ /**
+ * Returns the underlying Optional<T>.
+ *
+ * <p>
+ * <b>Note:</b> The returned {@link Optional} is a snapshot-in-time of
the current value.
+ * Resetting this {@link Setting} will not affect the returned {@link
Optional} instance.
+ * To get an updated value after resetting, call this method again.
+ *
+ * @return The optional value.
+ */
+ public Optional<T> asOptional() {
+ return opt(get());
+ }
+
+ /**
+ * If a value is present, applies the provided mapping function to it
and returns a Setting describing the result.
+ *
+ * <p>
+ * The returned Setting maintains its own cache, independent of this
supplier.
+ * Resetting the mapped supplier does not affect this supplier, and
vice versa.
+ *
+ * @param <U> The type of the result of the mapping function.
+ * @param mapper A mapping function to apply to the value, if present.
Must not be <jk>null</jk>.
+ * @return A Setting describing the result of applying a mapping
function to the value of this Setting, if a value is present, otherwise an
empty Setting.
+ */
+ @Override
+ public <U> Setting<U> map(Function<? super T, ? extends U> mapper) {
+ assertArgNotNull("mapper", mapper);
+ return new Setting<>(settings, () -> {
+ T value = get();
+ return nn(value) ? mapper.apply(value) : null;
+ });
+ }
+
+ /**
+ * If a value is present, and the value matches the given predicate,
returns a Setting describing the value, otherwise returns an empty Setting.
+ *
+ * <p>
+ * The returned Setting maintains its own cache, independent of this
supplier.
+ * Resetting the filtered supplier does not affect this supplier, and
vice versa.
+ *
+ * @param predicate A predicate to apply to the value, if present. Must
not be <jk>null</jk>.
+ * @return A Setting describing the value of this Setting if a value is
present and the value matches the given predicate, otherwise an empty Setting.
+ */
+ @Override
+ public Setting<T> filter(Predicate<? super T> predicate) {
+ assertArgNotNull("predicate", predicate);
+ return new Setting<>(settings, () -> {
+ T value = get();
+ return (nn(value) && predicate.test(value)) ? value :
null;
+ });
+ }
+}
+
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java
index 5fb3891c0c..909d974e9e 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java
@@ -20,10 +20,7 @@ import static
org.apache.juneau.commons.utils.AssertionUtils.*;
import static org.apache.juneau.commons.utils.ThrowableUtils.*;
import static org.apache.juneau.commons.utils.Utils.*;
-import java.io.*;
-import java.net.*;
import java.nio.charset.*;
-import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
@@ -62,29 +59,37 @@ import org.apache.juneau.commons.function.*;
*
* <h5 class='section'>Features:</h5>
* <ul class='spaced-list'>
- * <li>System property access - read Java system properties with type
conversion
+ * <li>System property access - read Java system properties with type
conversion via {@link StringSetting}
* <li>Global overrides - override system properties globally for all
threads (stored in a {@link SettingStore})
* <li>Per-thread overrides - override system properties for specific
threads (stored in a per-thread {@link SettingStore})
* <li>Custom sources - add arbitrary property sources (e.g., Spring
properties, environment variables, config files) via the {@link Builder}
* <li>Disable override support - system property to prevent new global
overrides from being set
- * <li>Type-safe accessors - convenience methods for common types:
Integer, Long, Boolean, Double, Float, File, Path, URI, Charset
+ * <li>Type-safe accessors - type conversion methods on {@link
StringSetting} for common types: Integer, Long, Boolean, Double, Float, File,
Path, URI, Charset
+ * <li>Resettable suppliers - settings are returned as {@link
StringSetting} instances that can be reset to force recomputation
* </ul>
*
* <h5 class='section'>Usage Examples:</h5>
* <p class='bjava'>
- * <jc>// Get a system property as a string (using singleton instance)</jc>
- * Optional<String> <jv>value</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.property"</js>);
+ * <jc>// Get a system property as a StringSetting (using singleton
instance)</jc>
+ * StringSetting <jv>setting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.property"</js>);
+ * String <jv>value</jv> = <jv>setting</jv>.get(); <jc>// Get the string
value</jc>
*
- * <jc>// Get with type conversion</jc>
- * Optional<Integer> <jv>intValue</jv> =
Settings.<jsf>get</jsf>().getInteger(<js>"my.int.property"</js>);
- * Optional<Long> <jv>longValue</jv> =
Settings.<jsf>get</jsf>().getLong(<js>"my.long.property"</js>);
- * Optional<Boolean> <jv>boolValue</jv> =
Settings.<jsf>get</jsf>().getBoolean(<js>"my.bool.property"</js>);
- * Optional<Double> <jv>doubleValue</jv> =
Settings.<jsf>get</jsf>().getDouble(<js>"my.double.property"</js>);
- * Optional<Float> <jv>floatValue</jv> =
Settings.<jsf>get</jsf>().getFloat(<js>"my.float.property"</js>);
- * Optional<File> <jv>fileValue</jv> =
Settings.<jsf>get</jsf>().getFile(<js>"my.file.property"</js>);
- * Optional<Path> <jv>pathValue</jv> =
Settings.<jsf>get</jsf>().getPath(<js>"my.path.property"</js>);
- * Optional<URI> <jv>uriValue</jv> =
Settings.<jsf>get</jsf>().getURI(<js>"my.uri.property"</js>);
- * Optional<Charset> <jv>charsetValue</jv> =
Settings.<jsf>get</jsf>().getCharset(<js>"my.charset.property"</js>);
+ * <jc>// Get with type conversion using StringSetting methods</jc>
+ * Setting<Integer> <jv>intSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.int.property"</js>).asInteger();
+ * Setting<Long> <jv>longSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.long.property"</js>).asLong();
+ * Setting<Boolean> <jv>boolSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.bool.property"</js>).asBoolean();
+ * Setting<Double> <jv>doubleSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.double.property"</js>).asDouble();
+ * Setting<Float> <jv>floatSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.float.property"</js>).asFloat();
+ * Setting<File> <jv>fileSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.file.property"</js>).asFile();
+ * Setting<Path> <jv>pathSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.path.property"</js>).asPath();
+ * Setting<URI> <jv>uriSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.uri.property"</js>).asURI();
+ * Setting<Charset> <jv>charsetSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.charset.property"</js>).asCharset();
+ *
+ * <jc>// Use custom type conversion</jc>
+ * Setting<MyCustomType> <jv>customSetting</jv> =
Settings.<jsf>get</jsf>().get(<js>"my.custom.property"</js>).asType(MyCustomType.<jk>class</jk>);
+ *
+ * <jc>// Reset a setting to force recomputation</jc>
+ * <jv>setting</jv>.reset();
*
* <jc>// Override for current thread (useful in unit tests)</jc>
* Settings.<jsf>get</jsf>().setLocal(<js>"my.property"</js>,
<js>"test-value"</js>);
@@ -325,7 +330,12 @@ public class Settings {
}
/**
- * Returns the value of the specified system property.
+ * Returns a {@link StringSetting} for the specified system property.
+ *
+ * <p>
+ * The returned {@link StringSetting} is a resettable supplier that
caches the lookup result.
+ * Use the {@link StringSetting#asInteger()}, {@link
StringSetting#asBoolean()}, etc. methods
+ * to convert to different types.
*
* <p>
* The lookup order is:
@@ -337,31 +347,32 @@ public class Settings {
* <li>System environment variable source (default, always last)
* </ol>
*
- * @param name The property name.
- * @return The property value, or {@link Optional#empty()} if not found.
+ * @param name The property name. Must not be <jk>null</jk>.
+ * @return A {@link StringSetting} that provides the property value, or
<jk>null</jk> if not found.
*/
- public Optional<String> get(String name) {
+ public StringSetting get(String name) {
assertArgNotNull("name", name);
+ return new StringSetting(this, () -> {
+ // 1. Check thread-local override
+ var v = localStore.get().get(name);
+ if (v != null)
+ return v.orElse(null); // v is Optional.empty()
if key exists with null value, or Optional.of(value) if present
+
+ // 2. Check global override
+ v = globalStore.get().get(name);
+ if (v != null)
+ return v.orElse(null); // v is Optional.empty()
if key exists with null value, or Optional.of(value) if present
+
+ // 3. Check sources in reverse order (last added first)
+ for (int i = sources.size() - 1; i >= 0; i--) {
+ var source = sources.get(i);
+ var result = source.get(name);
+ if (result != null)
+ return result.orElse(null);
+ }
- // 1. Check thread-local override
- var v = localStore.get().get(name);
- if (v != null)
- return v; // v is Optional.empty() if key exists with
null value, or Optional.of(value) if present
-
- // 2. Check global override
- v = globalStore.get().get(name);
- if (v != null)
- return v; // v is Optional.empty() if key exists with
null value, or Optional.of(value) if present
-
- // 3. Check sources in reverse order (last added first)
- for (int i = sources.size() - 1; i >= 0; i--) {
- var source = sources.get(i);
- var result = source.get(name);
- if (result != null)
- return result;
- }
-
- return opte();
+ return null;
+ });
}
/**
@@ -391,138 +402,10 @@ public class Settings {
* @see #get(String)
* @see #toType(String, Object)
*/
+ @SuppressWarnings("unchecked")
public <T> T get(String name, T def) {
assertArgNotNull("def", def);
- return get(name).map(x -> toType(x, def)).orElse(def);
- }
-
- /**
- * Returns the value of the specified system property as an Integer.
- *
- * <p>
- * The property value is parsed using {@link Integer#valueOf(String)}.
If the property is not found
- * or cannot be parsed as an integer, returns {@link Optional#empty()}.
- *
- * @param name The property name.
- * @return The property value as an Integer, or {@link
Optional#empty()} if not found or not a valid integer.
- */
- public Optional<Integer> getInteger(String name) {
- return get(name).map(v ->
safeOrNull(()->Integer.valueOf(v))).filter(Objects::nonNull);
- }
-
- /**
- * Returns the value of the specified system property as a Long.
- *
- * <p>
- * The property value is parsed using {@link Long#valueOf(String)}. If
the property is not found
- * or cannot be parsed as a long, returns {@link Optional#empty()}.
- *
- * @param name The property name.
- * @return The property value as a Long, or {@link Optional#empty()} if
not found or not a valid long.
- */
- public Optional<Long> getLong(String name) {
- return get(name).map(v ->
safeOrNull(()->Long.valueOf(v))).filter(Objects::nonNull);
- }
-
- /**
- * Returns the value of the specified system property as a Boolean.
- *
- * <p>
- * The property value is parsed using {@link
Boolean#parseBoolean(String)}, which returns <c>true</c>
- * if the value is (case-insensitive) "true", otherwise <c>false</c>.
Note that this method will
- * return <c>Optional.of(false)</c> for any non-empty value that is not
"true", and
- * {@link Optional#empty()} only if the property is not set.
- *
- * @param name The property name.
- * @return The property value as a Boolean, or {@link Optional#empty()}
if not found.
- */
- public Optional<Boolean> getBoolean(String name) {
- return get(name).map(v -> Boolean.parseBoolean(v));
- }
-
- /**
- * Returns the value of the specified system property as a Double.
- *
- * <p>
- * The property value is parsed using {@link Double#valueOf(String)}.
If the property is not found
- * or cannot be parsed as a double, returns {@link Optional#empty()}.
- *
- * @param name The property name.
- * @return The property value as a Double, or {@link Optional#empty()}
if not found or not a valid double.
- */
- public Optional<Double> getDouble(String name) {
- return get(name).map(v ->
safeOrNull(()->Double.valueOf(v))).filter(Objects::nonNull);
- }
-
- /**
- * Returns the value of the specified system property as a Float.
- *
- * <p>
- * The property value is parsed using {@link Float#valueOf(String)}. If
the property is not found
- * or cannot be parsed as a float, returns {@link Optional#empty()}.
- *
- * @param name The property name.
- * @return The property value as a Float, or {@link Optional#empty()}
if not found or not a valid float.
- */
- public Optional<Float> getFloat(String name) {
- return get(name).map(v ->
safeOrNull(()->Float.valueOf(v))).filter(Objects::nonNull);
- }
-
- /**
- * Returns the value of the specified system property as a File.
- *
- * <p>
- * The property value is converted to a {@link File} using the {@link
File#File(String)} constructor.
- * If the property is not found, returns {@link Optional#empty()}. Note
that this method does not
- * validate that the file path is valid or that the file exists.
- *
- * @param name The property name.
- * @return The property value as a File, or {@link Optional#empty()} if
not found.
- */
- public Optional<File> getFile(String name) {
- return get(name).map(v -> new File(v));
- }
-
- /**
- * Returns the value of the specified system property as a Path.
- *
- * <p>
- * The property value is converted to a {@link Path} using {@link
Paths#get(String, String...)}.
- * If the property is not found or the path string is invalid, returns
{@link Optional#empty()}.
- *
- * @param name The property name.
- * @return The property value as a Path, or {@link Optional#empty()} if
not found or not a valid path.
- */
- public Optional<Path> getPath(String name) {
- return get(name).map(v ->
safeOrNull(()->Paths.get(v))).filter(Objects::nonNull);
- }
-
- /**
- * Returns the value of the specified system property as a URI.
- *
- * <p>
- * The property value is converted to a {@link URI} using {@link
URI#create(String)}.
- * If the property is not found or the URI string is invalid, returns
{@link Optional#empty()}.
- *
- * @param name The property name.
- * @return The property value as a URI, or {@link Optional#empty()} if
not found or not a valid URI.
- */
- public Optional<URI> getURI(String name) {
- return get(name).map(v ->
safeOrNull(()->URI.create(v))).filter(Objects::nonNull);
- }
-
- /**
- * Returns the value of the specified system property as a Charset.
- *
- * <p>
- * The property value is converted to a {@link Charset} using {@link
Charset#forName(String)}.
- * If the property is not found or the charset name is not supported,
returns {@link Optional#empty()}.
- *
- * @param name The property name.
- * @return The property value as a Charset, or {@link Optional#empty()}
if not found or not a valid charset.
- */
- public Optional<Charset> getCharset(String name) {
- return get(name).map(v ->
safeOrNull(()->Charset.forName(v))).filter(Objects::nonNull);
+ return get(name).asType((Class<T>)def.getClass()).orElse(def);
}
/**
@@ -654,13 +537,14 @@ public class Settings {
*
* @param <T> The target type.
* @param s The string to convert. Must not be <jk>null</jk>.
- * @param def The default value (used to determine the target type).
Must not be <jk>null</jk>.
+ * @param c The target class. Must not be <jk>null</jk>.
* @return The converted value.
* @throws RuntimeException If the type is not supported for conversion.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
- private <T> T toType(String s, T def) {
- var c = (Class<T>)def.getClass();
+ protected <T> T toType(String s, Class<T> c) {
+ assertArgNotNull("s", s);
+ assertArgNotNull("c", c);
var f = (Function<String,T>)customTypeFunctions.get(c);
if (f == null) {
if (c == String.class)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/StringSetting.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/StringSetting.java
new file mode 100644
index 0000000000..0f553e927d
--- /dev/null
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/StringSetting.java
@@ -0,0 +1,223 @@
+/*
+ * 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.settings;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+import static org.apache.juneau.commons.utils.Utils.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.charset.*;
+import java.nio.file.*;
+import java.util.*;
+import java.util.function.*;
+
+/**
+ * A specialized {@link Setting} for string values that provides convenience
methods for type conversion.
+ *
+ * <p>
+ * This class extends {@link Setting} with type-specific conversion methods
such as {@link #asInteger()},
+ * {@link #asBoolean()}, {@link #asCharset()}, etc.
+ */
+public class StringSetting extends Setting<String> {
+
+ /**
+ * Creates a new StringSetting from a Settings instance and a Supplier.
+ *
+ * @param settings The Settings instance that created this setting.
Must not be <jk>null</jk>.
+ * @param supplier The supplier that provides the string value. Must
not be <jk>null</jk>.
+ */
+ public StringSetting(Settings settings, Supplier<String> supplier) {
+ super(settings, supplier);
+ }
+
+ /**
+ * If a value is present, applies the provided mapping function to it
and returns a StringSetting describing the result.
+ *
+ * <p>
+ * This method is specifically for String-to-String mappings. For
mappings to other types, use the inherited {@link #map(Function)} method.
+ *
+ * <p>
+ * The returned StringSetting maintains its own cache, independent of
this supplier.
+ * Resetting the mapped supplier does not affect this supplier, and
vice versa.
+ *
+ * @param mapper A mapping function to apply to the value, if present.
Must not be <jk>null</jk>.
+ * @return A StringSetting describing the result of applying a mapping
function to the value of this StringSetting, if a value is present, otherwise
an empty StringSetting.
+ */
+ public StringSetting mapString(Function<? super String, ? extends
String> mapper) {
+ assertArgNotNull("mapper", mapper);
+ return new StringSetting(getSettings(), () -> {
+ String value = get();
+ return nn(value) ? mapper.apply(value) : null;
+ });
+ }
+
+ /**
+ * If a value is present, and the value matches the given predicate,
returns a StringSetting describing the value, otherwise returns an empty
StringSetting.
+ *
+ * <p>
+ * The returned StringSetting maintains its own cache, independent of
this supplier.
+ * Resetting the filtered supplier does not affect this supplier, and
vice versa.
+ *
+ * @param predicate A predicate to apply to the value, if present. Must
not be <jk>null</jk>.
+ * @return A StringSetting describing the value of this StringSetting
if a value is present and the value matches the given predicate, otherwise an
empty StringSetting.
+ */
+ @Override
+ public StringSetting filter(Predicate<? super String> predicate) {
+ assertArgNotNull("predicate", predicate);
+ return new StringSetting(getSettings(), () -> {
+ String value = get();
+ return (nn(value) && predicate.test(value)) ? value :
null;
+ });
+ }
+
+ /**
+ * Converts the string value to an Integer.
+ *
+ * <p>
+ * The property value is parsed using {@link Integer#valueOf(String)}.
If the property is not found
+ * or cannot be parsed as an integer, returns {@link Optional#empty()}.
+ *
+ * @return The property value as an Integer, or {@link
Optional#empty()} if not found or not a valid integer.
+ */
+ public Setting<Integer> asInteger() {
+ return map(v -> safeOrNull(() ->
Integer.valueOf(v))).filter(Objects::nonNull);
+ }
+
+ /**
+ * Converts the string value to a Long.
+ *
+ * <p>
+ * The property value is parsed using {@link Long#valueOf(String)}. If
the property is not found
+ * or cannot be parsed as a long, returns {@link Optional#empty()}.
+ *
+ * @return The property value as a Long, or {@link Optional#empty()} if
not found or not a valid long.
+ */
+ public Setting<Long> asLong() {
+ return map(v -> safeOrNull(() ->
Long.valueOf(v))).filter(Objects::nonNull);
+ }
+
+ /**
+ * Converts the string value to a Boolean.
+ *
+ * <p>
+ * The property value is parsed using {@link
Boolean#parseBoolean(String)}, which returns <c>true</c>
+ * if the value is (case-insensitive) "true", otherwise <c>false</c>.
Note that this method will
+ * return <c>Optional.of(false)</c> for any non-empty value that is not
"true", and
+ * {@link Optional#empty()} only if the property is not set.
+ *
+ * @return The property value as a Boolean, or {@link Optional#empty()}
if not found.
+ */
+ public Setting<Boolean> asBoolean() {
+ return map(v -> Boolean.parseBoolean(v));
+ }
+
+ /**
+ * Converts the string value to a Double.
+ *
+ * <p>
+ * The property value is parsed using {@link Double#valueOf(String)}.
If the property is not found
+ * or cannot be parsed as a double, returns {@link Optional#empty()}.
+ *
+ * @return The property value as a Double, or {@link Optional#empty()}
if not found or not a valid double.
+ */
+ public Setting<Double> asDouble() {
+ return map(v -> safeOrNull(() ->
Double.valueOf(v))).filter(Objects::nonNull);
+ }
+
+ /**
+ * Converts the string value to a Float.
+ *
+ * <p>
+ * The property value is parsed using {@link Float#valueOf(String)}. If
the property is not found
+ * or cannot be parsed as a float, returns {@link Optional#empty()}.
+ *
+ * @return The property value as a Float, or {@link Optional#empty()}
if not found or not a valid float.
+ */
+ public Setting<Float> asFloat() {
+ return map(v -> safeOrNull(() ->
Float.valueOf(v))).filter(Objects::nonNull);
+ }
+
+ /**
+ * Converts the string value to a File.
+ *
+ * <p>
+ * The property value is converted to a {@link File} using the {@link
File#File(String)} constructor.
+ * If the property is not found, returns {@link Optional#empty()}. Note
that this method does not
+ * validate that the file path is valid or that the file exists.
+ *
+ * @return The property value as a File, or {@link Optional#empty()} if
not found.
+ */
+ public Setting<File> asFile() {
+ return map(v -> new File(v));
+ }
+
+ /**
+ * Converts the string value to a Path.
+ *
+ * <p>
+ * The property value is converted to a {@link Path} using {@link
Paths#get(String, String...)}.
+ * If the property is not found or the path string is invalid, returns
{@link Optional#empty()}.
+ *
+ * @return The property value as a Path, or {@link Optional#empty()} if
not found or not a valid path.
+ */
+ public Setting<Path> asPath() {
+ return map(v -> safeOrNull(() ->
Paths.get(v))).filter(Objects::nonNull);
+ }
+
+ /**
+ * Converts the string value to a URI.
+ *
+ * <p>
+ * The property value is converted to a {@link URI} using {@link
URI#create(String)}.
+ * If the property is not found or the URI string is invalid, returns
{@link Optional#empty()}.
+ *
+ * @return The property value as a URI, or {@link Optional#empty()} if
not found or not a valid URI.
+ */
+ public Setting<URI> asURI() {
+ return map(v -> safeOrNull(() ->
URI.create(v))).filter(Objects::nonNull);
+ }
+
+ /**
+ * Converts the string value to a Charset.
+ *
+ * <p>
+ * The property value is converted to a {@link Charset} using {@link
Charset#forName(String)}.
+ * If the property is not found or the charset name is not supported,
returns {@link Optional#empty()}.
+ *
+ * @return The property value as a Charset, or {@link Optional#empty()}
if not found or not a valid charset.
+ */
+ public Setting<Charset> asCharset() {
+ return map(v -> safeOrNull(() ->
Charset.forName(v))).filter(Objects::nonNull);
+ }
+
+ /**
+ * Converts the string value to the specified type using the Settings
type conversion functions.
+ *
+ * <p>
+ * The property value is converted using {@link Settings#toType(String,
Class)}. If the property is not found
+ * or cannot be converted to the specified type, returns {@link
Optional#empty()}.
+ *
+ * @param <T> The target type.
+ * @param c The target class. Must not be <jk>null</jk>.
+ * @return The property value as the specified type, or {@link
Optional#empty()} if not found or not a valid conversion.
+ */
+ public <T> Setting<T> asType(Class<T> c) {
+ assertArgNotNull("c", c);
+ return map(v -> getSettings().toType(v,
c)).filter(Objects::nonNull);
+ }
+}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/AssertionUtils.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/AssertionUtils.java
index 7f9b31ce2a..514accf17f 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/AssertionUtils.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/AssertionUtils.java
@@ -19,8 +19,6 @@ package org.apache.juneau.commons.utils;
import static org.apache.juneau.commons.utils.ThrowableUtils.*;
import static org.apache.juneau.commons.utils.Utils.*;
-import java.util.function.*;
-
/**
* Utility methods for argument validation and assertion.
*
@@ -122,6 +120,16 @@ public class AssertionUtils {
return o;
}
+ /**
+ * Asserts that the specified object is not <jk>null</jk>, throwing an
{@link IllegalStateException} if it is.
+ *
+ * @param <T> The type of the object.
+ * @param o The object to check. Can be <jk>null</jk>.
+ * @param msg The error message format string. Must not be
<jk>null</jk>.
+ * @param args The arguments for the message format string.
+ * @return The non-null object.
+ * @throws IllegalStateException If the object is <jk>null</jk>.
+ */
public static final <T> T assertNotNull(T o, String msg, Object...args)
throws IllegalStateException {
if (o == null)
throw illegalState(msg, args);
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
index 7621517243..a8dabce9a0 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
@@ -20,7 +20,6 @@ import static org.apache.juneau.commons.utils.Utils.*;
import java.io.*;
import java.util.*;
-import java.util.concurrent.atomic.*;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.commons.settings.*;
@@ -30,7 +29,7 @@ import org.apache.juneau.commons.settings.*;
*/
public class ThrowableUtils {
- static AtomicBoolean VERBOSE = new
AtomicBoolean(Settings.get().getBoolean("juneau.enableVerboseExceptions").orElse(false));
+ static Setting<Boolean> VERBOSE =
Settings.get().get("juneau.enableVerboseExceptions").asBoolean();
/**
* Interface used with {@link
Utils#safeSupplier(SupplierWithThrowable)}.
@@ -337,7 +336,7 @@ public class ThrowableUtils {
}
private static <T extends Throwable> T log(T exception) {
- if (VERBOSE.get()) exception.printStackTrace();
+ if (VERBOSE.orElse(false)) exception.printStackTrace();
return exception;
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/settings/Settings_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/settings/Settings_Test.java
index b471c88e4f..e0bce38301 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/settings/Settings_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/settings/Settings_Test.java
@@ -117,7 +117,7 @@ class Settings_Test extends TestBase {
@Test
void b01_getInteger_valid() {
System.setProperty(TEST_PROP, "123");
- var result = Settings.get().getInteger(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asInteger();
assertTrue(result.isPresent());
assertEquals(123, result.get());
}
@@ -125,20 +125,20 @@ class Settings_Test extends TestBase {
@Test
void b02_getInteger_invalid() {
System.setProperty(TEST_PROP, "not-a-number");
- var result = Settings.get().getInteger(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asInteger();
assertFalse(result.isPresent());
}
@Test
void b03_getInteger_notFound() {
- var result = Settings.get().getInteger("nonexistent.property");
+ var result =
Settings.get().get("nonexistent.property").asInteger();
assertFalse(result.isPresent());
}
@Test
void b04_getInteger_fromOverride() {
Settings.get().setLocal(TEST_PROP, "456");
- var result = Settings.get().getInteger(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asInteger();
assertTrue(result.isPresent());
assertEquals(456, result.get());
}
@@ -149,7 +149,7 @@ class Settings_Test extends TestBase {
@Test
void c01_getLong_valid() {
System.setProperty(TEST_PROP, "123456789");
- var result = Settings.get().getLong(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asLong();
assertTrue(result.isPresent());
assertEquals(123456789L, result.get());
}
@@ -157,14 +157,14 @@ class Settings_Test extends TestBase {
@Test
void c02_getLong_invalid() {
System.setProperty(TEST_PROP, "not-a-number");
- var result = Settings.get().getLong(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asLong();
assertFalse(result.isPresent());
}
@Test
void c03_getLong_fromOverride() {
Settings.get().setLocal(TEST_PROP, "987654321");
- var result = Settings.get().getLong(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asLong();
assertTrue(result.isPresent());
assertEquals(987654321L, result.get());
}
@@ -175,7 +175,7 @@ class Settings_Test extends TestBase {
@Test
void d01_getBoolean_true() {
System.setProperty(TEST_PROP, "true");
- var result = Settings.get().getBoolean(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asBoolean();
assertTrue(result.isPresent());
assertTrue(result.get());
}
@@ -183,7 +183,7 @@ class Settings_Test extends TestBase {
@Test
void d02_getBoolean_false() {
System.setProperty(TEST_PROP, "false");
- var result = Settings.get().getBoolean(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asBoolean();
assertTrue(result.isPresent());
assertFalse(result.get());
}
@@ -191,7 +191,7 @@ class Settings_Test extends TestBase {
@Test
void d03_getBoolean_caseInsensitive() {
System.setProperty(TEST_PROP, "TRUE");
- var result = Settings.get().getBoolean(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asBoolean();
assertTrue(result.isPresent());
assertTrue(result.get());
}
@@ -199,14 +199,14 @@ class Settings_Test extends TestBase {
@Test
void d04_getBoolean_nonTrueValue() {
System.setProperty(TEST_PROP, "anything");
- var result = Settings.get().getBoolean(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asBoolean();
assertTrue(result.isPresent());
assertFalse(result.get());
}
@Test
void d05_getBoolean_notFound() {
- var result = Settings.get().getBoolean("nonexistent.property");
+ var result =
Settings.get().get("nonexistent.property").asBoolean();
assertFalse(result.isPresent());
}
@@ -216,7 +216,7 @@ class Settings_Test extends TestBase {
@Test
void e01_getDouble_valid() {
System.setProperty(TEST_PROP, "123.456");
- var result = Settings.get().getDouble(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asDouble();
assertTrue(result.isPresent());
assertEquals(123.456, result.get(), 0.0001);
}
@@ -224,7 +224,7 @@ class Settings_Test extends TestBase {
@Test
void e02_getDouble_invalid() {
System.setProperty(TEST_PROP, "not-a-number");
- var result = Settings.get().getDouble(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asDouble();
assertFalse(result.isPresent());
}
@@ -234,7 +234,7 @@ class Settings_Test extends TestBase {
@Test
void f01_getFloat_valid() {
System.setProperty(TEST_PROP, "123.456");
- var result = Settings.get().getFloat(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asFloat();
assertTrue(result.isPresent());
assertEquals(123.456f, result.get(), 0.0001f);
}
@@ -242,7 +242,7 @@ class Settings_Test extends TestBase {
@Test
void f02_getFloat_invalid() {
System.setProperty(TEST_PROP, "not-a-number");
- var result = Settings.get().getFloat(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asFloat();
assertFalse(result.isPresent());
}
@@ -252,14 +252,14 @@ class Settings_Test extends TestBase {
@Test
void g01_getFile_valid() {
System.setProperty(TEST_PROP, "/tmp/test.txt");
- var result = Settings.get().getFile(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asFile();
assertTrue(result.isPresent());
assertEquals(new File("/tmp/test.txt"), result.get());
}
@Test
void g02_getFile_notFound() {
- var result = Settings.get().getFile("nonexistent.property");
+ var result =
Settings.get().get("nonexistent.property").asFile();
assertFalse(result.isPresent());
}
@@ -269,7 +269,7 @@ class Settings_Test extends TestBase {
@Test
void h01_getPath_valid() {
System.setProperty(TEST_PROP, "/tmp/test.txt");
- var result = Settings.get().getPath(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asPath();
assertTrue(result.isPresent());
assertEquals(Paths.get("/tmp/test.txt"), result.get());
}
@@ -279,7 +279,7 @@ class Settings_Test extends TestBase {
// Paths.get() can throw exceptions for invalid paths on some
systems
// This test verifies that invalid paths return empty
System.setProperty(TEST_PROP, "\0invalid");
- var result = Settings.get().getPath(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asPath();
// May or may not be empty depending on OS, but should not throw
assertNotNull(result);
}
@@ -290,7 +290,7 @@ class Settings_Test extends TestBase {
@Test
void i01_getURI_valid() {
System.setProperty(TEST_PROP, "http://example.com/test");
- var result = Settings.get().getURI(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asURI();
assertTrue(result.isPresent());
assertEquals(URI.create("http://example.com/test"),
result.get());
}
@@ -298,7 +298,7 @@ class Settings_Test extends TestBase {
@Test
void i02_getURI_invalid() {
System.setProperty(TEST_PROP, "not a valid uri");
- var result = Settings.get().getURI(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asURI();
assertFalse(result.isPresent());
}
@@ -308,7 +308,7 @@ class Settings_Test extends TestBase {
@Test
void j01_getCharset_valid() {
System.setProperty(TEST_PROP, "UTF-8");
- var result = Settings.get().getCharset(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asCharset();
assertTrue(result.isPresent());
assertEquals(Charset.forName("UTF-8"), result.get());
}
@@ -316,7 +316,7 @@ class Settings_Test extends TestBase {
@Test
void j02_getCharset_invalid() {
System.setProperty(TEST_PROP, "INVALID-CHARSET");
- var result = Settings.get().getCharset(TEST_PROP);
+ var result = Settings.get().get(TEST_PROP).asCharset();
assertFalse(result.isPresent());
}