This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/master by this push:
new 27bb36b Use utility executor for scanning
27bb36b is described below
commit 27bb36bdaa56d62fdcc28d3b8e3d4c25f4a44a50
Author: remm <[email protected]>
AuthorDate: Fri Sep 18 11:40:17 2020 +0200
Use utility executor for scanning
PR354 submitted by Jatin Kamnani.
The flag in on the context, as that's where it belongs. Defaults to
false, can be configured through the context defaults or per context.
---
java/org/apache/catalina/Context.java | 14 +++
java/org/apache/catalina/core/StandardContext.java | 19 ++++
.../apache/catalina/core/mbeans-descriptors.xml | 4 +
.../org/apache/catalina/startup/ContextConfig.java | 116 +++++++++++++++++----
.../org/apache/catalina/startup/FailedContext.java | 5 +
.../catalina/startup/LocalStrings.properties | 1 +
test/org/apache/tomcat/unittest/TesterContext.java | 5 +
webapps/docs/changelog.xml | 4 +
webapps/docs/config/context.xml | 8 ++
9 files changed, 154 insertions(+), 22 deletions(-)
diff --git a/java/org/apache/catalina/Context.java
b/java/org/apache/catalina/Context.java
index b8f55a0..4cf844c 100644
--- a/java/org/apache/catalina/Context.java
+++ b/java/org/apache/catalina/Context.java
@@ -762,6 +762,20 @@ public interface Context extends Container, ContextBind {
public String getContainerSciFilter();
+ /**
+ * @return the value of the parallel annotation scanning flag. If true,
+ * it will dispatch scanning to the utility executor.
+ */
+ public boolean isParallelAnnotationScanning();
+
+ /**
+ * Set the parallel annotation scanning value.
+ *
+ * @param parallelAnnotationScanning new parallel annotation scanning flag
+ */
+ public void setParallelAnnotationScanning(boolean
parallelAnnotationScanning);
+
+
// --------------------------------------------------------- Public Methods
/**
diff --git a/java/org/apache/catalina/core/StandardContext.java
b/java/org/apache/catalina/core/StandardContext.java
index ce51d6c..a34f14e 100644
--- a/java/org/apache/catalina/core/StandardContext.java
+++ b/java/org/apache/catalina/core/StandardContext.java
@@ -833,6 +833,8 @@ public class StandardContext extends ContainerBase
private boolean dispatcherWrapsSameObject =
Globals.STRICT_SERVLET_COMPLIANCE;
+ private boolean parallelAnnotationScanning = false;
+
// ----------------------------------------------------- Context Properties
@Override
@@ -1447,6 +1449,23 @@ public class StandardContext extends ContainerBase
}
+ @Override
+ public void setParallelAnnotationScanning(boolean
parallelAnnotationScanning) {
+
+ boolean oldParallelAnnotationScanning =
this.parallelAnnotationScanning;
+ this.parallelAnnotationScanning = parallelAnnotationScanning;
+ support.firePropertyChange("parallelAnnotationScanning",
oldParallelAnnotationScanning,
+ this.parallelAnnotationScanning);
+
+ }
+
+
+ @Override
+ public boolean isParallelAnnotationScanning() {
+ return this.parallelAnnotationScanning;
+ }
+
+
/**
* @return the Locale to character set mapper for this Context.
*/
diff --git a/java/org/apache/catalina/core/mbeans-descriptors.xml
b/java/org/apache/catalina/core/mbeans-descriptors.xml
index 1c0733f..50be99f 100644
--- a/java/org/apache/catalina/core/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/core/mbeans-descriptors.xml
@@ -208,6 +208,10 @@
description="The name of this Context"
type="java.lang.String"/>
+ <attribute name="parallelAnnotationScanning"
+ description="The parallel annotation scanning flag"
+ type="boolean"/>
+
<attribute name="parentClassLoader"
description="Parent class loader."
type="java.lang.ClassLoader" />
diff --git a/java/org/apache/catalina/startup/ContextConfig.java
b/java/org/apache/catalina/startup/ContextConfig.java
index c1fe029..a3cfcb8 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -39,6 +39,8 @@ import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.ServletContainerInitializer;
@@ -122,7 +124,6 @@ public class ContextConfig implements LifecycleListener {
private static final Log log = LogFactory.getLog(ContextConfig.class);
-
/**
* The string resources for this package.
*/
@@ -1374,7 +1375,14 @@ public class ContextConfig implements LifecycleListener {
protected void processClasses(WebXml webXml, Set<WebXml> orderedFragments)
{
// Step 4. Process /WEB-INF/classes for annotations and
// @HandlesTypes matches
- Map<String, JavaClassCacheEntry> javaClassCache = new HashMap<>();
+
+ Map<String, JavaClassCacheEntry> javaClassCache;
+
+ if (context.isParallelAnnotationScanning()) {
+ javaClassCache = new ConcurrentHashMap<>();
+ } else {
+ javaClassCache = new HashMap<>();
+ }
if (ok) {
WebResource[] webResources =
@@ -2136,26 +2144,90 @@ public class ContextConfig implements LifecycleListener
{
}
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.isParallelAnnotationScanning()) {
+ processAnnotationsInParallel(fragments, handlesTypesOnly,
javaClassCache);
+ } else {
+ 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();
+ */
+ private class AnnotationScanTask implements Runnable {
+ 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 run() {
+ scanWebXmlFragment(handlesTypesOnly, fragment, javaClassCache);
+ }
+
+ }
+
+ /**
+ * 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<?>> futures = new ArrayList<>(fragments.size());
+ for (WebXml fragment : fragments) {
+ Runnable 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();
+ }
}
}
diff --git a/java/org/apache/catalina/startup/FailedContext.java
b/java/org/apache/catalina/startup/FailedContext.java
index 9a93207..84b12f5 100644
--- a/java/org/apache/catalina/startup/FailedContext.java
+++ b/java/org/apache/catalina/startup/FailedContext.java
@@ -831,4 +831,9 @@ public class FailedContext extends LifecycleMBeanBase
implements Context {
@Override
public void setDispatcherWrapsSameObject(boolean
dispatcherWrapsSameObject) {}
+ @Override
+ public boolean isParallelAnnotationScanning() { return false; }
+ @Override
+ public void setParallelAnnotationScanning(boolean
parallelAnnotationScanning) {}
+
}
\ No newline at end of file
diff --git a/java/org/apache/catalina/startup/LocalStrings.properties
b/java/org/apache/catalina/startup/LocalStrings.properties
index 5ea0c61..6d503e1 100644
--- a/java/org/apache/catalina/startup/LocalStrings.properties
+++ b/java/org/apache/catalina/startup/LocalStrings.properties
@@ -72,6 +72,7 @@ contextConfig.noAntiLocking=The value [{0}] configured for
java.io.tmpdir does n
contextConfig.processAnnotationsDir.debug=Scanning directory for class files
with annotations [{0}]
contextConfig.processAnnotationsJar.debug=Scanning jar file for class files
with annotations [{0}]
contextConfig.processAnnotationsWebDir.debug=Scanning web application
directory for class files with annotations [{0}]
+contextConfig.processAnnotationsInParallelFailure=Parallel execution failed
contextConfig.resourceJarFail=Failed to process JAR found at URL [{0}] for
static resources to be included in context with name [{1}]
contextConfig.role.auth=Security role name [{0}] used in an <auth-constraint>
without being defined in a <security-role>
contextConfig.role.link=Security role name [{0}] used in a <role-link> without
being defined in a <security-role>
diff --git a/test/org/apache/tomcat/unittest/TesterContext.java
b/test/org/apache/tomcat/unittest/TesterContext.java
index ebee177..33b1355 100644
--- a/test/org/apache/tomcat/unittest/TesterContext.java
+++ b/test/org/apache/tomcat/unittest/TesterContext.java
@@ -1290,4 +1290,9 @@ public class TesterContext implements Context {
@Override
public void setDispatcherWrapsSameObject(boolean
dispatcherWrapsSameObject) {}
+ @Override
+ public boolean isParallelAnnotationScanning() { return false; }
+ @Override
+ public void setParallelAnnotationScanning(boolean
parallelAnnotationScanning) {}
+
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 95b172a..f1d7a76 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -67,6 +67,10 @@
Authentication implementation. Patch provided by Robert Rodewald.
(markt)
</fix>
+ <update>
+ Allow using the utility executor for annotation scanning. Patch
+ provided by Jatin Kamnani. (remm)
+ </update>
</changelog>
</subsection>
<subsection name="Coyote">
diff --git a/webapps/docs/config/context.xml b/webapps/docs/config/context.xml
index 04f28ca..2eaf349 100644
--- a/webapps/docs/config/context.xml
+++ b/webapps/docs/config/context.xml
@@ -457,6 +457,14 @@
the same attribute explicitly for the Context.</p>
</attribute>
+ <attribute name="parallelAnnotationScanning" required="false">
+ <p>When set to <code>true</code> annotation scanning will be performed
+ using the utility executor. It will allow processing scanning in
+ parrallel which may improve deployment type at the expense of higher
+ server load. If not specified, the default of <code>false</code> is
+ used.</p>
+ </attribute>
+
<attribute name="path" required="false">
<p>The <em>context path</em> of this web application, which is
matched against the beginning of each request URI to select the
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]