mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
Config API updates
This commit is contained in:
parent
bb865d9aae
commit
9f54032c43
@ -0,0 +1,9 @@
|
||||
package io.nosqlbench.nb.api.config.standard;
|
||||
|
||||
import io.nosqlbench.nb.api.errors.BasicError;
|
||||
|
||||
public class NBConfigError extends BasicError {
|
||||
public NBConfigError(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
@ -1,18 +1,43 @@
|
||||
package io.nosqlbench.nb.api.config.standard;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This configuration model describes what is valid to submit
|
||||
* for configuration for a given configurable object.
|
||||
* <p>This configuration model describes what is valid to submit
|
||||
* for configuration for a given configurable object. Once this
|
||||
* is provided by a configurable element, it is used internally
|
||||
* by NoSQLBench to ensure that only valid configuration are
|
||||
* given to newly built objects.</p>
|
||||
*
|
||||
* <p>It is conventional to put the config model at the bottom of any
|
||||
* implementing class for quick reference.</p>
|
||||
*/
|
||||
public interface NBConfigModel {
|
||||
|
||||
Map<String, Param<?>> getElements();
|
||||
Map<String, Param<?>> getNamedParams();
|
||||
|
||||
List<Param<?>> getParams();
|
||||
|
||||
Class<?> getOf();
|
||||
|
||||
void assertValidConfig(Map<String, ?> config);
|
||||
|
||||
NBConfiguration apply(Map<String, ?> config);
|
||||
|
||||
<V> Param<V> getParam(String... name);
|
||||
|
||||
/**
|
||||
* Extract the fields from the shared config into a separate config,
|
||||
* removing those that are defined in this model and leaving
|
||||
* extraneous config fields in the provided model.
|
||||
*
|
||||
* <em>This method mutates the map that is provided.</em>
|
||||
*
|
||||
* @param sharedConfig A config map which can provide fields to multiple models
|
||||
* @return A new configuration for the extracted fields only.
|
||||
*/
|
||||
NBConfiguration extract(Map<String, ?> sharedConfig);
|
||||
|
||||
NBConfigModel add(NBConfigModel otherModel);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
package io.nosqlbench.nb.api.config.standard;
|
||||
|
||||
public interface NBCanValidateConfig {
|
||||
public interface NBConfigModelProvider {
|
||||
NBConfigModel getConfigModel();
|
||||
}
|
@ -3,12 +3,31 @@ package io.nosqlbench.nb.api.config.standard;
|
||||
/**
|
||||
* All implementation types which wish to have a type-marshalled configuration
|
||||
* should implement this interface.
|
||||
*
|
||||
* When a type which implements this interface is instantiated, and the
|
||||
* {@link NBConfiguration} was not injected into its constructor,
|
||||
* the builder should call {@link #applyConfig(NBConfiguration)} immediately
|
||||
* after calling the constructor.
|
||||
*/
|
||||
public interface NBConfigurable extends NBCanConfigure, NBCanValidateConfig {
|
||||
public interface NBConfigurable extends NBCanConfigure, NBConfigModelProvider {
|
||||
|
||||
/**
|
||||
* Implementors should take care to ensure that this can be called after
|
||||
* initial construction without unexpected interactions between
|
||||
* construction parameters and configuration parameters.
|
||||
* @param cfg The configuration data to be applied to a new instance
|
||||
*/
|
||||
@Override
|
||||
void applyConfig(NBConfiguration cfg);
|
||||
|
||||
/**
|
||||
* Implement this method by returning an instance of {@link ConfigModel}.
|
||||
* Any configuration which is provided to the {@link #applyConfig(NBConfiguration)}
|
||||
* method will be validated through this model. A configuration model
|
||||
* is <em>required</em> in order to build a validated configuration
|
||||
* from source data provided by a user.
|
||||
* @return A valid configuration model for the implementing class
|
||||
*/
|
||||
@Override
|
||||
NBConfigModel getConfigModel();
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package io.nosqlbench.nb.api.config.standard;
|
||||
|
||||
import io.nosqlbench.nb.api.NBEnvironment;
|
||||
import io.nosqlbench.nb.api.errors.BasicError;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -10,7 +9,7 @@ import java.util.Optional;
|
||||
|
||||
public class NBConfiguration {
|
||||
private final LinkedHashMap<String, Object> data;
|
||||
private final NBConfigModel configModel;
|
||||
private final NBConfigModel model;
|
||||
|
||||
/**
|
||||
* Create a NBConfigReader from a known valid configuration and a config model.
|
||||
@ -22,7 +21,7 @@ public class NBConfiguration {
|
||||
*/
|
||||
protected NBConfiguration(NBConfigModel model, LinkedHashMap<String, Object> validConfig) {
|
||||
this.data = validConfig;
|
||||
this.configModel = model;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,41 +38,55 @@ public class NBConfiguration {
|
||||
String span = optionalValue.get();
|
||||
Optional<String> maybeInterpolated = NBEnvironment.INSTANCE.interpolate(span);
|
||||
if (maybeInterpolated.isEmpty()) {
|
||||
throw new BasicError("Unable to interpolate '" + span +"' with env vars.");
|
||||
throw new NBConfigError("Unable to interpolate '" + span +"' with env vars.");
|
||||
}
|
||||
return maybeInterpolated;
|
||||
}
|
||||
|
||||
public String paramEnv(String name) {
|
||||
return paramEnv(name, String.class);
|
||||
public String getWithEnv(String name) {
|
||||
return getWithEnv(name, String.class);
|
||||
}
|
||||
|
||||
public <T> T paramEnv(String name, Class<? extends T> vclass) {
|
||||
T param = param(name, vclass);
|
||||
if (param instanceof String) {
|
||||
Optional<String> interpolated = NBEnvironment.INSTANCE.interpolate(param.toString());
|
||||
public <T> T getWithEnv(String name, Class<? extends T> vclass) {
|
||||
T value = get(name, vclass);
|
||||
if (value==null) {
|
||||
|
||||
}
|
||||
if (value instanceof String) {
|
||||
Optional<String> interpolated = NBEnvironment.INSTANCE.interpolate(value.toString());
|
||||
if (interpolated.isEmpty()) {
|
||||
throw new RuntimeException("Unable to interpolate env and sys props in '" + param + "'");
|
||||
throw new NBConfigError("Unable to interpolate env and sys props in '" + value + "'");
|
||||
}
|
||||
return (T) interpolated.get();
|
||||
String result = interpolated.get();
|
||||
return ConfigModel.convertValueTo(this.getClass().getSimpleName(),name, result, vclass);
|
||||
} else {
|
||||
return param;
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String get(String name) {
|
||||
return get(name, String.class);
|
||||
}
|
||||
|
||||
public <T> T get(String name, Class<? extends T> type) {
|
||||
if (!configModel.getElements().containsKey(name)) {
|
||||
throw new BasicError("Parameter named '" + name + "' is not valid for " + configModel.getOf().getSimpleName() + ".");
|
||||
Param<T> param = model.getParam(name);
|
||||
if (param==null) {
|
||||
throw new NBConfigError("Parameter named '" + name + "' is not valid for " + model.getOf().getSimpleName() + ".");
|
||||
}
|
||||
if (!param.isRequired()) {
|
||||
throw new NBConfigError("Non-optional get on parameter declared optional '" + name + "'");
|
||||
}
|
||||
|
||||
Object o = data.get(name);
|
||||
if (o == null) {
|
||||
throw new BasicError("config param '" + name + "' was not defined.");
|
||||
throw new NBConfigError("config param '" + name + "' was not defined.");
|
||||
}
|
||||
if (type.isAssignableFrom(o.getClass())) {
|
||||
return (T) o;
|
||||
}
|
||||
throw new BasicError("config param '" + name + "' was not assignable to class '" + type.getCanonicalName() + "'");
|
||||
return ConfigModel.convertValueTo(this.getClass().getSimpleName(), name,o,type);
|
||||
// if (type.isAssignableFrom(o.getClass())) {
|
||||
// return (T) o;
|
||||
// }
|
||||
// throw new NBConfigError("config param '" + name + "' was not assignable to class '" + type.getCanonicalName() + "'");
|
||||
}
|
||||
|
||||
public Optional<String> getOptional(String name) {
|
||||
@ -87,9 +100,12 @@ public class NBConfiguration {
|
||||
public <T> Optional<T> getOptional(Class<T> type, String... names) {
|
||||
Object o = null;
|
||||
for (String name : names) {
|
||||
o = data.get(name);
|
||||
if (o!=null) {
|
||||
break;
|
||||
Param<?> param = model.getParam(names);
|
||||
if (param!=null) {
|
||||
o = data.get(param.getNames());
|
||||
if (o!=null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (o==null) {
|
||||
@ -98,7 +114,7 @@ public class NBConfiguration {
|
||||
if (type.isAssignableFrom(o.getClass())) {
|
||||
return Optional.of((T) o);
|
||||
}
|
||||
throw new BasicError("config param " + Arrays.toString(names) +" was not assignable to class '" + type.getCanonicalName() + "'");
|
||||
throw new NBConfigError("config param " + Arrays.toString(names) +" was not assignable to class '" + type.getCanonicalName() + "'");
|
||||
}
|
||||
|
||||
public <T> T getOrDefault(String name, T defaultValue) {
|
||||
@ -109,14 +125,14 @@ public class NBConfiguration {
|
||||
if (defaultValue.getClass().isAssignableFrom(o.getClass())) {
|
||||
return (T) o;
|
||||
}
|
||||
throw new BasicError("config parameter '" + name + "' is not assignable to required type '" + defaultValue.getClass() + "'");
|
||||
throw new NBConfigError("config parameter '" + name + "' is not assignable to required type '" + defaultValue.getClass() + "'");
|
||||
}
|
||||
|
||||
public <T> T param(String name, Class<? extends T> vclass) {
|
||||
Object o = data.get(name);
|
||||
Param<?> elem = configModel.getElements().get(name);
|
||||
Param<?> elem = model.getNamedParams().get(name);
|
||||
if (elem == null) {
|
||||
throw new RuntimeException("Invalid config element named '" + name + "'");
|
||||
throw new NBConfigError("Invalid config element named '" + name + "'");
|
||||
}
|
||||
Class<T> type = (Class<T>) elem.getType();
|
||||
T typeCastedValue = type.cast(o);
|
||||
@ -125,8 +141,8 @@ public class NBConfiguration {
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.configModel.getOf().getSimpleName()).append(":");
|
||||
sb.append(this.configModel);
|
||||
sb.append(this.model.getOf().getSimpleName()).append(":");
|
||||
sb.append(this.model);
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,6 @@ package io.nosqlbench.nb.api.config.standard;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface NBMapConfigurable extends NBCanValidateConfig {
|
||||
public interface NBMapConfigurable extends NBConfigModelProvider {
|
||||
void applyConfig(Map<String, ?> providedConfig);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.nosqlbench.nb.api.config.standard;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -11,7 +12,7 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class Param<T> {
|
||||
|
||||
public final String name;
|
||||
public final List<String> names;
|
||||
public final Class<? extends T> type;
|
||||
public String description;
|
||||
private final T defaultValue;
|
||||
@ -19,35 +20,85 @@ public class Param<T> {
|
||||
private Pattern regex;
|
||||
|
||||
public Param(
|
||||
String name,
|
||||
List<String> names,
|
||||
Class<? extends T> type,
|
||||
String description,
|
||||
boolean required,
|
||||
T defaultValue
|
||||
) {
|
||||
this.name = name;
|
||||
this.names = names;
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
this.required = required;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public static <V> Param<V> optional(String name) {
|
||||
return (Param<V>) optional(name,String.class);
|
||||
/**
|
||||
* Declare an optional String parameter with the given name.
|
||||
* @param name the name of the parameter
|
||||
*/
|
||||
public static Param<String> optional(String name) {
|
||||
return optional(List.of(name), String.class);
|
||||
}
|
||||
|
||||
public static <V> Param<V> optional(String name, Class<V> type) {
|
||||
return new Param<V>(name,type,null,false,null);
|
||||
/**
|
||||
* Declare an optional String parameter specified by any of the names. They act as synonyms.
|
||||
* When users provide more than one of these in configuration data, it is considered an error.
|
||||
*
|
||||
* @param names one or more names that the parameter can be specified with.
|
||||
*/
|
||||
public static Param<String> optional(List<String> names) {
|
||||
return optional(names, String.class);
|
||||
}
|
||||
public static <V> Param<V> defaultTo(String name, V defaultValue) {
|
||||
return new Param<V>(name,(Class<V>) defaultValue.getClass(),null,false,null);
|
||||
|
||||
/**
|
||||
* Declare an optional parameter specified by any of the names which must be assignable to
|
||||
* (returnable as) the specified type.
|
||||
* When users provide more than one of these in configuration data, it is considered an error.
|
||||
*
|
||||
* @param names one or more names that the parameter can be specified with.
|
||||
* @param type The type of value that the provided configuration value must be returnable as (assignable to)
|
||||
* @param <V> Generic type for inference.
|
||||
*/
|
||||
public static <V> Param<V> optional(List<String> names, Class<V> type) {
|
||||
return new Param<V>(names, type, null, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare an optional parameter for the given name which must be assignable to
|
||||
* (returnable as) the specified type.
|
||||
* When users provide more than one of these in configuration data, it is considered an error.
|
||||
*
|
||||
* @param name the name of the parameter
|
||||
* @param type The type of value that the provided configuration value must be returnable as (assignable to)
|
||||
* @param <V> Generic type for inference.
|
||||
*/
|
||||
public static <V> Param<V> optional(String name, Class<V> type) {
|
||||
return new Param<V>(List.of(name), type, null, false, null);
|
||||
}
|
||||
|
||||
public static <V> Param<V> defaultTo(String name, V defaultValue) {
|
||||
return new Param<V>(List.of(name), (Class<V>) defaultValue.getClass(), null, false, null);
|
||||
}
|
||||
|
||||
public static <V> Param<V> defaultTo(List<String> names, V defaultValue) {
|
||||
return new Param<V>(names, (Class<V>) defaultValue.getClass(), null, false, null);
|
||||
}
|
||||
|
||||
public static <V> Param<V> required(String name, Class<V> type) {
|
||||
return new Param<V>(List.of(name), type, null, true, null);
|
||||
}
|
||||
|
||||
public static <V> Param<V> required(List<String> names, Class<V> type) {
|
||||
return new Param<V>(names, type, null, true, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Element{" +
|
||||
"name='" + name + '\'' +
|
||||
"names='" + names.toString() + '\'' +
|
||||
", type=" + type +
|
||||
", description='" + description + '\'' +
|
||||
", required=" + required +
|
||||
@ -55,8 +106,8 @@ public class Param<T> {
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
public List<String> getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
@ -71,8 +122,9 @@ public class Param<T> {
|
||||
return required;
|
||||
}
|
||||
|
||||
public void setRequired(boolean required) {
|
||||
public Param<?> setRequired(boolean required) {
|
||||
this.required = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
public T getDefaultValue() {
|
||||
@ -88,6 +140,7 @@ public class Param<T> {
|
||||
this.regex = regex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Param<T> setRegex(String pattern) {
|
||||
this.regex = Pattern.compile(pattern);
|
||||
return this;
|
||||
@ -102,9 +155,9 @@ public class Param<T> {
|
||||
|
||||
if (value == null) {
|
||||
if (isRequired()) {
|
||||
return CheckResult.INVALID(this, null, "Value is null but " + this.getName() + " is required");
|
||||
return CheckResult.INVALID(this, null, "Value is null but " + this.getNames() + " is required");
|
||||
} else {
|
||||
return CheckResult.VALID(this, null, "Value is null, but " + this.getName() + " is not required");
|
||||
return CheckResult.VALID(this, null, "Value is null, but " + this.getNames() + " is not required");
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,11 +172,11 @@ public class Param<T> {
|
||||
if (!matcher.matches()) {
|
||||
return CheckResult.INVALID(this, value,
|
||||
"Could not match required pattern (" + getRegex().toString() +
|
||||
") with value '" + value + "' for field '" + getName() + "'");
|
||||
") with value '" + value + "' for field '" + getNames() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
return CheckResult.VALID(this,value,"All validators passed for field '" + getName() + "'");
|
||||
return CheckResult.VALID(this, value, "All validators passed for field '" + getNames() + "'");
|
||||
}
|
||||
|
||||
public final static class CheckResult<T> {
|
||||
|
Loading…
Reference in New Issue
Block a user