From ef82c00c49d97b831682cf07ad9c6408aed89fe7 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 4 Nov 2022 19:59:12 +0200 Subject: [PATCH] Add new MVEL classes and util methods --- .../api/mvel/DefaultMvelInvokeService.java | 11 +-- .../thingsboard/script/api/mvel/TbDate.java | 95 +++++++++++++++++++ .../thingsboard/script/api/mvel/TbUtils.java | 80 ++++++++++++++++ pom.xml | 2 +- 4 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbDate.java diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/DefaultMvelInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/DefaultMvelInvokeService.java index 393f935861..901a49180e 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/DefaultMvelInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/DefaultMvelInvokeService.java @@ -15,7 +15,6 @@ */ 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.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; @@ -111,12 +110,13 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem OptimizerFactory.setDefaultOptimizer(OptimizerFactory.SAFE_REFLECTIVE); parserConfig = new SandboxedParserConfiguration(); parserConfig.addImport("JSON", TbJson.class); + parserConfig.registerDataType("Date", TbDate.class, date -> 8L); TbUtils.register(parserConfig); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(threadPoolSize, "mvel-executor")); try { // Special command to warm up MVEL engine 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) { // do nothing } @@ -146,11 +146,6 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem @Override protected ListenableFuture 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(() -> { try { Serializable compiledScript = MVEL.compileExpression(scriptBody, new SandboxedParserContext(parserConfig)); @@ -165,7 +160,7 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem @Override 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(() -> { MvelScript script = scriptMap.get(scriptId); if (script == null) { diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbDate.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbDate.java new file mode 100644 index 0000000000..9b58bd9357 --- /dev/null +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbDate.java @@ -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); + } + +} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbUtils.java index 03d9f77b85..ae38c13125 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/mvel/TbUtils.java @@ -39,6 +39,18 @@ public class TbUtils { ExecutionContext.class, String.class))); parserConfig.addImport("stringToBytes", new MethodStub(TbUtils.class.getMethod("stringToBytes", 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) { @@ -84,4 +96,72 @@ public class TbUtils { } 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; + } } diff --git a/pom.xml b/pom.xml index a64aaf8813..60e8783bca 100755 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ 3.5.5 3.21.9 1.42.1 - 2.4.22TB + 2.4.23TB 1.18.18 1.2.4 4.1.75.Final