Skip to content

Commit

Permalink
Add an ELParser cache to the ExpressionBuilder. Re-using ELParser ins…
Browse files Browse the repository at this point in the history
…tances is measureably more efficient.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1653546 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
markt-asf committed Jan 21, 2015
1 parent 5f74de2 commit 0e780bb
Showing 1 changed file with 88 additions and 6 deletions.
94 changes: 88 additions & 6 deletions java/org/apache/el/lang/ExpressionBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.el.lang;

import java.io.StringReader;
Expand Down Expand Up @@ -49,6 +48,8 @@
*/
public final class ExpressionBuilder implements NodeVisitor {

private static final SynchronizedStack<ELParser> parserCache = new SynchronizedStack<>();

private static final int CACHE_SIZE;
private static final String CACHE_SIZE_PROP =
"org.apache.el.ExpressionBuilder.CACHE_SIZE";
Expand All @@ -70,7 +71,7 @@ public Integer run() {
}
}

private static final ConcurrentCache<String, Node> cache =
private static final ConcurrentCache<String, Node> expressionCache =
new ConcurrentCache<>(CACHE_SIZE);

private FunctionMapper fnMapper;
Expand Down Expand Up @@ -105,11 +106,16 @@ private static final Node createNodeInternal(String expr)
throw new ELException(MessageFactory.get("error.null"));
}

Node n = cache.get(expr);
Node n = expressionCache.get(expr);
if (n == null) {
ELParser parser = parserCache.pop();
try {
n = (new ELParser(new StringReader(expr)))
.CompositeExpression();
if (parser == null) {
parser = new ELParser(new StringReader(expr));
} else {
parser.ReInit(new StringReader(expr));
}
n = parser.CompositeExpression();

// validate composite expression
int numChildren = n.jjtGetNumChildren();
Expand Down Expand Up @@ -137,10 +143,14 @@ private static final Node createNodeInternal(String expr)
|| n instanceof AstDynamicExpression) {
n = n.jjtGetChild(0);
}
cache.put(expr, n);
expressionCache.put(expr, n);
} catch (Exception e) {
throw new ELException(
MessageFactory.get("error.parseFail", expr), e);
} finally {
if (parser != null) {
parserCache.push(parser);
}
}
}
return n;
Expand Down Expand Up @@ -252,4 +262,76 @@ public MethodExpression createMethodExpression(Class<?> expectedReturnType,
+ expression);
}
}

/*
* Copied from org.apache.tomcat.util.collections.SynchronizedStack since
* we don't want the EL implementation to depend on the JAR where that
* class resides.
*/
private static class SynchronizedStack<T> {

public static final int DEFAULT_SIZE = 128;
private static final int DEFAULT_LIMIT = -1;

private int size;
private final int limit;

/*
* Points to the next available object in the stack
*/
private int index = -1;

private Object[] stack;


public SynchronizedStack() {
this(DEFAULT_SIZE, DEFAULT_LIMIT);
}

public SynchronizedStack(int size, int limit) {
this.size = size;
this.limit = limit;
stack = new Object[size];
}


public synchronized boolean push(T obj) {
index++;
if (index == size) {
if (limit == -1 || size < limit) {
expand();
} else {
index--;
return false;
}
}
stack[index] = obj;
return true;
}

@SuppressWarnings("unchecked")
public synchronized T pop() {
if (index == -1) {
return null;
}
T result = (T) stack[index];
stack[index--] = null;
return result;
}

private void expand() {
int newSize = size * 2;
if (limit != -1 && newSize > limit) {
newSize = limit;
}
Object[] newStack = new Object[newSize];
System.arraycopy(stack, 0, newStack, 0, size);
// This is the only point where garbage is created by throwing away the
// old array. Note it is only the array, not the contents, that becomes
// garbage.
stack = newStack;
size = newSize;
}
}

}

0 comments on commit 0e780bb

Please sign in to comment.