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

markt pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/11.0.x by this push:
     new 0fc7fecb9c Fix bloom filter index for JARs in packed WARs
0fc7fecb9c is described below

commit 0fc7fecb9c0d626622004e397f6a5fd18eb28c95
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue Jul 22 15:39:35 2025 +0100

    Fix bloom filter index for JARs in packed WARs
---
 .../webresources/AbstractArchiveResourceSet.java   |  8 +-
 .../apache/catalina/webresources/JarContents.java  | 96 +++++++++++++---------
 .../catalina/webresources/JarWarResourceSet.java   | 24 ++++++
 webapps/docs/changelog.xml                         | 15 +---
 4 files changed, 88 insertions(+), 55 deletions(-)

diff --git 
a/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java 
b/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java
index eace3665a7..ff0ca34b33 100644
--- a/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java
+++ b/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java
@@ -36,12 +36,12 @@ public abstract class AbstractArchiveResourceSet extends 
AbstractResourceSet {
 
     private URL baseUrl;
     private String baseUrlString;
-    private JarFile archive = null;
+    protected JarFile archive = null;
     protected Map<String,JarEntry> archiveEntries = null;
     protected final Object archiveLock = new Object();
-    private long archiveUseCount = 0;
-    private JarContents jarContents;
-    private boolean retainBloomFilterForArchives = false;
+    protected long archiveUseCount = 0;
+    protected JarContents jarContents;
+    protected boolean retainBloomFilterForArchives = false;
 
     protected final void setBaseUrl(URL baseUrl) {
         this.baseUrl = baseUrl;
diff --git a/java/org/apache/catalina/webresources/JarContents.java 
b/java/org/apache/catalina/webresources/JarContents.java
index 58e0bff993..31438bd88d 100644
--- a/java/org/apache/catalina/webresources/JarContents.java
+++ b/java/org/apache/catalina/webresources/JarContents.java
@@ -17,6 +17,7 @@
 package org.apache.catalina.webresources;
 
 import java.util.BitSet;
+import java.util.Collection;
 import java.util.Enumeration;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
@@ -27,8 +28,7 @@ import java.util.jar.JarFile;
  * from the beginning of the key. The hash methods are simple but good enough 
for this purpose.
  */
 public final class JarContents {
-    private final BitSet bits1;
-    private final BitSet bits2;
+
     /**
      * Constant used by a typical hashing method.
      */
@@ -44,6 +44,10 @@ public final class JarContents {
      */
     private static final int TABLE_SIZE = 2048;
 
+    private final BitSet bits1 = new BitSet(TABLE_SIZE);
+    private final BitSet bits2 = new BitSet(TABLE_SIZE);
+
+
     /**
      * Parses the passed-in jar and populates the bit array.
      *
@@ -51,52 +55,66 @@ public final class JarContents {
      */
     public JarContents(JarFile jar) {
         Enumeration<JarEntry> entries = jar.entries();
-        bits1 = new BitSet(TABLE_SIZE);
-        bits2 = new BitSet(TABLE_SIZE);
-
         while (entries.hasMoreElements()) {
             JarEntry entry = entries.nextElement();
-            String name = entry.getName();
-            int startPos = 0;
-
-            // If the path starts with a slash, that's not useful information.
-            // Skipping it increases the significance of our key by
-            // removing an insignificant character.
-            boolean precedingSlash = name.charAt(0) == '/';
-            if (precedingSlash) {
-                startPos = 1;
-            }
+            processEntry(entry);
+        }
+    }
 
-            // Versioned entries should be added to the table according to 
their real name
-            if (name.startsWith("META-INF/versions/", startPos)) {
-                int i = name.indexOf('/', 18 + startPos);
-                if (i > 0) {
-                    int version = Integer.parseInt(name.substring(18 + 
startPos, i));
-                    if (version <= Runtime.version().feature()) {
-                        startPos = i + 1;
-                    }
-                }
-                if (startPos == name.length()) {
-                    continue;
+
+    /**
+     * Populates the bit array from the provided set of JAR entries.
+     *
+     * @param entries The set of entries for the JAR file being processed
+     */
+    public JarContents(Collection<JarEntry> entries) {
+        for (JarEntry entry : entries) {
+            processEntry(entry);
+        }
+    }
+
+
+    private void processEntry(JarEntry entry) {
+        String name = entry.getName();
+        int startPos = 0;
+
+        // If the path starts with a slash, that's not useful information.
+        // Skipping it increases the significance of our key by
+        // removing an insignificant character.
+        boolean precedingSlash = name.charAt(0) == '/';
+        if (precedingSlash) {
+            startPos = 1;
+        }
+
+        // Versioned entries should be added to the table according to their 
real name
+        if (name.startsWith("META-INF/versions/", startPos)) {
+            int i = name.indexOf('/', 18 + startPos);
+            if (i > 0) {
+                int version = Integer.parseInt(name.substring(18 + startPos, 
i));
+                if (version <= Runtime.version().feature()) {
+                    startPos = i + 1;
                 }
             }
+            if (startPos == name.length()) {
+                return;
+            }
+        }
 
-            // Find the correct table slot
-            int pathHash1 = hashcode(name, startPos, HASH_PRIME_1);
-            int pathHash2 = hashcode(name, startPos, HASH_PRIME_2);
+        // Find the correct table slot
+        int pathHash1 = hashcode(name, startPos, HASH_PRIME_1);
+        int pathHash2 = hashcode(name, startPos, HASH_PRIME_2);
 
-            bits1.set(pathHash1 % TABLE_SIZE);
-            bits2.set(pathHash2 % TABLE_SIZE);
+        bits1.set(pathHash1 % TABLE_SIZE);
+        bits2.set(pathHash2 % TABLE_SIZE);
 
-            // While directory entry names always end in "/", application code
-            // may look them up without the trailing "/". Add this second form.
-            if (entry.isDirectory()) {
-                pathHash1 = hashcode(name, startPos, name.length() - 1, 
HASH_PRIME_1);
-                pathHash2 = hashcode(name, startPos, name.length() - 1, 
HASH_PRIME_2);
+        // While directory entry names always end in "/", application code
+        // may look them up without the trailing "/". Add this second form.
+        if (entry.isDirectory()) {
+            pathHash1 = hashcode(name, startPos, name.length() - 1, 
HASH_PRIME_1);
+            pathHash2 = hashcode(name, startPos, name.length() - 1, 
HASH_PRIME_2);
 
-                bits1.set(pathHash1 % TABLE_SIZE);
-                bits2.set(pathHash2 % TABLE_SIZE);
-            }
+            bits1.set(pathHash1 % TABLE_SIZE);
+            bits2.set(pathHash2 % TABLE_SIZE);
         }
     }
 
diff --git a/java/org/apache/catalina/webresources/JarWarResourceSet.java 
b/java/org/apache/catalina/webresources/JarWarResourceSet.java
index 8ab6418499..f557c6e714 100644
--- a/java/org/apache/catalina/webresources/JarWarResourceSet.java
+++ b/java/org/apache/catalina/webresources/JarWarResourceSet.java
@@ -27,6 +27,7 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.jar.JarInputStream;
 import java.util.jar.Manifest;
+import java.util.zip.ZipFile;
 
 import org.apache.catalina.LifecycleException;
 import org.apache.catalina.WebResource;
@@ -146,11 +147,34 @@ public class JarWarResourceSet extends 
AbstractArchiveResourceSet {
                     }
                 }
             }
+            WebResourceRoot root = getRoot();
+            if (root.getArchiveIndexStrategyEnum().getUsesBloom()) {
+                jarContents = new JarContents(archiveEntries.values());
+                retainBloomFilterForArchives = 
root.getArchiveIndexStrategyEnum().getRetain();
+            }
             return archiveEntries;
         }
     }
 
 
+    /**
+     * {@inheritDoc}
+     * <p>
+     * JarWar needs to generate jarContents for the inner JAR, not the outer 
WAR.
+     */
+    @Override
+    protected JarFile openJarFile() throws IOException {
+        synchronized (archiveLock) {
+            if (archive == null) {
+                archive = new JarFile(new File(getBase()), true, 
ZipFile.OPEN_READ, Runtime.version());
+                // Don't populate JarContents here. Populate at the end of 
getArchiveEntries()
+            }
+            archiveUseCount++;
+            return archive;
+        }
+    }
+
+
     protected void processArchivesEntriesForMultiRelease() {
 
         int targetVersion = Runtime.version().feature();
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 901e5c6d23..b1b4836fa0 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -105,23 +105,14 @@
   issues do not "pop up" wrt. others).
 -->
 <section name="Tomcat 11.0.10 (markt)" rtext="in development">
-  <subsection name="Coyote">
+  <subsection name="Catalina">
     <changelog>
       <fix>
-        <bug>69748</bug>: Add missing call to set keep-alive timeout when using
-        HTTP/1.1 following an async request, which was present for AJP.
-        (remm/markt)
+        Fix bloom filter population for archive indexing when using an packed
+        WAR containing one or more JAR files. (markt)
       </fix>
     </changelog>
   </subsection>
-  <subsection name="Cluster">
-    <changelog>
-      <update>
-        Add <code>enableStatistics</code> configuration attribute for the
-        <code>DeltaManager</code>, defaulting to <code>true</code>. (remm)
-      </update>
-    </changelog>
-  </subsection>
 </section>
 <section name="Tomcat 11.0.9 (markt)" rtext="2025-07-04">
   <subsection name="Catalina">


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

Reply via email to