You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2015/01/21 15:49:25 UTC

svn commit: r1653546 - /tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java

Author: markt
Date: Wed Jan 21 14:49:25 2015
New Revision: 1653546

URL: http://svn.apache.org/r1653546
Log:
Add an ELParser cache to the ExpressionBuilder. Re-using ELParser instances is measureably more efficient.

Modified:
    tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java

Modified: tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java?rev=1653546&r1=1653545&r2=1653546&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java (original)
+++ tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java Wed Jan 21 14:49:25 2015
@@ -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;
@@ -49,6 +48,8 @@ import org.apache.el.util.MessageFactory
  */
 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";
@@ -70,7 +71,7 @@ public final class ExpressionBuilder imp
         }
     }
 
-    private static final ConcurrentCache<String, Node> cache =
+    private static final ConcurrentCache<String, Node> expressionCache =
             new ConcurrentCache<>(CACHE_SIZE);
 
     private FunctionMapper fnMapper;
@@ -105,11 +106,16 @@ public final class ExpressionBuilder imp
             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();
@@ -137,10 +143,14 @@ public final class ExpressionBuilder imp
                         || 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;
@@ -252,4 +262,76 @@ public final class ExpressionBuilder imp
                     + 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;
+        }
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org