Author: jboynes Date: Thu Apr 5 17:48:41 2018 New Revision: 1828458 URL: http://svn.apache.org/viewvc?rev=1828458&view=rev Log: 57434 - Fix race condition in ELEvaluator for 1.0 expressions
Modified: tomcat/taglibs/standard/trunk/CHANGES.txt tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/ELEvaluator.java tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/Evaluator.java Modified: tomcat/taglibs/standard/trunk/CHANGES.txt URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/CHANGES.txt?rev=1828458&r1=1828457&r2=1828458&view=diff ============================================================================== --- tomcat/taglibs/standard/trunk/CHANGES.txt (original) +++ tomcat/taglibs/standard/trunk/CHANGES.txt Thu Apr 5 17:48:41 2018 @@ -1,5 +1,6 @@ Changes in 1.2.6 release 60950 JSTL TransformSupport XSL import not finding relative path +57434 Race condition in EL1.0 validation Changes in 1.2.5 release - Set version identifiers correctly, no other changes. Modified: tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/ELEvaluator.java URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/ELEvaluator.java?rev=1828458&r1=1828457&r2=1828458&view=diff ============================================================================== --- tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/ELEvaluator.java (original) +++ tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/ELEvaluator.java Thu Apr 5 17:48:41 2018 @@ -102,13 +102,13 @@ public class ELEvaluator { * growth. * <p>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,11 +123,12 @@ public class ELEvaluator { /** * Flag if the cache should be bypassed * */ - boolean mBypassCache; + private volatile boolean mBypassCache; /** * The PageContext * */ + // TODO: Find a better way to override the expression cache size that does not need this field. PageContext pageContext; @@ -256,40 +257,46 @@ public class ELEvaluator { 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 <BADLY_ESCAPED_STRING_LITERAL> or - // <ILLEGAL_CHARACTER> - 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 <BADLY_ESCAPED_STRING_LITERAL> or + // <ILLEGAL_CHARACTER> + throw new ELException(exc.getMessage()); } - return ret; } //------------------------------------- @@ -358,31 +365,35 @@ public class ELEvaluator { * 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. - */ - private synchronized void createExpressionStringMap() { - if (sCachedExpressionStrings != null) { - return; - } - - final int maxSize; - if ((pageContext != null) && (pageContext.getServletContext() != null)) { - String value = pageContext.getServletContext().getInitParameter(EXPR_CACHE_PARAM); - if (value != null) { - maxSize = Integer.valueOf(value); + * + * 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 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); + } 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; } //------------------------------------- Modified: tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/Evaluator.java URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/Evaluator.java?rev=1828458&r1=1828457&r2=1828458&view=diff ============================================================================== --- tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/Evaluator.java (original) +++ tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/lang/jstl/Evaluator.java Thu Apr 5 17:48:41 2018 @@ -67,9 +67,7 @@ public class Evaluator 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) { --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org