Author: markt
Date: Tue Nov 20 16:54:28 2018
New Revision: 1847028

URL: http://svn.apache.org/viewvc?rev=1847028&view=rev
Log:
Complete the fix for https://bz.apache.org/bugzilla/show_bug.cgi?id=53737
Extend JspC to include support for resource JARs 

Modified:
    tomcat/trunk/java/org/apache/jasper/JspC.java
    tomcat/trunk/java/org/apache/jasper/servlet/JspCServletContext.java
    tomcat/trunk/test/org/apache/jasper/servlet/TestJspCServletContext.java
    tomcat/trunk/webapps/docs/changelog.xml

Modified: tomcat/trunk/java/org/apache/jasper/JspC.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/JspC.java?rev=1847028&r1=1847027&r2=1847028&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/JspC.java (original)
+++ tomcat/trunk/java/org/apache/jasper/JspC.java Tue Nov 20 16:54:28 2018
@@ -37,7 +37,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.Stack;
 import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.concurrent.Callable;
@@ -1398,41 +1397,48 @@ public class JspC extends Task implement
      * Locate all jsp files in the webapp. Used if no explicit
      * jsps are specified.
      * @param base Base path
+     *
+     * @deprecated This will be removed in Tomcat 10. Use {@link #scanFiles()}
      */
-    public void scanFiles( File base ) {
-        Stack<String> dirs = new Stack<>();
-        dirs.push(base.toString());
+    @Deprecated
+    public void scanFiles(File base) {
+        scanFiles();
+    }
 
+
+    /**
+     * Locate all jsp files in the webapp. Used if no explicit jsps are
+     * specified. Scan is performed via the ServletContext and will include any
+     * JSPs located in resource JARs.
+     */
+    public void scanFiles() {
         // Make sure default extensions are always included
         if ((getExtensions() == null) || (getExtensions().size() < 2)) {
             addExtension("jsp");
             addExtension("jspx");
         }
 
-        while (!dirs.isEmpty()) {
-            String s = dirs.pop();
-            File f = new File(s);
-            if (f.exists() && f.isDirectory()) {
-                String[] files = f.list();
-                String ext;
-                for (int i = 0; (files != null) && i < files.length; i++) {
-                    File f2 = new File(s, files[i]);
-                    if (f2.isDirectory()) {
-                        dirs.push(f2.getPath());
-                    } else {
-                        String path = f2.getPath();
-                        String uri = path.substring(uriRoot.length());
-                        ext = files[i].substring(files[i].lastIndexOf('.') +1);
-                        if (getExtensions().contains(ext) ||
-                            jspConfig.isJspPage(uri)) {
-                            pages.add(path);
-                        }
-                    }
+        scanFilesInternal("/");
+    }
+
+
+    private void scanFilesInternal(String input) {
+        Set<String> paths = context.getResourcePaths(input);
+        for (String path : paths) {
+            if (path.endsWith("/")) {
+                scanFilesInternal(input.substring(0, input.length() -1) + 
path);
+            } else if (jspConfig.isJspPage(input + path)) {
+                pages.add(path);
+            } else {
+                String ext = path.substring(path.lastIndexOf('.') + 1);
+                if (extensions.contains(ext)) {
+                    pages.add(input + path.substring(1));
                 }
             }
         }
     }
 
+
     /**
      * Executes the compilation.
      */
@@ -1474,20 +1480,15 @@ public class JspC extends Task implement
 
             // No explicit pages, we'll process all .jsp in the webapp
             if (pages.size() == 0) {
-                scanFiles(uriRootF);
-            }
-
-            initWebXml();
-
-            int errorCount = 0;
-            long start = System.currentTimeMillis();
-
-            ExecutorService threadPool = 
Executors.newFixedThreadPool(threadCount);
-            ExecutorCompletionService<Void> service = new 
ExecutorCompletionService<>(threadPool);
-            try {
-                int pageCount = pages.size();
+                scanFiles();
+            } else {
+                // Ensure pages are all relative to the uriRoot.
+                // Those that are not will trigger an error later. The error
+                // could be detected earlier but isn't to support the use case
+                // when failFast is not used.
+                for (int i = 0; i < pages.size(); i++) {
+                    String nextjsp = pages.get(i);
 
-                for (String nextjsp : pages) {
                     File fjsp = new File(nextjsp);
                     if (!fjsp.isAbsolute()) {
                         fjsp = new File(uriRootF, nextjsp);
@@ -1506,6 +1507,20 @@ public class JspC extends Task implement
                     if (nextjsp.startsWith("." + File.separatorChar)) {
                         nextjsp = nextjsp.substring(2);
                     }
+                    pages.set(i, nextjsp);
+                }
+            }
+
+            initWebXml();
+
+            int errorCount = 0;
+            long start = System.currentTimeMillis();
+
+            ExecutorService threadPool = 
Executors.newFixedThreadPool(threadCount);
+            ExecutorCompletionService<Void> service = new 
ExecutorCompletionService<>(threadPool);
+            try {
+                int pageCount = pages.size();
+                for (String nextjsp : pages) {
                     service.submit(new ProcessFile(nextjsp));
                 }
                 JasperException reportableError = null;

Modified: tomcat/trunk/java/org/apache/jasper/servlet/JspCServletContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/servlet/JspCServletContext.java?rev=1847028&r1=1847027&r2=1847028&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/jasper/servlet/JspCServletContext.java 
(original)
+++ tomcat/trunk/java/org/apache/jasper/servlet/JspCServletContext.java Tue Nov 
20 16:54:28 2018
@@ -22,12 +22,16 @@ import java.io.InputStream;
 import java.io.PrintWriter;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.EventListener;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.Vector;
@@ -49,10 +53,12 @@ import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
 import org.apache.jasper.compiler.Localizer;
 import org.apache.jasper.runtime.ExceptionUtils;
+import org.apache.tomcat.Jar;
 import org.apache.tomcat.JarScanType;
 import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
 import org.apache.tomcat.util.descriptor.web.WebXml;
 import org.apache.tomcat.util.descriptor.web.WebXmlParser;
+import org.apache.tomcat.util.scan.JarFactory;
 import org.apache.tomcat.util.scan.StandardJarScanFilter;
 import org.apache.tomcat.util.scan.StandardJarScanner;
 
@@ -100,8 +106,12 @@ public class JspCServletContext implemen
     private WebXml webXml;
 
 
+    private List<URL> resourceJARs;
+
+
     private JspConfigDescriptor jspConfigDescriptor;
 
+
     /**
      * Web application class loader.
      */
@@ -167,11 +177,45 @@ public class JspCServletContext implemen
         Map<String, WebXml> fragments = scanForFragments(webXmlParser);
         Set<WebXml> orderedFragments = WebXml.orderWebFragments(webXml, 
fragments, this);
 
+        // Find resource JARs
+        this.resourceJARs = scanForResourceJARs(orderedFragments, 
fragments.values());
+
         // JspC is not affected by annotations so skip that processing, 
proceed to merge
         webXml.merge(orderedFragments);
         return webXml;
     }
 
+
+    private List<URL> scanForResourceJARs(Set<WebXml> orderedFragments, 
Collection<WebXml> fragments)
+            throws JasperException {
+        List<URL> resourceJars = new ArrayList<>();
+        // Build list of potential resource JARs. Use same ordering as 
ContextConfig
+        Set<WebXml> resourceFragments = new LinkedHashSet<>();
+        for (WebXml fragment : orderedFragments) {
+            resourceFragments.add(fragment);
+        }
+        for (WebXml fragment : fragments) {
+            if (!resourceFragments.contains(fragment)) {
+                resourceFragments.add(fragment);
+            }
+        }
+
+        for (WebXml resourceFragment : resourceFragments) {
+            try (Jar jar = JarFactory.newInstance(resourceFragment.getURL())) {
+                if (jar.exists("META-INF/resources/")) {
+                    // This is a resource JAR
+                    resourceJars.add(resourceFragment.getURL());
+                }
+                jar.close();
+            } catch (IOException ioe) {
+                throw new JasperException(ioe);
+            }
+        }
+
+        return resourceJars;
+    }
+
+
     private Map<String, WebXml> scanForFragments(WebXmlParser webXmlParser) 
throws JasperException {
         StandardJarScanner scanner = new StandardJarScanner();
         // TODO - enabling this means initializing the classloader first in 
JspC
@@ -337,15 +381,34 @@ public class JspCServletContext implemen
     @Override
     public URL getResource(String path) throws MalformedURLException {
 
-        if (!path.startsWith("/"))
-            throw new MalformedURLException("Path '" + path +
-                                            "' does not start with '/'");
-        URL url = new URL(myResourceBaseURL, path.substring(1));
+        if (!path.startsWith("/")) {
+            throw new MalformedURLException("Path '" + path + "' does not 
start with '/'");
+        }
+
+        // Strip leading '/'
+        path = path.substring(1);
+
+        URL url = new URL(myResourceBaseURL, path);
         try (InputStream is = url.openStream()) {
         } catch (Throwable t) {
             ExceptionUtils.handleThrowable(t);
             url = null;
         }
+
+        // During initialisation, getResource() is called before resourceJARs 
is
+        // initialised
+        if (url == null && resourceJARs != null) {
+            String jarPath = "META-INF/resources/" + path;
+            for (URL jarUrl : resourceJARs) {
+                try (Jar jar = JarFactory.newInstance(jarUrl)) {
+                    if (jar.exists(jarPath)) {
+                        return new URL(jar.getURL(jarPath));
+                    }
+                } catch (IOException ioe) {
+                    // Ignore
+                }
+            }
+        }
         return url;
     }
 
@@ -377,27 +440,57 @@ public class JspCServletContext implemen
     public Set<String> getResourcePaths(String path) {
 
         Set<String> thePaths = new HashSet<>();
-        if (!path.endsWith("/"))
+        if (!path.endsWith("/")) {
             path += "/";
+        }
         String basePath = getRealPath(path);
-        if (basePath == null)
-            return thePaths;
-        File theBaseDir = new File(basePath);
-        if (!theBaseDir.exists() || !theBaseDir.isDirectory())
-            return thePaths;
-        String theFiles[] = theBaseDir.list();
-        if (theFiles == null) {
-            return thePaths;
-        }
-        for (int i = 0; i < theFiles.length; i++) {
-            File testFile = new File(basePath + File.separator + theFiles[i]);
-            if (testFile.isFile())
-                thePaths.add(path + theFiles[i]);
-            else if (testFile.isDirectory())
-                thePaths.add(path + theFiles[i] + "/");
+        if (basePath != null) {
+            File theBaseDir = new File(basePath);
+            if (theBaseDir.isDirectory()) {
+                String theFiles[] = theBaseDir.list();
+                if (theFiles != null) {
+                    for (int i = 0; i < theFiles.length; i++) {
+                        File testFile = new File(basePath + File.separator + 
theFiles[i]);
+                        if (testFile.isFile()) {
+                            thePaths.add(path + theFiles[i]);
+                        } else if (testFile.isDirectory()) {
+                            thePaths.add(path + theFiles[i] + "/");
+                        }
+                    }
+                }
+            }
         }
-        return thePaths;
 
+        // During initialisation, getResourcePaths() is called before
+        // resourceJARs is initialised
+        if (resourceJARs != null) {
+            String jarPath = "META-INF/resources" + path;
+            for (URL jarUrl : resourceJARs) {
+                try (Jar jar = JarFactory.newInstance(jarUrl)) {
+                    jar.nextEntry();
+                    for (String entryName = jar.getEntryName();
+                            entryName != null;
+                            jar.nextEntry(), entryName = jar.getEntryName()) {
+                        if (entryName.startsWith(jarPath) &&
+                                entryName.length() > jarPath.length()) {
+                            // Let the Set implementation handle duplicates
+                            int sep = entryName.indexOf("/", jarPath.length());
+                            if (sep < 0) {
+                                // This is a file
+                                
thePaths.add(entryName.substring(jarPath.length() - 1));
+                            } else {
+                                // This is a directory
+                                
thePaths.add(entryName.substring(jarPath.length() - 1, sep + 1));
+                            }
+                        }
+                    }
+                } catch (IOException e) {
+                    log(e.getMessage(), e);
+                }
+            }
+        }
+
+        return thePaths;
     }
 
 

Modified: 
tomcat/trunk/test/org/apache/jasper/servlet/TestJspCServletContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/jasper/servlet/TestJspCServletContext.java?rev=1847028&r1=1847027&r2=1847028&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/jasper/servlet/TestJspCServletContext.java 
(original)
+++ tomcat/trunk/test/org/apache/jasper/servlet/TestJspCServletContext.java Tue 
Nov 20 16:54:28 2018
@@ -19,6 +19,7 @@ package org.apache.jasper.servlet;
 import java.io.File;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.Set;
 
 import javax.servlet.descriptor.JspConfigDescriptor;
 import javax.servlet.descriptor.JspPropertyGroupDescriptor;
@@ -131,4 +132,25 @@ public class TestJspCServletContext {
         Assert.assertEquals(4, context.getEffectiveMajorVersion());
         Assert.assertEquals(0, context.getEffectiveMinorVersion());
     }
+
+
+    @Test
+    public void testResourceJARs() throws Exception {
+        File appDir = new File("test/webapp-fragments");
+        JspCServletContext context = new JspCServletContext(
+                null, appDir.toURI().toURL(), null, false, false);
+
+        Set<String> paths = context.getResourcePaths("/");
+        Assert.assertEquals(paths.size(), 10);
+        Assert.assertTrue(paths.contains("/WEB-INF/"));
+        Assert.assertTrue(paths.contains("/folder/"));
+        Assert.assertTrue(paths.contains("/'singlequote.jsp"));
+        Assert.assertTrue(paths.contains("/'singlequote2.jsp"));
+        Assert.assertTrue(paths.contains("/bug51396.jsp"));
+        Assert.assertTrue(paths.contains("/jndi.jsp"));
+        Assert.assertTrue(paths.contains("/resourceA.jsp"));
+        Assert.assertTrue(paths.contains("/resourceB.jsp"));
+        Assert.assertTrue(paths.contains("/resourceF.jsp"));
+        Assert.assertTrue(paths.contains("/warDirContext.jsp"));
+    }
 }

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1847028&r1=1847027&r2=1847028&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Tue Nov 20 16:54:28 2018
@@ -136,6 +136,10 @@
         Update the Eclipse Compiler for Java to 4.9. Additional patch by Lukasz
         Jader. (markt)
       </update>
+      <add>
+        <bug>53737</bug>: Extend JspC, the precompilation tool, to include
+        support for resource JARs. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Web applications">



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

Reply via email to