This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new b17fed248d Fix bloom filter index for JARs in packed WARs b17fed248d is described below commit b17fed248d1f6dded0b7e7c4f7c912795237e23b 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 | 25 ++++++ webapps/docs/changelog.xml | 15 +--- 4 files changed, 89 insertions(+), 55 deletions(-) diff --git a/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java b/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java index cfcb490e35..bfdb487690 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 8be14e47f4..7db24997bc 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; @@ -29,8 +30,7 @@ import org.apache.tomcat.util.compat.JreCompat; * 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. */ @@ -46,6 +46,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. * @@ -53,52 +57,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 <= JreCompat.getInstance().jarFileRuntimeMajorVersion()) { - 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 <= JreCompat.getInstance().jarFileRuntimeMajorVersion()) { + 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 974a8da595..1645ab443f 100644 --- a/java/org/apache/catalina/webresources/JarWarResourceSet.java +++ b/java/org/apache/catalina/webresources/JarWarResourceSet.java @@ -86,6 +86,7 @@ public class JarWarResourceSet extends AbstractArchiveResourceSet { * <p> * JarWar can't optimise for a single resource so the Map is always returned. */ + @SuppressWarnings("deprecation") @Override protected Map<String,JarEntry> getArchiveEntries(boolean single) { synchronized (archiveLock) { @@ -148,11 +149,35 @@ public class JarWarResourceSet extends AbstractArchiveResourceSet { } } } + WebResourceRoot root = getRoot(); + if (root.getArchiveIndexStrategyEnum().getUsesBloom() || + root.getContext() != null && root.getContext().getUseBloomFilterForArchives()) { + 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 = JreCompat.getInstance().jarFileNewInstance(getBase()); + // Don't populate JarContents here. Populate at the end of getArchiveEntries() + } + archiveUseCount++; + return archive; + } + } + + protected void processArchivesEntriesForMultiRelease() { int targetVersion = JreCompat.getInstance().jarFileRuntimeMajorVersion(); diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 8e8c03cd57..a88779c3bc 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 9.0.108 (remm)" 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 a 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 9.0.107 (remm)" 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