Author: markt
Date: Wed Jul 11 13:02:46 2018
New Revision: 1835639

URL: http://svn.apache.org/viewvc?rev=1835639&view=rev
Log:
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62453
Add a performance optimisation for using expressions in tags that depend on 
uninitialised tag attributes with implied scope.
Generally, using an explicit scope with tag attributes in EL is the best way to 
avoid various potential performance issues.

Added:
    tomcat/trunk/test/javax/el/TestImportHandlerStandardPackages.java   (with 
props)
    tomcat/trunk/test/javax/el/TesterImportHandlerPerformance.java   (with 
props)
    tomcat/trunk/test/javax/servlet/jsp/el/TestScopedAttributeELResolver.java   
(with props)
    tomcat/trunk/test/webapp/WEB-INF/tags/bug62453.tag
    tomcat/trunk/test/webapp/bug6nnnn/bug62453.jsp   (with props)
Modified:
    tomcat/trunk/java/javax/el/ImportHandler.java
    tomcat/trunk/webapps/docs/changelog.xml

Modified: tomcat/trunk/java/javax/el/ImportHandler.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/javax/el/ImportHandler.java?rev=1835639&r1=1835638&r2=1835639&view=diff
==============================================================================
--- tomcat/trunk/java/javax/el/ImportHandler.java (original)
+++ tomcat/trunk/java/javax/el/ImportHandler.java Wed Jul 11 13:02:46 2018
@@ -19,9 +19,11 @@ package javax.el;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -29,7 +31,242 @@ import java.util.concurrent.ConcurrentHa
  */
 public class ImportHandler {
 
-    private List<String> packageNames = new ArrayList<>();
+    private static final Map<String,Set<String>> standardPackages = new 
HashMap<>();
+
+    static {
+        // Servlet 4.0
+        Set<String> servletClassNames = new HashSet<>();
+        // Interfaces
+        servletClassNames.add("AsyncContext");
+        servletClassNames.add("AsyncListener");
+        servletClassNames.add("Filter");
+        servletClassNames.add("FilterChain");
+        servletClassNames.add("FilterConfig");
+        servletClassNames.add("FilterRegistration");
+        servletClassNames.add("FilterRegistration.Dynamic");
+        servletClassNames.add("ReadListener");
+        servletClassNames.add("Registration");
+        servletClassNames.add("Registration.Dynamic");
+        servletClassNames.add("RequestDispatcher");
+        servletClassNames.add("Servlet");
+        servletClassNames.add("ServletConfig");
+        servletClassNames.add("ServletContainerInitializer");
+        servletClassNames.add("ServletContext");
+        servletClassNames.add("ServletContextAttributeListener");
+        servletClassNames.add("ServletContextListener");
+        servletClassNames.add("ServletRegistration");
+        servletClassNames.add("ServletRegistration.Dynamic");
+        servletClassNames.add("ServletRequest");
+        servletClassNames.add("ServletRequestAttributeListener");
+        servletClassNames.add("ServletRequestListener");
+        servletClassNames.add("ServletResponse");
+        servletClassNames.add("SessionCookieConfig");
+        servletClassNames.add("SingleThreadModel");
+        servletClassNames.add("WriteListener");
+        // Classes
+        servletClassNames.add("AsyncEvent");
+        servletClassNames.add("GenericFilter");
+        servletClassNames.add("GenericServlet");
+        servletClassNames.add("HttpConstraintElement");
+        servletClassNames.add("HttpMethodConstraintElement");
+        servletClassNames.add("MultipartConfigElement");
+        servletClassNames.add("ServletContextAttributeEvent");
+        servletClassNames.add("ServletContextEvent");
+        servletClassNames.add("ServletInputStream");
+        servletClassNames.add("ServletOutputStream");
+        servletClassNames.add("ServletRequestAttributeEvent");
+        servletClassNames.add("ServletRequestEvent");
+        servletClassNames.add("ServletRequestWrapper");
+        servletClassNames.add("ServletResponseWrapper");
+        servletClassNames.add("ServletSecurityElement");
+        // Enums
+        servletClassNames.add("DispatcherType");
+        servletClassNames.add("SessionTrackingMode");
+        // Exceptions
+        servletClassNames.add("ServletException");
+        servletClassNames.add("UnavailableException");
+        standardPackages.put("javax.servlet", servletClassNames);
+
+        // Servlet 4.0
+        Set<String> servletHttpClassNames = new HashSet<>();
+        // Interfaces
+        servletHttpClassNames.add("HttpServletMapping");
+        servletHttpClassNames.add("HttpServletRequest");
+        servletHttpClassNames.add("HttpServletResponse");
+        servletHttpClassNames.add("HttpSession");
+        servletHttpClassNames.add("HttpSessionActivationListener");
+        servletHttpClassNames.add("HttpSessionAttributeListener");
+        servletHttpClassNames.add("HttpSessionBindingListener");
+        servletHttpClassNames.add("HttpSessionContext");
+        servletHttpClassNames.add("HttpSessionIdListener");
+        servletHttpClassNames.add("HttpSessionListener");
+        servletHttpClassNames.add("HttpUpgradeHandler");
+        servletHttpClassNames.add("Part");
+        servletHttpClassNames.add("PushBuilder");
+        servletHttpClassNames.add("WebConnection");
+        // Classes
+        servletHttpClassNames.add("Cookie");
+        servletHttpClassNames.add("HttpFilter");
+        servletHttpClassNames.add("HttpServlet");
+        servletHttpClassNames.add("HttpServletRequestWrapper");
+        servletHttpClassNames.add("HttpServletResponseWrapper");
+        servletHttpClassNames.add("HttpSessionBindingEvent");
+        servletHttpClassNames.add("HttpSessionEvent");
+        servletHttpClassNames.add("HttpUtils");
+        // Enums
+        servletHttpClassNames.add("MappingMatch");
+        standardPackages.put("javax.servlet.http", servletHttpClassNames);
+
+        // JSP 2.3
+        Set<String> servletJspClassNames = new HashSet<>();
+        //Interfaces
+        servletJspClassNames.add("HttpJspPage");
+        servletJspClassNames.add("JspApplicationContext");
+        servletJspClassNames.add("JspPage");
+        // Classes
+        servletJspClassNames.add("ErrorData");
+        servletJspClassNames.add("JspContext");
+        servletJspClassNames.add("JspEngineInfo");
+        servletJspClassNames.add("JspFactory");
+        servletJspClassNames.add("JspWriter");
+        servletJspClassNames.add("PageContext");
+        servletJspClassNames.add("Exceptions");
+        servletJspClassNames.add("JspException");
+        servletJspClassNames.add("JspTagException");
+        servletJspClassNames.add("SkipPageException");
+        standardPackages.put("javax.servlet.jsp", servletJspClassNames);
+
+        Set<String> javaLangClassNames = new HashSet<>();
+        // Taken from Java 11 EA18 Javadoc
+        // Interfaces
+        javaLangClassNames.add("Appendable");
+        javaLangClassNames.add("AutoCloseable");
+        javaLangClassNames.add("CharSequence");
+        javaLangClassNames.add("Cloneable");
+        javaLangClassNames.add("Comparable");
+        javaLangClassNames.add("Iterable");
+        javaLangClassNames.add("ProcessHandle");
+        javaLangClassNames.add("ProcessHandle.Info");
+        javaLangClassNames.add("Readable");
+        javaLangClassNames.add("Runnable");
+        javaLangClassNames.add("StackWalker.StackFrame");
+        javaLangClassNames.add("System.Logger");
+        javaLangClassNames.add("Thread.UncaughtExceptionHandler");
+        //Classes
+        javaLangClassNames.add("Boolean");
+        javaLangClassNames.add("Byte");
+        javaLangClassNames.add("Character");
+        javaLangClassNames.add("Character.Subset");
+        javaLangClassNames.add("Character.UnicodeBlock");
+        javaLangClassNames.add("Class");
+        javaLangClassNames.add("ClassLoader");
+        javaLangClassNames.add("ClassValue");
+        javaLangClassNames.add("Compiler");
+        javaLangClassNames.add("Double");
+        javaLangClassNames.add("Enum");
+        javaLangClassNames.add("Float");
+        javaLangClassNames.add("InheritableThreadLocal");
+        javaLangClassNames.add("Integer");
+        javaLangClassNames.add("Long");
+        javaLangClassNames.add("Math");
+        javaLangClassNames.add("Module");
+        javaLangClassNames.add("ModuleLayer");
+        javaLangClassNames.add("ModuleLayer.Controller");
+        javaLangClassNames.add("Number");
+        javaLangClassNames.add("Object");
+        javaLangClassNames.add("Package");
+        javaLangClassNames.add("Process");
+        javaLangClassNames.add("ProcessBuilder");
+        javaLangClassNames.add("ProcessBuilder.Redirect");
+        javaLangClassNames.add("Runtime");
+        javaLangClassNames.add("Runtime.Version");
+        javaLangClassNames.add("RuntimePermission");
+        javaLangClassNames.add("SecurityManager");
+        javaLangClassNames.add("Short");
+        javaLangClassNames.add("StackTraceElement");
+        javaLangClassNames.add("StackWalker");
+        javaLangClassNames.add("StrictMath");
+        javaLangClassNames.add("String");
+        javaLangClassNames.add("StringBuffer");
+        javaLangClassNames.add("StringBuilder");
+        javaLangClassNames.add("System");
+        javaLangClassNames.add("System.LoggerFinder");
+        javaLangClassNames.add("Thread");
+        javaLangClassNames.add("ThreadGroup");
+        javaLangClassNames.add("ThreadLocal");
+        javaLangClassNames.add("Throwable");
+        javaLangClassNames.add("Void");
+        //Enums
+        javaLangClassNames.add("Character.UnicodeScript");
+        javaLangClassNames.add("ProcessBuilder.Redirect.Type");
+        javaLangClassNames.add("StackWalker.Option");
+        javaLangClassNames.add("System.Logger.Level");
+        javaLangClassNames.add("Thread.State");
+        //Exceptions
+        javaLangClassNames.add("ArithmeticException");
+        javaLangClassNames.add("ArrayIndexOutOfBoundsException");
+        javaLangClassNames.add("ArrayStoreException");
+        javaLangClassNames.add("ClassCastException");
+        javaLangClassNames.add("ClassNotFoundException");
+        javaLangClassNames.add("CloneNotSupportedException");
+        javaLangClassNames.add("EnumConstantNotPresentException");
+        javaLangClassNames.add("Exception");
+        javaLangClassNames.add("IllegalAccessException");
+        javaLangClassNames.add("IllegalArgumentException");
+        javaLangClassNames.add("IllegalCallerException");
+        javaLangClassNames.add("IllegalMonitorStateException");
+        javaLangClassNames.add("IllegalStateException");
+        javaLangClassNames.add("IllegalThreadStateException");
+        javaLangClassNames.add("IndexOutOfBoundsException");
+        javaLangClassNames.add("InstantiationException");
+        javaLangClassNames.add("InterruptedException");
+        javaLangClassNames.add("LayerInstantiationException");
+        javaLangClassNames.add("NegativeArraySizeException");
+        javaLangClassNames.add("NoSuchFieldException");
+        javaLangClassNames.add("NoSuchMethodException");
+        javaLangClassNames.add("NullPointerException");
+        javaLangClassNames.add("NumberFormatException");
+        javaLangClassNames.add("ReflectiveOperationException");
+        javaLangClassNames.add("RuntimeException");
+        javaLangClassNames.add("SecurityException");
+        javaLangClassNames.add("StringIndexOutOfBoundsException");
+        javaLangClassNames.add("TypeNotPresentException");
+        javaLangClassNames.add("UnsupportedOperationException");
+        //Errors
+        javaLangClassNames.add("AbstractMethodError");
+        javaLangClassNames.add("AssertionError");
+        javaLangClassNames.add("BootstrapMethodError");
+        javaLangClassNames.add("ClassCircularityError");
+        javaLangClassNames.add("ClassFormatError");
+        javaLangClassNames.add("Error");
+        javaLangClassNames.add("ExceptionInInitializerError");
+        javaLangClassNames.add("IllegalAccessError");
+        javaLangClassNames.add("IncompatibleClassChangeError");
+        javaLangClassNames.add("InstantiationError");
+        javaLangClassNames.add("InternalError");
+        javaLangClassNames.add("LinkageError");
+        javaLangClassNames.add("NoClassDefFoundError");
+        javaLangClassNames.add("NoSuchFieldError");
+        javaLangClassNames.add("NoSuchMethodError");
+        javaLangClassNames.add("OutOfMemoryError");
+        javaLangClassNames.add("StackOverflowError");
+        javaLangClassNames.add("ThreadDeath");
+        javaLangClassNames.add("UnknownError");
+        javaLangClassNames.add("UnsatisfiedLinkError");
+        javaLangClassNames.add("UnsupportedClassVersionError");
+        javaLangClassNames.add("VerifyError");
+        javaLangClassNames.add("VirtualMachineError");
+        //Annotation Types
+        javaLangClassNames.add("Deprecated");
+        javaLangClassNames.add("FunctionalInterface");
+        javaLangClassNames.add("Override");
+        javaLangClassNames.add("SafeVarargs");
+        javaLangClassNames.add("SuppressWarnings");
+        standardPackages.put("java.lang", javaLangClassNames);
+
+    }
+
+    private Map<String,Set<String>> packageNames = new ConcurrentHashMap<>();
     private Map<String,String> classNames = new ConcurrentHashMap<>();
     private Map<String,Class<?>> clazzes = new ConcurrentHashMap<>();
     private Map<String,Class<?>> statics = new ConcurrentHashMap<>();
@@ -127,7 +364,12 @@ public class ImportHandler {
         // a) for sake of performance when used in JSPs (BZ 57142),
         // b) java.lang.Package.getPackage(name) is not reliable (BZ 57574),
         // c) such check is not required by specification.
-        packageNames.add(name);
+        Set<String> preloaded = standardPackages.get(name);
+        if (preloaded == null) {
+            packageNames.put(name, Collections.emptySet());
+        } else {
+            packageNames.put(name, preloaded);
+        }
     }
 
 
@@ -159,8 +401,17 @@ public class ImportHandler {
 
         // Search the package imports - note there may be multiple matches
         // (which correctly triggers an error)
-        for (String p : packageNames) {
-            className = p + '.' + name;
+        for (Map.Entry<String,Set<String>> entry : packageNames.entrySet()) {
+            if (!entry.getValue().isEmpty()) {
+                // Standard package where we know all the class names
+                if (!entry.getValue().contains(name)) {
+                    // Requested name isn't in the list so it isn't in this
+                    // package so move on to next package. This allows the
+                    // class loader look-up to be skipped.
+                    continue;
+                }
+            }
+            className = entry.getKey() + '.' + name;
             Class<?> clazz = findClass(className, false);
             if (clazz != null) {
                 if (result != null) {

Added: tomcat/trunk/test/javax/el/TestImportHandlerStandardPackages.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/javax/el/TestImportHandlerStandardPackages.java?rev=1835639&view=auto
==============================================================================
--- tomcat/trunk/test/javax/el/TestImportHandlerStandardPackages.java (added)
+++ tomcat/trunk/test/javax/el/TestImportHandlerStandardPackages.java Wed Jul 
11 13:02:46 2018
@@ -0,0 +1,155 @@
+/*
+ * 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 javax.el;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.tomcat.util.compat.JreCompat;
+
+public class TestImportHandlerStandardPackages {
+
+    @Test
+    public void testClassListsAreComplete() throws Exception {
+        // Use reflection to get hold of the internal Map
+        Class<?> clazz = ImportHandler.class;
+        Field f = clazz.getDeclaredField("standardPackages");
+        f.setAccessible(true);
+        Object obj = f.get(null);
+
+        Assert.assertTrue("Not Map", obj instanceof Map);
+
+        @SuppressWarnings("unchecked")
+        Map<String,Set<String>> standardPackageName = (Map<String, 
Set<String>>) obj;
+
+        for (Map.Entry<String,Set<String>> entry : 
standardPackageName.entrySet()) {
+            checkPackageClassList(entry.getKey(), entry.getValue());
+        }
+    }
+
+
+    private void checkPackageClassList(String packageName, Set<String> 
classNames) throws Exception {
+
+        if ("java.lang".equals(packageName)) {
+            // The code below is designed to run on Java 9 so skip this check
+            // if running on Java 8. The test has previously been run with Java
+            // 9 (and later) so it is not necessary that this is executed on
+            // every test run. The intention is that it will catch new classes
+            // when the tests are run on a newer JRE.
+            // The latest version of the JRE where this test is known to pass 
is
+            // Java 11, Early Access 21
+            if (!JreCompat.isJre9Available()) {
+                return;
+            }
+            getJavaBaseClasses().filter(c -> (c.startsWith("java/lang/")))
+                    .filter(c -> c.lastIndexOf('/') == 9)             // 
Exclude sub-packages
+                    .filter(c -> c.endsWith(".class"))                // 
Exclude non-class resources
+                    .map(c -> c.substring(10, c.length() - 6))        // 
Extract class name
+                    .map(c-> {
+                        try {
+                            return Class.forName("java.lang." + c);   // Get 
the class object
+                        } catch (ClassNotFoundException e) {
+                            throw new RuntimeException();
+                        }
+                    })
+                    .filter(c -> Modifier.isPublic(c.getModifiers())) // 
Exclude non-public classes
+                    .map(c -> c.getName().substring(10))              // Back 
to the class name
+                    .map(c -> c.replace('$',  '.'))
+                    .filter(c -> !classNames.contains(c))             // Skip 
classes already listed
+                    .filter(c -> !c.startsWith("FdLibm."))            // Skip 
public inner class
+                    .filter(c -> !c.startsWith("LiveStackFrame."))    // Skip 
public inner class
+                    .filter(c -> !c.startsWith("WeakPairMap."))       // Skip 
public inner class
+                    .forEach(c -> Assert.fail("java.lang." + c));     // 
Should have in list
+        } else {
+            // When this test runs, the class loader will be loading resources
+            // from a directory for each of these packages.
+            ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+            String path = packageName.replace('.', '/');
+            Enumeration<URL> resources = cl.getResources(path);
+            while (resources.hasMoreElements()) {
+                URL resource = resources.nextElement();
+                File dir = new File(resource.toURI());
+                String[] files = dir.list();
+                for (String file : files) {
+                    if (!file.endsWith(".class")) {
+                        // Skip non-class resoucres
+                        continue;
+                    }
+                    if (file.startsWith("Test")) {
+                        // Skip test resources
+                        continue;
+                    }
+                    if (file.matches(".*\\$[0-9]?\\.class")) {
+                        // Skip anonymous inner classes
+                        continue;
+                    }
+                    String name = file.substring(0, file.length() - 6);
+                    name = name.replace('$', '.');
+                    if (classNames.contains(name)) {
+                        // Skip classes already known
+                        continue;
+                    }
+                    File f = new File (dir, file);
+                    if (!f.isFile()) {
+                        // Skip directories
+                        continue;
+                    }
+                    Class<?> clazz = Class.forName(packageName + "." + name);
+                    if (!Modifier.isPublic(clazz.getModifiers())) {
+                        // Skip non-public classes
+                        continue;
+                    }
+
+                    // There should be nothing left unless we missed something
+                    Assert.fail(packageName + "." + name);
+                }
+            }
+        }
+    }
+
+
+    private static Stream<String> getJavaBaseClasses() throws Exception {
+        // While this code is only used on Java 9 and later, it needs to 
compile
+        // with Java 8 so use reflection for now.
+        Class<?> clazzModuleFinder = 
Class.forName("java.lang.module.ModuleFinder");
+        Class<?> clazzModuleReference = 
Class.forName("java.lang.module.ModuleReference");
+        Class<?> clazzModuleReader = 
Class.forName("java.lang.module.ModuleReader");
+
+        // Returns ModuleFinder
+        Object mf = clazzModuleFinder.getMethod("ofSystem").invoke(null);
+        // Returns ModuleReference
+        Object mRef = ((Optional<?>) clazzModuleFinder.getMethod(
+                "find", String.class).invoke(mf, "java.base")).get();
+        // Returns ModuleReader
+        Object mr = clazzModuleReference.getMethod("open").invoke(mRef);
+        // Returns the contents of the module
+        @SuppressWarnings("unchecked")
+        Stream<String> result = (Stream<String>) 
clazzModuleReader.getMethod("list").invoke(mr);
+        return result;
+    }
+}

Propchange: tomcat/trunk/test/javax/el/TestImportHandlerStandardPackages.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/test/javax/el/TesterImportHandlerPerformance.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/javax/el/TesterImportHandlerPerformance.java?rev=1835639&view=auto
==============================================================================
--- tomcat/trunk/test/javax/el/TesterImportHandlerPerformance.java (added)
+++ tomcat/trunk/test/javax/el/TesterImportHandlerPerformance.java Wed Jul 11 
13:02:46 2018
@@ -0,0 +1,49 @@
+/*
+ * 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 javax.el;
+
+import org.junit.Test;
+
+public class TesterImportHandlerPerformance {
+
+    /*
+     * This test is looking at the cost of looking up a class when the standard
+     * JSP package imports are present:
+     * - java.lang
+     * - javax.servlet
+     * - javax.servlet.http
+     * - javax.servlet.jsp
+     *
+     * Before optimisation, this test took ~4.6s on markt's desktop
+     * After optimisation, this test took ~0.05s on markt's desktop
+     */
+    @Test
+    public void testBug62453() throws Exception {
+        long totalTime = 0;
+        for (int i = 0; i < 100000; i++) {
+            ImportHandler ih = new ImportHandler();
+            ih.importPackage("javax.servlet");
+            ih.importPackage("javax.servlet.http");
+            ih.importPackage("javax.servlet.jsp");
+            long start = System.nanoTime();
+            ih.resolveClass("unknown");
+            long end = System.nanoTime();
+            totalTime += (end -start);
+        }
+        System.out.println("Time taken: " + totalTime + "ns");
+    }
+}

Propchange: tomcat/trunk/test/javax/el/TesterImportHandlerPerformance.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/test/javax/servlet/jsp/el/TestScopedAttributeELResolver.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/javax/servlet/jsp/el/TestScopedAttributeELResolver.java?rev=1835639&view=auto
==============================================================================
--- tomcat/trunk/test/javax/servlet/jsp/el/TestScopedAttributeELResolver.java 
(added)
+++ tomcat/trunk/test/javax/servlet/jsp/el/TestScopedAttributeELResolver.java 
Wed Jul 11 13:02:46 2018
@@ -0,0 +1,39 @@
+/*
+ * 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 javax.servlet.jsp.el;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TestScopedAttributeELResolver extends TomcatBaseTest {
+
+    @Test
+    public void testBug49196() throws Exception {
+        getTomcatInstanceTestWebapp(true, true);
+
+        ByteChunk res = getUrl("http://localhost:"; + getPort() +
+                "/test/bug6nnnn/bug62453.jsp");
+
+        String result = res.toString();
+        Assert.assertTrue(result, result.contains("<div>foo:  OK</div>"));
+        Assert.assertTrue(result, result.contains("<div>bar:  </div>"));
+        Assert.assertTrue(result, result.contains("<div>baz:  </div>"));
+    }
+}

Propchange: 
tomcat/trunk/test/javax/servlet/jsp/el/TestScopedAttributeELResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/test/webapp/WEB-INF/tags/bug62453.tag
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/webapp/WEB-INF/tags/bug62453.tag?rev=1835639&view=auto
==============================================================================
--- tomcat/trunk/test/webapp/WEB-INF/tags/bug62453.tag (added)
+++ tomcat/trunk/test/webapp/WEB-INF/tags/bug62453.tag Wed Jul 11 13:02:46 2018
@@ -0,0 +1,26 @@
+<%--
+ 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.
+--%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"; %>
+<%@ attribute name="foo" required="true" %>
+<%@ attribute name="bar" %>
+<%@ attribute name="baz" %>
+
+<div>
+  <div>foo:  ${foo.toString()}</div>
+  <div>bar:  ${bar.toString()}</div>
+  <div>baz:  ${baz.toString()}</div>
+</div>

Added: tomcat/trunk/test/webapp/bug6nnnn/bug62453.jsp
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/webapp/bug6nnnn/bug62453.jsp?rev=1835639&view=auto
==============================================================================
--- tomcat/trunk/test/webapp/bug6nnnn/bug62453.jsp (added)
+++ tomcat/trunk/test/webapp/bug6nnnn/bug62453.jsp Wed Jul 11 13:02:46 2018
@@ -0,0 +1,24 @@
+<%--
+ 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.
+--%>
+<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
+
+<html>
+  <body>
+    <h2>Example of Uninitialized Tag Attributes</h2>
+    <tags:bug62453 foo="OK"/>
+  </body>
+</html>

Propchange: tomcat/trunk/test/webapp/bug6nnnn/bug62453.jsp
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1835639&r1=1835638&r2=1835639&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Wed Jul 11 13:02:46 2018
@@ -118,6 +118,12 @@
         By default, one thread will be used per core. Based on a patch by Dan
         Fabulich. (markt)
       </add>
+      <add>
+        <bug>62453</bug>: Add a performance optimisation for using expressions
+        in tags that depend on uninitialised tag attributes with implied scope.
+        Generally, using an explicit scope with tag attributes in EL is the 
best
+        way to avoid various potential performance issues. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Other">



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

Reply via email to