martin-g commented on a change in pull request #354: URL: https://github.com/apache/tomcat/pull/354#discussion_r486870063
########## File path: java/org/apache/catalina/startup/LocalStrings_fr.properties ########## @@ -69,6 +69,7 @@ contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réal contextConfig.processAnnotationsDir.debug=Balayage du répertoire pour trouver des fichiers de classe avec annotations [{0}] contextConfig.processAnnotationsJar.debug=Analyse du fichier jars pour des classes annotées avec [{0}] contextConfig.processAnnotationsWebDir.debug=Balayage du répertoire d''applications web, pour fichiers de classe avec annotations [{0}] +contextConfig.processAnnotationsInParallelFailure=exécution parallèle a échoué Review comment: Should `exécution` be capital cased like the other messages ? ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { + private final WebXml fragment; + private final boolean handlesTypesOnly; + private Map<String, JavaClassCacheEntry> javaClassCache; + + private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + this.fragment = fragment; + this.handlesTypesOnly = handlesTypesOnly; + this.javaClassCache = javaClassCache; + } + + @Override + public Void call() { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + + return null; + } + + } + + /** + * Parallelized version of processAnnotationsInParallel(). Constructs tasks, + * submits them as they're created, then waits for completion. + * + * @param fragments Set of parallelizable scans + * @param handlesTypesOnly Important parameter for the underlying scan + */ + protected void processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly, + Map<String, JavaClassCacheEntry> javaClassCache) { + + + Server s = getServer(); + ExecutorService pool = null; + try { + pool = s.getUtilityExecutor(); + List<Future<Void>> futures = new ArrayList<>(fragments.size()); + for (WebXml fragment : fragments) { + Callable<Void> task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache); + futures.add(pool.submit(task)); + } + try { + for (Future<?> future : futures) { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e); + } + } finally { + if (pool != null) { + pool.shutdownNow(); Review comment: This is not really needed because org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#shutdown methods do nothing, but anyway it is a good practice to do it. ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { Review comment: Why `Callable<Void>` ? You can use `Runnable` ########## File path: java/org/apache/catalina/startup/LocalStrings_fr.properties ########## @@ -69,6 +69,7 @@ contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réal contextConfig.processAnnotationsDir.debug=Balayage du répertoire pour trouver des fichiers de classe avec annotations [{0}] contextConfig.processAnnotationsJar.debug=Analyse du fichier jars pour des classes annotées avec [{0}] contextConfig.processAnnotationsWebDir.debug=Balayage du répertoire d''applications web, pour fichiers de classe avec annotations [{0}] +contextConfig.processAnnotationsInParallelFailure=exécution parallèle a échoué Review comment: Should `exécution` be capital cased like the other messages ? ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { + private final WebXml fragment; + private final boolean handlesTypesOnly; + private Map<String, JavaClassCacheEntry> javaClassCache; + + private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + this.fragment = fragment; + this.handlesTypesOnly = handlesTypesOnly; + this.javaClassCache = javaClassCache; + } + + @Override + public Void call() { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + + return null; + } + + } + + /** + * Parallelized version of processAnnotationsInParallel(). Constructs tasks, + * submits them as they're created, then waits for completion. + * + * @param fragments Set of parallelizable scans + * @param handlesTypesOnly Important parameter for the underlying scan + */ + protected void processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly, + Map<String, JavaClassCacheEntry> javaClassCache) { + + + Server s = getServer(); + ExecutorService pool = null; + try { + pool = s.getUtilityExecutor(); + List<Future<Void>> futures = new ArrayList<>(fragments.size()); + for (WebXml fragment : fragments) { + Callable<Void> task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache); + futures.add(pool.submit(task)); + } + try { + for (Future<?> future : futures) { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e); + } + } finally { + if (pool != null) { + pool.shutdownNow(); Review comment: This is not really needed because org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#shutdown methods do nothing, but anyway it is a good practice to do it. ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { Review comment: Why `Callable<Void>` ? You can use `Runnable` ########## File path: java/org/apache/catalina/startup/LocalStrings_fr.properties ########## @@ -69,6 +69,7 @@ contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réal contextConfig.processAnnotationsDir.debug=Balayage du répertoire pour trouver des fichiers de classe avec annotations [{0}] contextConfig.processAnnotationsJar.debug=Analyse du fichier jars pour des classes annotées avec [{0}] contextConfig.processAnnotationsWebDir.debug=Balayage du répertoire d''applications web, pour fichiers de classe avec annotations [{0}] +contextConfig.processAnnotationsInParallelFailure=exécution parallèle a échoué Review comment: Should `exécution` be capital cased like the other messages ? ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { + private final WebXml fragment; + private final boolean handlesTypesOnly; + private Map<String, JavaClassCacheEntry> javaClassCache; + + private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + this.fragment = fragment; + this.handlesTypesOnly = handlesTypesOnly; + this.javaClassCache = javaClassCache; + } + + @Override + public Void call() { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + + return null; + } + + } + + /** + * Parallelized version of processAnnotationsInParallel(). Constructs tasks, + * submits them as they're created, then waits for completion. + * + * @param fragments Set of parallelizable scans + * @param handlesTypesOnly Important parameter for the underlying scan + */ + protected void processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly, + Map<String, JavaClassCacheEntry> javaClassCache) { + + + Server s = getServer(); + ExecutorService pool = null; + try { + pool = s.getUtilityExecutor(); + List<Future<Void>> futures = new ArrayList<>(fragments.size()); + for (WebXml fragment : fragments) { + Callable<Void> task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache); + futures.add(pool.submit(task)); + } + try { + for (Future<?> future : futures) { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e); + } + } finally { + if (pool != null) { + pool.shutdownNow(); Review comment: This is not really needed because org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#shutdown methods do nothing, but anyway it is a good practice to do it. ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { Review comment: Why `Callable<Void>` ? You can use `Runnable` ########## File path: java/org/apache/catalina/startup/LocalStrings_fr.properties ########## @@ -69,6 +69,7 @@ contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réal contextConfig.processAnnotationsDir.debug=Balayage du répertoire pour trouver des fichiers de classe avec annotations [{0}] contextConfig.processAnnotationsJar.debug=Analyse du fichier jars pour des classes annotées avec [{0}] contextConfig.processAnnotationsWebDir.debug=Balayage du répertoire d''applications web, pour fichiers de classe avec annotations [{0}] +contextConfig.processAnnotationsInParallelFailure=exécution parallèle a échoué Review comment: Should `exécution` be capital cased like the other messages ? ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { + private final WebXml fragment; + private final boolean handlesTypesOnly; + private Map<String, JavaClassCacheEntry> javaClassCache; + + private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + this.fragment = fragment; + this.handlesTypesOnly = handlesTypesOnly; + this.javaClassCache = javaClassCache; + } + + @Override + public Void call() { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + + return null; + } + + } + + /** + * Parallelized version of processAnnotationsInParallel(). Constructs tasks, + * submits them as they're created, then waits for completion. + * + * @param fragments Set of parallelizable scans + * @param handlesTypesOnly Important parameter for the underlying scan + */ + protected void processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly, + Map<String, JavaClassCacheEntry> javaClassCache) { + + + Server s = getServer(); + ExecutorService pool = null; + try { + pool = s.getUtilityExecutor(); + List<Future<Void>> futures = new ArrayList<>(fragments.size()); + for (WebXml fragment : fragments) { + Callable<Void> task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache); + futures.add(pool.submit(task)); + } + try { + for (Future<?> future : futures) { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e); + } + } finally { + if (pool != null) { + pool.shutdownNow(); Review comment: This is not really needed because org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#shutdown methods do nothing, but anyway it is a good practice to do it. ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { Review comment: Why `Callable<Void>` ? You can use `Runnable` ########## File path: java/org/apache/catalina/startup/LocalStrings_fr.properties ########## @@ -69,6 +69,7 @@ contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réal contextConfig.processAnnotationsDir.debug=Balayage du répertoire pour trouver des fichiers de classe avec annotations [{0}] contextConfig.processAnnotationsJar.debug=Analyse du fichier jars pour des classes annotées avec [{0}] contextConfig.processAnnotationsWebDir.debug=Balayage du répertoire d''applications web, pour fichiers de classe avec annotations [{0}] +contextConfig.processAnnotationsInParallelFailure=exécution parallèle a échoué Review comment: Should `exécution` be capital cased like the other messages ? ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { + private final WebXml fragment; + private final boolean handlesTypesOnly; + private Map<String, JavaClassCacheEntry> javaClassCache; + + private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + this.fragment = fragment; + this.handlesTypesOnly = handlesTypesOnly; + this.javaClassCache = javaClassCache; + } + + @Override + public Void call() { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + + return null; + } + + } + + /** + * Parallelized version of processAnnotationsInParallel(). Constructs tasks, + * submits them as they're created, then waits for completion. + * + * @param fragments Set of parallelizable scans + * @param handlesTypesOnly Important parameter for the underlying scan + */ + protected void processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly, + Map<String, JavaClassCacheEntry> javaClassCache) { + + + Server s = getServer(); + ExecutorService pool = null; + try { + pool = s.getUtilityExecutor(); + List<Future<Void>> futures = new ArrayList<>(fragments.size()); + for (WebXml fragment : fragments) { + Callable<Void> task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache); + futures.add(pool.submit(task)); + } + try { + for (Future<?> future : futures) { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e); + } + } finally { + if (pool != null) { + pool.shutdownNow(); Review comment: This is not really needed because org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#shutdown methods do nothing, but anyway it is a good practice to do it. ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { Review comment: Why `Callable<Void>` ? You can use `Runnable` ########## File path: java/org/apache/catalina/startup/LocalStrings_fr.properties ########## @@ -69,6 +69,7 @@ contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réal contextConfig.processAnnotationsDir.debug=Balayage du répertoire pour trouver des fichiers de classe avec annotations [{0}] contextConfig.processAnnotationsJar.debug=Analyse du fichier jars pour des classes annotées avec [{0}] contextConfig.processAnnotationsWebDir.debug=Balayage du répertoire d''applications web, pour fichiers de classe avec annotations [{0}] +contextConfig.processAnnotationsInParallelFailure=exécution parallèle a échoué Review comment: Should `exécution` be capital cased like the other messages ? ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { + private final WebXml fragment; + private final boolean handlesTypesOnly; + private Map<String, JavaClassCacheEntry> javaClassCache; + + private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + this.fragment = fragment; + this.handlesTypesOnly = handlesTypesOnly; + this.javaClassCache = javaClassCache; + } + + @Override + public Void call() { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + + return null; + } + + } + + /** + * Parallelized version of processAnnotationsInParallel(). Constructs tasks, + * submits them as they're created, then waits for completion. + * + * @param fragments Set of parallelizable scans + * @param handlesTypesOnly Important parameter for the underlying scan + */ + protected void processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly, + Map<String, JavaClassCacheEntry> javaClassCache) { + + + Server s = getServer(); + ExecutorService pool = null; + try { + pool = s.getUtilityExecutor(); + List<Future<Void>> futures = new ArrayList<>(fragments.size()); + for (WebXml fragment : fragments) { + Callable<Void> task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache); + futures.add(pool.submit(task)); + } + try { + for (Future<?> future : futures) { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e); + } + } finally { + if (pool != null) { + pool.shutdownNow(); Review comment: This is not really needed because org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#shutdown methods do nothing, but anyway it is a good practice to do it. ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { Review comment: Why `Callable<Void>` ? You can use `Runnable` ########## File path: java/org/apache/catalina/startup/LocalStrings_fr.properties ########## @@ -69,6 +69,7 @@ contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réal contextConfig.processAnnotationsDir.debug=Balayage du répertoire pour trouver des fichiers de classe avec annotations [{0}] contextConfig.processAnnotationsJar.debug=Analyse du fichier jars pour des classes annotées avec [{0}] contextConfig.processAnnotationsWebDir.debug=Balayage du répertoire d''applications web, pour fichiers de classe avec annotations [{0}] +contextConfig.processAnnotationsInParallelFailure=exécution parallèle a échoué Review comment: Should `exécution` be capital cased like the other messages ? ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { + private final WebXml fragment; + private final boolean handlesTypesOnly; + private Map<String, JavaClassCacheEntry> javaClassCache; + + private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + this.fragment = fragment; + this.handlesTypesOnly = handlesTypesOnly; + this.javaClassCache = javaClassCache; + } + + @Override + public Void call() { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + + return null; + } + + } + + /** + * Parallelized version of processAnnotationsInParallel(). Constructs tasks, + * submits them as they're created, then waits for completion. + * + * @param fragments Set of parallelizable scans + * @param handlesTypesOnly Important parameter for the underlying scan + */ + protected void processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly, + Map<String, JavaClassCacheEntry> javaClassCache) { + + + Server s = getServer(); + ExecutorService pool = null; + try { + pool = s.getUtilityExecutor(); + List<Future<Void>> futures = new ArrayList<>(fragments.size()); + for (WebXml fragment : fragments) { + Callable<Void> task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache); + futures.add(pool.submit(task)); + } + try { + for (Future<?> future : futures) { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e); + } + } finally { + if (pool != null) { + pool.shutdownNow(); Review comment: This is not really needed because org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#shutdown methods do nothing, but anyway it is a good practice to do it. ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { Review comment: Why `Callable<Void>` ? You can use `Runnable` ########## File path: java/org/apache/catalina/startup/LocalStrings_fr.properties ########## @@ -69,6 +69,7 @@ contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réal contextConfig.processAnnotationsDir.debug=Balayage du répertoire pour trouver des fichiers de classe avec annotations [{0}] contextConfig.processAnnotationsJar.debug=Analyse du fichier jars pour des classes annotées avec [{0}] contextConfig.processAnnotationsWebDir.debug=Balayage du répertoire d''applications web, pour fichiers de classe avec annotations [{0}] +contextConfig.processAnnotationsInParallelFailure=exécution parallèle a échoué Review comment: Should `exécution` be capital cased like the other messages ? ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { + private final WebXml fragment; + private final boolean handlesTypesOnly; + private Map<String, JavaClassCacheEntry> javaClassCache; + + private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + this.fragment = fragment; + this.handlesTypesOnly = handlesTypesOnly; + this.javaClassCache = javaClassCache; + } + + @Override + public Void call() { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + + return null; + } + + } + + /** + * Parallelized version of processAnnotationsInParallel(). Constructs tasks, + * submits them as they're created, then waits for completion. + * + * @param fragments Set of parallelizable scans + * @param handlesTypesOnly Important parameter for the underlying scan + */ + protected void processAnnotationsInParallel(Set<WebXml> fragments, boolean handlesTypesOnly, + Map<String, JavaClassCacheEntry> javaClassCache) { + + + Server s = getServer(); + ExecutorService pool = null; + try { + pool = s.getUtilityExecutor(); + List<Future<Void>> futures = new ArrayList<>(fragments.size()); + for (WebXml fragment : fragments) { + Callable<Void> task = new AnnotationScanTask(fragment, handlesTypesOnly, javaClassCache); + futures.add(pool.submit(task)); + } + try { + for (Future<?> future : futures) { + future.get(); + } + } catch (Exception e) { + throw new RuntimeException(sm.getString("contextConfig.processAnnotationsInParallelFailure"), e); + } + } finally { + if (pool != null) { + pool.shutdownNow(); Review comment: This is not really needed because org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#shutdown methods do nothing, but anyway it is a good practice to do it. ########## File path: java/org/apache/catalina/startup/ContextConfig.java ########## @@ -2136,26 +2146,98 @@ protected InputSource getWebXmlSource(String filename, boolean global) { } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { - for(WebXml fragment : fragments) { - // Only need to scan for @HandlesTypes matches if any of the - // following are true: - // - it has already been determined only @HandlesTypes is required - // (e.g. main web.xml has metadata-complete="true" - // - this fragment is for a container JAR (Servlet 3.1 section 8.1) - // - this fragment has metadata-complete="true" - boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || - fragment.isMetadataComplete(); - - WebXml annotations = new WebXml(); - // no impact on distributable - annotations.setDistributable(true); - URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly, javaClassCache); - Set<WebXml> set = new HashSet<>(); - set.add(annotations); - // Merge annotations into fragment - fragment takes priority - fragment.merge(set); + boolean handlesTypesOnly, Map<String, JavaClassCacheEntry> javaClassCache) { + + if (context.getParent() instanceof Host && ((Host) context.getParent()).isParallelAnnotationScanning()) { + processAnnotationsInParallel(fragments, handlesTypesOnly, javaClassCache); + return; + } + + for (WebXml fragment : fragments) { + scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache); + } + } + + private void scanWebXmlFragment(boolean handlesTypesOnly, WebXml fragment, Map<String, JavaClassCacheEntry> javaClassCache) { + + // Only need to scan for @HandlesTypes matches if any of the + // following are true: + // - it has already been determined only @HandlesTypes is required + // (e.g. main web.xml has metadata-complete="true" + // - this fragment is for a container JAR (Servlet 3.1 section 8.1) + // - this fragment has metadata-complete="true" + boolean htOnly = handlesTypesOnly || !fragment.getWebappJar() || + fragment.isMetadataComplete(); + + WebXml annotations = new WebXml(); + // no impact on distributable + annotations.setDistributable(true); + URL url = fragment.getURL(); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); + Set<WebXml> set = new HashSet<>(); + set.add(annotations); + // Merge annotations into fragment - fragment takes priority + fragment.merge(set); + } + + /** + * Executable task to scan a segment for annotations. Each task does the + * same work as the for loop inside processAnnotations(); + * + * @author Engebretson, John + * @author Kamnani, Jatin + */ + private class AnnotationScanTask implements Callable<Void> { Review comment: Why `Callable<Void>` ? You can use `Runnable` ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org