Merge branch 'feature/mvel-executor' of github.com:thingsboard/thingsboard into feature/mvel-executor
This commit is contained in:
commit
e21e37469b
@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.thingsboard.common.util.ListeningExecutor;
|
||||
import org.thingsboard.rule.engine.api.MailService;
|
||||
import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
|
||||
@ -443,19 +444,41 @@ class DefaultTbContext implements TbContext {
|
||||
return mainCtx.getExternalCallExecutorService();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public ScriptEngine createJsScriptEngine(String script, String... argNames) {
|
||||
return new RuleNodeJsScriptEngine(getTenantId(), mainCtx.getJsInvokeService(), script, argNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngine createMvelScriptEngine(String script, String... argNames) {
|
||||
private ScriptEngine createMvelScriptEngine(String script, String... argNames) {
|
||||
if (mainCtx.getMvelInvokeService() == null) {
|
||||
throw new RuntimeException("MVEL execution is disabled!");
|
||||
}
|
||||
return new RuleNodeMvelScriptEngine(getTenantId(), mainCtx.getMvelInvokeService(), script, argNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngine createScriptEngine(ScriptLanguage scriptLang, String script, String... argNames) {
|
||||
if (scriptLang == null) {
|
||||
scriptLang = ScriptLanguage.JS;
|
||||
}
|
||||
if (StringUtils.isBlank(script)) {
|
||||
throw new RuntimeException(scriptLang.name() + " script is blank!");
|
||||
}
|
||||
switch (scriptLang) {
|
||||
case JS:
|
||||
return createJsScriptEngine(script, argNames);
|
||||
case MVEL:
|
||||
if (Arrays.isNullOrEmpty(argNames)) {
|
||||
return createMvelScriptEngine(script, "msg", "metadata", "msgType");
|
||||
} else {
|
||||
return createMvelScriptEngine(script, argNames);
|
||||
}
|
||||
default:
|
||||
throw new RuntimeException("Unsupported script language: " + scriptLang.name());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logJsEvalRequest() {
|
||||
if (mainCtx.isStatisticsEnabled()) {
|
||||
@ -708,24 +731,6 @@ class DefaultTbContext implements TbContext {
|
||||
return mainCtx.getTenantProfileCache().get(getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngine createScriptEngine(ScriptLanguage scriptLang, String script) {
|
||||
if (scriptLang == null) {
|
||||
scriptLang = ScriptLanguage.JS;
|
||||
}
|
||||
if (StringUtils.isBlank(script)) {
|
||||
throw new RuntimeException(scriptLang.name() + " script is blank!");
|
||||
}
|
||||
switch (scriptLang) {
|
||||
case JS:
|
||||
return createJsScriptEngine(script);
|
||||
case MVEL:
|
||||
return createMvelScriptEngine(script, "msg", "metadata", "msgType");
|
||||
default:
|
||||
throw new RuntimeException("Unsupported script language: " + scriptLang.name());
|
||||
}
|
||||
}
|
||||
|
||||
private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) {
|
||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||
metaData.putValue("ruleNodeId", ruleNodeId.toString());
|
||||
|
||||
@ -262,9 +262,15 @@ public interface TbContext {
|
||||
|
||||
SmsSenderFactory getSmsSenderFactory();
|
||||
|
||||
/**
|
||||
* Creates JS Script Engine
|
||||
* @deprecated
|
||||
* <p> Use {@link #createScriptEngine} instead.
|
||||
*
|
||||
*/
|
||||
ScriptEngine createJsScriptEngine(String script, String... argNames);
|
||||
|
||||
ScriptEngine createMvelScriptEngine(String script, String... argNames);
|
||||
ScriptEngine createScriptEngine(ScriptLanguage scriptLang, String script, String... argNames);
|
||||
|
||||
void logJsEvalRequest();
|
||||
|
||||
@ -302,5 +308,4 @@ public interface TbContext {
|
||||
|
||||
TenantProfile getTenantProfile();
|
||||
|
||||
ScriptEngine createScriptEngine(ScriptLanguage scriptLang, String s);
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
|
||||
import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
|
||||
@Slf4j
|
||||
@ -47,14 +48,15 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
public class TbLogNode implements TbNode {
|
||||
|
||||
private TbLogNodeConfiguration config;
|
||||
private ScriptEngine jsEngine;
|
||||
private ScriptEngine scriptEngine;
|
||||
private boolean standard;
|
||||
|
||||
@Override
|
||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||
this.config = TbNodeUtils.convert(configuration, TbLogNodeConfiguration.class);
|
||||
this.standard = new TbLogNodeConfiguration().defaultConfiguration().getJsScript().equals(config.getJsScript());
|
||||
this.jsEngine = this.standard ? null : ctx.createJsScriptEngine(config.getJsScript());
|
||||
this.scriptEngine = this.standard ? null : ctx.createScriptEngine(config.getScriptLang(),
|
||||
ScriptLanguage.MVEL.equals(config.getScriptLang()) ? config.getMvelScript() : config.getJsScript());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -65,7 +67,7 @@ public class TbLogNode implements TbNode {
|
||||
}
|
||||
|
||||
ctx.logJsEvalRequest();
|
||||
Futures.addCallback(jsEngine.executeToStringAsync(msg), new FutureCallback<String>() {
|
||||
Futures.addCallback(scriptEngine.executeToStringAsync(msg), new FutureCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable String result) {
|
||||
ctx.logJsEvalResponse();
|
||||
@ -94,8 +96,8 @@ public class TbLogNode implements TbNode {
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (jsEngine != null) {
|
||||
jsEngine.destroy();
|
||||
if (scriptEngine != null) {
|
||||
scriptEngine.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,16 +17,21 @@ package org.thingsboard.rule.engine.action;
|
||||
|
||||
import lombok.Data;
|
||||
import org.thingsboard.rule.engine.api.NodeConfiguration;
|
||||
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
||||
|
||||
@Data
|
||||
public class TbLogNodeConfiguration implements NodeConfiguration {
|
||||
|
||||
private ScriptLanguage scriptLang;
|
||||
private String jsScript;
|
||||
private String mvelScript;
|
||||
|
||||
@Override
|
||||
public TbLogNodeConfiguration defaultConfiguration() {
|
||||
TbLogNodeConfiguration configuration = new TbLogNodeConfiguration();
|
||||
configuration.setScriptLang(ScriptLanguage.MVEL);
|
||||
configuration.setJsScript("return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);");
|
||||
configuration.setMvelScript("return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);");
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||
import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
|
||||
@ -60,7 +61,7 @@ public class TbMsgGeneratorNode implements TbNode {
|
||||
private static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg";
|
||||
|
||||
private TbMsgGeneratorNodeConfiguration config;
|
||||
private ScriptEngine jsEngine;
|
||||
private ScriptEngine scriptEngine;
|
||||
private long delay;
|
||||
private long lastScheduledTs;
|
||||
private int currentMsgCount;
|
||||
@ -93,7 +94,8 @@ public class TbMsgGeneratorNode implements TbNode {
|
||||
log.trace("updateGeneratorState, config {}", config);
|
||||
if (ctx.isLocalEntity(originatorId)) {
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType");
|
||||
this.scriptEngine = ctx.createScriptEngine(config.getScriptLang(),
|
||||
ScriptLanguage.MVEL.equals(config.getScriptLang()) ? config.getMvelScript() : config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType");
|
||||
scheduleTickMsg(ctx);
|
||||
}
|
||||
} else if (initialized.compareAndSet(true, false)) {
|
||||
@ -146,7 +148,7 @@ public class TbMsgGeneratorNode implements TbNode {
|
||||
}
|
||||
if (initialized.get()) {
|
||||
ctx.logJsEvalRequest();
|
||||
return Futures.transformAsync(jsEngine.executeGenerateAsync(prevMsg), generated -> {
|
||||
return Futures.transformAsync(scriptEngine.executeGenerateAsync(prevMsg), generated -> {
|
||||
log.trace("generate process response, generated {}, config {}", generated, config);
|
||||
ctx.logJsEvalResponse();
|
||||
prevMsg = ctx.newMsg(null, generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData());
|
||||
@ -161,9 +163,9 @@ public class TbMsgGeneratorNode implements TbNode {
|
||||
public void destroy() {
|
||||
log.trace("destroy, config {}", config);
|
||||
prevMsg = null;
|
||||
if (jsEngine != null) {
|
||||
jsEngine.destroy();
|
||||
jsEngine = null;
|
||||
if (scriptEngine != null) {
|
||||
scriptEngine.destroy();
|
||||
scriptEngine = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,27 +18,33 @@ package org.thingsboard.rule.engine.debug;
|
||||
import lombok.Data;
|
||||
import org.thingsboard.rule.engine.api.NodeConfiguration;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
||||
|
||||
@Data
|
||||
public class TbMsgGeneratorNodeConfiguration implements NodeConfiguration<TbMsgGeneratorNodeConfiguration> {
|
||||
|
||||
public static final int UNLIMITED_MSG_COUNT = 0;
|
||||
public static final String DEFAULT_SCRIPT = "var msg = { temp: 42, humidity: 77 };\n" +
|
||||
"var metadata = { data: 40 };\n" +
|
||||
"var msgType = \"POST_TELEMETRY_REQUEST\";\n\n" +
|
||||
"return { msg: msg, metadata: metadata, msgType: msgType };";
|
||||
|
||||
private int msgCount;
|
||||
private int periodInSeconds;
|
||||
private String originatorId;
|
||||
private EntityType originatorType;
|
||||
private ScriptLanguage scriptLang;
|
||||
private String jsScript;
|
||||
private String mvelScript;
|
||||
|
||||
@Override
|
||||
public TbMsgGeneratorNodeConfiguration defaultConfiguration() {
|
||||
TbMsgGeneratorNodeConfiguration configuration = new TbMsgGeneratorNodeConfiguration();
|
||||
configuration.setMsgCount(UNLIMITED_MSG_COUNT);
|
||||
configuration.setPeriodInSeconds(1);
|
||||
configuration.setJsScript("var msg = { temp: 42, humidity: 77 };\n" +
|
||||
"var metadata = { data: 40 };\n" +
|
||||
"var msgType = \"POST_TELEMETRY_REQUEST\";\n\n" +
|
||||
"return { msg: msg, metadata: metadata, msgType: msgType };");
|
||||
configuration.setScriptLang(ScriptLanguage.MVEL);
|
||||
configuration.setJsScript(DEFAULT_SCRIPT);
|
||||
configuration.setMvelScript(DEFAULT_SCRIPT);
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.thingsboard.common.util.ListeningExecutor;
|
||||
import org.thingsboard.rule.engine.api.RuleNode;
|
||||
import org.thingsboard.rule.engine.api.ScriptEngine;
|
||||
import org.thingsboard.rule.engine.api.TbContext;
|
||||
@ -29,6 +28,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
|
||||
import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
|
||||
import java.util.Set;
|
||||
@ -50,18 +50,19 @@ import java.util.Set;
|
||||
public class TbJsSwitchNode implements TbNode {
|
||||
|
||||
private TbJsSwitchNodeConfiguration config;
|
||||
private ScriptEngine jsEngine;
|
||||
private ScriptEngine scriptEngine;
|
||||
|
||||
@Override
|
||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||
this.config = TbNodeUtils.convert(configuration, TbJsSwitchNodeConfiguration.class);
|
||||
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
|
||||
this.scriptEngine = ctx.createScriptEngine(config.getScriptLang(),
|
||||
ScriptLanguage.MVEL.equals(config.getScriptLang()) ? config.getMvelScript() : config.getJsScript());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMsg(TbContext ctx, TbMsg msg) {
|
||||
ctx.logJsEvalRequest();
|
||||
Futures.addCallback(jsEngine.executeSwitchAsync(msg), new FutureCallback<Set<String>>() {
|
||||
Futures.addCallback(scriptEngine.executeSwitchAsync(msg), new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Set<String> result) {
|
||||
ctx.logJsEvalResponse();
|
||||
@ -82,8 +83,8 @@ public class TbJsSwitchNode implements TbNode {
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (jsEngine != null) {
|
||||
jsEngine.destroy();
|
||||
if (scriptEngine != null) {
|
||||
scriptEngine.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,24 +18,31 @@ package org.thingsboard.rule.engine.filter;
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.Data;
|
||||
import org.thingsboard.rule.engine.api.NodeConfiguration;
|
||||
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
public class TbJsSwitchNodeConfiguration implements NodeConfiguration<TbJsSwitchNodeConfiguration> {
|
||||
|
||||
private static final String DEFAULT_SCRIPT = "function nextRelation(metadata, msg) {\n" +
|
||||
" return ['one','nine'];\n" +
|
||||
"}\n" +
|
||||
"if(msgType === 'POST_TELEMETRY_REQUEST') {\n" +
|
||||
" return ['two'];\n" +
|
||||
"}\n" +
|
||||
"return nextRelation(metadata, msg);";
|
||||
|
||||
private ScriptLanguage scriptLang;
|
||||
private String jsScript;
|
||||
private String mvelScript;
|
||||
|
||||
@Override
|
||||
public TbJsSwitchNodeConfiguration defaultConfiguration() {
|
||||
TbJsSwitchNodeConfiguration configuration = new TbJsSwitchNodeConfiguration();
|
||||
configuration.setJsScript("function nextRelation(metadata, msg) {\n" +
|
||||
" return ['one','nine'];\n" +
|
||||
"}\n" +
|
||||
"if(msgType === 'POST_TELEMETRY_REQUEST') {\n" +
|
||||
" return ['two'];\n" +
|
||||
"}\n" +
|
||||
"return nextRelation(metadata, msg);");
|
||||
configuration.setScriptLang(ScriptLanguage.MVEL);
|
||||
configuration.setJsScript(DEFAULT_SCRIPT);
|
||||
configuration.setMvelScript(DEFAULT_SCRIPT);
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||
import org.thingsboard.server.common.data.id.RuleNodeId;
|
||||
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.TbMsgDataType;
|
||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||
@ -56,8 +57,6 @@ public class TbJsSwitchNodeTest {
|
||||
@Mock
|
||||
private TbContext ctx;
|
||||
@Mock
|
||||
private ListeningExecutor executor;
|
||||
@Mock
|
||||
private ScriptEngine scriptEngine;
|
||||
|
||||
private RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased());
|
||||
@ -80,22 +79,14 @@ public class TbJsSwitchNodeTest {
|
||||
|
||||
private void initWithScript() throws TbNodeException {
|
||||
TbJsSwitchNodeConfiguration config = new TbJsSwitchNodeConfiguration();
|
||||
config.setScriptLang(ScriptLanguage.JS);
|
||||
config.setJsScript("scr");
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
||||
|
||||
when(ctx.createJsScriptEngine("scr")).thenReturn(scriptEngine);
|
||||
when(ctx.createScriptEngine(ScriptLanguage.JS, "scr")).thenReturn(scriptEngine);
|
||||
|
||||
node = new TbJsSwitchNode();
|
||||
node.init(ctx, nodeConfiguration);
|
||||
}
|
||||
|
||||
private void verifyError(TbMsg msg, String message, Class expectedClass) {
|
||||
ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass(Throwable.class);
|
||||
verify(ctx).tellFailure(same(msg), captor.capture());
|
||||
|
||||
Throwable value = captor.getValue();
|
||||
assertEquals(expectedClass, value.getClass());
|
||||
assertEquals(message, value.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user