This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/schema-builder-cache in repository https://gitbox.apache.org/repos/asf/ws-xmlschema.git
commit 1caaf988c4dc7c9283c8a958aea80f6a094f856f Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Tue Aug 25 11:10:51 2026 +0100 Fix resolved schema cache key collisions and validate cache hits --- .../apache/ws/commons/schema/SchemaBuilder.java | 28 +++++++++++++++--- .../ws/commons/schema/SchemaBuilderCacheTest.java | 34 ++++++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/xmlschema-core/src/main/java/org/apache/ws/commons/schema/SchemaBuilder.java b/xmlschema-core/src/main/java/org/apache/ws/commons/schema/SchemaBuilder.java index 65150643..92b366af 100644 --- a/xmlschema-core/src/main/java/org/apache/ws/commons/schema/SchemaBuilder.java +++ b/xmlschema-core/src/main/java/org/apache/ws/commons/schema/SchemaBuilder.java @@ -669,8 +669,12 @@ public class SchemaBuilder { XmlSchema resolveXmlSchema(String targetNamespace, String schemaLocation, String baseUri, TargetNamespaceValidator validator) { - if (getCachedSchema(targetNamespace, schemaLocation, baseUri) != null) { - return getCachedSchema(targetNamespace, schemaLocation, baseUri); + final XmlSchema cachedSchema = getCachedSchema(targetNamespace, schemaLocation, baseUri); + if (cachedSchema != null) { + if (validator != null) { + validator.validate(cachedSchema); + } + return cachedSchema; } // use the entity resolver provided if the schema location is present @@ -772,7 +776,7 @@ public class SchemaBuilder { // only by a trailing slash. As it is now, we assume a single // character difference // means it's a schema that has yet to be resolved. - String schemaKey = targetNamespace + schemaLocation + baseUri; + String schemaKey = getCacheKey(targetNamespace, schemaLocation, baseUri); SoftReference<XmlSchema> softref = threadResolvedSchemas.get(schemaKey); if (softref != null) { resolvedSchema = softref.get(); @@ -782,6 +786,22 @@ public class SchemaBuilder { return resolvedSchema; } + private static String getCacheKey(String targetNamespace, String schemaLocation, String baseUri) { + StringBuilder key = new StringBuilder(); + appendCacheKeyComponent(key, targetNamespace); + appendCacheKeyComponent(key, schemaLocation); + appendCacheKeyComponent(key, baseUri); + return key.toString(); + } + + private static void appendCacheKeyComponent(StringBuilder key, String component) { + if (component == null) { + key.append("-|"); + } else { + key.append(component.length()).append('|').append(component); + } + } + private List<Node> getChildren(Element content) { List<Node> result = new ArrayList<Node>(); for (Node n = content.getFirstChild(); n != null; n = n.getNextSibling()) { @@ -1893,7 +1913,7 @@ public class SchemaBuilder { if (resolvedSchemas != null) { Map<String, SoftReference<XmlSchema>> threadResolvedSchemas = resolvedSchemas.get(); if (threadResolvedSchemas != null) { - String schemaKey = targetNamespace + schemaLocation + baseUri; + String schemaKey = getCacheKey(targetNamespace, schemaLocation, baseUri); threadResolvedSchemas.put(schemaKey, new SoftReference<XmlSchema>(readSchema)); } } diff --git a/xmlschema-core/src/test/java/org/apache/ws/commons/schema/SchemaBuilderCacheTest.java b/xmlschema-core/src/test/java/org/apache/ws/commons/schema/SchemaBuilderCacheTest.java index 7bc06921..a4035b3b 100644 --- a/xmlschema-core/src/test/java/org/apache/ws/commons/schema/SchemaBuilderCacheTest.java +++ b/xmlschema-core/src/test/java/org/apache/ws/commons/schema/SchemaBuilderCacheTest.java @@ -19,12 +19,15 @@ package org.apache.ws.commons.schema; +import java.io.StringReader; import java.lang.ref.SoftReference; import java.util.Map; import javax.xml.parsers.DocumentBuilderFactory; +import org.apache.ws.commons.schema.resolver.URIResolver; import org.w3c.dom.Document; +import org.xml.sax.InputSource; import org.junit.Assert; import org.junit.Ignore; @@ -171,6 +174,37 @@ public class SchemaBuilderCacheTest extends Assert { assertNull(getThreadResolvedSchemaHashtable()); } + @Test + public void testCacheKeySeparatesComponents() { + try { + SchemaBuilder.initCache(); + XmlSchemaCollection schemaCollection = new XmlSchemaCollection(); + CountingResolver resolver = new CountingResolver(); + schemaCollection.setSchemaResolver(resolver); + SchemaBuilder builder = new SchemaBuilder(schemaCollection, null); + + XmlSchema first = builder.resolveXmlSchema("urn:test", "a", "bc", null); + XmlSchema second = builder.resolveXmlSchema("urn:test", "ab", "c", null); + + assertNotSame(first, second); + assertEquals(2, resolver.resolveCount); + } finally { + resetResolvedSchemasHashtable(); + } + } + + private static final class CountingResolver implements URIResolver { + private int resolveCount; + + public InputSource resolveEntity(String targetNamespace, String schemaLocation, String baseUri) { + resolveCount++; + InputSource source = new InputSource(new StringReader( + "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" targetNamespace=\"urn:test\"/>")); + source.setSystemId("memory:" + schemaLocation); + return source; + } + } + /** * Configure and start the test threads for the multi-threaded testing. The * threads will perform various tests between themselves such as clearing
