Add new MVEL classes and util methods
This commit is contained in:
parent
cdd7eff00e
commit
ef82c00c49
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.script.api.mvel;
|
package org.thingsboard.script.api.mvel;
|
||||||
|
|
||||||
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.ListeningExecutorService;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
@ -111,12 +110,13 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem
|
|||||||
OptimizerFactory.setDefaultOptimizer(OptimizerFactory.SAFE_REFLECTIVE);
|
OptimizerFactory.setDefaultOptimizer(OptimizerFactory.SAFE_REFLECTIVE);
|
||||||
parserConfig = new SandboxedParserConfiguration();
|
parserConfig = new SandboxedParserConfiguration();
|
||||||
parserConfig.addImport("JSON", TbJson.class);
|
parserConfig.addImport("JSON", TbJson.class);
|
||||||
|
parserConfig.registerDataType("Date", TbDate.class, date -> 8L);
|
||||||
TbUtils.register(parserConfig);
|
TbUtils.register(parserConfig);
|
||||||
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(threadPoolSize, "mvel-executor"));
|
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(threadPoolSize, "mvel-executor"));
|
||||||
try {
|
try {
|
||||||
// Special command to warm up MVEL engine
|
// Special command to warm up MVEL engine
|
||||||
Serializable script = MVEL.compileExpression("var warmUp = {}; warmUp", new SandboxedParserContext(parserConfig));
|
Serializable script = MVEL.compileExpression("var warmUp = {}; warmUp", new SandboxedParserContext(parserConfig));
|
||||||
MVEL.executeTbExpression(script, new ExecutionContext(), Collections.emptyMap());
|
MVEL.executeTbExpression(script, new ExecutionContext(parserConfig), Collections.emptyMap());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
@ -146,11 +146,6 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ListenableFuture<UUID> doEvalScript(TenantId tenantId, ScriptType scriptType, String scriptBody, UUID scriptId, String[] argNames) {
|
protected ListenableFuture<UUID> doEvalScript(TenantId tenantId, ScriptType scriptType, String scriptBody, UUID scriptId, String[] argNames) {
|
||||||
if (NEW_KEYWORD_PATTERN.matcher(scriptBody).matches()) {
|
|
||||||
//TODO: output line number and char pos.
|
|
||||||
return Futures.immediateFailedFuture(new TbScriptException(scriptId, TbScriptException.ErrorCode.COMPILATION, scriptBody,
|
|
||||||
new IllegalArgumentException("Keyword 'new' is forbidden!")));
|
|
||||||
}
|
|
||||||
return executor.submit(() -> {
|
return executor.submit(() -> {
|
||||||
try {
|
try {
|
||||||
Serializable compiledScript = MVEL.compileExpression(scriptBody, new SandboxedParserContext(parserConfig));
|
Serializable compiledScript = MVEL.compileExpression(scriptBody, new SandboxedParserContext(parserConfig));
|
||||||
@ -165,7 +160,7 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MvelScriptExecutionTask doInvokeFunction(UUID scriptId, Object[] args) {
|
protected MvelScriptExecutionTask doInvokeFunction(UUID scriptId, Object[] args) {
|
||||||
ExecutionContext executionContext = new ExecutionContext(maxMemoryLimitMb * 1024 * 1024);
|
ExecutionContext executionContext = new ExecutionContext(this.parserConfig, maxMemoryLimitMb * 1024 * 1024);
|
||||||
return new MvelScriptExecutionTask(executionContext, executor.submit(() -> {
|
return new MvelScriptExecutionTask(executionContext, executor.submit(() -> {
|
||||||
MvelScript script = scriptMap.get(scriptId);
|
MvelScript script = scriptMap.get(scriptId);
|
||||||
if (script == null) {
|
if (script == null) {
|
||||||
|
|||||||
@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2022 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.script.api.mvel;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
public class TbDate extends Date {
|
||||||
|
|
||||||
|
public TbDate() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TbDate(String s) {
|
||||||
|
super(parse(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
public TbDate(long date) {
|
||||||
|
super(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TbDate(int year, int month, int date) {
|
||||||
|
this(year, month, date, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TbDate(int year, int month, int date, int hrs, int min) {
|
||||||
|
this(year, month, date, hrs, min, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TbDate(int year, int month, int date,
|
||||||
|
int hrs, int min, int second) {
|
||||||
|
super(new GregorianCalendar(year, month, date, hrs, min, second).getTimeInMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toDateString() {
|
||||||
|
DateFormat formatter = DateFormat.getDateInstance();
|
||||||
|
return formatter.format(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toTimeString() {
|
||||||
|
DateFormat formatter = DateFormat.getTimeInstance(DateFormat.LONG);
|
||||||
|
return formatter.format(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toISOString() {
|
||||||
|
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sssZ");
|
||||||
|
return formatter.format(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toLocaleString(String locale) {
|
||||||
|
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.forLanguageTag(locale));
|
||||||
|
return formatter.format(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toLocaleString(String locale, String tz) {
|
||||||
|
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.forLanguageTag(locale));
|
||||||
|
formatter.setTimeZone(TimeZone.getTimeZone(tz));
|
||||||
|
return formatter.format(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long now() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long parse(String value) {
|
||||||
|
try {
|
||||||
|
return Date.parse(value);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long UTC(int year, int month, int date,
|
||||||
|
int hrs, int min, int sec) {
|
||||||
|
return Date.UTC(year - 1900, month, date, hrs, min, sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -39,6 +39,18 @@ public class TbUtils {
|
|||||||
ExecutionContext.class, String.class)));
|
ExecutionContext.class, String.class)));
|
||||||
parserConfig.addImport("stringToBytes", new MethodStub(TbUtils.class.getMethod("stringToBytes",
|
parserConfig.addImport("stringToBytes", new MethodStub(TbUtils.class.getMethod("stringToBytes",
|
||||||
ExecutionContext.class, String.class, String.class)));
|
ExecutionContext.class, String.class, String.class)));
|
||||||
|
parserConfig.addImport("parseInt", new MethodStub(TbUtils.class.getMethod("parseInt",
|
||||||
|
String.class)));
|
||||||
|
parserConfig.addImport("parseInt", new MethodStub(TbUtils.class.getMethod("parseInt",
|
||||||
|
String.class, int.class)));
|
||||||
|
parserConfig.addImport("parseFloat", new MethodStub(TbUtils.class.getMethod("parseFloat",
|
||||||
|
String.class)));
|
||||||
|
parserConfig.addImport("parseDouble", new MethodStub(TbUtils.class.getMethod("parseDouble",
|
||||||
|
String.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(Integer.class == int.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String btoa(String input) {
|
public static String btoa(String input) {
|
||||||
@ -84,4 +96,72 @@ public class TbUtils {
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Integer parseInt(String value) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
int radix = 10;
|
||||||
|
if (isHexadecimal(value)) {
|
||||||
|
radix = 16;
|
||||||
|
}
|
||||||
|
return Integer.parseInt(prepareNumberString(value), radix);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Float f = parseFloat(value);
|
||||||
|
if (f != null) {
|
||||||
|
return f.intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer parseInt(String value, int radix) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(prepareNumberString(value), radix);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Float f = parseFloat(value);
|
||||||
|
if (f != null) {
|
||||||
|
return f.intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Float parseFloat(String value) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return Float.parseFloat(prepareNumberString(value));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Double parseDouble(String value) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(prepareNumberString(value));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isHexadecimal(String value) {
|
||||||
|
return value != null && (value.contains("0x") || value.contains("0X"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String prepareNumberString(String value) {
|
||||||
|
if (value != null) {
|
||||||
|
value = value.trim();
|
||||||
|
if (isHexadecimal(value)) {
|
||||||
|
value = value.replace("0x", "");
|
||||||
|
value = value.replace("0X", "");
|
||||||
|
}
|
||||||
|
value = value.replace(",", ".");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@ -77,7 +77,7 @@
|
|||||||
<zookeeper.version>3.5.5</zookeeper.version>
|
<zookeeper.version>3.5.5</zookeeper.version>
|
||||||
<protobuf.version>3.21.9</protobuf.version>
|
<protobuf.version>3.21.9</protobuf.version>
|
||||||
<grpc.version>1.42.1</grpc.version>
|
<grpc.version>1.42.1</grpc.version>
|
||||||
<mvel.version>2.4.22TB</mvel.version>
|
<mvel.version>2.4.23TB</mvel.version>
|
||||||
<lombok.version>1.18.18</lombok.version>
|
<lombok.version>1.18.18</lombok.version>
|
||||||
<paho.client.version>1.2.4</paho.client.version>
|
<paho.client.version>1.2.4</paho.client.version>
|
||||||
<netty.version>4.1.75.Final</netty.version>
|
<netty.version>4.1.75.Final</netty.version>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user