directapi mapping logic

This commit is contained in:
Jonathan Shook 2021-07-06 11:11:12 -05:00
parent de26371496
commit 68c0ba277b
7 changed files with 188 additions and 16 deletions

View File

@ -1,8 +1,24 @@
package io.nosqlbench.driver.direct;
import java.lang.reflect.Method;
public class DirectCall implements Runnable {
private final Method method;
private final Object[] args;
private final Object instance;
public DirectCall(Method method, Object instance, Object[] args) {
this.method = method;
this.instance = instance;
this.args = args;
}
@Override
public void run() {
try {
method.invoke(instance,args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,35 @@
package io.nosqlbench.driver.direct;
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
import io.nosqlbench.engine.api.activityimpl.uniform.BaseDriverAdapter;
import io.nosqlbench.engine.api.activityimpl.uniform.DriverAdapter;
import io.nosqlbench.engine.api.templating.ParsedCommand;
import io.nosqlbench.nb.annotations.Service;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
/**
* This activity type driver allows you to dynamically map any available
* Java API which is exposed to the NoSQLBench runtime, executing methods
* on this API by name, (optionally) storing named results, and re-using
* these named results as arguments to subsequent calls.
*
* It supports static method dispatch, instance methods, and per-thread
* object scoping.
*/
@Service(value = DriverAdapter.class, selector = "directapi")
public class DirectCallAdapter extends BaseDriverAdapter<DirectCall> {
@Override
public List<Function<String, Optional<Map<String, Object>>>> getStmtRemappers() {
return List.of(new DirectCallStmtParser());
}
@Override
public Function<ParsedCommand, OpDispenser<DirectCall>> getOpMapper() {
return new DirectOpMapper();
}
}

View File

@ -0,0 +1,44 @@
package io.nosqlbench.driver.direct;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DirectCallStmtParser implements Function<String, Optional<Map<String, Object>>> {
@Override
public Optional<Map<String, Object>> apply(String s) {
Pattern stmtPattern = Pattern.compile(
"(?<package>[a-z](\\.[a-z]+)?)?(?<class>[A-Z]\\w+)(\\.(?<staticfield>\\w+))?(\\.(?<method>\\w+))\\((?<args>.+)\\)"
);
Matcher matcher = stmtPattern.matcher(s);
if (matcher.matches()) {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
List.of("package","class","staticfield","method").forEach(n -> {
if (matcher.group(n)!=null) {
map.put(n,matcher.group(n));
}
});
if (matcher.group("args")!=null) {
String args = matcher.group("args");
String[] argsplit = args.split(",");
for (int i = 0; i < argsplit.length; i++) {
String val = argsplit[i];
if (val.startsWith("\\") && val.endsWith("\\")) {
val = val.substring(1,val.length()-2);
} else if (val.startsWith("'") && val.endsWith("'")) {
val = val.substring(1,val.length()-2);
}
map.put("_arg"+i,argsplit[i]);
}
}
return Optional.of(map);
} else {
return Optional.empty();
}
}
}

View File

@ -1,25 +1,76 @@
package io.nosqlbench.driver.direct;
import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate;
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
import io.nosqlbench.engine.api.templating.ParsedCommand;
import io.nosqlbench.nb.api.errors.OpConfigError;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Function;
import java.util.function.LongFunction;
import java.util.stream.Collectors;
public class DirectOpMapper implements OpDispenser<DirectCall> {
public class DirectOpMapper implements Function<ParsedCommand, OpDispenser<DirectCall>> {
private final OpTemplate opTemplate;
private final LongFunction<DirectCall> readyOp;
@Override
public OpDispenser<DirectCall> apply(ParsedCommand cmd) {
public DirectOpMapper(OpTemplate opTemplate) {
this.opTemplate = opTemplate;
this.readyOp = resolve(opTemplate);
}
String pkg = cmd.getStaticValueOptionally("package", String.class).orElse("java.lang");
String cls = cmd.getStaticValue("class");
String fq = pkg + "." + cls;
Class<?> clazz = null;
Object instance = null;
try {
clazz = Class.forName(fq);
private LongFunction<DirectCall> resolve(OpTemplate opTemplate) {
return new DynamicCallDispenser(opTemplate);
}
Class<?> finalClazz = clazz;
Optional<Field> staticfield =
cmd.getStaticValueOptionally("staticfield", String.class)
.map(name -> {
try {
return finalClazz.getDeclaredField(name);
} catch (Exception e) {
e.printStackTrace();
}
return null;
});
if (staticfield.isPresent()) {
Field sfield = staticfield.get();
if ((sfield.getModifiers() | Modifier.STATIC) > 0) {
instance = sfield.get(null);
clazz = instance.getClass();
} else {
throw new OpConfigError("staticfield '" + cmd.getStaticValue("staticfield", String.class) + "' is not static");
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
String methodName = cmd.getStaticValue("method");
Map<String, Object> protomap = cmd.getMap(0L);
List<Class<?>> protoargs = new ArrayList<>();
List<String> argnames = protomap.keySet().stream()
.filter(n -> n.startsWith("_"))
.collect(Collectors.toList());
LongFunction<List<Object>> argsbinder = cmd.newListBinder(argnames);
List<Object> args = argsbinder.apply(0L);
List<Class<?>> types = args.stream().map(Object::getClass).collect(Collectors.toList());
Class<?>[] argTypes = types.toArray(new Class<?>[0]);
Method method = null;
try {
method = clazz.getMethod(methodName, argTypes);
return new StaticMethodOpDispenser(method, instance, cmd.newArrayBinder(argnames));
} catch (Exception e) {
throw new RuntimeException(e);
}
public DirectCall apply(long value) {
return readyOp.apply(value);
}
}

View File

@ -0,0 +1,24 @@
package io.nosqlbench.driver.direct;
import io.nosqlbench.engine.api.activityimpl.OpDispenser;
import java.lang.reflect.Method;
import java.util.function.LongFunction;
public class StaticMethodOpDispenser implements OpDispenser<DirectCall> {
private final LongFunction<Object[]> argsfunc;
private final Method method;
private final Object instance;
public StaticMethodOpDispenser(Method method, Object instance, LongFunction<Object[]> argsfunc) {
this.method = method;
this.instance = instance;
this.argsfunc = argsfunc;
}
@Override
public DirectCall apply(long value) {
Object[] args = argsfunc.apply(value);
return new DirectCall(method, instance, args);
}
}

View File

@ -0,0 +1 @@
io.nosqlbench.nb.annotations.ServiceProcessor

View File

@ -4,6 +4,7 @@ This is a unique type of NoSQLBench driver which assumes no particular
runtime API, instead relying on runtime reflection to find and invoke
the methods as specified in the op template.
Presently, therea re two
```yaml
statements:
- "java.lang.System.out.println(\"Testing\");"
@ -13,10 +14,10 @@ statements:
class: java.lang.System
field: out
method: println
arg0: Testing
_arg0: Testing
- op:
class: java.lang.System
field: out
staticfield: out
method: println
_x: Testing
- op: