blacklisted Script error should show initial exception
This commit is contained in:
parent
831f6201db
commit
5ea053b71b
@ -17,13 +17,13 @@ package org.thingsboard.server.service.script;
|
|||||||
|
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
import delight.nashornsandbox.NashornSandbox;
|
import delight.nashornsandbox.NashornSandbox;
|
||||||
import delight.nashornsandbox.NashornSandboxes;
|
import delight.nashornsandbox.NashornSandboxes;
|
||||||
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
||||||
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
|
|
||||||
@ -45,10 +45,9 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
private NashornSandbox sandbox;
|
private NashornSandbox sandbox;
|
||||||
private ScriptEngine engine;
|
private ScriptEngine engine;
|
||||||
private ExecutorService monitorExecutorService;
|
private ExecutorService monitorExecutorService;
|
||||||
private ListeningExecutorService evalExecutorService;
|
|
||||||
|
|
||||||
private final Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
|
private final Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
|
||||||
private final Map<BlackListKey, AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
|
private final Map<BlackListKey, BlackListInfo> blackListedFunctions = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final Map<String, ScriptInfo> scriptKeyToInfo = new ConcurrentHashMap<>();
|
private final Map<String, ScriptInfo> scriptKeyToInfo = new ConcurrentHashMap<>();
|
||||||
private final Map<UUID, ScriptInfo> scriptIdToInfo = new ConcurrentHashMap<>();
|
private final Map<UUID, ScriptInfo> scriptIdToInfo = new ConcurrentHashMap<>();
|
||||||
@ -58,7 +57,6 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
if (useJsSandbox()) {
|
if (useJsSandbox()) {
|
||||||
sandbox = NashornSandboxes.create();
|
sandbox = NashornSandboxes.create();
|
||||||
monitorExecutorService = Executors.newFixedThreadPool(getMonitorThreadPoolSize());
|
monitorExecutorService = Executors.newFixedThreadPool(getMonitorThreadPoolSize());
|
||||||
evalExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
|
|
||||||
sandbox.setExecutor(monitorExecutorService);
|
sandbox.setExecutor(monitorExecutorService);
|
||||||
sandbox.setMaxCPUTime(getMaxCpuTime());
|
sandbox.setMaxCPUTime(getMaxCpuTime());
|
||||||
sandbox.allowNoBraces(false);
|
sandbox.allowNoBraces(false);
|
||||||
@ -74,9 +72,6 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
if (monitorExecutorService != null) {
|
if (monitorExecutorService != null) {
|
||||||
monitorExecutorService.shutdownNow();
|
monitorExecutorService.shutdownNow();
|
||||||
}
|
}
|
||||||
if (evalExecutorService != null) {
|
|
||||||
evalExecutorService.shutdownNow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean useJsSandbox();
|
protected abstract boolean useJsSandbox();
|
||||||
@ -124,10 +119,28 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
public ListenableFuture<Object> invokeFunction(UUID scriptId, EntityId entityId, Object... args) {
|
public ListenableFuture<Object> invokeFunction(UUID scriptId, EntityId entityId, Object... args) {
|
||||||
String functionName = functionsMap.get(scriptId);
|
String functionName = functionsMap.get(scriptId);
|
||||||
if (functionName == null) {
|
if (functionName == null) {
|
||||||
return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!"));
|
String message = "No compiled script found for scriptId: [" + scriptId + "]!";
|
||||||
|
log.warn(message);
|
||||||
|
return Futures.immediateFailedFuture(new RuntimeException(message));
|
||||||
}
|
}
|
||||||
if (!isBlackListed(scriptId)) {
|
|
||||||
|
BlackListInfo blackListInfo = blackListedFunctions.get(new BlackListKey(scriptId, entityId));
|
||||||
|
if (blackListInfo != null && blackListInfo.getCount() >= getMaxErrors()) {
|
||||||
|
RuntimeException throwable = new RuntimeException("Script is blacklisted due to maximum error count " + getMaxErrors() + "!", blackListInfo.getCause());
|
||||||
|
throwable.printStackTrace();
|
||||||
|
return Futures.immediateFailedFuture(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
return invoke(functionName, args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
BlackListKey blackListKey = new BlackListKey(scriptId, entityId);
|
||||||
|
blackListedFunctions.computeIfAbsent(blackListKey, key -> new BlackListInfo()).incrementWithReason(e);
|
||||||
|
return Futures.immediateFailedFuture(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListenableFuture<Object> invoke(String functionName, Object... args) throws ScriptException, NoSuchMethodException {
|
||||||
Object result;
|
Object result;
|
||||||
if (useJsSandbox()) {
|
if (useJsSandbox()) {
|
||||||
result = sandbox.getSandboxedInvocable().invokeFunction(functionName, args);
|
result = sandbox.getSandboxedInvocable().invokeFunction(functionName, args);
|
||||||
@ -135,15 +148,6 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
result = ((Invocable) engine).invokeFunction(functionName, args);
|
result = ((Invocable) engine).invokeFunction(functionName, args);
|
||||||
}
|
}
|
||||||
return Futures.immediateFuture(result);
|
return Futures.immediateFuture(result);
|
||||||
} catch (Exception e) {
|
|
||||||
BlackListKey blackListKey = new BlackListKey(scriptId, entityId);
|
|
||||||
blackListedFunctions.computeIfAbsent(blackListKey, 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
|
@Override
|
||||||
@ -182,15 +186,6 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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) {
|
private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) {
|
||||||
switch (scriptType) {
|
switch (scriptType) {
|
||||||
case RULE_NODE_SCRIPT:
|
case RULE_NODE_SCRIPT:
|
||||||
@ -233,13 +228,33 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
|
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
@Getter
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
private static class BlackListKey {
|
private static class BlackListKey {
|
||||||
private final UUID scriptId;
|
private final UUID scriptId;
|
||||||
private final EntityId entityId;
|
private final EntityId entityId;
|
||||||
|
|
||||||
public BlackListKey(UUID scriptId, EntityId entityId) {
|
}
|
||||||
this.scriptId = scriptId;
|
|
||||||
this.entityId = entityId;
|
@Data
|
||||||
|
private static class BlackListInfo {
|
||||||
|
private final AtomicInteger count;
|
||||||
|
private Exception ex;
|
||||||
|
|
||||||
|
BlackListInfo() {
|
||||||
|
this.count = new AtomicInteger(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void incrementWithReason(Exception e) {
|
||||||
|
count.incrementAndGet();
|
||||||
|
ex = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCount() {
|
||||||
|
return count.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception getCause() {
|
||||||
|
return ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -171,10 +171,10 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
|
|||||||
if (e.getCause() instanceof ScriptException) {
|
if (e.getCause() instanceof ScriptException) {
|
||||||
throw (ScriptException)e.getCause();
|
throw (ScriptException)e.getCause();
|
||||||
} else {
|
} else {
|
||||||
throw new ScriptException("Failed to execute js script: " + e.getMessage());
|
throw new ScriptException(e);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ScriptException("Failed to execute js script: " + e.getMessage());
|
throw new ScriptException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user