Commit f73fbc5ee94b64874f5b329c85a28b686204797e

Authored by Igor Kulikov
1 parent 04432ee7

JavaScript Sandbox Service improvements.

Showing 22 changed files with 302 additions and 136 deletions
@@ -44,7 +44,7 @@ import org.thingsboard.server.dao.relation.RelationService; @@ -44,7 +44,7 @@ import org.thingsboard.server.dao.relation.RelationService;
44 import org.thingsboard.server.dao.rule.RuleChainService; 44 import org.thingsboard.server.dao.rule.RuleChainService;
45 import org.thingsboard.server.dao.timeseries.TimeseriesService; 45 import org.thingsboard.server.dao.timeseries.TimeseriesService;
46 import org.thingsboard.server.dao.user.UserService; 46 import org.thingsboard.server.dao.user.UserService;
47 -import org.thingsboard.server.service.script.JsScriptEngine; 47 +import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
48 import scala.concurrent.duration.Duration; 48 import scala.concurrent.duration.Duration;
49 49
50 import java.util.Collections; 50 import java.util.Collections;
@@ -151,8 +151,8 @@ class DefaultTbContext implements TbContext { @@ -151,8 +151,8 @@ class DefaultTbContext implements TbContext {
151 } 151 }
152 152
153 @Override 153 @Override
154 - public ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames) {  
155 - return new JsScriptEngine(mainCtx.getJsSandbox(), script, functionName, argNames); 154 + public ScriptEngine createJsScriptEngine(String script, String... argNames) {
  155 + return new RuleNodeJsScriptEngine(mainCtx.getJsSandbox(), script, argNames);
156 } 156 }
157 157
158 @Override 158 @Override
@@ -50,9 +50,8 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; @@ -50,9 +50,8 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
50 import org.thingsboard.server.common.msg.TbMsg; 50 import org.thingsboard.server.common.msg.TbMsg;
51 import org.thingsboard.server.common.msg.TbMsgMetaData; 51 import org.thingsboard.server.common.msg.TbMsgMetaData;
52 import org.thingsboard.server.dao.event.EventService; 52 import org.thingsboard.server.dao.event.EventService;
53 -import org.thingsboard.server.service.script.JsExecutorService;  
54 import org.thingsboard.server.service.script.JsSandboxService; 53 import org.thingsboard.server.service.script.JsSandboxService;
55 -import org.thingsboard.server.service.script.JsScriptEngine; 54 +import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
56 55
57 import java.util.List; 56 import java.util.List;
58 import java.util.Map; 57 import java.util.Map;
@@ -266,7 +265,6 @@ public class RuleChainController extends BaseController { @@ -266,7 +265,6 @@ public class RuleChainController extends BaseController {
266 try { 265 try {
267 String script = inputParams.get("script").asText(); 266 String script = inputParams.get("script").asText();
268 String scriptType = inputParams.get("scriptType").asText(); 267 String scriptType = inputParams.get("scriptType").asText();
269 - String functionName = inputParams.get("functionName").asText();  
270 JsonNode argNamesJson = inputParams.get("argNames"); 268 JsonNode argNamesJson = inputParams.get("argNames");
271 String[] argNames = objectMapper.treeToValue(argNamesJson, String[].class); 269 String[] argNames = objectMapper.treeToValue(argNamesJson, String[].class);
272 270
@@ -278,7 +276,7 @@ public class RuleChainController extends BaseController { @@ -278,7 +276,7 @@ public class RuleChainController extends BaseController {
278 String errorText = ""; 276 String errorText = "";
279 ScriptEngine engine = null; 277 ScriptEngine engine = null;
280 try { 278 try {
281 - engine = new JsScriptEngine(jsSandboxService, script, functionName, argNames); 279 + engine = new RuleNodeJsScriptEngine(jsSandboxService, script, argNames);
282 TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L); 280 TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L);
283 switch (scriptType) { 281 switch (scriptType) {
284 case "update": 282 case "update":
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.service.script;
  18 +
  19 +import com.google.common.util.concurrent.Futures;
  20 +import com.google.common.util.concurrent.ListenableFuture;
  21 +import delight.nashornsandbox.NashornSandbox;
  22 +import delight.nashornsandbox.NashornSandboxes;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +
  25 +import javax.annotation.PostConstruct;
  26 +import javax.annotation.PreDestroy;
  27 +import javax.script.ScriptException;
  28 +import java.util.Map;
  29 +import java.util.UUID;
  30 +import java.util.concurrent.ConcurrentHashMap;
  31 +import java.util.concurrent.ExecutorService;
  32 +import java.util.concurrent.Executors;
  33 +import java.util.concurrent.atomic.AtomicInteger;
  34 +
  35 +@Slf4j
  36 +public abstract class AbstractNashornJsSandboxService implements JsSandboxService {
  37 +
  38 + private NashornSandbox sandbox = NashornSandboxes.create();
  39 + private ExecutorService monitorExecutorService;
  40 +
  41 + private Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
  42 +
  43 + private Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
  44 +
  45 + @PostConstruct
  46 + public void init() {
  47 + monitorExecutorService = Executors.newFixedThreadPool(getMonitorThreadPoolSize());
  48 + sandbox.setExecutor(monitorExecutorService);
  49 + sandbox.setMaxCPUTime(getMaxCpuTime());
  50 + sandbox.allowNoBraces(false);
  51 + sandbox.setMaxPreparedStatements(30);
  52 + }
  53 +
  54 + @PreDestroy
  55 + public void stop() {
  56 + if (monitorExecutorService != null) {
  57 + monitorExecutorService.shutdownNow();
  58 + }
  59 + }
  60 +
  61 + protected abstract int getMonitorThreadPoolSize();
  62 +
  63 + protected abstract long getMaxCpuTime();
  64 +
  65 + protected abstract int getMaxErrors();
  66 +
  67 + @Override
  68 + public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) {
  69 + UUID scriptId = UUID.randomUUID();
  70 + String functionName = "invokeInternal_" + scriptId.toString().replace('-','_');
  71 + String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
  72 + try {
  73 + sandbox.eval(jsScript);
  74 + functionsMap.put(scriptId, functionName);
  75 + } catch (Exception e) {
  76 + log.warn("Failed to compile JS script: {}", e.getMessage(), e);
  77 + return Futures.immediateFailedFuture(e);
  78 + }
  79 + return Futures.immediateFuture(scriptId);
  80 + }
  81 +
  82 + @Override
  83 + public ListenableFuture<Object> invokeFunction(UUID scriptId, Object... args) {
  84 + String functionName = functionsMap.get(scriptId);
  85 + if (functionName == null) {
  86 + return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!"));
  87 + }
  88 + if (!isBlackListed(scriptId)) {
  89 + try {
  90 + return Futures.immediateFuture(sandbox.getSandboxedInvocable().invokeFunction(functionName, args));
  91 + } catch (Exception e) {
  92 + blackListedFunctions.computeIfAbsent(scriptId, key -> new AtomicInteger(0)).incrementAndGet();
  93 + return Futures.immediateFailedFuture(e);
  94 + }
  95 + } else {
  96 + return Futures.immediateFailedFuture(
  97 + new RuntimeException("Script is blacklisted due to maximum error count " + getMaxErrors() + "!"));
  98 + }
  99 + }
  100 +
  101 + @Override
  102 + public ListenableFuture<Void> release(UUID scriptId) {
  103 + String functionName = functionsMap.get(scriptId);
  104 + if (functionName != null) {
  105 + try {
  106 + sandbox.eval(functionName + " = undefined;");
  107 + functionsMap.remove(scriptId);
  108 + blackListedFunctions.remove(scriptId);
  109 + } catch (ScriptException e) {
  110 + return Futures.immediateFailedFuture(e);
  111 + }
  112 + }
  113 + return Futures.immediateFuture(null);
  114 + }
  115 +
  116 + private boolean isBlackListed(UUID scriptId) {
  117 + if (blackListedFunctions.containsKey(scriptId)) {
  118 + AtomicInteger errorCount = blackListedFunctions.get(scriptId);
  119 + return errorCount.get() >= getMaxErrors();
  120 + } else {
  121 + return false;
  122 + }
  123 + }
  124 +
  125 + private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) {
  126 + switch (scriptType) {
  127 + case RULE_NODE_SCRIPT:
  128 + return RuleNodeScriptFactory.generateRuleNodeScript(functionName, scriptBody, argNames);
  129 + default:
  130 + throw new RuntimeException("No script factory implemented for scriptType: " + scriptType);
  131 + }
  132 + }
  133 +}
@@ -16,12 +16,16 @@ @@ -16,12 +16,16 @@
16 16
17 package org.thingsboard.server.service.script; 17 package org.thingsboard.server.service.script;
18 18
19 -import javax.script.ScriptException; 19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +
  21 +import java.util.UUID;
20 22
21 public interface JsSandboxService { 23 public interface JsSandboxService {
22 24
23 - Object eval(String js) throws ScriptException; 25 + ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames);
  26 +
  27 + ListenableFuture<Object> invokeFunction(UUID scriptId, Object... args);
24 28
25 - Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException; 29 + ListenableFuture<Void> release(UUID scriptId);
26 30
27 } 31 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.service.script;
  18 +
  19 +public enum JsScriptType {
  20 + RULE_NODE_SCRIPT
  21 +}
@@ -16,21 +16,13 @@ @@ -16,21 +16,13 @@
16 16
17 package org.thingsboard.server.service.script; 17 package org.thingsboard.server.service.script;
18 18
19 -import delight.nashornsandbox.NashornSandbox;  
20 -import delight.nashornsandbox.NashornSandboxes;  
21 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
23 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
24 22
25 -import javax.annotation.PostConstruct;  
26 -import javax.annotation.PreDestroy;  
27 -import javax.script.ScriptException;  
28 -import java.util.concurrent.ExecutorService;  
29 -import java.util.concurrent.Executors;  
30 -  
31 @Slf4j 23 @Slf4j
32 @Service 24 @Service
33 -public class NashornJsSandboxService implements JsSandboxService { 25 +public class NashornJsSandboxService extends AbstractNashornJsSandboxService {
34 26
35 @Value("${actors.rule.js_sandbox.monitor_thread_pool_size}") 27 @Value("${actors.rule.js_sandbox.monitor_thread_pool_size}")
36 private int monitorThreadPoolSize; 28 private int monitorThreadPoolSize;
@@ -38,33 +30,21 @@ public class NashornJsSandboxService implements JsSandboxService { @@ -38,33 +30,21 @@ public class NashornJsSandboxService implements JsSandboxService {
38 @Value("${actors.rule.js_sandbox.max_cpu_time}") 30 @Value("${actors.rule.js_sandbox.max_cpu_time}")
39 private long maxCpuTime; 31 private long maxCpuTime;
40 32
41 - private NashornSandbox sandbox = NashornSandboxes.create();  
42 - private ExecutorService monitorExecutorService;  
43 -  
44 - @PostConstruct  
45 - public void init() {  
46 - monitorExecutorService = Executors.newFixedThreadPool(monitorThreadPoolSize);  
47 - sandbox.setExecutor(monitorExecutorService);  
48 - sandbox.setMaxCPUTime(maxCpuTime);  
49 - sandbox.allowNoBraces(false);  
50 - sandbox.setMaxPreparedStatements(30);  
51 - } 33 + @Value("${actors.rule.js_sandbox.max_errors}")
  34 + private int maxErrors;
52 35
53 - @PreDestroy  
54 - public void stop() {  
55 - if (monitorExecutorService != null) {  
56 - monitorExecutorService.shutdownNow();  
57 - } 36 + @Override
  37 + protected int getMonitorThreadPoolSize() {
  38 + return monitorThreadPoolSize;
58 } 39 }
59 40
60 @Override 41 @Override
61 - public Object eval(String js) throws ScriptException {  
62 - return sandbox.eval(js); 42 + protected long getMaxCpuTime() {
  43 + return maxCpuTime;
63 } 44 }
64 45
65 @Override 46 @Override
66 - public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {  
67 - return sandbox.getSandboxedInvocable().invokeFunction(name, args); 47 + protected int getMaxErrors() {
  48 + return maxErrors;
68 } 49 }
69 -  
70 } 50 }
application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java renamed from application/src/main/java/org/thingsboard/server/service/script/JsScriptEngine.java
@@ -28,56 +28,23 @@ import javax.script.ScriptException; @@ -28,56 +28,23 @@ import javax.script.ScriptException;
28 import java.util.Collections; 28 import java.util.Collections;
29 import java.util.Map; 29 import java.util.Map;
30 import java.util.Set; 30 import java.util.Set;
  31 +import java.util.UUID;
  32 +import java.util.concurrent.ExecutionException;
31 33
32 34
33 @Slf4j 35 @Slf4j
34 -public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEngine {  
35 -  
36 - public static final String MSG = "msg";  
37 - public static final String METADATA = "metadata";  
38 - public static final String MSG_TYPE = "msgType";  
39 -  
40 - private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msgStr, metadataStr, msgType) { " +  
41 - " var msg = JSON.parse(msgStr); " +  
42 - " var metadata = JSON.parse(metadataStr); " +  
43 - " return JSON.stringify(%s(msg, metadata, msgType));" +  
44 - " function %s(%s, %s, %s) {";  
45 - private static final String JS_WRAPPER_SUFFIX = "}" +  
46 - "\n}"; 36 +public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEngine {
47 37
48 private static final ObjectMapper mapper = new ObjectMapper(); 38 private static final ObjectMapper mapper = new ObjectMapper();
49 -// private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory();  
50 -// private ScriptEngine engine = factory.getScriptEngine(new String[]{"--no-java"});  
51 private final JsSandboxService sandboxService; 39 private final JsSandboxService sandboxService;
52 40
53 - private final String invokeFunctionName; 41 + private final UUID scriptId;
54 42
55 - public JsScriptEngine(JsSandboxService sandboxService, String script, String functionName, String... argNames) { 43 + public RuleNodeJsScriptEngine(JsSandboxService sandboxService, String script, String... argNames) {
56 this.sandboxService = sandboxService; 44 this.sandboxService = sandboxService;
57 - this.invokeFunctionName = "invokeInternal" + this.hashCode();  
58 - String msgArg;  
59 - String metadataArg;  
60 - String msgTypeArg;  
61 - if (argNames != null && argNames.length == 3) {  
62 - msgArg = argNames[0];  
63 - metadataArg = argNames[1];  
64 - msgTypeArg = argNames[2];  
65 - } else {  
66 - msgArg = MSG;  
67 - metadataArg = METADATA;  
68 - msgTypeArg = MSG_TYPE;  
69 - }  
70 - String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, this.invokeFunctionName,  
71 - functionName, functionName, msgArg, metadataArg, msgTypeArg);  
72 - compileScript(jsWrapperPrefix + script + JS_WRAPPER_SUFFIX);  
73 - }  
74 -  
75 - private void compileScript(String script) {  
76 try { 45 try {
77 - //engine.eval(script);  
78 - sandboxService.eval(script);  
79 - } catch (ScriptException e) {  
80 - log.warn("Failed to compile JS script: {}", e.getMessage(), e); 46 + this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get();
  47 + } catch (Exception e) {
81 throw new IllegalArgumentException("Can't compile script: " + e.getMessage()); 48 throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
82 } 49 }
83 } 50 }
@@ -103,17 +70,17 @@ public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEng @@ -103,17 +70,17 @@ public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEng
103 String data = null; 70 String data = null;
104 Map<String, String> metadata = null; 71 Map<String, String> metadata = null;
105 String messageType = null; 72 String messageType = null;
106 - if (msgData.has(MSG)) {  
107 - JsonNode msgPayload = msgData.get(MSG); 73 + if (msgData.has(RuleNodeScriptFactory.MSG)) {
  74 + JsonNode msgPayload = msgData.get(RuleNodeScriptFactory.MSG);
108 data = mapper.writeValueAsString(msgPayload); 75 data = mapper.writeValueAsString(msgPayload);
109 } 76 }
110 - if (msgData.has(METADATA)) {  
111 - JsonNode msgMetadata = msgData.get(METADATA); 77 + if (msgData.has(RuleNodeScriptFactory.METADATA)) {
  78 + JsonNode msgMetadata = msgData.get(RuleNodeScriptFactory.METADATA);
112 metadata = mapper.convertValue(msgMetadata, new TypeReference<Map<String, String>>() { 79 metadata = mapper.convertValue(msgMetadata, new TypeReference<Map<String, String>>() {
113 }); 80 });
114 } 81 }
115 - if (msgData.has(MSG_TYPE)) {  
116 - messageType = msgData.get(MSG_TYPE).asText(); 82 + if (msgData.has(RuleNodeScriptFactory.MSG_TYPE)) {
  83 + messageType = msgData.get(RuleNodeScriptFactory.MSG_TYPE).asText();
117 } 84 }
118 String newData = data != null ? data : msg.getData(); 85 String newData = data != null ? data : msg.getData();
119 TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData().copy(); 86 TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData().copy();
@@ -195,18 +162,20 @@ public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEng @@ -195,18 +162,20 @@ public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEng
195 private JsonNode executeScript(TbMsg msg) throws ScriptException { 162 private JsonNode executeScript(TbMsg msg) throws ScriptException {
196 try { 163 try {
197 String[] inArgs = prepareArgs(msg); 164 String[] inArgs = prepareArgs(msg);
198 - //String eval = ((Invocable)engine).invokeFunction(this.invokeFunctionName, inArgs[0], inArgs[1], inArgs[2]).toString();  
199 - String eval = sandboxService.invokeFunction(this.invokeFunctionName, inArgs[0], inArgs[1], inArgs[2]).toString(); 165 + String eval = sandboxService.invokeFunction(this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString();
200 return mapper.readTree(eval); 166 return mapper.readTree(eval);
201 - } catch (ScriptException | IllegalArgumentException th) {  
202 - throw th;  
203 - } catch (Throwable th) {  
204 - th.printStackTrace();  
205 - throw new RuntimeException("Failed to execute js script", th); 167 + } catch (ExecutionException e) {
  168 + if (e.getCause() instanceof ScriptException) {
  169 + throw (ScriptException)e.getCause();
  170 + } else {
  171 + throw new ScriptException("Failed to execute js script: " + e.getMessage());
  172 + }
  173 + } catch (Exception e) {
  174 + throw new ScriptException("Failed to execute js script: " + e.getMessage());
206 } 175 }
207 } 176 }
208 177
209 public void destroy() { 178 public void destroy() {
210 - //engine = null; 179 + sandboxService.release(this.scriptId);
211 } 180 }
212 } 181 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.service.script;
  18 +
  19 +public class RuleNodeScriptFactory {
  20 +
  21 + public static final String MSG = "msg";
  22 + public static final String METADATA = "metadata";
  23 + public static final String MSG_TYPE = "msgType";
  24 + public static final String RULE_NODE_FUNCTION_NAME = "ruleNodeFunc";
  25 +
  26 + private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msgStr, metadataStr, msgType) { " +
  27 + " var msg = JSON.parse(msgStr); " +
  28 + " var metadata = JSON.parse(metadataStr); " +
  29 + " return JSON.stringify(%s(msg, metadata, msgType));" +
  30 + " function %s(%s, %s, %s) {";
  31 + private static final String JS_WRAPPER_SUFFIX = "}" +
  32 + "\n}";
  33 +
  34 +
  35 + public static String generateRuleNodeScript(String functionName, String scriptBody, String... argNames) {
  36 + String msgArg;
  37 + String metadataArg;
  38 + String msgTypeArg;
  39 + if (argNames != null && argNames.length == 3) {
  40 + msgArg = argNames[0];
  41 + metadataArg = argNames[1];
  42 + msgTypeArg = argNames[2];
  43 + } else {
  44 + msgArg = MSG;
  45 + metadataArg = METADATA;
  46 + msgTypeArg = MSG_TYPE;
  47 + }
  48 + String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, functionName,
  49 + RULE_NODE_FUNCTION_NAME, RULE_NODE_FUNCTION_NAME, msgArg, metadataArg, msgTypeArg);
  50 + return jsWrapperPrefix + scriptBody + JS_WRAPPER_SUFFIX;
  51 + }
  52 +
  53 +}
@@ -243,6 +243,8 @@ actors: @@ -243,6 +243,8 @@ actors:
243 monitor_thread_pool_size: "${ACTORS_RULE_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}" 243 monitor_thread_pool_size: "${ACTORS_RULE_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}"
244 # Maximum CPU time in milliseconds allowed for script execution 244 # Maximum CPU time in milliseconds allowed for script execution
245 max_cpu_time: "${ACTORS_RULE_JS_SANDBOX_MAX_CPU_TIME:100}" 245 max_cpu_time: "${ACTORS_RULE_JS_SANDBOX_MAX_CPU_TIME:100}"
  246 + # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted
  247 + max_errors: "${ACTORS_RULE_JS_SANDBOX_MAX_ERRORS:3}"
246 chain: 248 chain:
247 # Errors for particular actor are persisted once per specified amount of milliseconds 249 # Errors for particular actor are persisted once per specified amount of milliseconds
248 error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}" 250 error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}"
application/src/test/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngineTest.java renamed from application/src/test/java/org/thingsboard/server/service/script/JsScriptEngineTest.java
@@ -27,30 +27,28 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -27,30 +27,28 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
27 import javax.script.ScriptException; 27 import javax.script.ScriptException;
28 28
29 import java.util.Set; 29 import java.util.Set;
30 -import java.util.concurrent.ExecutorService;  
31 -import java.util.concurrent.Executors;  
32 30
33 import static org.junit.Assert.*; 31 import static org.junit.Assert.*;
34 32
35 -public class JsScriptEngineTest { 33 +public class RuleNodeJsScriptEngineTest {
36 34
37 private ScriptEngine scriptEngine; 35 private ScriptEngine scriptEngine;
38 private TestNashornJsSandboxService jsSandboxService; 36 private TestNashornJsSandboxService jsSandboxService;
39 37
40 @Before 38 @Before
41 public void beforeTest() throws Exception { 39 public void beforeTest() throws Exception {
42 - jsSandboxService = new TestNashornJsSandboxService(1, 100); 40 + jsSandboxService = new TestNashornJsSandboxService(1, 100, 3);
43 } 41 }
44 42
45 @After 43 @After
46 public void afterTest() throws Exception { 44 public void afterTest() throws Exception {
47 - jsSandboxService.destroy(); 45 + jsSandboxService.stop();
48 } 46 }
49 47
50 @Test 48 @Test
51 public void msgCanBeUpdated() throws ScriptException { 49 public void msgCanBeUpdated() throws ScriptException {
52 String function = "metadata.temp = metadata.temp * 10; return {metadata: metadata};"; 50 String function = "metadata.temp = metadata.temp * 10; return {metadata: metadata};";
53 - scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform"); 51 + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
54 52
55 TbMsgMetaData metaData = new TbMsgMetaData(); 53 TbMsgMetaData metaData = new TbMsgMetaData();
56 metaData.putValue("temp", "7"); 54 metaData.putValue("temp", "7");
@@ -61,12 +59,13 @@ public class JsScriptEngineTest { @@ -61,12 +59,13 @@ public class JsScriptEngineTest {
61 59
62 TbMsg actual = scriptEngine.executeUpdate(msg); 60 TbMsg actual = scriptEngine.executeUpdate(msg);
63 assertEquals("70", actual.getMetaData().getValue("temp")); 61 assertEquals("70", actual.getMetaData().getValue("temp"));
  62 + scriptEngine.destroy();
64 } 63 }
65 64
66 @Test 65 @Test
67 public void newAttributesCanBeAddedInMsg() throws ScriptException { 66 public void newAttributesCanBeAddedInMsg() throws ScriptException {
68 String function = "metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};"; 67 String function = "metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};";
69 - scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform"); 68 + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
70 TbMsgMetaData metaData = new TbMsgMetaData(); 69 TbMsgMetaData metaData = new TbMsgMetaData();
71 metaData.putValue("temp", "7"); 70 metaData.putValue("temp", "7");
72 metaData.putValue("humidity", "99"); 71 metaData.putValue("humidity", "99");
@@ -76,12 +75,13 @@ public class JsScriptEngineTest { @@ -76,12 +75,13 @@ public class JsScriptEngineTest {
76 75
77 TbMsg actual = scriptEngine.executeUpdate(msg); 76 TbMsg actual = scriptEngine.executeUpdate(msg);
78 assertEquals("94", actual.getMetaData().getValue("newAttr")); 77 assertEquals("94", actual.getMetaData().getValue("newAttr"));
  78 + scriptEngine.destroy();
79 } 79 }
80 80
81 @Test 81 @Test
82 public void payloadCanBeUpdated() throws ScriptException { 82 public void payloadCanBeUpdated() throws ScriptException {
83 String function = "msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};"; 83 String function = "msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};";
84 - scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform"); 84 + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
85 TbMsgMetaData metaData = new TbMsgMetaData(); 85 TbMsgMetaData metaData = new TbMsgMetaData();
86 metaData.putValue("temp", "7"); 86 metaData.putValue("temp", "7");
87 metaData.putValue("humidity", "99"); 87 metaData.putValue("humidity", "99");
@@ -93,12 +93,13 @@ public class JsScriptEngineTest { @@ -93,12 +93,13 @@ public class JsScriptEngineTest {
93 93
94 String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}"; 94 String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
95 assertEquals(expectedJson, actual.getData()); 95 assertEquals(expectedJson, actual.getData());
  96 + scriptEngine.destroy();
96 } 97 }
97 98
98 @Test 99 @Test
99 public void metadataAccessibleForFilter() throws ScriptException { 100 public void metadataAccessibleForFilter() throws ScriptException {
100 String function = "return metadata.humidity < 15;"; 101 String function = "return metadata.humidity < 15;";
101 - scriptEngine = new JsScriptEngine(jsSandboxService, function, "Filter"); 102 + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
102 TbMsgMetaData metaData = new TbMsgMetaData(); 103 TbMsgMetaData metaData = new TbMsgMetaData();
103 metaData.putValue("temp", "7"); 104 metaData.putValue("temp", "7");
104 metaData.putValue("humidity", "99"); 105 metaData.putValue("humidity", "99");
@@ -106,12 +107,13 @@ public class JsScriptEngineTest { @@ -106,12 +107,13 @@ public class JsScriptEngineTest {
106 107
107 TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); 108 TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
108 assertFalse(scriptEngine.executeFilter(msg)); 109 assertFalse(scriptEngine.executeFilter(msg));
  110 + scriptEngine.destroy();
109 } 111 }
110 112
111 @Test 113 @Test
112 public void dataAccessibleForFilter() throws ScriptException { 114 public void dataAccessibleForFilter() throws ScriptException {
113 String function = "return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 7 && msg.bigObj.prop == 42;"; 115 String function = "return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 7 && msg.bigObj.prop == 42;";
114 - scriptEngine = new JsScriptEngine(jsSandboxService, function, "Filter"); 116 + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function);
115 TbMsgMetaData metaData = new TbMsgMetaData(); 117 TbMsgMetaData metaData = new TbMsgMetaData();
116 metaData.putValue("temp", "7"); 118 metaData.putValue("temp", "7");
117 metaData.putValue("humidity", "99"); 119 metaData.putValue("humidity", "99");
@@ -119,6 +121,7 @@ public class JsScriptEngineTest { @@ -119,6 +121,7 @@ public class JsScriptEngineTest {
119 121
120 TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); 122 TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
121 assertTrue(scriptEngine.executeFilter(msg)); 123 assertTrue(scriptEngine.executeFilter(msg));
  124 + scriptEngine.destroy();
122 } 125 }
123 126
124 @Test 127 @Test
@@ -131,7 +134,7 @@ public class JsScriptEngineTest { @@ -131,7 +134,7 @@ public class JsScriptEngineTest {
131 "};\n" + 134 "};\n" +
132 "\n" + 135 "\n" +
133 "return nextRelation(metadata, msg);"; 136 "return nextRelation(metadata, msg);";
134 - scriptEngine = new JsScriptEngine(jsSandboxService, jsCode, "Switch"); 137 + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, jsCode);
135 TbMsgMetaData metaData = new TbMsgMetaData(); 138 TbMsgMetaData metaData = new TbMsgMetaData();
136 metaData.putValue("temp", "10"); 139 metaData.putValue("temp", "10");
137 metaData.putValue("humidity", "99"); 140 metaData.putValue("humidity", "99");
@@ -140,6 +143,7 @@ public class JsScriptEngineTest { @@ -140,6 +143,7 @@ public class JsScriptEngineTest {
140 TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); 143 TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
141 Set<String> actual = scriptEngine.executeSwitch(msg); 144 Set<String> actual = scriptEngine.executeSwitch(msg);
142 assertEquals(Sets.newHashSet("one"), actual); 145 assertEquals(Sets.newHashSet("one"), actual);
  146 + scriptEngine.destroy();
143 } 147 }
144 148
145 @Test 149 @Test
@@ -152,7 +156,7 @@ public class JsScriptEngineTest { @@ -152,7 +156,7 @@ public class JsScriptEngineTest {
152 "};\n" + 156 "};\n" +
153 "\n" + 157 "\n" +
154 "return nextRelation(metadata, msg);"; 158 "return nextRelation(metadata, msg);";
155 - scriptEngine = new JsScriptEngine(jsSandboxService, jsCode, "Switch"); 159 + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, jsCode);
156 TbMsgMetaData metaData = new TbMsgMetaData(); 160 TbMsgMetaData metaData = new TbMsgMetaData();
157 metaData.putValue("temp", "10"); 161 metaData.putValue("temp", "10");
158 metaData.putValue("humidity", "99"); 162 metaData.putValue("humidity", "99");
@@ -161,6 +165,7 @@ public class JsScriptEngineTest { @@ -161,6 +165,7 @@ public class JsScriptEngineTest {
161 TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); 165 TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L);
162 Set<String> actual = scriptEngine.executeSwitch(msg); 166 Set<String> actual = scriptEngine.executeSwitch(msg);
163 assertEquals(Sets.newHashSet("one", "three"), actual); 167 assertEquals(Sets.newHashSet("one", "three"), actual);
  168 + scriptEngine.destroy();
164 } 169 }
165 170
166 } 171 }
@@ -16,39 +16,40 @@ @@ -16,39 +16,40 @@
16 16
17 package org.thingsboard.server.service.script; 17 package org.thingsboard.server.service.script;
18 18
  19 +import com.google.common.util.concurrent.ListenableFuture;
19 import delight.nashornsandbox.NashornSandbox; 20 import delight.nashornsandbox.NashornSandbox;
20 import delight.nashornsandbox.NashornSandboxes; 21 import delight.nashornsandbox.NashornSandboxes;
21 22
22 import javax.script.ScriptException; 23 import javax.script.ScriptException;
  24 +import java.util.UUID;
23 import java.util.concurrent.ExecutorService; 25 import java.util.concurrent.ExecutorService;
24 import java.util.concurrent.Executors; 26 import java.util.concurrent.Executors;
25 27
26 -public class TestNashornJsSandboxService implements JsSandboxService { 28 +public class TestNashornJsSandboxService extends AbstractNashornJsSandboxService {
27 29
28 - private NashornSandbox sandbox = NashornSandboxes.create();  
29 - private ExecutorService monitorExecutorService; 30 + private final int monitorThreadPoolSize;
  31 + private final long maxCpuTime;
  32 + private final int maxErrors;
30 33
31 - public TestNashornJsSandboxService(int monitorThreadPoolSize, long maxCpuTime) {  
32 - monitorExecutorService = Executors.newFixedThreadPool(monitorThreadPoolSize);  
33 - sandbox.setExecutor(monitorExecutorService);  
34 - sandbox.setMaxCPUTime(maxCpuTime);  
35 - sandbox.allowNoBraces(false);  
36 - sandbox.setMaxPreparedStatements(30); 34 + public TestNashornJsSandboxService(int monitorThreadPoolSize, long maxCpuTime, int maxErrors) {
  35 + this.monitorThreadPoolSize = monitorThreadPoolSize;
  36 + this.maxCpuTime = maxCpuTime;
  37 + this.maxErrors = maxErrors;
  38 + init();
37 } 39 }
38 40
39 @Override 41 @Override
40 - public Object eval(String js) throws ScriptException {  
41 - return sandbox.eval(js); 42 + protected int getMonitorThreadPoolSize() {
  43 + return monitorThreadPoolSize;
42 } 44 }
43 45
44 @Override 46 @Override
45 - public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {  
46 - return sandbox.getSandboxedInvocable().invokeFunction(name, args); 47 + protected long getMaxCpuTime() {
  48 + return maxCpuTime;
47 } 49 }
48 50
49 - public void destroy() {  
50 - if (monitorExecutorService != null) {  
51 - monitorExecutorService.shutdownNow();  
52 - } 51 + @Override
  52 + protected int getMaxErrors() {
  53 + return maxErrors;
53 } 54 }
54 } 55 }
@@ -90,6 +90,6 @@ public interface TbContext { @@ -90,6 +90,6 @@ public interface TbContext {
90 90
91 MailService getMailService(); 91 MailService getMailService();
92 92
93 - ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames); 93 + ScriptEngine createJsScriptEngine(String script, String... argNames);
94 94
95 } 95 }
@@ -43,7 +43,7 @@ public abstract class TbAbstractAlarmNode<C extends TbAbstractAlarmNodeConfigura @@ -43,7 +43,7 @@ public abstract class TbAbstractAlarmNode<C extends TbAbstractAlarmNodeConfigura
43 @Override 43 @Override
44 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { 44 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
45 this.config = loadAlarmNodeConfig(configuration); 45 this.config = loadAlarmNodeConfig(configuration);
46 - this.buildDetailsJsEngine = ctx.createJsScriptEngine(config.getAlarmDetailsBuildJs(), "Details"); 46 + this.buildDetailsJsEngine = ctx.createJsScriptEngine(config.getAlarmDetailsBuildJs());
47 } 47 }
48 48
49 protected abstract C loadAlarmNodeConfig(TbNodeConfiguration configuration) throws TbNodeException; 49 protected abstract C loadAlarmNodeConfig(TbNodeConfiguration configuration) throws TbNodeException;
@@ -46,7 +46,7 @@ public class TbLogNode implements TbNode { @@ -46,7 +46,7 @@ public class TbLogNode implements TbNode {
46 @Override 46 @Override
47 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { 47 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
48 this.config = TbNodeUtils.convert(configuration, TbLogNodeConfiguration.class); 48 this.config = TbNodeUtils.convert(configuration, TbLogNodeConfiguration.class);
49 - this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "ToString"); 49 + this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
50 } 50 }
51 51
52 @Override 52 @Override
@@ -65,7 +65,7 @@ public class TbMsgGeneratorNode implements TbNode { @@ -65,7 +65,7 @@ public class TbMsgGeneratorNode implements TbNode {
65 } else { 65 } else {
66 originatorId = ctx.getSelfId(); 66 originatorId = ctx.getSelfId();
67 } 67 }
68 - this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "Generate", "prevMsg", "prevMetadata", "prevMsgType"); 68 + this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType");
69 sentTickMsg(ctx); 69 sentTickMsg(ctx);
70 } 70 }
71 71
@@ -45,7 +45,7 @@ public class TbJsFilterNode implements TbNode { @@ -45,7 +45,7 @@ public class TbJsFilterNode implements TbNode {
45 @Override 45 @Override
46 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { 46 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
47 this.config = TbNodeUtils.convert(configuration, TbJsFilterNodeConfiguration.class); 47 this.config = TbNodeUtils.convert(configuration, TbJsFilterNodeConfiguration.class);
48 - this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "Filter"); 48 + this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
49 } 49 }
50 50
51 @Override 51 @Override
@@ -47,7 +47,7 @@ public class TbJsSwitchNode implements TbNode { @@ -47,7 +47,7 @@ public class TbJsSwitchNode implements TbNode {
47 @Override 47 @Override
48 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { 48 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
49 this.config = TbNodeUtils.convert(configuration, TbJsSwitchNodeConfiguration.class); 49 this.config = TbNodeUtils.convert(configuration, TbJsSwitchNodeConfiguration.class);
50 - this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "Switch"); 50 + this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
51 } 51 }
52 52
53 @Override 53 @Override
@@ -43,7 +43,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode { @@ -43,7 +43,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode {
43 @Override 43 @Override
44 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { 44 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
45 this.config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class); 45 this.config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class);
46 - this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "Transform"); 46 + this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
47 setConfig(config); 47 setConfig(config);
48 } 48 }
49 49
@@ -152,7 +152,7 @@ public class TbAlarmNodeTest { @@ -152,7 +152,7 @@ public class TbAlarmNodeTest {
152 152
153 verifyError(msg, "message", NotImplementedException.class); 153 verifyError(msg, "message", NotImplementedException.class);
154 154
155 - verify(ctx).createJsScriptEngine("DETAILS", "Details"); 155 + verify(ctx).createJsScriptEngine("DETAILS");
156 verify(ctx, times(1)).getJsExecutor(); 156 verify(ctx, times(1)).getJsExecutor();
157 verify(ctx).getAlarmService(); 157 verify(ctx).getAlarmService();
158 verify(ctx, times(2)).getDbCallbackExecutor(); 158 verify(ctx, times(2)).getDbCallbackExecutor();
@@ -314,7 +314,7 @@ public class TbAlarmNodeTest { @@ -314,7 +314,7 @@ public class TbAlarmNodeTest {
314 ObjectMapper mapper = new ObjectMapper(); 314 ObjectMapper mapper = new ObjectMapper();
315 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); 315 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
316 316
317 - when(ctx.createJsScriptEngine("DETAILS", "Details")).thenReturn(detailsJs); 317 + when(ctx.createJsScriptEngine("DETAILS")).thenReturn(detailsJs);
318 318
319 when(ctx.getTenantId()).thenReturn(tenantId); 319 when(ctx.getTenantId()).thenReturn(tenantId);
320 when(ctx.getJsExecutor()).thenReturn(executor); 320 when(ctx.getJsExecutor()).thenReturn(executor);
@@ -338,7 +338,7 @@ public class TbAlarmNodeTest { @@ -338,7 +338,7 @@ public class TbAlarmNodeTest {
338 ObjectMapper mapper = new ObjectMapper(); 338 ObjectMapper mapper = new ObjectMapper();
339 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); 339 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
340 340
341 - when(ctx.createJsScriptEngine("DETAILS", "Details")).thenReturn(detailsJs); 341 + when(ctx.createJsScriptEngine("DETAILS")).thenReturn(detailsJs);
342 342
343 when(ctx.getTenantId()).thenReturn(tenantId); 343 when(ctx.getTenantId()).thenReturn(tenantId);
344 when(ctx.getJsExecutor()).thenReturn(executor); 344 when(ctx.getJsExecutor()).thenReturn(executor);
@@ -97,7 +97,7 @@ public class TbJsFilterNodeTest { @@ -97,7 +97,7 @@ public class TbJsFilterNodeTest {
97 ObjectMapper mapper = new ObjectMapper(); 97 ObjectMapper mapper = new ObjectMapper();
98 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); 98 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
99 99
100 - when(ctx.createJsScriptEngine("scr", "Filter")).thenReturn(scriptEngine); 100 + when(ctx.createJsScriptEngine("scr")).thenReturn(scriptEngine);
101 101
102 node = new TbJsFilterNode(); 102 node = new TbJsFilterNode();
103 node.init(ctx, nodeConfiguration); 103 node.init(ctx, nodeConfiguration);
@@ -79,7 +79,7 @@ public class TbJsSwitchNodeTest { @@ -79,7 +79,7 @@ public class TbJsSwitchNodeTest {
79 ObjectMapper mapper = new ObjectMapper(); 79 ObjectMapper mapper = new ObjectMapper();
80 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); 80 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
81 81
82 - when(ctx.createJsScriptEngine("scr", "Switch")).thenReturn(scriptEngine); 82 + when(ctx.createJsScriptEngine("scr")).thenReturn(scriptEngine);
83 83
84 node = new TbJsSwitchNode(); 84 node = new TbJsSwitchNode();
85 node.init(ctx, nodeConfiguration); 85 node.init(ctx, nodeConfiguration);
@@ -97,7 +97,7 @@ public class TbTransformMsgNodeTest { @@ -97,7 +97,7 @@ public class TbTransformMsgNodeTest {
97 ObjectMapper mapper = new ObjectMapper(); 97 ObjectMapper mapper = new ObjectMapper();
98 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); 98 TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
99 99
100 - when(ctx.createJsScriptEngine("scr", "Transform")).thenReturn(scriptEngine); 100 + when(ctx.createJsScriptEngine("scr")).thenReturn(scriptEngine);
101 101
102 node = new TbTransformMsgNode(); 102 node = new TbTransformMsgNode();
103 node.init(ctx, nodeConfiguration); 103 node.init(ctx, nodeConfiguration);