Index: jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/ELEvaluator.java =================================================================== --- jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/ELEvaluator.java (revision 1828438) +++ jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/ELEvaluator.java (working copy) @@ -102,13 +102,13 @@ * growth. *

NOTE: use LinkedHashmap if a dependency on J2SE 1.4+ is ok */ - static Map sCachedExpressionStrings = null; + private static Map sCachedExpressionStrings = null; /** * The mapping from ExpectedType to Maps mapping literal String to * parsed value * */ - static Map sCachedExpectedTypes = new HashMap(); + private static final Map sCachedExpectedTypes = new HashMap(); /** * The static Logger * @@ -123,7 +123,7 @@ /** * Flag if the cache should be bypassed * */ - boolean mBypassCache; + private volatile boolean mBypassCache; /** * The PageContext * @@ -256,40 +256,46 @@ return ""; } - if (!(mBypassCache) && (sCachedExpressionStrings == null)) { - createExpressionStringMap(); + if (mBypassCache) { + return parseExpressionUncached(pExpressionString); } + Map cache = getOrCreateExpressionStringMap(pageContext); + // See if it's in the cache - Object ret = - mBypassCache ? - null : - sCachedExpressionStrings.get(pExpressionString); + Object ret = cache.get(pExpressionString); + if (ret != null) { + return ret; + } - if (ret == null) { - // Parse the expression + ret = parseExpressionUncached(pExpressionString); + cache.put(pExpressionString, ret); + return ret; + } + + /** + * Parse an expression string bypassing the cache. + * + * This allows expressions to be validated at translation time without polluting the cache. + * + * @param pExpressionString the text to parse + * @return the parse result + * @throws ELException if there was a problem parsing the expression text + */ + public Object parseExpressionUncached(String pExpressionString) throws ELException { + try { Reader r = new StringReader(pExpressionString); ELParser parser = new ELParser(r); - try { - ret = parser.ExpressionString(); - if (!mBypassCache) { - sCachedExpressionStrings.put(pExpressionString, ret); - } - } - catch (ParseException exc) { - throw new ELException - (formatParseException(pExpressionString, - exc)); - } - catch (TokenMgrError exc) { - // Note - this should never be reached, since the parser is - // constructed to tokenize any input (illegal inputs get - // parsed to or - // - throw new ELException(exc.getMessage()); - } + return parser.ExpressionString(); + } catch (ParseException exc) { + throw new ELException(formatParseException(pExpressionString, exc)); + } catch (TokenMgrError exc) { + // Note - this should never be reached, since the parser is + // constructed to tokenize any input (illegal inputs get + // parsed to or + // + throw new ELException(exc.getMessage()); } - return ret; } //------------------------------------- @@ -358,31 +364,35 @@ * Creates LRU map of expression strings. If context parameter * specifying cache size is present use that as the maximum size * of the LRU map otherwise use default. + * + * TODO: Using the context parameter means the cache is sized based on the configuration + * of the first web application that calls this. This might be a problem if this jar is + * installed in the application server's classpath rather than the application's. */ - private synchronized void createExpressionStringMap() { - if (sCachedExpressionStrings != null) { - return; - } + private static synchronized Map getOrCreateExpressionStringMap(PageContext pageContext) { + if (sCachedExpressionStrings == null) { - final int maxSize; - if ((pageContext != null) && (pageContext.getServletContext() != null)) { - String value = pageContext.getServletContext().getInitParameter(EXPR_CACHE_PARAM); - if (value != null) { - maxSize = Integer.valueOf(value); + final int maxSize; + if ((pageContext != null) && (pageContext.getServletContext() != null)) { + String value = pageContext.getServletContext().getInitParameter(EXPR_CACHE_PARAM); + if (value != null) { + maxSize = Integer.valueOf(value); + } else { + maxSize = MAX_SIZE; + } } else { maxSize = MAX_SIZE; } - } else { - maxSize = MAX_SIZE; + + // fall through if it couldn't find the parameter + sCachedExpressionStrings = Collections.synchronizedMap(new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > maxSize; + } + }); } - - // fall through if it couldn't find the parameter - sCachedExpressionStrings = Collections.synchronizedMap(new LinkedHashMap() { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > maxSize; - } - }); + return sCachedExpressionStrings; } //------------------------------------- Index: jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/Evaluator.java =================================================================== --- jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/Evaluator.java (revision 1828438) +++ jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/Evaluator.java (working copy) @@ -67,9 +67,7 @@ public String validate(String pAttributeName, String pAttributeValue) { try { - sEvaluator.setBypassCache(true); - sEvaluator.parseExpressionString(pAttributeValue); - sEvaluator.setBypassCache(false); + sEvaluator.parseExpressionUncached(pAttributeValue); return null; } catch (ELException exc) {