9 changed files with 16 additions and 364 deletions
@ -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; |
|
||||
} |
|
||||
} |
|
||||
@ -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; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -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); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue