mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
added a richer config type for custom configs
This commit is contained in:
parent
477af8c7bc
commit
7f3fcd5769
55
virtdata-api/docs/custom_elements.md
Normal file
55
virtdata-api/docs/custom_elements.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Custom Elements (rename to VirtData Config Elements)
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
The current progress of custom elements includes:
|
||||||
|
- ConfigData config type
|
||||||
|
- a proof of concept for "customConfig" values
|
||||||
|
|
||||||
|
The next step should be to introduce ConfigData to elements that support it via one of:
|
||||||
|
- automatic injection into constructors by parameter type
|
||||||
|
- Annotation support
|
||||||
|
- ThreadLocal configuration types
|
||||||
|
- ConfigAware injection
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Functions may sometimes need to access environmental configuration data. When this is the case, it is not usually
|
||||||
|
reasonable to impose on the user to pass this into a function as initializer data within the siganature of each function
|
||||||
|
recipe.
|
||||||
|
|
||||||
|
Custom elements are a way to support this. A custom element is simply an object that can be injected into the virtdata's
|
||||||
|
resolver environment before function resolution continues. This is allows any function which may need custom elements to
|
||||||
|
ask the virtdata environment for them.
|
||||||
|
|
||||||
|
## Custom Elements Structure
|
||||||
|
|
||||||
|
Custom Elements are provided to callers as a <String,Object> map. Internally, elements may be structured as a list of
|
||||||
|
maps, so that layers of config can be added with precedence or overrides.
|
||||||
|
|
||||||
|
All access to the custom elements service by accessors should require a typed getter, with a type check in the call for
|
||||||
|
assignability to the target type. It is expected that functions which use these elements will use this typed accessor in
|
||||||
|
order to assert that:
|
||||||
|
|
||||||
|
1. a (potentially) required element is provided
|
||||||
|
2. it is of the type required for the caller.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
It is important to document what every custom element does, and where it will be used. Specifically, functions which use
|
||||||
|
custom functions must provide additional details for users that specify what names, types, and effects custom elemements
|
||||||
|
may have on them.
|
||||||
|
|
||||||
|
### Functional Impact
|
||||||
|
|
||||||
|
Custom elements may change the semantics of function use. Specifically, when the effective value of custom elements
|
||||||
|
changes, functions will cease to act as pure functions in some way.
|
||||||
|
|
||||||
|
|
||||||
|
## Using Custom Elements
|
||||||
|
|
||||||
|
When you implement a function which uses custom elements, the function should *only* access the elements for the
|
||||||
|
purposes of instance and type checking in the constructor. Some custom elements may trigger additional initialization
|
||||||
|
logic before a function can ultimately return a value, but when possible, building any cacheable values should be
|
||||||
|
deferred to a lazy property to be used in the main apply method.
|
||||||
|
|
@ -54,6 +54,7 @@ public class VirtDataComposer {
|
|||||||
private final static MethodHandles.Lookup lookup = MethodHandles.publicLookup();
|
private final static MethodHandles.Lookup lookup = MethodHandles.publicLookup();
|
||||||
|
|
||||||
private final VirtDataFunctionLibrary functionLibrary;
|
private final VirtDataFunctionLibrary functionLibrary;
|
||||||
|
|
||||||
private final Map<String,Object> customElements = new HashMap<>();
|
private final Map<String,Object> customElements = new HashMap<>();
|
||||||
|
|
||||||
public VirtDataComposer(VirtDataFunctionLibrary functionLibrary) {
|
public VirtDataComposer(VirtDataFunctionLibrary functionLibrary) {
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
package io.nosqlbench.virtdata.core.config;
|
||||||
|
|
||||||
|
import io.nosqlbench.nb.api.errors.BasicError;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ConfigData {
|
||||||
|
private final ConfigData inner;
|
||||||
|
private final LinkedHashMap<String,Object> configs;
|
||||||
|
|
||||||
|
public ConfigData(LinkedHashMap<String,Object> configs, ConfigData inner) {
|
||||||
|
this.configs = configs;
|
||||||
|
this.inner =inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigData(LinkedHashMap<String,Object> configs) {
|
||||||
|
this.configs = configs;
|
||||||
|
this.inner = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigData() {
|
||||||
|
this.configs = new LinkedHashMap<>();
|
||||||
|
this.inner = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigData layer() {
|
||||||
|
return new ConfigData(new LinkedHashMap<>(),this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigData layer(Map<String,Object> configs ){
|
||||||
|
return new ConfigData(new LinkedHashMap<>(configs),this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the typed optional value for the requested parameter name.
|
||||||
|
* @param name The name of the parameter to use
|
||||||
|
* @param type The class type which the value must be assignable to.
|
||||||
|
* @param <T> The generic parameter of the class type
|
||||||
|
* @return An optional of type T
|
||||||
|
* @throws BasicError if a value is found which can't be returned as the
|
||||||
|
* specified type.
|
||||||
|
*/
|
||||||
|
public <T> Optional<T> get(String name, Class<? extends T> type) {
|
||||||
|
Object o = configs.get(name);
|
||||||
|
if (o!=null) {
|
||||||
|
if (type.isAssignableFrom(o.getClass())) {
|
||||||
|
return Optional.of(type.cast(o));
|
||||||
|
} else {
|
||||||
|
throw new BasicError("Tried to access a virtdata config element named '" + name +
|
||||||
|
"' as a '" + type.getCanonicalName() + "', but it was not compatible with that type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inner !=null) {
|
||||||
|
return inner.get(name, type);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the typed optional list for the requested list name. This is no different than
|
||||||
|
* getting an object without the list qualifier, except that the type checking is done for
|
||||||
|
* you internal to the getList method.
|
||||||
|
* @param name The name of the parameter to return
|
||||||
|
* @param type The type of the list element. Every element must be assignable to this type.
|
||||||
|
* @param <T> The generic parameter of the list element type
|
||||||
|
* @return An optional list of T
|
||||||
|
* @throws BasicError if any of the elements are not assignable to the required element type
|
||||||
|
*/
|
||||||
|
public <T> Optional<List<T>> getList(String name, Class<? extends T> type) {
|
||||||
|
Optional<List> found = get(name, List.class);
|
||||||
|
if (found.isPresent()) {
|
||||||
|
ArrayList<T> list = new ArrayList<>();
|
||||||
|
for (Object o : found.get()) {
|
||||||
|
if (type.isAssignableFrom(o.getClass())) {
|
||||||
|
list.add(type.cast(o));
|
||||||
|
} else {
|
||||||
|
throw new BasicError("Tried to access a virtdata config list element found under name '" + name +
|
||||||
|
"' as a '" + type.getCanonicalName() + "', but it was not compatible with that type");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.of(list);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String paramName, List<String> paramValue) {
|
||||||
|
this.configs.put(paramName,paramValue);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package io.nosqlbench.virtdata.core.config;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class ConfigDataTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLayer() {
|
||||||
|
ConfigData conf = new ConfigData();
|
||||||
|
conf.put("test1", List.of("t","e","s","t","1"));
|
||||||
|
Optional<List> test1 = conf.get("test1", List.class);
|
||||||
|
assertThat(test1).isPresent();
|
||||||
|
assertThat(test1.get()).containsExactly("t","e","s","t","1");
|
||||||
|
ConfigData layer2 = conf.layer(Map.of("test1",List.of("another")));
|
||||||
|
Optional<List> test2 = layer2.get("test1", List.class);
|
||||||
|
assertThat(test2).isPresent();
|
||||||
|
assertThat(test2.get()).containsExactly("another");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testList() {
|
||||||
|
ConfigData conf = new ConfigData();
|
||||||
|
conf.put("test1", List.of("t","e","s","t","1"));
|
||||||
|
Optional<List<String>> test1 = conf.getList("test1", String.class);
|
||||||
|
assertThat(test1).isPresent();
|
||||||
|
assertThat(test1.get()).containsExactly("t","e","s","t","1");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user