This is an automated email from the ASF dual-hosted git repository.

rzo1 pushed a commit to branch tomee-10.x
in repository https://gitbox.apache.org/repos/asf/tomee.git

commit 49c721dde22a6a8c29cbc71b3db5e1becda6cdf7
Author: tandraschko <[email protected]>
AuthorDate: Tue Jul 28 18:19:26 2026 +0200

    Speed up annotation processing during WAR deploy
    
    (cherry picked from commit d747030cf328f4429315839983201ce84f294648)
---
 .../catalina/startup/OpenEJBContextConfig.java     | 65 +++++++++++++++++-----
 1 file changed, 51 insertions(+), 14 deletions(-)

diff --git 
a/tomee/tomee-catalina/src/main/java/org/apache/catalina/startup/OpenEJBContextConfig.java
 
b/tomee/tomee-catalina/src/main/java/org/apache/catalina/startup/OpenEJBContextConfig.java
index 079dc4f092..70d230bf6c 100644
--- 
a/tomee/tomee-catalina/src/main/java/org/apache/catalina/startup/OpenEJBContextConfig.java
+++ 
b/tomee/tomee-catalina/src/main/java/org/apache/catalina/startup/OpenEJBContextConfig.java
@@ -109,6 +109,13 @@ public class OpenEJBContextConfig extends ContextConfig {
     // since we store all classes in WEB-INF we will do it only once so use 
this boolean to avoid multiple processing
     private Collection<String> webInfClassesAnnotationsProcessed = new 
ArrayList<>(1);
 
+    // isIncluded() resolves the same module roots and the same web resource 
once
+    // per candidate class, and every resolution is a filesystem syscall. Both
+    // mappings are stable for the lifetime of a deploy, and this config 
instance
+    // is per context, so they are memoised here rather than recomputed.
+    private final Map<String, File> canonicalFiles = new HashMap<>();
+    private final Map<String, File> urlAsFile = new HashMap<>();
+
     public OpenEJBContextConfig(final TomcatWebAppBuilder.StandardContextInfo 
standardContextInfo) {
         logger.debug("OpenEJBContextConfig({0})", 
standardContextInfo.toString());
         info = standardContextInfo;
@@ -565,7 +572,17 @@ public class OpenEJBContextConfig extends ContextConfig {
                                                  final 
Map<String,JavaClassCacheEntry> javaClassCache) {
         final WebAppInfo webAppInfo = info.get();
         if (webAppInfo != null && FileResource.class.isInstance(webResource)) {
+            // Tomcat calls this for every web resource, but once every module 
has been processed
+            // there is nothing left to match against and resolving the 
resource is wasted work.
+            if (allWebAnnotationsProcessed(webAppInfo)) {
+                return;
+            }
+
             final File file = new 
File(FileResource.class.cast(webResource).getCanonicalPath());
+            // the path already comes from getCanonicalPath(), so record it as 
its own canonical
+            // form instead of letting isIncluded() resolve it a second time
+            canonicalFiles.putIfAbsent(file.getPath(), file);
+
             for (final ClassListInfo info : webAppInfo.webAnnotatedClasses) {
                 if (webInfClassesAnnotationsProcessed.contains(info.name)) {
                     continue;
@@ -724,19 +741,8 @@ public class OpenEJBContextConfig extends ContextConfig {
     }
 
     private boolean isIncluded(final File root, final File clazz) {
-        File file;
-        try { // symb links
-            file = root.getCanonicalFile();
-        } catch (final IOException e) {
-            file = root;
-        }
-
-        File current;
-        try { // symb links and windows long home names
-            current = clazz.getCanonicalFile();
-        } catch (final IOException e) {
-            current = clazz;
-        }
+        final File file = canonical(root); // symb links
+        File current = canonical(clazz); // symb links and windows long home 
names
         while (current != null && current.exists()) {
             if (current.equals(file)) {
                 final File parent = current.getParentFile();
@@ -751,7 +757,38 @@ public class OpenEJBContextConfig extends ContextConfig {
     }
 
     private boolean isIncludedIn(final String filePath, final File 
classAsFile) throws MalformedURLException {
-        return isIncluded(URLs.toFile(new URL(filePath)), classAsFile);
+        File root = urlAsFile.get(filePath);
+        if (root == null) {
+            root = URLs.toFile(new URL(filePath));
+            urlAsFile.put(filePath, root);
+        }
+        return isIncluded(root, classAsFile);
+    }
+
+    private boolean allWebAnnotationsProcessed(final WebAppInfo webAppInfo) {
+        for (final ClassListInfo classes : webAppInfo.webAnnotatedClasses) {
+            if (!webInfClassesAnnotationsProcessed.contains(classes.name)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private File canonical(final File file) {
+        final String key = file.getPath();
+        final File cached = canonicalFiles.get(key);
+        if (cached != null) {
+            return cached;
+        }
+
+        File resolved;
+        try {
+            resolved = file.getCanonicalFile();
+        } catch (final IOException e) {
+            resolved = file;
+        }
+        canonicalFiles.put(key, resolved);
+        return resolved;
     }
 
 }

Reply via email to