Update mvel2 dependency

This commit is contained in:
Igor Kulikov 2022-10-27 11:24:48 +03:00
parent 830a612bf8
commit 4607d89369
9 changed files with 16 additions and 364 deletions

View File

@ -85,7 +85,7 @@
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.mvel</groupId>
<groupId>org.thingsboard</groupId>
<artifactId>mvel2</artifactId>
</dependency>
<dependency>

View File

@ -22,15 +22,15 @@ import com.google.common.util.concurrent.MoreExecutors;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.mvel2.ExecutionContext;
import org.mvel2.MVEL;
import org.mvel2.ParserContext;
import org.mvel2.optimizers.AccessorOptimizer;
import org.mvel2.SandboxedParserContext;
import org.mvel2.optimizers.OptimizerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.script.api.AbstractScriptInvokeService;
import org.thingsboard.script.api.ScriptType;
@ -101,12 +101,9 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem
@PostConstruct
public void init() {
super.init();
Field field = ReflectionUtils.findField(OptimizerFactory.class, "accessorCompilers");
ReflectionUtils.makeAccessible(field);
Map<String, AccessorOptimizer> accessorCompilers = (Map<String, AccessorOptimizer>) field.get(null);
accessorCompilers.put(OptimizerFactory.SAFE_REFLECTIVE, new TbReflectiveAccessorOptimizer());
parserContext = new ParserContext(new TbMvelParserConfiguration());
OptimizerFactory.setDefaultOptimizer(OptimizerFactory.SAFE_REFLECTIVE);
parserContext = new SandboxedParserContext();
parserContext.addImport("JSON", TbJson.class);
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(2, "mvel-executor"));
}
@ -159,7 +156,9 @@ public class DefaultMvelInvokeService extends AbstractScriptInvokeService implem
throw new TbScriptException(scriptId, TbScriptException.ErrorCode.OTHER, null, new RuntimeException("Script not found!"));
}
try {
return MVEL.executeExpression(script.getCompiledScript(), script.createVars(args));
ExecutionContext executionContext = new ExecutionContext();
// TODO:
return MVEL.executeExpression(script.getCompiledScript(), executionContext, script.createVars(args));
} catch (OutOfMemoryError e) {
Runtime.getRuntime().gc();
throw new TbScriptException(scriptId, TbScriptException.ErrorCode.OTHER, script.getScriptBody(), new RuntimeException("Memory error!"));

View File

@ -16,6 +16,8 @@
package org.thingsboard.script.api.mvel;
import com.fasterxml.jackson.databind.JsonNode;
import org.mvel2.ExecutionContext;
import org.mvel2.util.ArgsRepackUtil;
import org.thingsboard.common.util.JacksonUtil;
import java.io.IOException;
@ -28,13 +30,13 @@ public class TbJson {
return value != null ? JacksonUtil.toString(value) : "null";
}
public static Object parse(String value) throws IOException {
public static Object parse(ExecutionContext ctx, String value) throws IOException {
if (value != null) {
JsonNode node = JacksonUtil.toJsonNode(value);
if (node.isObject()) {
return JacksonUtil.convertValue(node, Map.class);
return ArgsRepackUtil.repack(ctx, JacksonUtil.convertValue(node, Map.class));
} else if (node.isArray()) {
return JacksonUtil.convertValue(node, List.class);
return ArgsRepackUtil.repack(ctx, JacksonUtil.convertValue(node, List.class));
} else if (node.isDouble()) {
return node.doubleValue();
} else if (node.isLong()) {

View File

@ -1,65 +0,0 @@
/**
* 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 org.mvel2.compiler.Accessor;
import org.mvel2.compiler.ExecutableAccessor;
import org.mvel2.compiler.ExecutableStatement;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.optimizers.impl.refl.collection.ExprValueAccessor;
import java.util.HashMap;
import java.util.Map;
public class TbMapCreator implements Accessor {
private Accessor[] keys;
private Accessor[] vals;
private int size;
public Object getValue(Object ctx, Object elCtx, VariableResolverFactory variableFactory) {
Map map = new HashMap<>(size * 2);
for (int i = size - 1; i != -1; i--) {
//noinspection unchecked
map.put(getKey(i, ctx, elCtx, variableFactory), vals[i].getValue(ctx, elCtx, variableFactory));
}
return map;
}
private Object getKey(int index, Object ctx, Object elCtx, VariableResolverFactory variableFactory) {
Accessor keyAccessor = keys[index];
if (keyAccessor instanceof ExprValueAccessor) {
ExecutableStatement executableStatement = ((ExprValueAccessor) keyAccessor).stmt;
if (executableStatement instanceof ExecutableAccessor) {
return ((ExecutableAccessor) executableStatement).getNode().getName();
}
}
return keys[index].getValue(ctx, elCtx, variableFactory);
}
public TbMapCreator(Accessor[] keys, Accessor[] vals) {
this.size = (this.keys = keys).length;
this.vals = vals;
}
public Object setValue(Object ctx, Object elCtx, VariableResolverFactory variableFactory, Object value) {
// not implemented
return null;
}
public Class getKnownEgressType() {
return Map.class;
}
}

View File

@ -1,92 +0,0 @@
/**
* 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 org.mvel2.compiler.AbstractParser;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Set;
public class TbMvelClassLoader extends URLClassLoader {
private static final Set<String> allowedClasses = new HashSet<>();
private static final Set<String> allowedPackages = new HashSet<>();
static {
AbstractParser.LITERALS.remove("System");
AbstractParser.LITERALS.remove("Runtime");
AbstractParser.LITERALS.remove("Class");
AbstractParser.LITERALS.remove("ClassLoader");
AbstractParser.LITERALS.remove("Thread");
AbstractParser.LITERALS.remove("Compiler");
AbstractParser.LITERALS.remove("ThreadLocal");
AbstractParser.LITERALS.remove("SecurityManager");
AbstractParser.CLASS_LITERALS.remove("System");
AbstractParser.CLASS_LITERALS.remove("Runtime");
AbstractParser.CLASS_LITERALS.remove("Class");
AbstractParser.CLASS_LITERALS.remove("ClassLoader");
AbstractParser.CLASS_LITERALS.remove("Thread");
AbstractParser.CLASS_LITERALS.remove("Compiler");
AbstractParser.CLASS_LITERALS.remove("ThreadLocal");
AbstractParser.CLASS_LITERALS.remove("SecurityManager");
AbstractParser.CLASS_LITERALS.put("JSON", TbJson.class);
AbstractParser.LITERALS.put("JSON", TbJson.class);
AbstractParser.CLASS_LITERALS.values().forEach(val -> allowedClasses.add(((Class) val).getName()));
}
static {
allowedPackages.add("org.mvel2");
allowedPackages.add("java.util");
}
public TbMvelClassLoader() {
super(new URL[0], Thread.currentThread().getContextClassLoader());
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (!classNameAllowed(name)) {
throw new ClassNotFoundException();
}
return super.loadClass(name, resolve);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (!classNameAllowed(name)) {
throw new ClassNotFoundException();
}
return super.loadClass(name);
}
private boolean classNameAllowed(String name) {
if (allowedClasses.contains(name)) {
return true;
}
for (String pkgName : allowedPackages) {
if (name.startsWith(pkgName)) {
return true;
}
}
return false;
}
}

View File

@ -1,37 +0,0 @@
/**
* 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 org.mvel2.ParserConfiguration;
import org.mvel2.integration.VariableResolverFactory;
public class TbMvelParserConfiguration extends ParserConfiguration {
private static final long serialVersionUID = 5558151976348875590L;
TbMvelParserConfiguration() {
setClassLoader(new TbMvelClassLoader());
}
@Override
public VariableResolverFactory getVariableFactory(VariableResolverFactory factory) {
if (Thread.interrupted()) {
throw new RuntimeException("Thread is interrupted!");
}
return new TbMvelResolverFactory(factory);
}
}

View File

@ -1,35 +0,0 @@
/**
* 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 org.mvel2.integration.VariableResolver;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.integration.impl.StackResetResolverFactory;
public class TbMvelResolverFactory extends StackResetResolverFactory {
public TbMvelResolverFactory(VariableResolverFactory delegate) {
super(delegate);
}
@Override
public VariableResolver getVariableResolver(String name) {
if (Thread.interrupted()) {
throw new RuntimeException("Thread is interrupted!");
}
return super.getVariableResolver(name);
}
}

View File

@ -1,120 +0,0 @@
/**
* 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 org.mvel2.ParserContext;
import org.mvel2.compiler.Accessor;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer;
import org.mvel2.optimizers.impl.refl.collection.ArrayCreator;
import org.mvel2.optimizers.impl.refl.collection.ExprValueAccessor;
import org.mvel2.optimizers.impl.refl.collection.ListCreator;
import org.mvel2.optimizers.impl.refl.nodes.Union;
import java.util.List;
import java.util.Map;
import static org.mvel2.util.CompilerTools.expectType;
import static org.mvel2.util.ParseTools.findClass;
import static org.mvel2.util.ParseTools.getBaseComponentType;
import static org.mvel2.util.ParseTools.getSubComponentType;
import static org.mvel2.util.ParseTools.repeatChar;
public class TbReflectiveAccessorOptimizer extends ReflectiveAccessorOptimizer {
private Object ctx;
private Class returnType;
private VariableResolverFactory variableFactory;
@Override
public Accessor optimizeCollection(ParserContext pCtx, Object o, Class type, char[] property, int start, int offset,
Object ctx, Object thisRef, VariableResolverFactory factory) {
this.start = this.cursor = start;
this.length = start + offset;
this.returnType = type;
this.ctx = ctx;
this.variableFactory = factory;
this.pCtx = pCtx;
Accessor root = _getAccessor(o, returnType);
if (property != null && length > start) {
return new Union(pCtx, root, property, cursor, offset);
} else {
return root;
}
}
private Accessor _getAccessor(Object o, Class type) {
if (o instanceof List) {
Accessor[] a = new Accessor[((List) o).size()];
int i = 0;
for (Object item : (List) o) {
a[i++] = _getAccessor(item, type);
}
returnType = List.class;
return new ListCreator(a);
} else if (o instanceof Map) {
Accessor[] k = new Accessor[((Map) o).size()];
Accessor[] v = new Accessor[k.length];
int i = 0;
for (Object item : ((Map) o).keySet()) {
k[i] = _getAccessor(item, type); // key
v[i++] = _getAccessor(((Map) o).get(item), type); // value
}
returnType = Map.class;
return new TbMapCreator(k, v);
} else if (o instanceof Object[]) {
Accessor[] a = new Accessor[((Object[]) o).length];
int i = 0;
int dim = 0;
if (type != null) {
String nm = type.getName();
while (nm.charAt(dim) == '[') dim++;
} else {
type = Object[].class;
dim = 1;
}
try {
Class base = getBaseComponentType(type);
Class cls = dim > 1 ? findClass(null, repeatChar('[', dim - 1) + "L" + base.getName() + ";", pCtx)
: type;
for (Object item : (Object[]) o) {
expectType(pCtx, a[i++] = _getAccessor(item, cls), base, true);
}
return new ArrayCreator(a, getSubComponentType(type));
} catch (ClassNotFoundException e) {
throw new RuntimeException("this error should never throw:" + getBaseComponentType(type).getName(), e);
}
} else {
if (returnType == null) returnType = Object.class;
if (type.isArray()) {
return new ExprValueAccessor((String) o, type, ctx, variableFactory, pCtx);
} else {
return new ExprValueAccessor((String) o, Object.class, ctx, variableFactory, pCtx);
}
}
}
}

View File

@ -77,7 +77,7 @@
<zookeeper.version>3.5.5</zookeeper.version>
<protobuf.version>3.17.2</protobuf.version>
<grpc.version>1.42.1</grpc.version>
<mvel.version>2.4.14.Final</mvel.version>
<mvel.version>2.4.16TB</mvel.version>
<lombok.version>1.18.18</lombok.version>
<paho.client.version>1.2.4</paho.client.version>
<netty.version>4.1.75.Final</netty.version>
@ -1575,7 +1575,7 @@
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>org.mvel</groupId>
<groupId>org.thingsboard</groupId>
<artifactId>mvel2</artifactId>
<version>${mvel.version}</version>
</dependency>