Showing
12 changed files
with
218 additions
and
22 deletions
... | ... | @@ -256,6 +256,10 @@ |
256 | 256 | <groupId>org.hsqldb</groupId> |
257 | 257 | <artifactId>hsqldb</artifactId> |
258 | 258 | </dependency> |
259 | + <dependency> | |
260 | + <groupId>org.javadelight</groupId> | |
261 | + <artifactId>delight-nashorn-sandbox</artifactId> | |
262 | + </dependency> | |
259 | 263 | </dependencies> |
260 | 264 | |
261 | 265 | <build> | ... | ... |
... | ... | @@ -62,6 +62,7 @@ import org.thingsboard.server.service.mail.MailExecutorService; |
62 | 62 | import org.thingsboard.server.service.queue.MsgQueueService; |
63 | 63 | import org.thingsboard.server.service.rpc.DeviceRpcService; |
64 | 64 | import org.thingsboard.server.service.script.JsExecutorService; |
65 | +import org.thingsboard.server.service.script.JsSandboxService; | |
65 | 66 | import org.thingsboard.server.service.state.DeviceStateService; |
66 | 67 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
67 | 68 | |
... | ... | @@ -164,6 +165,10 @@ public class ActorSystemContext { |
164 | 165 | |
165 | 166 | @Autowired |
166 | 167 | @Getter |
168 | + private JsSandboxService jsSandbox; | |
169 | + | |
170 | + @Autowired | |
171 | + @Getter | |
167 | 172 | private JsExecutorService jsExecutor; |
168 | 173 | |
169 | 174 | @Autowired | ... | ... |
... | ... | @@ -44,7 +44,7 @@ import org.thingsboard.server.dao.relation.RelationService; |
44 | 44 | import org.thingsboard.server.dao.rule.RuleChainService; |
45 | 45 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
46 | 46 | import org.thingsboard.server.dao.user.UserService; |
47 | -import org.thingsboard.server.service.script.NashornJsEngine; | |
47 | +import org.thingsboard.server.service.script.JsScriptEngine; | |
48 | 48 | import scala.concurrent.duration.Duration; |
49 | 49 | |
50 | 50 | import java.util.Collections; |
... | ... | @@ -152,7 +152,7 @@ class DefaultTbContext implements TbContext { |
152 | 152 | |
153 | 153 | @Override |
154 | 154 | public ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames) { |
155 | - return new NashornJsEngine(script, functionName, argNames); | |
155 | + return new JsScriptEngine(mainCtx.getJsSandbox(), script, functionName, argNames); | |
156 | 156 | } |
157 | 157 | |
158 | 158 | @Override | ... | ... |
... | ... | @@ -50,7 +50,9 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
50 | 50 | import org.thingsboard.server.common.msg.TbMsg; |
51 | 51 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
52 | 52 | import org.thingsboard.server.dao.event.EventService; |
53 | -import org.thingsboard.server.service.script.NashornJsEngine; | |
53 | +import org.thingsboard.server.service.script.JsExecutorService; | |
54 | +import org.thingsboard.server.service.script.JsSandboxService; | |
55 | +import org.thingsboard.server.service.script.JsScriptEngine; | |
54 | 56 | |
55 | 57 | import java.util.List; |
56 | 58 | import java.util.Map; |
... | ... | @@ -69,6 +71,9 @@ public class RuleChainController extends BaseController { |
69 | 71 | @Autowired |
70 | 72 | private EventService eventService; |
71 | 73 | |
74 | + @Autowired | |
75 | + private JsSandboxService jsSandboxService; | |
76 | + | |
72 | 77 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
73 | 78 | @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET) |
74 | 79 | @ResponseBody |
... | ... | @@ -273,7 +278,7 @@ public class RuleChainController extends BaseController { |
273 | 278 | String errorText = ""; |
274 | 279 | ScriptEngine engine = null; |
275 | 280 | try { |
276 | - engine = new NashornJsEngine(script, functionName, argNames); | |
281 | + engine = new JsScriptEngine(jsSandboxService, script, functionName, argNames); | |
277 | 282 | TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L); |
278 | 283 | switch (scriptType) { |
279 | 284 | case "update": | ... | ... |
... | ... | @@ -54,6 +54,10 @@ public abstract class AbstractListeningExecutor implements ListeningExecutor { |
54 | 54 | service.execute(command); |
55 | 55 | } |
56 | 56 | |
57 | + public ListeningExecutorService executor() { | |
58 | + return service; | |
59 | + } | |
60 | + | |
57 | 61 | protected abstract int getThreadPollSize(); |
58 | 62 | |
59 | 63 | } | ... | ... |
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 javax.script.ScriptException; | |
20 | + | |
21 | +public interface JsSandboxService { | |
22 | + | |
23 | + Object eval(String js) throws ScriptException; | |
24 | + | |
25 | + Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException; | |
26 | + | |
27 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/script/JsScriptEngine.java
renamed from
application/src/main/java/org/thingsboard/server/service/script/NashornJsEngine.java
... | ... | @@ -19,14 +19,11 @@ import com.fasterxml.jackson.core.type.TypeReference; |
19 | 19 | import com.fasterxml.jackson.databind.JsonNode; |
20 | 20 | import com.fasterxml.jackson.databind.ObjectMapper; |
21 | 21 | import com.google.common.collect.Sets; |
22 | -import jdk.nashorn.api.scripting.NashornScriptEngineFactory; | |
23 | 22 | import lombok.extern.slf4j.Slf4j; |
24 | 23 | import org.apache.commons.lang3.StringUtils; |
25 | 24 | import org.thingsboard.server.common.msg.TbMsg; |
26 | 25 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
27 | 26 | |
28 | -import javax.script.Invocable; | |
29 | -import javax.script.ScriptEngine; | |
30 | 27 | import javax.script.ScriptException; |
31 | 28 | import java.util.Collections; |
32 | 29 | import java.util.Map; |
... | ... | @@ -34,7 +31,7 @@ import java.util.Set; |
34 | 31 | |
35 | 32 | |
36 | 33 | @Slf4j |
37 | -public class NashornJsEngine implements org.thingsboard.rule.engine.api.ScriptEngine { | |
34 | +public class JsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEngine { | |
38 | 35 | |
39 | 36 | public static final String MSG = "msg"; |
40 | 37 | public static final String METADATA = "metadata"; |
... | ... | @@ -49,12 +46,14 @@ public class NashornJsEngine implements org.thingsboard.rule.engine.api.ScriptEn |
49 | 46 | "\n}"; |
50 | 47 | |
51 | 48 | private static final ObjectMapper mapper = new ObjectMapper(); |
52 | - private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); | |
53 | - private ScriptEngine engine = factory.getScriptEngine(new String[]{"--no-java"}); | |
49 | +// private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); | |
50 | +// private ScriptEngine engine = factory.getScriptEngine(new String[]{"--no-java"}); | |
51 | + private final JsSandboxService sandboxService; | |
54 | 52 | |
55 | 53 | private final String invokeFunctionName; |
56 | 54 | |
57 | - public NashornJsEngine(String script, String functionName, String... argNames) { | |
55 | + public JsScriptEngine(JsSandboxService sandboxService, String script, String functionName, String... argNames) { | |
56 | + this.sandboxService = sandboxService; | |
58 | 57 | this.invokeFunctionName = "invokeInternal" + this.hashCode(); |
59 | 58 | String msgArg; |
60 | 59 | String metadataArg; |
... | ... | @@ -75,7 +74,8 @@ public class NashornJsEngine implements org.thingsboard.rule.engine.api.ScriptEn |
75 | 74 | |
76 | 75 | private void compileScript(String script) { |
77 | 76 | try { |
78 | - engine.eval(script); | |
77 | + //engine.eval(script); | |
78 | + sandboxService.eval(script); | |
79 | 79 | } catch (ScriptException e) { |
80 | 80 | log.warn("Failed to compile JS script: {}", e.getMessage(), e); |
81 | 81 | throw new IllegalArgumentException("Can't compile script: " + e.getMessage()); |
... | ... | @@ -195,7 +195,8 @@ public class NashornJsEngine implements org.thingsboard.rule.engine.api.ScriptEn |
195 | 195 | private JsonNode executeScript(TbMsg msg) throws ScriptException { |
196 | 196 | try { |
197 | 197 | String[] inArgs = prepareArgs(msg); |
198 | - String eval = ((Invocable)engine).invokeFunction(this.invokeFunctionName, inArgs[0], inArgs[1], inArgs[2]).toString(); | |
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(); | |
199 | 200 | return mapper.readTree(eval); |
200 | 201 | } catch (ScriptException | IllegalArgumentException th) { |
201 | 202 | throw th; |
... | ... | @@ -206,6 +207,6 @@ public class NashornJsEngine implements org.thingsboard.rule.engine.api.ScriptEn |
206 | 207 | } |
207 | 208 | |
208 | 209 | public void destroy() { |
209 | - engine = null; | |
210 | + //engine = null; | |
210 | 211 | } |
211 | 212 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/script/NashornJsSandboxService.java
0 → 100644
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 delight.nashornsandbox.NashornSandbox; | |
20 | +import delight.nashornsandbox.NashornSandboxes; | |
21 | +import lombok.extern.slf4j.Slf4j; | |
22 | +import org.springframework.beans.factory.annotation.Value; | |
23 | +import org.springframework.stereotype.Service; | |
24 | + | |
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 | |
32 | +@Service | |
33 | +public class NashornJsSandboxService implements JsSandboxService { | |
34 | + | |
35 | + @Value("${actors.rule.js_sandbox.monitor_thread_pool_size}") | |
36 | + private int monitorThreadPoolSize; | |
37 | + | |
38 | + @Value("${actors.rule.js_sandbox.max_cpu_time}") | |
39 | + private long maxCpuTime; | |
40 | + | |
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 | + } | |
52 | + | |
53 | + @PreDestroy | |
54 | + public void stop() { | |
55 | + if (monitorExecutorService != null) { | |
56 | + monitorExecutorService.shutdownNow(); | |
57 | + } | |
58 | + } | |
59 | + | |
60 | + @Override | |
61 | + public Object eval(String js) throws ScriptException { | |
62 | + return sandbox.eval(js); | |
63 | + } | |
64 | + | |
65 | + @Override | |
66 | + public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException { | |
67 | + return sandbox.getSandboxedInvocable().invokeFunction(name, args); | |
68 | + } | |
69 | + | |
70 | +} | ... | ... |
... | ... | @@ -238,6 +238,11 @@ actors: |
238 | 238 | mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:10}" |
239 | 239 | # Specify thread pool size for external call service |
240 | 240 | external_call_thread_pool_size: "${ACTORS_RULE_EXTERNAL_CALL_THREAD_POOL_SIZE:10}" |
241 | + js_sandbox: | |
242 | + # Specify thread pool size for JavaScript sandbox resource monitor | |
243 | + monitor_thread_pool_size: "${ACTORS_RULE_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}" | |
244 | + # Maximum CPU time in milliseconds allowed for script execution | |
245 | + max_cpu_time: "${ACTORS_RULE_JS_SANDBOX_MAX_CPU_TIME:100}" | |
241 | 246 | chain: |
242 | 247 | # Errors for particular actor are persisted once per specified amount of milliseconds |
243 | 248 | error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}" | ... | ... |
application/src/test/java/org/thingsboard/server/service/script/JsScriptEngineTest.java
renamed from
application/src/test/java/org/thingsboard/server/service/script/NashornJsEngineTest.java
... | ... | @@ -17,6 +17,8 @@ package org.thingsboard.server.service.script; |
17 | 17 | |
18 | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | 19 | import com.google.common.collect.Sets; |
20 | +import org.junit.After; | |
21 | +import org.junit.Before; | |
20 | 22 | import org.junit.Test; |
21 | 23 | import org.thingsboard.rule.engine.api.ScriptEngine; |
22 | 24 | import org.thingsboard.server.common.msg.TbMsg; |
... | ... | @@ -25,17 +27,30 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; |
25 | 27 | import javax.script.ScriptException; |
26 | 28 | |
27 | 29 | import java.util.Set; |
30 | +import java.util.concurrent.ExecutorService; | |
31 | +import java.util.concurrent.Executors; | |
28 | 32 | |
29 | 33 | import static org.junit.Assert.*; |
30 | 34 | |
31 | -public class NashornJsEngineTest { | |
35 | +public class JsScriptEngineTest { | |
32 | 36 | |
33 | 37 | private ScriptEngine scriptEngine; |
38 | + private TestNashornJsSandboxService jsSandboxService; | |
39 | + | |
40 | + @Before | |
41 | + public void beforeTest() throws Exception { | |
42 | + jsSandboxService = new TestNashornJsSandboxService(1, 100); | |
43 | + } | |
44 | + | |
45 | + @After | |
46 | + public void afterTest() throws Exception { | |
47 | + jsSandboxService.destroy(); | |
48 | + } | |
34 | 49 | |
35 | 50 | @Test |
36 | 51 | public void msgCanBeUpdated() throws ScriptException { |
37 | 52 | String function = "metadata.temp = metadata.temp * 10; return {metadata: metadata};"; |
38 | - scriptEngine = new NashornJsEngine(function, "Transform"); | |
53 | + scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform"); | |
39 | 54 | |
40 | 55 | TbMsgMetaData metaData = new TbMsgMetaData(); |
41 | 56 | metaData.putValue("temp", "7"); |
... | ... | @@ -51,7 +66,7 @@ public class NashornJsEngineTest { |
51 | 66 | @Test |
52 | 67 | public void newAttributesCanBeAddedInMsg() throws ScriptException { |
53 | 68 | String function = "metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};"; |
54 | - scriptEngine = new NashornJsEngine(function, "Transform"); | |
69 | + scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform"); | |
55 | 70 | TbMsgMetaData metaData = new TbMsgMetaData(); |
56 | 71 | metaData.putValue("temp", "7"); |
57 | 72 | metaData.putValue("humidity", "99"); |
... | ... | @@ -66,7 +81,7 @@ public class NashornJsEngineTest { |
66 | 81 | @Test |
67 | 82 | public void payloadCanBeUpdated() throws ScriptException { |
68 | 83 | String function = "msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};"; |
69 | - scriptEngine = new NashornJsEngine(function, "Transform"); | |
84 | + scriptEngine = new JsScriptEngine(jsSandboxService, function, "Transform"); | |
70 | 85 | TbMsgMetaData metaData = new TbMsgMetaData(); |
71 | 86 | metaData.putValue("temp", "7"); |
72 | 87 | metaData.putValue("humidity", "99"); |
... | ... | @@ -83,7 +98,7 @@ public class NashornJsEngineTest { |
83 | 98 | @Test |
84 | 99 | public void metadataAccessibleForFilter() throws ScriptException { |
85 | 100 | String function = "return metadata.humidity < 15;"; |
86 | - scriptEngine = new NashornJsEngine(function, "Filter"); | |
101 | + scriptEngine = new JsScriptEngine(jsSandboxService, function, "Filter"); | |
87 | 102 | TbMsgMetaData metaData = new TbMsgMetaData(); |
88 | 103 | metaData.putValue("temp", "7"); |
89 | 104 | metaData.putValue("humidity", "99"); |
... | ... | @@ -96,7 +111,7 @@ public class NashornJsEngineTest { |
96 | 111 | @Test |
97 | 112 | public void dataAccessibleForFilter() throws ScriptException { |
98 | 113 | String function = "return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 7 && msg.bigObj.prop == 42;"; |
99 | - scriptEngine = new NashornJsEngine(function, "Filter"); | |
114 | + scriptEngine = new JsScriptEngine(jsSandboxService, function, "Filter"); | |
100 | 115 | TbMsgMetaData metaData = new TbMsgMetaData(); |
101 | 116 | metaData.putValue("temp", "7"); |
102 | 117 | metaData.putValue("humidity", "99"); |
... | ... | @@ -116,7 +131,7 @@ public class NashornJsEngineTest { |
116 | 131 | "};\n" + |
117 | 132 | "\n" + |
118 | 133 | "return nextRelation(metadata, msg);"; |
119 | - scriptEngine = new NashornJsEngine(jsCode, "Switch"); | |
134 | + scriptEngine = new JsScriptEngine(jsSandboxService, jsCode, "Switch"); | |
120 | 135 | TbMsgMetaData metaData = new TbMsgMetaData(); |
121 | 136 | metaData.putValue("temp", "10"); |
122 | 137 | metaData.putValue("humidity", "99"); |
... | ... | @@ -137,7 +152,7 @@ public class NashornJsEngineTest { |
137 | 152 | "};\n" + |
138 | 153 | "\n" + |
139 | 154 | "return nextRelation(metadata, msg);"; |
140 | - scriptEngine = new NashornJsEngine(jsCode, "Switch"); | |
155 | + scriptEngine = new JsScriptEngine(jsSandboxService, jsCode, "Switch"); | |
141 | 156 | TbMsgMetaData metaData = new TbMsgMetaData(); |
142 | 157 | metaData.putValue("temp", "10"); |
143 | 158 | metaData.putValue("humidity", "99"); | ... | ... |
application/src/test/java/org/thingsboard/server/service/script/TestNashornJsSandboxService.java
0 → 100644
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 delight.nashornsandbox.NashornSandbox; | |
20 | +import delight.nashornsandbox.NashornSandboxes; | |
21 | + | |
22 | +import javax.script.ScriptException; | |
23 | +import java.util.concurrent.ExecutorService; | |
24 | +import java.util.concurrent.Executors; | |
25 | + | |
26 | +public class TestNashornJsSandboxService implements JsSandboxService { | |
27 | + | |
28 | + private NashornSandbox sandbox = NashornSandboxes.create(); | |
29 | + private ExecutorService monitorExecutorService; | |
30 | + | |
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); | |
37 | + } | |
38 | + | |
39 | + @Override | |
40 | + public Object eval(String js) throws ScriptException { | |
41 | + return sandbox.eval(js); | |
42 | + } | |
43 | + | |
44 | + @Override | |
45 | + public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException { | |
46 | + return sandbox.getSandboxedInvocable().invokeFunction(name, args); | |
47 | + } | |
48 | + | |
49 | + public void destroy() { | |
50 | + if (monitorExecutorService != null) { | |
51 | + monitorExecutorService.shutdownNow(); | |
52 | + } | |
53 | + } | |
54 | +} | ... | ... |
... | ... | @@ -81,6 +81,7 @@ |
81 | 81 | org/thingsboard/server/extensions/core/plugin/telemetry/gen/**/* |
82 | 82 | </sonar.exclusions> |
83 | 83 | <elasticsearch.version>5.0.2</elasticsearch.version> |
84 | + <delight-nashorn-sandbox.version>0.1.14</delight-nashorn-sandbox.version> | |
84 | 85 | </properties> |
85 | 86 | |
86 | 87 | <modules> |
... | ... | @@ -814,6 +815,11 @@ |
814 | 815 | <artifactId>rest</artifactId> |
815 | 816 | <version>${elasticsearch.version}</version> |
816 | 817 | </dependency> |
818 | + <dependency> | |
819 | + <groupId>org.javadelight</groupId> | |
820 | + <artifactId>delight-nashorn-sandbox</artifactId> | |
821 | + <version>${delight-nashorn-sandbox.version}</version> | |
822 | + </dependency> | |
817 | 823 | </dependencies> |
818 | 824 | </dependencyManagement> |
819 | 825 | ... | ... |