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

baedke pushed a commit to branch OAK-9586
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/OAK-9586 by this push:
     new 1372efd135 OAK-9586: SysViewImportHandler does not support expanded 
names in sv:name attributes
1372efd135 is described below

commit 1372efd135c942c3b8095200069b6ba8ae547337
Author: Manfred Baedke <[email protected]>
AuthorDate: Thu Apr 10 16:48:58 2025 +0200

    OAK-9586: SysViewImportHandler does not support expanded names in sv:name 
attributes
    
    Prelimary fix, needs beautification.
---
 .../oak/jcr/xml/SysViewImportHandler.java          |  42 +++++-
 .../apache/jackrabbit/oak/jcr/xml/ImportTest.java  | 155 +++++++++++++++++++++
 2 files changed, 195 insertions(+), 2 deletions(-)

diff --git 
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/SysViewImportHandler.java
 
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/SysViewImportHandler.java
index 933446142f..7cfb1c63b1 100644
--- 
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/SysViewImportHandler.java
+++ 
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/SysViewImportHandler.java
@@ -20,8 +20,10 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Stack;
+import java.util.UUID;
 
 import javax.jcr.InvalidSerializedDataException;
+import javax.jcr.NamespaceException;
 import javax.jcr.NamespaceRegistry;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
@@ -93,6 +95,42 @@ class SysViewImportHandler extends TargetImportHandler {
         }
     }
 
