JavaScript Sandbox Service improvements.
This commit is contained in:
parent
04432ee73f
commit
f73fbc5ee9
@ -44,7 +44,7 @@ import org.thingsboard.server.dao.relation.RelationService;
|
|||||||
import org.thingsboard.server.dao.rule.RuleChainService;
|
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||||
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
||||||
import org.thingsboard.server.dao.user.UserService;
|
import org.thingsboard.server.dao.user.UserService;
|
||||||
import org.thingsboard.server.service.script.JsScriptEngine;
|
import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
|
||||||
import scala.concurrent.duration.Duration;
|
import scala.concurrent.duration.Duration;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -151,8 +151,8 @@ class DefaultTbContext implements TbContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames) {
|
public ScriptEngine createJsScriptEngine(String script, String... argNames) {
|
||||||
return new JsScriptEngine(mainCtx.getJsSandbox(), script, functionName, argNames);
|
return new RuleNodeJsScriptEngine(mainCtx.getJsSandbox(), script, argNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -50,9 +50,8 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
|
|||||||
import org.thingsboard.server.common.msg.TbMsg;
|
import org.thingsboard.server.common.msg.TbMsg;
|
||||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||||
import org.thingsboard.server.dao.event.EventService;
|
import org.thingsboard.server.dao.event.EventService;
|
||||||
import org.thingsboard.server.service.script.JsExecutorService;
|
|
||||||
import org.thingsboard.server.service.script.JsSandboxService;
|
import org.thingsboard.server.service.script.JsSandboxService;
|
||||||
import org.thingsboard.server.service.script.JsScriptEngine;
|
import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -266,7 +265,6 @@ public class RuleChainController extends BaseController {
|
|||||||
try {
|
try {
|
||||||
String script = inputParams.get("script").asText();
|
String script = inputParams.get("script").asText();
|
||||||
String scriptType = inputParams.get("scriptType").asText();
|
String scriptType = inputParams.get("scriptType").asText();
|
||||||
String functionName = inputParams.get("functionName").asText();
|
|
||||||
JsonNode argNamesJson = inputParams.get("argNames");
|
JsonNode argNamesJson = inputParams.get("argNames");
|
||||||
String[] argNames = objectMapper.treeToValue(argNamesJson, String[].class);
|
String[] argNames = objectMapper.treeToValue(argNamesJson, String[].class);
|
||||||
|
|
||||||
@ -278,7 +276,7 @@ public class RuleChainController extends BaseController {
|
|||||||
String errorText = "";
|
String errorText = "";
|
||||||
ScriptEngine engine = null;
|
ScriptEngine engine = null;
|
||||||
try {
|
try {
|
||||||
engine = new JsScriptEngine(jsSandboxService, script, functionName, argNames);
|
engine = new RuleNodeJsScriptEngine(jsSandboxService, script, argNames);
|
||||||
TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L);
|
TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L);
|
||||||
switch (scriptType) {
|
switch (scriptType) {
|
||||||
case "update":
|
case "update":
|
||||||
|
|||||||
@ -0,0 +1,133 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2018 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.thingsboard.server.service.script;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import delight.nashornsandbox.NashornSandbox;
|
||||||
|
import delight.nashornsandbox.NashornSandboxes;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public abstract class AbstractNashornJsSandboxService implements JsSandboxService {
|
||||||
|
|
||||||
|
private NashornSandbox sandbox = NashornSandboxes.create();
|
||||||
|
private ExecutorService monitorExecutorService;
|
||||||
|
|
||||||
|
private Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
monitorExecutorService = Executors.newFixedThreadPool(getMonitorThreadPoolSize());
|
||||||
|
sandbox.setExecutor(monitorExecutorService);
|
||||||
|
sandbox.setMaxCPUTime(getMaxCpuTime());
|
||||||
|
sandbox.allowNoBraces(false);
|
||||||
|
sandbox.setMaxPreparedStatements(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void stop() {
|
||||||
|
if (monitorExecutorService != null) {
|
||||||
|
monitorExecutorService.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int getMonitorThreadPoolSize();
|
||||||
|
|
||||||
|
protected abstract long getMaxCpuTime();
|
||||||
|
|
||||||
|
protected abstract int getMaxErrors();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) {
|
||||||
|
UUID scriptId = UUID.randomUUID();
|
||||||
|
String functionName = "invokeInternal_" + scriptId.toString().replace('-','_');
|
||||||
|
String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
|
||||||
|
try {
|
||||||
|
sandbox.eval(jsScript);
|
||||||
|
functionsMap.put(scriptId, functionName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to compile JS script: {}", e.getMessage(), e);
|
||||||
|
return Futures.immediateFailedFuture(e);
|
||||||
|
}
|
||||||
|
return Futures.immediateFuture(scriptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<Object> invokeFunction(UUID scriptId, Object... args) {
|
||||||
|
String functionName = functionsMap.get(scriptId);
|
||||||
|
if (functionName == null) {
|
||||||
|
return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!"));
|
||||||
|
}
|
||||||
|
if (!isBlackListed(scriptId)) {
|
||||||
|
try {
|
||||||
|
return Futures.immediateFuture(sandbox.getSandboxedInvocable().invokeFunction(functionName, args));
|
||||||
|
} catch (Exception e) {
|
||||||
|
blackListedFunctions.computeIfAbsent(scriptId, key -> new AtomicInteger(0)).incrementAndGet();
|
||||||
|
return Futures.immediateFailedFuture(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Futures.immediateFailedFuture(
|
||||||
|
new RuntimeException("Script is blacklisted due to maximum error count " + getMaxErrors() + "!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<Void> release(UUID scriptId) {
|
||||||
|
String functionName = functionsMap.get(scriptId);
|
||||||
|
if (functionName != null) {
|
||||||
|
try {
|
||||||
|
sandbox.eval(functionName + " = undefined;");
|
||||||
|
functionsMap.remove(scriptId);
|
||||||
|
blackListedFunctions.remove(scriptId);
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
return Futures.immediateFailedFuture(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Futures.immediateFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBlackListed(UUID scriptId) {
|
||||||
|
if (blackListedFunctions.containsKey(scriptId)) {
|
||||||
|
AtomicInteger errorCount = blackListedFunctions.get(scriptId);
|
||||||
|
return errorCount.get() >= getMaxErrors();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) {
|
||||||
|
switch (scriptType) {
|
||||||
|
case RULE_NODE_SCRIPT:
|
||||||
|
return RuleNodeScriptFactory.generateRuleNodeScript(functionName, scriptBody, argNames);
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("No script factory implemented for scriptType: " + scriptType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,12 +16,16 @@
|
|||||||
|
|
||||||
package org.thingsboard.server.service.script;
|
package org.thingsboard.server.service.script;
|
||||||
|
|
||||||
import javax.script.ScriptException;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public interface JsSandboxService {
|
public interface JsSandboxService {
|
||||||
|
|
||||||
Object eval(String js) throws ScriptException;
|
ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames);
|
||||||
|
|
||||||
Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException;
|
ListenableFuture<Object> invokeFunction(UUID scriptId, Object... args);
|
||||||
|
|
||||||
|
ListenableFuture<Void> release(UUID scriptId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2018 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.thingsboard.server.service.script;
|
||||||
|
|
||||||
|
public enum JsScriptType {
|
||||||
|
RULE_NODE_SCRIPT
|
||||||
|
}
|
||||||
@ -16,21 +16,13 @@
|
|||||||
|
|
||||||
package org.thingsboard.server.service.script;
|
package org.thingsboard.server.service.script;
|
||||||
|
|
||||||
import delight.nashornsandbox.NashornSandbox;
|
|
||||||
import delight.nashornsandbox.NashornSandboxes;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.annotation.PreDestroy;
|
|
||||||
import javax.script.ScriptException;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class NashornJsSandboxService implements JsSandboxService {
|
public class NashornJsSandboxService extends AbstractNashornJsSandboxService {
|
||||||
|
|
||||||
@Value("${actors.rule.js_sandbox.monitor_thread_pool_size}")
|
@Value("${actors.rule.js_sandbox.monitor_thread_pool_size}")
|
||||||
private int monitorThreadPoolSize;
|
private int monitorThreadPoolSize;
|
||||||
@ -38,33 +30,21 @@ public class NashornJsSandboxService implements JsSandboxService {
|
|||||||
@Value("${actors.rule.js_sandbox.max_cpu_time}")
|
@Value("${actors.rule.js_sandbox.max_cpu_time}")
|
||||||
private long maxCpuTime;
|
private long maxCpuTime;
|
||||||
|
|
||||||
private NashornSandbox sandbox = NashornSandboxes.create();
|
@Value("${actors.rule.js_sandbox.max_errors}")
|
||||||
private ExecutorService monitorExecutorService;
|
private int maxErrors;
|
||||||
|
|
||||||
@PostConstruct
|
@Override
|
||||||
public void init() {
|
protected int getMonitorThreadPoolSize() {
|
||||||
monitorExecutorService = Executors.newFixedThreadPool(monitorThreadPoolSize);
|
return monitorThreadPoolSize;
|
||||||
sandbox.setExecutor(monitorExecutorService);
|
|
||||||
sandbox.setMaxCPUTime(maxCpuTime);
|
|
||||||
sandbox.allowNoBraces(false);
|
|
||||||
sandbox.setMaxPreparedStatements(30);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void stop() {
|
|
||||||
if (monitorExecutorService != null) {
|
|
||||||
monitorExecutorService.shutdownNow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object eval(String js) throws ScriptException {
|
protected long getMaxCpuTime() {
|
||||||
return sandbox.eval(js);
|
return maxCpuTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {
|
protected int getMaxErrors() {
|
||||||
return sandbox.getSandboxedInvocable().invokeFunction(name, args);
|
return maxErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,56 +28,23 @@ import javax.script.ScriptException;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEngine {
|
public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEngine {
|
||||||
|
|
||||||
public static final String MSG = "msg";
|
|
||||||
public static final String METADATA = "metadata";
|
|
||||||
public static final String MSG_TYPE = "msgType";
|
|
||||||
|
|
||||||
private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msgStr, metadataStr, msgType) { " +
|
|
||||||
" var msg = JSON.parse(msgStr); " +
|
|
||||||
" var metadata = JSON.parse(metadataStr); " +
|
|
||||||
" return JSON.stringify(%s(msg, metadata, msgType));" +
|
|
||||||
" function %s(%s, %s, %s) {";
|
|
||||||
private static final String JS_WRAPPER_SUFFIX = "}" +
|
|
||||||
"\n}";
|
|
||||||
|
|
||||||
private static final ObjectMapper mapper = new ObjectMapper();
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
// private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
|
|
||||||
// private ScriptEngine engine = factory.getScriptEngine(new String[]{"--no-java"});
|
|
||||||
private final JsSandboxService sandboxService;
|
private final JsSandboxService sandboxService;
|
||||||
|
|
||||||
private final String invokeFunctionName;
|
private final UUID scriptId;
|
||||||
|
|
||||||
public JsScriptEngine(JsSandboxService sandboxService, String script, String functionName, String... argNames) {
|
public RuleNodeJsScriptEngine(JsSandboxService sandboxService, String script, String... argNames) {
|
||||||
this.sandboxService = sandboxService;
|
this.sandboxService = sandboxService;
|
||||||
this.invokeFunctionName = "invokeInternal" + this.hashCode();
|
|
||||||
String msgArg;
|
|
||||||
String metadataArg;
|
|
||||||
String msgTypeArg;
|
|
||||||
if (argNames != null && argNames.length == 3) {
|
|
||||||
msgArg = argNames[0];
|
|
||||||
metadataArg = argNames[1];
|
|
||||||
msgTypeArg = argNames[2];
|
|
||||||
} else {
|
|
||||||
msgArg = MSG;
|
|
||||||
metadataArg = METADATA;
|
|
||||||
msgTypeArg = MSG_TYPE;
|
|
||||||
}
|
|
||||||
String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, this.invokeFunctionName,
|
|
||||||
functionName, functionName, msgArg, metadataArg, msgTypeArg);
|
|
||||||
compileScript(jsWrapperPrefix + script + JS_WRAPPER_SUFFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void compileScript(String script) {
|
|
||||||
try {
|
try {
|
||||||
//engine.eval(script);
|
this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get();
|
||||||
sandboxService.eval(script);
|
} catch (Exception e) {
|
||||||
} catch (ScriptException e) {
|
|
||||||
log.warn("Failed to compile JS script: {}", e.getMessage(), e);
|
|
||||||
throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
|
throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,17 +70,17 @@ public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEng
|
|||||||
String data = null;
|
String data = null;
|
||||||
Map<String, String> metadata = null;
|
Map<String, String> metadata = null;
|
||||||
String messageType = null;
|
String messageType = null;
|
||||||
if (msgData.has(MSG)) {
|
if (msgData.has(RuleNodeScriptFactory.MSG)) {
|
||||||
JsonNode msgPayload = msgData.get(MSG);
|
JsonNode msgPayload = msgData.get(RuleNodeScriptFactory.MSG);
|
||||||
data = mapper.writeValueAsString(msgPayload);
|
data = mapper.writeValueAsString(msgPayload);
|
||||||
}
|
}
|
||||||
if (msgData.has(METADATA)) {
|
if (msgData.has(RuleNodeScriptFactory.METADATA)) {
|
||||||
JsonNode msgMetadata = msgData.get(METADATA);
|
JsonNode msgMetadata = msgData.get(RuleNodeScriptFactory.METADATA);
|
||||||
metadata = mapper.convertValue(msgMetadata, new TypeReference<Map<String, String>>() {
|
metadata = mapper.convertValue(msgMetadata, new TypeReference<Map<String, String>>() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (msgData.has(MSG_TYPE)) {
|
if (msgData.has(RuleNodeScriptFactory.MSG_TYPE)) {
|
||||||
messageType = msgData.get(MSG_TYPE).asText();
|
messageType = msgData.get(RuleNodeScriptFactory.MSG_TYPE).asText();
|
||||||
}
|
}
|
||||||
String newData = data != null ? data : msg.getData();
|
String newData = data != null ? data : msg.getData();
|
||||||
TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData().copy();
|
TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData().copy();
|
||||||
@ -195,18 +162,20 @@ public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEng
|
|||||||
private JsonNode executeScript(TbMsg msg) throws ScriptException {
|
private JsonNode executeScript(TbMsg msg) throws ScriptException {
|
||||||
try {
|
try {
|
||||||
String[] inArgs = prepareArgs(msg);
|
String[] inArgs = prepareArgs(msg);
|
||||||
//String eval = ((Invocable)engine).invokeFunction(this.invokeFunctionName, inArgs[0], inArgs[1], inArgs[2]).toString();
|
String eval = sandboxService.invokeFunction(this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString();
|
||||||
String eval = sandboxService.invokeFunction(this.invokeFunctionName, inArgs[0], inArgs[1], inArgs[2]).toString();
|
|
||||||
return mapper.readTree(eval);
|
return mapper.readTree(eval);
|
||||||
} catch (ScriptException | IllegalArgumentException th) {
|
} catch (ExecutionException e) {
|
||||||
throw th;
|
if (e.getCause() instanceof ScriptException) {
|
||||||
} catch (Throwable th) {
|
throw (ScriptException)e.getCause();
|
||||||
th.printStackTrace();
|
} else {
|
||||||
throw new RuntimeException("Failed to execute js script", th);
|
throw new ScriptException("Failed to execute js script: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ScriptException("Failed to execute js script: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
//engine = null;
|
sandboxService.release(this.scriptId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2018 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.thingsboard.server.service.script;
|
||||||
|
|
||||||
|
public class RuleNodeScriptFactory {
|
||||||
|
|
||||||
|
public static final String MSG = "msg";
|
||||||
|
public static final String METADATA = "metadata";
|
||||||
|
public static final String MSG_TYPE = "msgType";
|
||||||
|
public static final String RULE_NODE_FUNCTION_NAME = "ruleNodeFunc";
|
||||||
|
|
||||||
|
private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msgStr, metadataStr, msgType) { " +
|
||||||
|
" var msg = JSON.parse(msgStr); " +
|
||||||
|
" var metadata = JSON.parse(metadataStr); " +
|
||||||
|
" return JSON.stringify(%s(msg, metadata, msgType));" +
|
||||||
|
" function %s(%s, %s, %s) {";
|
||||||
|
private static final String JS_WRAPPER_SUFFIX = "}" +
|
||||||
|
"\n}";
|
||||||
|
|
||||||
|
|
||||||
|
public static String generateRuleNodeScript(String functionName, String scriptBody, String... argNames) {
|
||||||
|
String msgArg;
|
||||||
|
String metadataArg;
|
||||||
|
String msgTypeArg;
|
||||||
|
if (argNames != null && argNames.length == 3) {
|
||||||
|
msgArg = argNames[0];
|
||||||
|
metadataArg = argNames[1];
|
||||||
|
msgTypeArg = argNames[2];
|
||||||
|
} else {
|
||||||
|
msgArg = MSG;
|
||||||
|
metadataArg = METADATA;
|
||||||
|
msgTypeArg = MSG_TYPE;
|
||||||
|
}
|
||||||
|
String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, functionName,
|
||||||
|
RULE_NODE_FUNCTION_NAME, RULE_NODE_FUNCTION_NAME, msgArg, metadataArg, msgTypeArg);
|
||||||
|
return jsWrapperPrefix + scriptBody + JS_WRAPPER_SUFFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -243,6 +243,8 @@ actors:
|
|||||||
monitor_thread_pool_size: "${ACTORS_RULE_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}"
|
monitor_thread_pool_size: "${ACTORS_RULE_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}"
|
||||||
# Maximum CPU time in milliseconds allowed for script execution
|
# Maximum CPU time in milliseconds allowed for script execution
|
||||||
max_cpu_time: "${ACTORS_RULE_JS_SANDBOX_MAX_CPU_TIME:100}"
|
max_cpu_time: "${ACTORS_RULE_JS_SANDBOX_MAX_CPU_TIME:100}"
|
||||||
|
# Maximum allowed JavaScript execution errors before JavaScript will be blacklisted
|
||||||
|
max_errors: "${ACTORS_RULE_JS_SANDBOX_MAX_ERRORS:3}"
|
||||||
chain:
|
chain:
|
||||||
# Errors for particular actor are persisted once per specified amount of milliseconds
|
# Errors for particular actor are persisted once per specified amount of milliseconds
|
||||||
error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}"
|
error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}"
|
||||||
|
|||||||
@ -27,30 +27,28 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
|
|||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class JsScriptEngineTest {
|
public class RuleNodeJsScriptEngineTest {
|
||||||
|
|
||||||
private ScriptEngine scriptEngine;
|
private ScriptEngine scriptEngine;
|
||||||
private TestNashornJsSandboxService jsSandboxService;
|
private TestNashornJsSandboxService jsSandboxService;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeTest() throws Exception {
|
public void beforeTest() throws Exception {
|
||||||
jsSandboxService = new TestNashornJsSandboxService(1, 100);
|
jsSandboxService = new TestNashornJsSandboxService(1, 100, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterTest() throws Exception {
|
public void afterTest() throws Exception {
|
||||||
jsSandboxService.destroy();
|
jsSandboxService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void msgCanBeUpdated() throws ScriptException {
|
public void msgCanBeUpdated() throws ScriptException {
|
||||||
String function = "metadata.temp = metadata.temp * 10; return {metadata: metadata};";
|
String function = "metadata.temp = metadata.temp * 10; return {metadata: metadata};";
|
||||||
scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform");
|
scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
|
||||||
|
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "7");
|
metaData.putValue("temp", "7");
|
||||||
@ -61,12 +59,13 @@ public class JsScriptEngineTest {
|
|||||||
|
|
||||||
TbMsg actual = scriptEngine.executeUpdate(msg);
|
TbMsg actual = scriptEngine.executeUpdate(msg);
|
||||||
assertEquals("70", actual.getMetaData().getValue("temp"));
|
assertEquals("70", actual.getMetaData().getValue("temp"));
|
||||||
|
scriptEngine.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void newAttributesCanBeAddedInMsg() throws ScriptException {
|
public void newAttributesCanBeAddedInMsg() throws ScriptException {
|
||||||
String function = "metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};";
|
String function = "metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};";
|
||||||
scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform");
|
scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "7");
|
metaData.putValue("temp", "7");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
@ -76,12 +75,13 @@ public class JsScriptEngineTest {
|
|||||||
|
|
||||||
TbMsg actual = scriptEngine.executeUpdate(msg);
|
TbMsg actual = scriptEngine.executeUpdate(msg);
|
||||||
assertEquals("94", actual.getMetaData().getValue("newAttr"));
|
assertEquals("94", actual.getMetaData().getValue("newAttr"));
|
||||||
|
scriptEngine.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void payloadCanBeUpdated() throws ScriptException {
|
public void payloadCanBeUpdated() throws ScriptException {
|
||||||
String function = "msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};";
|
String function = "msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};";
|
||||||
scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform");
|
scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "7");
|
metaData.putValue("temp", "7");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
@ -93,12 +93,13 @@ public class JsScriptEngineTest {
|
|||||||
|
|
||||||
String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
|
String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
|
||||||
assertEquals(expectedJson, actual.getData());
|
assertEquals(expectedJson, actual.getData());
|
||||||
|
scriptEngine.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void metadataAccessibleForFilter() throws ScriptException {
|
public void metadataAccessibleForFilter() throws ScriptException {
|
||||||
String function = "return metadata.humidity < 15;";
|
String function = "return metadata.humidity < 15;";
|
||||||
scriptEngine = new JsScriptEngine(jsSandboxService, function, "Filter");
|
scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "7");
|
metaData.putValue("temp", "7");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
@ -106,12 +107,13 @@ public class JsScriptEngineTest {
|
|||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
|
||||||
assertFalse(scriptEngine.executeFilter(msg));
|
assertFalse(scriptEngine.executeFilter(msg));
|
||||||
|
scriptEngine.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataAccessibleForFilter() throws ScriptException {
|
public void dataAccessibleForFilter() throws ScriptException {
|
||||||
String function = "return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 7 && msg.bigObj.prop == 42;";
|
String function = "return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 7 && msg.bigObj.prop == 42;";
|
||||||
scriptEngine = new JsScriptEngine(jsSandboxService, function, "Filter");
|
scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "7");
|
metaData.putValue("temp", "7");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
@ -119,6 +121,7 @@ public class JsScriptEngineTest {
|
|||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
|
||||||
assertTrue(scriptEngine.executeFilter(msg));
|
assertTrue(scriptEngine.executeFilter(msg));
|
||||||
|
scriptEngine.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -131,7 +134,7 @@ public class JsScriptEngineTest {
|
|||||||
"};\n" +
|
"};\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"return nextRelation(metadata, msg);";
|
"return nextRelation(metadata, msg);";
|
||||||
scriptEngine = new JsScriptEngine(jsSandboxService, jsCode, "Switch");
|
scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, jsCode);
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "10");
|
metaData.putValue("temp", "10");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
@ -140,6 +143,7 @@ public class JsScriptEngineTest {
|
|||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
|
||||||
Set<String> actual = scriptEngine.executeSwitch(msg);
|
Set<String> actual = scriptEngine.executeSwitch(msg);
|
||||||
assertEquals(Sets.newHashSet("one"), actual);
|
assertEquals(Sets.newHashSet("one"), actual);
|
||||||
|
scriptEngine.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -152,7 +156,7 @@ public class JsScriptEngineTest {
|
|||||||
"};\n" +
|
"};\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"return nextRelation(metadata, msg);";
|
"return nextRelation(metadata, msg);";
|
||||||
scriptEngine = new JsScriptEngine(jsSandboxService, jsCode, "Switch");
|
scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, jsCode);
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "10");
|
metaData.putValue("temp", "10");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
@ -161,6 +165,7 @@ public class JsScriptEngineTest {
|
|||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
|
||||||
Set<String> actual = scriptEngine.executeSwitch(msg);
|
Set<String> actual = scriptEngine.executeSwitch(msg);
|
||||||
assertEquals(Sets.newHashSet("one", "three"), actual);
|
assertEquals(Sets.newHashSet("one", "three"), actual);
|
||||||
|
scriptEngine.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -16,39 +16,40 @@
|
|||||||
|
|
||||||
package org.thingsboard.server.service.script;
|
package org.thingsboard.server.service.script;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import delight.nashornsandbox.NashornSandbox;
|
import delight.nashornsandbox.NashornSandbox;
|
||||||
import delight.nashornsandbox.NashornSandboxes;
|
import delight.nashornsandbox.NashornSandboxes;
|
||||||
|
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class TestNashornJsSandboxService implements JsSandboxService {
|
public class TestNashornJsSandboxService extends AbstractNashornJsSandboxService {
|
||||||
|
|
||||||
private NashornSandbox sandbox = NashornSandboxes.create();
|
private final int monitorThreadPoolSize;
|
||||||
private ExecutorService monitorExecutorService;
|
private final long maxCpuTime;
|
||||||
|
private final int maxErrors;
|
||||||
|
|
||||||
public TestNashornJsSandboxService(int monitorThreadPoolSize, long maxCpuTime) {
|
public TestNashornJsSandboxService(int monitorThreadPoolSize, long maxCpuTime, int maxErrors) {
|
||||||
monitorExecutorService = Executors.newFixedThreadPool(monitorThreadPoolSize);
|
this.monitorThreadPoolSize = monitorThreadPoolSize;
|
||||||
sandbox.setExecutor(monitorExecutorService);
|
this.maxCpuTime = maxCpuTime;
|
||||||
sandbox.setMaxCPUTime(maxCpuTime);
|
this.maxErrors = maxErrors;
|
||||||
sandbox.allowNoBraces(false);
|
init();
|
||||||
sandbox.setMaxPreparedStatements(30);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object eval(String js) throws ScriptException {
|
protected int getMonitorThreadPoolSize() {
|
||||||
return sandbox.eval(js);
|
return monitorThreadPoolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {
|
protected long getMaxCpuTime() {
|
||||||
return sandbox.getSandboxedInvocable().invokeFunction(name, args);
|
return maxCpuTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
@Override
|
||||||
if (monitorExecutorService != null) {
|
protected int getMaxErrors() {
|
||||||
monitorExecutorService.shutdownNow();
|
return maxErrors;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,6 +90,6 @@ public interface TbContext {
|
|||||||
|
|
||||||
MailService getMailService();
|
MailService getMailService();
|
||||||
|
|
||||||
ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames);
|
ScriptEngine createJsScriptEngine(String script, String... argNames);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,7 +43,7 @@ public abstract class TbAbstractAlarmNode<C extends TbAbstractAlarmNodeConfigura
|
|||||||
@Override
|
@Override
|
||||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||||
this.config = loadAlarmNodeConfig(configuration);
|
this.config = loadAlarmNodeConfig(configuration);
|
||||||
this.buildDetailsJsEngine = ctx.createJsScriptEngine(config.getAlarmDetailsBuildJs(), "Details");
|
this.buildDetailsJsEngine = ctx.createJsScriptEngine(config.getAlarmDetailsBuildJs());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract C loadAlarmNodeConfig(TbNodeConfiguration configuration) throws TbNodeException;
|
protected abstract C loadAlarmNodeConfig(TbNodeConfiguration configuration) throws TbNodeException;
|
||||||
|
|||||||
@ -46,7 +46,7 @@ public class TbLogNode implements TbNode {
|
|||||||
@Override
|
@Override
|
||||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||||
this.config = TbNodeUtils.convert(configuration, TbLogNodeConfiguration.class);
|
this.config = TbNodeUtils.convert(configuration, TbLogNodeConfiguration.class);
|
||||||
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "ToString");
|
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -65,7 +65,7 @@ public class TbMsgGeneratorNode implements TbNode {
|
|||||||
} else {
|
} else {
|
||||||
originatorId = ctx.getSelfId();
|
originatorId = ctx.getSelfId();
|
||||||
}
|
}
|
||||||
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "Generate", "prevMsg", "prevMetadata", "prevMsgType");
|
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType");
|
||||||
sentTickMsg(ctx);
|
sentTickMsg(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ public class TbJsFilterNode implements TbNode {
|
|||||||
@Override
|
@Override
|
||||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||||
this.config = TbNodeUtils.convert(configuration, TbJsFilterNodeConfiguration.class);
|
this.config = TbNodeUtils.convert(configuration, TbJsFilterNodeConfiguration.class);
|
||||||
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "Filter");
|
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -47,7 +47,7 @@ public class TbJsSwitchNode implements TbNode {
|
|||||||
@Override
|
@Override
|
||||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||||
this.config = TbNodeUtils.convert(configuration, TbJsSwitchNodeConfiguration.class);
|
this.config = TbNodeUtils.convert(configuration, TbJsSwitchNodeConfiguration.class);
|
||||||
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "Switch");
|
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -43,7 +43,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode {
|
|||||||
@Override
|
@Override
|
||||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||||
this.config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class);
|
this.config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class);
|
||||||
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "Transform");
|
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
|
||||||
setConfig(config);
|
setConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -152,7 +152,7 @@ public class TbAlarmNodeTest {
|
|||||||
|
|
||||||
verifyError(msg, "message", NotImplementedException.class);
|
verifyError(msg, "message", NotImplementedException.class);
|
||||||
|
|
||||||
verify(ctx).createJsScriptEngine("DETAILS", "Details");
|
verify(ctx).createJsScriptEngine("DETAILS");
|
||||||
verify(ctx, times(1)).getJsExecutor();
|
verify(ctx, times(1)).getJsExecutor();
|
||||||
verify(ctx).getAlarmService();
|
verify(ctx).getAlarmService();
|
||||||
verify(ctx, times(2)).getDbCallbackExecutor();
|
verify(ctx, times(2)).getDbCallbackExecutor();
|
||||||
@ -314,7 +314,7 @@ public class TbAlarmNodeTest {
|
|||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
||||||
|
|
||||||
when(ctx.createJsScriptEngine("DETAILS", "Details")).thenReturn(detailsJs);
|
when(ctx.createJsScriptEngine("DETAILS")).thenReturn(detailsJs);
|
||||||
|
|
||||||
when(ctx.getTenantId()).thenReturn(tenantId);
|
when(ctx.getTenantId()).thenReturn(tenantId);
|
||||||
when(ctx.getJsExecutor()).thenReturn(executor);
|
when(ctx.getJsExecutor()).thenReturn(executor);
|
||||||
@ -338,7 +338,7 @@ public class TbAlarmNodeTest {
|
|||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
||||||
|
|
||||||
when(ctx.createJsScriptEngine("DETAILS", "Details")).thenReturn(detailsJs);
|
when(ctx.createJsScriptEngine("DETAILS")).thenReturn(detailsJs);
|
||||||
|
|
||||||
when(ctx.getTenantId()).thenReturn(tenantId);
|
when(ctx.getTenantId()).thenReturn(tenantId);
|
||||||
when(ctx.getJsExecutor()).thenReturn(executor);
|
when(ctx.getJsExecutor()).thenReturn(executor);
|
||||||
|
|||||||
@ -97,7 +97,7 @@ public class TbJsFilterNodeTest {
|
|||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
||||||
|
|
||||||
when(ctx.createJsScriptEngine("scr", "Filter")).thenReturn(scriptEngine);
|
when(ctx.createJsScriptEngine("scr")).thenReturn(scriptEngine);
|
||||||
|
|
||||||
node = new TbJsFilterNode();
|
node = new TbJsFilterNode();
|
||||||
node.init(ctx, nodeConfiguration);
|
node.init(ctx, nodeConfiguration);
|
||||||
|
|||||||
@ -79,7 +79,7 @@ public class TbJsSwitchNodeTest {
|
|||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
||||||
|
|
||||||
when(ctx.createJsScriptEngine("scr", "Switch")).thenReturn(scriptEngine);
|
when(ctx.createJsScriptEngine("scr")).thenReturn(scriptEngine);
|
||||||
|
|
||||||
node = new TbJsSwitchNode();
|
node = new TbJsSwitchNode();
|
||||||
node.init(ctx, nodeConfiguration);
|
node.init(ctx, nodeConfiguration);
|
||||||
|
|||||||
@ -97,7 +97,7 @@ public class TbTransformMsgNodeTest {
|
|||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
|
||||||
|
|
||||||
when(ctx.createJsScriptEngine("scr", "Transform")).thenReturn(scriptEngine);
|
when(ctx.createJsScriptEngine("scr")).thenReturn(scriptEngine);
|
||||||
|
|
||||||
node = new TbTransformMsgNode();
|
node = new TbTransformMsgNode();
|
||||||
node.init(ctx, nodeConfiguration);
|
node.init(ctx, nodeConfiguration);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user