You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by re...@apache.org on 2025/05/22 14:50:13 UTC

(jackrabbit-oak) branch OAK-11657b created (now 2fe00869bb)

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

reschke pushed a change to branch OAK-11657b
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


      at 2fe00869bb OAK-11657: JackrabbitSession.getExpandedName/Path need to return stable expanded names - fix test case

This branch includes the following new commits:

     new 8086104309 OAK-11657: JackrabbitSession.getExpandedName/Path need to return stable expanded names (#2229)
     new 2fe00869bb OAK-11657: JackrabbitSession.getExpandedName/Path need to return stable expanded names - fix test case

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(jackrabbit-oak) 01/02: OAK-11657: JackrabbitSession.getExpandedName/Path need to return stable expanded names (#2229)

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8086104309e91a324c61a678f0cbd55fbb5192a4
Author: Julian Reschke <re...@apache.org>
AuthorDate: Thu May 8 11:21:09 2025 +0200

    OAK-11657: JackrabbitSession.getExpandedName/Path need to return stable expanded names (#2229)
---
 .../apache/jackrabbit/oak/namepath/NameMapper.java |  4 ++--
 .../oak/namepath/impl/GlobalNameMapper.java        | 28 ++++++++++++++--------
 .../apache/jackrabbit/api/JackrabbitSession.java   |  9 ++++++-
 .../jackrabbit/oak/jcr/session/SessionImpl.java    |  7 +++++-
 .../oak/jcr/session/JackrabbitSessionTest.java     | 28 ++++++++++++++++++----
 5 files changed, 58 insertions(+), 18 deletions(-)

diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java
index 94c8d9bf84..dc94f4d706 100644
--- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java
+++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java
@@ -87,11 +87,11 @@ public interface NameMapper {
      * @param oakName Oak name
      * @return JCR name in expanded form
      * @since Oak 1.78.0
-     * @throws IllegalStateException in case the namespace URI for the given Oak name cannot be resolved
+     * @throws IllegalStateException in case the namespace URI for the given
+     * Oak name cannot be resolved or is invalid
      * 
      * @see <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.1%20Expanded%20Form">JCR 2.0, 3.2.5.1 Expanded Form</a>
      */
     @NotNull
     String getExpandedJcrName(@NotNull String oakName);
-
 }
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java
index 546d9aab13..ec26596ee5 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java
@@ -35,6 +35,7 @@ import static org.apache.jackrabbit.oak.spi.namespace.NamespaceConstants.REP_URI
 import java.util.Map;
 import java.util.Map.Entry;
 
+import javax.jcr.NamespaceException;
 import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.oak.api.Root;
@@ -65,16 +66,17 @@ public class GlobalNameMapper implements NameMapper {
         return name.startsWith(":");
     }
 
+    private static boolean isValidNamespaceName(String namespace) {
+        // 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)
+        return namespace.isEmpty() || namespace.equals(NamespaceConstants.NAMESPACE_REP) || namespace.contains(":");
+    }
+
     protected 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 isValidNamespaceName(name.substring(1, brace));
             }
         }
         return false;
@@ -143,17 +145,23 @@ public class GlobalNameMapper implements NameMapper {
         int colon = oakName.indexOf(':');
         if (colon > 0) {
             String oakPrefix = oakName.substring(0, colon);
-            // local mapping must take precedence...
-            uri = getSessionLocalMappings().get(oakPrefix);
+            uri = getNamespacesProperty(oakPrefix);
+            // global mapping must take precedence...
             if (uri == null) {
-                // ...over global mappings
-                uri = getNamespacesProperty(oakPrefix);
+                // ...over local mappings
+                uri = getSessionLocalMappings().get(oakPrefix);
             }
             if (uri == null) {
                 throw new IllegalStateException(
                         "No namespace mapping found for " + oakName);
             }
             localName = oakName.substring(colon + 1);
+            // check namespace name for validity in Oak
+            if (!isValidNamespaceName(uri)) {
+                throw new IllegalStateException(
+                    new NamespaceException("Cannot determine expanded name for '" + oakName +
+                        "' as registered namespace name '" + uri + "' is invalid"));
+            }
         } else {
             uri = "";
             localName = oakName;
diff --git a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java
index db1bf7cb0d..f7e1f71187 100644
--- a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java
+++ b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java
@@ -25,6 +25,7 @@ import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.Session;
 import javax.jcr.AccessDeniedException;
+import javax.jcr.NamespaceException;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 
@@ -282,7 +283,10 @@ public interface JackrabbitSession extends Session {
      * Returns the expanded name of the given {@code Item}.
      * @param item the item for which to retrieve the name
      * @return the name of the item in expanded form.
-     * @throws RepositoryException if another error occurs.
+     * @throws NamespaceException when no expanded name can
+     * be determined (for instance, if the registered namespace
+     * (name) is invalid)
+     * @throws RepositoryException if another error occurs
      * @since 1.78.0
      * @see <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.1%20Expanded%20Form">JCR 2.0, 3.2.5.1 Expanded Form</a>
      */
@@ -292,6 +296,9 @@ public interface JackrabbitSession extends Session {
      * Returns the expanded path of the given {@code Item}.
      * @param item the item for which to retrieve the name
      * @return the path of the item in expanded form.
+     * @throws NamespaceException when no expanded name can
+     * be determined (for instance, if the registered namespace
+     * (name) is invalid)
      * @throws RepositoryException if another error occurs.
      * @since 1.78.0
      * @see <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.1%20Expanded%20Form">JCR 2.0, 3.2.5.1 Expanded Form</a>
diff --git a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
index 5eb76868cb..6f37d57b0b 100644
--- a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
+++ b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
@@ -860,7 +860,12 @@ public class SessionImpl implements JackrabbitSession {
             ItemImpl<?> itemImpl = checkItemImpl(item);
             return itemImpl.sessionContext.getExpandedJcrName(itemImpl.getOakName());
         } catch (IllegalStateException e) {
-            throw new RepositoryException("Namespace exception " + e.getMessage());
+            // unwrap RepositoryException when available
+            if (e.getCause() instanceof RepositoryException) {
+                throw (RepositoryException) e.getCause();
+            } else {
+                throw new RepositoryException("Namespace exception " + e.getMessage());
+            }
         }
     }
 
diff --git a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
index f7c86562ad..834afd47b2 100644
--- a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
+++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
@@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable;
 
 import javax.jcr.GuestCredentials;
 import javax.jcr.Item;
+import javax.jcr.NamespaceException;
 import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
@@ -86,20 +87,39 @@ public class JackrabbitSessionTest extends AbstractJCRTest {
         assertEquals("{}testroot", s.getExpandedName(testRootNode));
         Node n = testRootNode.addNode("test:bar");
         assertEquals("{http://www.apache.org/jackrabbit/test}bar", s.getExpandedName(n));
-        // now remap namespace uri
+        // now remap namespace uri - should not affect expanded name
+        assertEquals("prefix 'test' has unexpected mapping",
+                "http://www.apache.org/jackrabbit/test", s.getNamespaceURI("test"));
         s.setNamespacePrefix("test", "urn:foo");
-        assertEquals("{urn:foo}bar", s.getExpandedName(n));
+        assertEquals("{http://www.apache.org/jackrabbit/test}bar", s.getExpandedName(n));
         // use special namespace uri
         n = testRootNode.addNode("rep:bar");
         assertEquals("{internal}bar", s.getExpandedName(n));
     }
 
+    public void testGetExpandedNameBrokenNamespace() throws RepositoryException {
+        // empty namespace uri
+        assertEquals("{}testroot", s.getExpandedName(testRootNode));
+
+        // register broken namespace name
+        s.getWorkspace().getNamespaceRegistry().registerNamespace("foo", "bar");
+        Node n = testRootNode.addNode("foo:qux");
+
+        try {
+            // there is no expanded name, thus we expect an exception here
+            s.getExpandedName(n);
+            fail("there is no expanded name in this case, so we expect the call to fail");
+        } catch (NamespaceException ex) {
+            // expected
+        }
+    }
+
     public void testGetExpandedPath() throws RepositoryException {
         assertEquals("/{}testroot", s.getExpandedPath(testRootNode));
         Node n = testRootNode.addNode("test:bar").addNode("rep:bar");
         assertEquals("/{}testroot/{http://www.apache.org/jackrabbit/test}bar/{internal}bar", s.getExpandedPath(n));
-        // now remap namespace uri
+        // now remap namespace uri - should not affect expanded name
         s.setNamespacePrefix("test", "urn:foo");
-        assertEquals("/{}testroot/{urn:foo}bar/{internal}bar", s.getExpandedPath(n));
+        assertEquals("/{}testroot/{http://www.apache.org/jackrabbit/test}bar/{internal}bar", s.getExpandedPath(n));
     }
 }
\ No newline at end of file


(jackrabbit-oak) 02/02: OAK-11657: JackrabbitSession.getExpandedName/Path need to return stable expanded names - fix test case

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 2fe00869bb84d53a149adec74a17178118216ac5
Author: Julian Reschke <re...@apache.org>
AuthorDate: Thu May 22 15:49:54 2025 +0100

    OAK-11657: JackrabbitSession.getExpandedName/Path need to return stable expanded names - fix test case
---
 .../oak/jcr/session/JackrabbitSessionTest.java     | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
index 834afd47b2..77175486bf 100644
--- a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
+++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
@@ -29,6 +29,8 @@ import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
 
+import java.util.UUID;
+
 import static org.mockito.Mockito.mock;
 
 public class JackrabbitSessionTest extends AbstractJCRTest {
@@ -87,11 +89,9 @@ public class JackrabbitSessionTest extends AbstractJCRTest {
         assertEquals("{}testroot", s.getExpandedName(testRootNode));
         Node n = testRootNode.addNode("test:bar");
         assertEquals("{http://www.apache.org/jackrabbit/test}bar", s.getExpandedName(n));
-        // now remap namespace uri - should not affect expanded name
-        assertEquals("prefix 'test' has unexpected mapping",
-                "http://www.apache.org/jackrabbit/test", s.getNamespaceURI("test"));
+        // now remap namespace uri
         s.setNamespacePrefix("test", "urn:foo");
-        assertEquals("{http://www.apache.org/jackrabbit/test}bar", s.getExpandedName(n));
+        assertEquals("{urn:foo}bar", s.getExpandedName(n));
         // use special namespace uri
         n = testRootNode.addNode("rep:bar");
         assertEquals("{internal}bar", s.getExpandedName(n));
@@ -101,16 +101,22 @@ public class JackrabbitSessionTest extends AbstractJCRTest {
         // empty namespace uri
         assertEquals("{}testroot", s.getExpandedName(testRootNode));
 
+        String randomNamespacePrefix = "prefix-" + UUID.randomUUID();
+        String randomNamespaceName = "name-" + UUID.randomUUID();
+
         // register broken namespace name
-        s.getWorkspace().getNamespaceRegistry().registerNamespace("foo", "bar");
-        Node n = testRootNode.addNode("foo:qux");
+        s.getWorkspace().getNamespaceRegistry().registerNamespace(randomNamespacePrefix, randomNamespaceName);
 
         try {
+            Node n = testRootNode.addNode(randomNamespacePrefix + ":qux");
+
             // there is no expanded name, thus we expect an exception here
             s.getExpandedName(n);
             fail("there is no expanded name in this case, so we expect the call to fail");
         } catch (NamespaceException ex) {
             // expected
+        } finally {
+            s.getWorkspace().getNamespaceRegistry().unregisterNamespace(randomNamespacePrefix);
         }
     }
 
@@ -118,8 +124,8 @@ public class JackrabbitSessionTest extends AbstractJCRTest {
         assertEquals("/{}testroot", s.getExpandedPath(testRootNode));
         Node n = testRootNode.addNode("test:bar").addNode("rep:bar");
         assertEquals("/{}testroot/{http://www.apache.org/jackrabbit/test}bar/{internal}bar", s.getExpandedPath(n));
-        // now remap namespace uri - should not affect expanded name
+        // now remap namespace uri
         s.setNamespacePrefix("test", "urn:foo");
-        assertEquals("/{}testroot/{http://www.apache.org/jackrabbit/test}bar/{internal}bar", s.getExpandedPath(n));
+        assertEquals("/{}testroot/{urn:foo}bar/{internal}bar", s.getExpandedPath(n));
     }
 }
\ No newline at end of file