Skip to content

Commit

Permalink
make ParserConfiguration thread safe
Browse files Browse the repository at this point in the history
  • Loading branch information
mariofusco committed Aug 31, 2017
1 parent a63efbd commit 061ce2d
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 57 deletions.
54 changes: 15 additions & 39 deletions src/main/java/org/mvel2/ParserConfiguration.java
Expand Up @@ -18,24 +18,24 @@

package org.mvel2;

import org.mvel2.ast.Proto;
import org.mvel2.compiler.AbstractParser;
import org.mvel2.integration.Interceptor;
import org.mvel2.util.MethodStub;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.mvel2.ast.Proto;
import org.mvel2.compiler.AbstractParser;
import org.mvel2.integration.Interceptor;
import org.mvel2.util.MethodStub;

import static org.mvel2.util.ParseTools.forNameWithInner;

/**
Expand All @@ -44,12 +44,12 @@
public class ParserConfiguration implements Serializable {
private static final int MAX_NEGATIVE_CACHE_SIZE;

protected Map<String, Object> imports;
protected final Map<String, Object> imports = new ConcurrentHashMap<String, Object>();
protected HashSet<String> packageImports;
protected Map<String, Interceptor> interceptors;
protected transient ClassLoader classLoader;

private transient Set<String> nonValidImports;
private final transient Set<String> nonValidImports = Collections.newSetFromMap( new ConcurrentHashMap<String, Boolean>() );

private boolean allowNakedMethCall = MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL;

Expand Down Expand Up @@ -89,18 +89,18 @@ public void setPackageImports(HashSet<String> packageImports) {
}

public Class getImport(String name) {
if (imports != null && imports.containsKey(name) && imports.get(name) instanceof Class) {
if (imports.containsKey(name) && imports.get(name) instanceof Class) {
return (Class) imports.get(name);
}
return (Class) (AbstractParser.LITERALS.get(name) instanceof Class ? AbstractParser.LITERALS.get(name) : null);
}

public MethodStub getStaticImport(String name) {
return imports != null ? (MethodStub) imports.get(name) : null;
return (MethodStub) imports.get(name);
}

public Object getStaticOrClassImport(String name) {
return (imports != null && imports.containsKey(name) ? imports.get(name) : AbstractParser.LITERALS.get(name));
return imports.containsKey(name) ? imports.get(name) : AbstractParser.LITERALS.get(name);
}

public void addPackageImport(String packageName) {
Expand All @@ -112,7 +112,6 @@ public void addPackageImport(String packageName) {
private boolean addClassMemberStaticImports(String packageName) {
try {
Class c = Class.forName(packageName);
initImports();
if (c.isEnum()) {

//noinspection unchecked
Expand Down Expand Up @@ -142,8 +141,6 @@ private boolean addClassMemberStaticImports(String packageName) {
public void addAllImports(Map<String, Object> imports) {
if (imports == null) return;

initImports();

Object o;

for (Map.Entry<String, Object> entry : imports.entrySet()) {
Expand All @@ -159,7 +156,7 @@ public void addAllImports(Map<String, Object> imports) {
private boolean checkForDynamicImport(String className) {
if (packageImports == null) return false;
if (!Character.isJavaIdentifierStart(className.charAt(0))) return false;
if (nonValidImports != null && nonValidImports.contains(className)) return false;
if (nonValidImports.contains(className)) return false;

int found = 0;
Class cls = null;
Expand All @@ -184,29 +181,20 @@ private boolean checkForDynamicImport(String className) {
}

public boolean hasImport(String name) {
return (imports != null && imports.containsKey(name)) ||
return (imports.containsKey(name)) ||
AbstractParser.CLASS_LITERALS.containsKey(name) ||
checkForDynamicImport(name);
}

private void initImports() {
if (this.imports == null) {
this.imports = new ConcurrentHashMap<String, Object>();
}
}

public void addImport(Class cls) {
initImports();
addImport(cls.getSimpleName(), cls);
}

public void addImport(String name, Class cls) {
initImports();
this.imports.put(name, cls);
}

public void addImport(String name, Proto proto) {
initImports();
this.imports.put(name, proto);
}

Expand All @@ -215,7 +203,6 @@ public void addImport(String name, Method method) {
}

public void addImport(String name, MethodStub method) {
initImports();
this.imports.put(name, method);
}

Expand Down Expand Up @@ -256,7 +243,7 @@ else if (val instanceof Proto) {
}

public boolean hasImports() {
return !(imports != null && imports.isEmpty()) || (packageImports != null && packageImports.size() != 0);
return !imports.isEmpty() || (packageImports != null && packageImports.size() != 0);
}

public ClassLoader getClassLoader() {
Expand All @@ -268,7 +255,6 @@ public void setClassLoader(ClassLoader classLoader) {
}

public void setAllImports(Map<String, Object> imports) {
initImports();
this.imports.clear();
if (imports != null) this.imports.putAll(imports);
}
Expand All @@ -279,21 +265,11 @@ public void setImports(HashMap<String, Object> imports) {
}

private void cacheNegativeHitForDynamicImport(String negativeHit) {
if (nonValidImports == null) {
nonValidImports = new LinkedHashSet<String>();
}
else if (nonValidImports.size() > 1000) {
Iterator<String> i = nonValidImports.iterator();
i.next();
i.remove();
}

nonValidImports.add(negativeHit);
}

public void flushCaches() {
if (nonValidImports != null)
nonValidImports.clear();
nonValidImports.clear();
}

public boolean isAllowNakedMethCall() {
Expand Down
25 changes: 18 additions & 7 deletions src/main/java/org/mvel2/ParserContext.java
Expand Up @@ -18,6 +18,23 @@

package org.mvel2;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.mvel2.ast.Function;
import org.mvel2.ast.LineLabel;
import org.mvel2.ast.Proto;
Expand All @@ -29,10 +46,6 @@
import org.mvel2.util.MethodStub;
import org.mvel2.util.ReflectionUtil;

import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;

/**
* The <tt>ParserContext</tt> is the main environment object used for sharing state throughout the entire
* parser/compileShared process.<br/><br/>
Expand Down Expand Up @@ -374,7 +387,6 @@ public boolean hasImport(String name) {
}

public boolean hasProtoImport(String name) {
if (parserConfiguration.getImports() == null) return false;
Object o = parserConfiguration.getImports().get(name);
return o != null && o instanceof Proto;
}
Expand Down Expand Up @@ -458,8 +470,7 @@ public void initializeTables() {
scope.addAll(variables.keySet());
scope.addAll(inputs.keySet());

if (parserConfiguration.getImports() != null)
scope.addAll(parserConfiguration.getImports().keySet());
scope.addAll( parserConfiguration.getImports().keySet() );

if (inputs.containsKey("this")) {
Class<?> ctxType = inputs.get("this");
Expand Down
13 changes: 5 additions & 8 deletions src/main/java/org/mvel2/compiler/AbstractParser.java
Expand Up @@ -17,6 +17,10 @@
*/
package org.mvel2.compiler;

