mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2024-11-26 10:40:30 -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 VirtDataFunctionLibrary functionLibrary;
|
||||
|
||||
private final Map<String,Object> customElements = new HashMap<>();
|
||||
|
||||
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