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

coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-xmlschema.git


The following commit(s) were added to refs/heads/master by this push:
     new 6cc9c10d Fix resolved schema cache key collisions and validate cache 
hits (#129)
6cc9c10d is described below

commit 6cc9c10db2d35adffe9328ce5d135e57d13b49b5
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Tue Aug 25 11:45:10 2026 +0100

    Fix resolved schema cache key collisions and validate cache hits (#129)
---
 .../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

Reply via email to