Author: henrib
Date: Fri Mar 10 14:39:20 2017
New Revision: 1786352
URL: http://svn.apache.org/viewvc?rev=1786352&view=rev
Log:
JEXL-216:
Use r/w lock on cache, busy-flag logic on parser (parser is not reentrant,
create a temp one if busy)
Added:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/SoftCache.java
(with props)
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java?rev=1786352&r1=1786351&r2=1786352&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java
Fri Mar 10 14:39:20 2017
@@ -43,11 +43,10 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
-import java.lang.ref.SoftReference;
import java.nio.charset.Charset;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -86,7 +85,12 @@ public class Engine extends JexlEngine {
*/
protected final Log logger;
/**
- * The {@link Parser}; when parsing expressions, this engine synchronizes
on the parser.
+ * The atomic parsing flag; true whilst parsing.
+ */
+ protected final AtomicBoolean parsing = new AtomicBoolean(false);
+ /**
+ * The {@link Parser}; when parsing expressions, this engine uses the
parser if it
+ * is not already in use otherwise it will create a new temporary one.
*/
protected final Parser parser = new Parser(new StringReader(";"));
//$NON-NLS-1$
/**
@@ -128,10 +132,6 @@ public class Engine extends JexlEngine {
* The default jxlt engine.
*/
protected volatile TemplateEngine jxlt = null;
- /**
- * The default cache load factor.
- */
- private static final float LOAD_FACTOR = 0.75f;
/**
* Creates an engine with default arguments.
@@ -214,6 +214,11 @@ public class Engine extends JexlEngine {
return strict;
}
+ //@Override
+ public boolean isCancellable() {
+ return this.cancellable;
+ }
+
@Override
public void setClassLoader(ClassLoader loader) {
uberspect.setClassLoader(loader);
@@ -229,116 +234,10 @@ public class Engine extends JexlEngine {
return new TemplateEngine(this, noScript, cacheSize, immediate,
deferred);
}
- /**
- * Swaps the current thread local context.
- * @param tls the context or null
- * @return the previous thread local context
- */
- protected JexlContext.ThreadLocal putThreadLocal(JexlContext.ThreadLocal
tls) {
- JexlContext.ThreadLocal local = CONTEXT.get();
- CONTEXT.set(tls);
- return local;
- }
-
- /**
- * A soft referenced cache.
- * <p>The actual cache is held through a soft reference, allowing it to be
GCed under
- * memory pressure.</p>
- * @param <K> the cache key entry type
- * @param <V> the cache key value type
- */
- protected class SoftCache<K, V> {
- /**
- * The cache size.
- */
- private final int size;
- /**
- * The soft reference to the cache map.
- */
- private SoftReference<Map<K, V>> ref = null;
-
- /**
- * Creates a new instance of a soft cache.
- * @param theSize the cache size
- */
- SoftCache(int theSize) {
- size = theSize;
- }
-
- /**
- * Returns the cache size.
- * @return the cache size
- */
- int size() {
- return size;
- }
-
- /**
- * Clears the cache.
- */
- void clear() {
- ref = null;
- }
-
- /**
- * Produces the cache entry set.
- * @return the cache entry set
- */
- Set<Entry<K, V>> entrySet() {
- Map<K, V> map = ref != null ? ref.get() : null;
- return map != null ? map.entrySet() : Collections.<Entry<K,
V>>emptySet();
- }
-
- /**
- * Gets a value from cache.
- * @param key the cache entry key
- * @return the cache entry value
- */
- V get(K key) {
- final Map<K, V> map = ref != null ? ref.get() : null;
- return map != null ? map.get(key) : null;
- }
-
- /**
- * Puts a value in cache.
- * @param key the cache entry key
- * @param script the cache entry value
- */
- void put(K key, V script) {
- Map<K, V> map = ref != null ? ref.get() : null;
- if (map == null) {
- map = createCache(size);
- ref = new SoftReference<Map<K, V>>(map);
- }
- map.put(key, script);
- }
- }
-
- /**
- * Creates a cache.
- * @param <K> the key type
- * @param <V> the value type
- * @param cacheSize the cache size, must be > 0
- * @return a Map usable as a cache bounded to the given size
- */
- protected <K, V> Map<K, V> createCache(final int cacheSize) {
- return new java.util.LinkedHashMap<K, V>(cacheSize, LOAD_FACTOR, true)
{
- /** Serial version UID. */
- private static final long serialVersionUID = 1L;
-
- @Override
- protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
- return size() > cacheSize;
- }
- };
- }
-
@Override
public void clearCache() {
- synchronized (parser) {
- if (cache != null) {
- cache.clear();
- }
+ if (cache != null) {
+ cache.clear();
}
}
@@ -509,6 +408,17 @@ public class Engine extends JexlEngine {
}
/**
+ * Swaps the current thread local context.
+ * @param tls the context or null
+ * @return the previous thread local context
+ */
+ protected JexlContext.ThreadLocal putThreadLocal(JexlContext.ThreadLocal
tls) {
+ JexlContext.ThreadLocal local = CONTEXT.get();
+ CONTEXT.set(tls);
+ return local;
+ }
+
+ /**
* Gets the list of variables accessed by a script.
* <p>This method will visit all nodes of a script and extract all
variables whether they
* are written in 'dot' or 'bracketed' notation. (a.b is equivalent to
a['b']).</p>
@@ -666,22 +576,33 @@ public class Engine extends JexlEngine {
*/
protected ASTJexlScript parse(JexlInfo info, String src, Scope scope,
boolean registers, boolean expression) {
final boolean cached = src.length() < cacheThreshold && cache != null;
- ASTJexlScript script;
- synchronized (parser) {
- if (cached) {
- script = cache.get(src);
- if (script != null) {
- Scope f = script.getScope();
- if ((f == null && scope == null) || (f != null &&
f.equals(scope))) {
- return script;
- }
+ ASTJexlScript script = null;
+ if (cached) {
+ script = cache.get(src);
+ if (script != null) {
+ Scope f = script.getScope();
+ if ((f == null && scope == null) || (f != null &&
f.equals(scope))) {
+ return script;
}
}
- final JexlInfo ninfo = info == null && debug? createInfo() : info;
- script = parser.parse(ninfo, src, scope, registers, expression);
- if (cached) {
- cache.put(src, script);
+ }
+ final JexlInfo ninfo = info == null && debug ? createInfo() : info;
+ // if parser not in use...
+ if (parsing.compareAndSet(false, true)) {
+ try {
+ // lets parse
+ script = parser.parse(ninfo, src, scope, registers,
expression);
+ } finally {
+ // no longer in use
+ parsing.set(false);
}
+ } else {
+ // ...otherwise parser was in use, create a new temporary one
+ Parser lparser = new Parser(new StringReader(";"));
+ script = lparser.parse(ninfo, src, scope, registers, expression);
+ }
+ if (cached) {
+ cache.put(src, script);
}
return script;
}
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java?rev=1786352&r1=1786351&r2=1786352&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
Fri Mar 10 14:39:20 2017
@@ -154,7 +154,7 @@ public abstract class InterpreterBase ex
return ocancellable.booleanValue();
}
}
- return jexl.cancellable;
+ return jexl.isCancellable();
}
/**
Added:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/SoftCache.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/SoftCache.java?rev=1786352&view=auto
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/SoftCache.java
(added)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/SoftCache.java
Fri Mar 10 14:39:20 2017
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.jexl3.internal;
+
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * A soft referenced cache.
+ * <p>
+ * The actual cache is held through a soft reference, allowing it to be GCed
+ * under memory pressure.</p>
+ *
+ * @param <K> the cache key entry type
+ * @param <V> the cache key value type
+ */
+public class SoftCache<K, V> {
+ /**
+ * The default cache load factor.
+ */
+ private static final float LOAD_FACTOR = 0.75f;
+ /**
+ * The cache size.
+ */
+ private final int size;
+ /**
+ * The soft reference to the cache map.
+ */
+ private SoftReference<Map<K, V>> ref = null;
+ /**
+ * The cache r/w lock.
+ */
+ private final ReadWriteLock lock;
+
+ /**
+ * Creates a new instance of a soft cache.
+ *
+ * @param theSize the cache size
+ */
+ SoftCache(int theSize) {
+ size = theSize;
+ lock = new ReentrantReadWriteLock();
+ }
+
+ /**
+ * Returns the cache size.
+ *
+ * @return the cache size
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public void clear() {
+ lock.writeLock().lock();
+ try {
+ ref = null;
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Gets a value from cache.
+ *
+ * @param key the cache entry key
+ * @return the cache entry value
+ */
+ public V get(K key) {
+ lock.readLock().lock();
+ try {
+ final Map<K, V> map = ref != null ? ref.get() : null;
+ return map != null ? map.get(key) : null;
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Puts a value in cache.
+ *
+ * @param key the cache entry key
+ * @param script the cache entry value
+ */
+ public void put(K key, V script) {
+ lock.writeLock().lock();
+ try {
+ Map<K, V> map = ref != null ? ref.get() : null;
+ if (map == null) {
+ map = createCache(size);
+ ref = new SoftReference<Map<K, V>>(map);
+ }
+ map.put(key, script);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Produces the cache entry set.
+ * <p>
+ * For testing only, perform deep copy of cache entries
+ *
+ * @return the cache entry list
+ */
+ public List<Map.Entry<K, V>> entries() {
+ lock.readLock().lock();
+ try {
+ Map<K, V> map = ref != null ? ref.get() : null;
+ if (map == null) {
+ return Collections.emptyList();
+ }
+ final Set<Map.Entry<K, V>> set = map.entrySet();
+ final List<Map.Entry<K, V>> entries = new ArrayList<Map.Entry<K,
V>>(set.size());
+ for (Map.Entry<K, V> e : set) {
+ entries.add(new SoftCacheEntry(e));
+ }
+ return entries;
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Creates the cache store.
+ *
+ * @param <K> the key type
+ * @param <V> the value type
+ * @param cacheSize the cache size, must be > 0
+ * @return a Map usable as a cache bounded to the given size
+ */
+ public <K, V> Map<K, V> createCache(final int cacheSize) {
+ return new java.util.LinkedHashMap<K, V>(cacheSize, LOAD_FACTOR, true)
{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+ return size() > cacheSize;
+ }
+ };
+ }
+}
+
+/**
+ * A soft cache entry.
+ *
+ * @param <K> key type
+ * @param <V> value type
+ */
+class SoftCacheEntry<K, V> implements Map.Entry<K, V> {
+ /**
+ * The entry key.
+ */
+ private final K key;
+ /**
+ * The entry value.
+ */
+ private final V value;
+
+ /**
+ * Creates an entry clone.
+ *
+ * @param e the entry to clone
+ */
+ SoftCacheEntry(Map.Entry<K, V> e) {
+ key = e.getKey();
+ value = e.getValue();
+ }
+
+ @Override
+ public K getKey() {
+ return key;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public V setValue(V value) {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+}
+
Propchange:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/SoftCache.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java?rev=1786352&r1=1786351&r2=1786352&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
Fri Mar 10 14:39:20 2017
@@ -41,7 +41,7 @@ import java.util.Set;
*/
public final class TemplateEngine extends JxltEngine {
/** The TemplateExpression cache. */
- private final Engine.SoftCache<String, TemplateExpression> cache;
+ private final SoftCache<String, TemplateExpression> cache;
/** The JEXL engine instance. */
private final Engine jexl;
/** The first character for immediate expressions. */
@@ -61,7 +61,7 @@ public final class TemplateEngine extend
*/
public TemplateEngine(Engine aJexl, boolean noScript, int cacheSize, char
immediate, char deferred) {
this.jexl = aJexl;
- this.cache = aJexl.new SoftCache<String,
TemplateExpression>(cacheSize);
+ this.cache = new SoftCache<String, TemplateExpression>(cacheSize);
immediateChar = immediate;
deferredChar = deferred;
noscript = noScript;
@@ -662,12 +662,10 @@ public final class TemplateEngine extend
if (cache == null) {
stmt = parseExpression(info, expression, null);
} else {
- synchronized (cache) {
- stmt = cache.get(expression);
- if (stmt == null) {
- stmt = parseExpression(info, expression, null);
- cache.put(expression, stmt);
- }
+ stmt = cache.get(expression);
+ if (stmt == null) {
+ stmt = parseExpression(info, expression, null);
+ cache.put(expression, stmt);
}
}
} catch (JexlException xjexl) {