import java.io.Serializable;
import java.util.HashMap;
import java.util.WeakHashMap;

import org.mvel2.CompileException;
import org.mvel2.ErrorDetail;
import org.mvel2.Operator;
Expand Down Expand Up @@ -82,17 +86,10 @@
import org.mvel2.util.ErrorUtil;
import org.mvel2.util.ExecutionStack;
import org.mvel2.util.FunctionParser;
import org.mvel2.util.PropertyTools;
import org.mvel2.util.ProtoParser;

import java.io.Serializable;
import java.util.HashMap;
import java.util.WeakHashMap;

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.lang.Double.parseDouble;
import static java.lang.Thread.currentThread;
import static org.mvel2.Operator.*;
import static org.mvel2.ast.TypeDescriptor.getClassReference;
import static org.mvel2.util.ArrayTools.findFirst;
Expand Down Expand Up @@ -1409,7 +1406,7 @@ else if (lastWasIdentifier) {
}
}

if (pCtx != null && pCtx.hasImports() && isArrayType(expr, st, end)) {
if (pCtx != null && isArrayType(expr, st, end)) {
if (pCtx.hasImport(new String(expr, st, cursor - st - 2))) {
lastWasIdentifier = true;
TypeDescriptor typeDescriptor = new TypeDescriptor(expr, st, cursor - st, fields);
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/mvel2/compiler/CompiledExpression.java
Expand Up @@ -18,6 +18,8 @@

package org.mvel2.compiler;

import java.io.Serializable;

import org.mvel2.ParserConfiguration;
import org.mvel2.ast.ASTNode;
import org.mvel2.ast.TypeCast;
Expand All @@ -28,8 +30,6 @@
import org.mvel2.optimizers.OptimizerFactory;
import org.mvel2.util.ASTLinkedList;

import java.io.Serializable;

import static org.mvel2.MVELRuntime.execute;
import static org.mvel2.optimizers.OptimizerFactory.setThreadAccessorOptimizer;

Expand All @@ -56,7 +56,7 @@ public CompiledExpression(ASTLinkedList astMap, String sourceName, Class egressT
this.knownEgressType = astMap.isSingleNode() ? astMap.firstNonSymbol().getEgressType() : egressType;
this.literalOnly = literalOnly;
this.parserConfiguration = parserConfiguration;
this.importInjectionRequired = parserConfiguration.getImports() != null && !parserConfiguration.getImports().isEmpty();
this.importInjectionRequired = !parserConfiguration.getImports().isEmpty();
}

public ASTNode getFirstNode() {
Expand Down

0 comments on commit 061ce2d

Please sign in to comment.