+    //TODO avoid code duplication (this is stolen from GlobalNameMapper)
+    private static boolean isExpandedName(String name) {
+        if (name.startsWith("{")) {
+            int brace = name.indexOf('}', 1);
+            if (brace != -1) {
+                String namespace = name.substring(1, brace);
+                // the empty namespace and "internal" are valid as well, 
otherwise it always contains a colon (as it is a URI)
+                // compare with RFC 3986, Section 3 
(https://datatracker.ietf.org/doc/html/rfc3986#section-3)
+                if (namespace.isEmpty() || 
namespace.equals(NamespaceConstants.NAMESPACE_REP)|| namespace.indexOf(':') != 
-1) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private NameInfo getNameInfo(String svName) throws RepositoryException {
+        if (isExpandedName(svName)) {
+            String namespaceUri = svName.substring(svName.indexOf("{") + 1, 
svName.indexOf("}"));
+            String localName = svName.substring(svName.indexOf("}") + 1);
+            NamespaceRegistry namespaceRegistry = 
sessionContext.getWorkspace().getNamespaceRegistry();
+            String prefix;
+            try {
+                prefix = namespaceRegistry.getPrefix(namespaceUri);
+            } catch (NamespaceException expected) {
+                // this is an expanded svName using an unregistered namespace
+                // we need to make up a prefix for the namespace
+                prefix = "ns_" + UUID.randomUUID().toString().substring(0, 8);
+                namespaceRegistry.registerNamespace(prefix, namespaceUri);
+            }
+            return new NameInfo(prefix, localName);
+        } else {
+            return new 
NameInfo(sessionContext.getJcrName(sessionContext.getOakName(svName)));
+        }
+    }
+    
     //-------------------------------------------------------< ContentHandler >
 
     @Override
@@ -123,7 +161,7 @@ class SysViewImportHandler extends TargetImportHandler {
             // push new ImportState instance onto the stack
             ImportState state = new ImportState();
             try {
-                state.nodeName = new NameInfo(svName).getRepoQualifiedName();
+                state.nodeName = getNameInfo(svName).getRepoQualifiedName();
             } catch (RepositoryException e) {
                 throw new SAXException(new 
InvalidSerializedDataException("illegal node name: " + svName, e));
             }
@@ -141,7 +179,7 @@ class SysViewImportHandler extends TargetImportHandler {
                         "missing mandatory sv:name attribute of element 
sv:property"));
             }
             try {
-                currentPropName = new NameInfo(svName);
+                currentPropName = getNameInfo(svName);
             } catch (RepositoryException e) {
                 throw new SAXException(new 
InvalidSerializedDataException("illegal property name: " + svName, e));
             }
diff --git 
a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/xml/ImportTest.java 
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/xml/ImportTest.java
index 631f3cbdc2..16157a07bb 100644
--- a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/xml/ImportTest.java
+++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/xml/ImportTest.java
@@ -24,17 +24,20 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.UUID;
 
 import javax.jcr.ImportUUIDBehavior;
 import javax.jcr.ItemExistsException;
 import javax.jcr.Node;
+import javax.jcr.NodeIterator;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.ConstraintViolationException;
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.junit.Ignore;
 
 
 public class ImportTest extends AbstractJCRTest {
@@ -310,4 +313,156 @@ public class ImportTest extends AbstractJCRTest {
         assertEquals("b", p2.getString());
         assertNotEquals(p1.getName(), p2.getName());
     }
+
+    private String createExpandedNameWithPrefixDefXml(String nid) {
+        return "<sv:node xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\"; " +
+                "xmlns:definedPrefix=\"" + "urn:" + nid  + "\"" +
+                " sv:name=\"foo\">" +
+                "<sv:node sv:name=\"{urn:" + nid  + "}bar\">" +
+                "<sv:property sv:type=\"Boolean\" sv:name=\"{" + "urn:" + nid  
+ "}bar\">" +
+                "<sv:value>true</sv:value>" +
+                "</sv:property>" +
+                "</sv:node>" +
+                "</sv:node>";
+    }
+
+    private String createExpandedNameWithoutPrefixDefXml(String nid) {
+        return "<sv:node xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\"; " +
+                " sv:name=\"foo\">" +
+                "<sv:node sv:name=\"{urn:" + nid  + "}bar\">" +
+                "<sv:property sv:type=\"Boolean\" sv:name=\"{" + "urn:" + nid  
+ "}bar\">" +
+                "<sv:value>true</sv:value>" +
+                "</sv:property>" +
+                "</sv:node>" +
+                "</sv:node>";
+    }
+
+    // Expanded names in content, prefix definition in XML, namespace not yet 
registered
+    // OAK-9586
+    public void testExpandedNameImportWithPrefixDefinition() throws Exception {
+        String nid = "unregisteredNS";
+        String prefix = null;
+        try {
+            String xml = createExpandedNameWithPrefixDefXml(nid);
+            InputStream input = new 
ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+            superuser.importXML(
+                    "/", input, 
ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+            NodeIterator nodes = 
superuser.getRootNode().getNode("foo").getNodes();
+            assertTrue(nodes.hasNext());
+            Node node = nodes.nextNode();
+
+            prefix = superuser.getNamespacePrefix("urn:" + nid);
+            assertEquals("definedPrefix", prefix);
+            String name = node.getName();
+            assertEquals("definedPrefix:bar", name);
+            Property p = node.getProperty("{urn:" + nid + "}bar");
+            assertNotNull(p);
+            Property q = node.getProperty("definedPrefix:bar");
+            assertNotNull(q);
+            assertEquals(p.getName(), q.getName());
+            assertEquals(p.getBoolean(), q.getBoolean());
+        } finally {
+            if (prefix != null) {
+                try {
+                    
superuser.getWorkspace().getNamespaceRegistry().unregisterNamespace(prefix);
+                } catch (Exception ignored) {}
+            }
+        }
+    }
+
+    // Expanded names in content, prefix definition in XML, prefix already 
registered
+    // OAK-9586
+    public void testExpandedNameImportWithCollidingPrefixDefinition() throws 
Exception {
+        String nid = "registeredNS";
+        String prefix = null;
+        try {
+            
superuser.getWorkspace().getNamespaceRegistry().registerNamespace("registeredPrefix",
 "urn:" + nid);
+            //superuser.setNamespacePrefix("registeredPrefix", "urn:" + nid);
+            String xml = createExpandedNameWithPrefixDefXml(nid);
+            InputStream input = new 
ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+            superuser.importXML(
+                    "/", input, 
ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+            NodeIterator nodes = 
superuser.getRootNode().getNode("foo").getNodes();
+            assertTrue(nodes.hasNext());
+            Node node = nodes.nextNode();
+
+            prefix = superuser.getNamespacePrefix("urn:" + nid);
+            assertEquals("registeredPrefix", prefix);
+            String name = node.getName();
+            assertEquals("registeredPrefix:bar", name);
+            Property p = node.getProperty("{urn:" + nid + "}bar");
+            assertNotNull(p);
+            Property q = node.getProperty("registeredPrefix:bar");
+            assertNotNull(q);
+            assertEquals(p.getName(), q.getName());
+            assertEquals(p.getBoolean(), q.getBoolean());
+        } finally {
+            if (prefix != null) {
+                try {
+                    
superuser.getWorkspace().getNamespaceRegistry().unregisterNamespace(prefix);
+                } catch (Exception ignored) {}
+            }
+        }
+    }
+
+    // Expanded names in content, no prefix definition in XML, prefix already 
registered
+    // OAK-9586
+    public void testExpandedNameImportWithoutPrefixDefinitionAndRegisteredNS() 
throws Exception {
+        String nid = "registeredNS";
+        String prefix = null;
+        try {
+            
superuser.getWorkspace().getNamespaceRegistry().registerNamespace("registeredPrefix",
 "urn:" + nid);
+            String xml = createExpandedNameWithoutPrefixDefXml(nid);
+            InputStream input = new 
ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+            superuser.importXML(
+                    "/", input, 
ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+            NodeIterator nodes = 
superuser.getRootNode().getNode("foo").getNodes();
+            assertTrue(nodes.hasNext());
+            Node node = nodes.nextNode();
+
+            prefix = superuser.getNamespacePrefix("urn:" + nid);
+            assertEquals("registeredPrefix", prefix);
+            String name = node.getName();
+            assertEquals("registeredPrefix:bar", name);
+            Property p = node.getProperty("{urn:" + nid + "}bar");
+            assertNotNull(p);
+            Property q = node.getProperty("registeredPrefix:bar");
+            assertNotNull(q);
+            assertEquals(p.getName(), q.getName());
+            assertEquals(p.getBoolean(), q.getBoolean());
+        } finally {
+            if (prefix != null) {
+                try {
+                    
superuser.getWorkspace().getNamespaceRegistry().unregisterNamespace(prefix);
+                } catch (Exception ignored) {}
+            }
+        }
+    }
+
+    // Expanded names in content, no prefix definition in XML, prefix not yet 
registered
+    // OAK-9586
+    public void 
testExpandedNameImportWithoutPrefixDefinitionAndUnregisteredNS() throws 
Exception {
+        String nid = "unregisteredNS";
+        String prefix = null;
+        try {
+            String xml = createExpandedNameWithoutPrefixDefXml(nid);
+            InputStream input = new 
ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+            superuser.importXML(
+                    "/", input, 
ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+            NodeIterator nodes = 
superuser.getRootNode().getNode("foo").getNodes();
+            assertTrue(nodes.hasNext());
+            Node node = nodes.nextNode();
+
+            prefix = superuser.getNamespacePrefix("urn:" + nid);
+            assertNotNull(prefix);
+            Property p = node.getProperty("{urn:" + nid + "}bar");
+            assertNotNull(p);
+        } finally {
+            if (prefix != null) {
+                try {
+                    
superuser.getWorkspace().getNamespaceRegistry().unregisterNamespace(prefix);
+                } catch (Exception ignored) {}
+            }
+        }
+    }
 }
\ No newline at end of file

Reply via email to