You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by re...@apache.org on 2025/11/24 19:16:45 UTC

(jackrabbit-filevault) 01/01: Fix handling of target node UUID for IdConflictPolicy.LEGACY

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

reschke pushed a commit to branch JCRVLT-828
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git

commit bd1d30c4d8822002d9061d2292c75777f13acc10
Author: Julian Reschke <re...@apache.org>
AuthorDate: Mon Nov 24 20:16:24 2025 +0100

    Fix handling of target node UUID for IdConflictPolicy.LEGACY
---
 .../ReferenceableIdentifiersImportIT.java          | 36 +++++++++++++++++++++-
 .../vault/fs/impl/io/DocViewImporter.java          | 25 ++++++++++++---
 2 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/ReferenceableIdentifiersImportIT.java b/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/ReferenceableIdentifiersImportIT.java
index 7694e504..aaa99abe 100644
--- a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/ReferenceableIdentifiersImportIT.java
+++ b/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/ReferenceableIdentifiersImportIT.java
@@ -697,4 +697,38 @@ public class ReferenceableIdentifiersImportIT extends IntegrationTestBase {
         options.setIdConflictPolicy(policy);
         extractVaultPackage("/test-packages/referenceable-dup.zip", options);
     }
-}
\ No newline at end of file
+
+    @Test
+    public void testUuidPreservedWithLegacyPolicy() throws Exception {
+        // 1. Install package with UUID from package
+        extractVaultPackageStrict("/test-packages/referenceable.zip");
+        Node node = admin.getNode("/tmp/referenceable");
+        String originalUuid = node.getIdentifier();
+        assertEquals(UUID_REFERENCEABLE, originalUuid);
+
+        // 2. Change the UUID manually (simulating an existing node with different UUID)
+        // This simulates the scenario where node exists with UUID1, package has UUID2
+        node.remove();
+        admin.save();
+        node = JcrUtils.getOrCreateByPath("/tmp/referenceable", null, JcrConstants.NT_UNSTRUCTURED, admin, true);
+        node.addMixin(JcrConstants.MIX_VERSIONABLE);
+        node.setProperty(PROPERTY_NAME, PROPERTY_VALUE); // Keep same property
+        admin.save();
+        String currentUuid = node.getIdentifier();
+        assertNotEquals("Node should have different UUID than package", UUID_REFERENCEABLE, currentUuid);
+
+        // 3. Re-import with LEGACY policy and UPDATE_PROPERTIES mode
+        // Expected: UUID should be preserved (not replaced with package UUID)
+        // Bug: UUID gets replaced unconditionally at line 996
+        ImportOptions opts = getDefaultOptions();
+        opts.setIdConflictPolicy(IdConflictPolicy.LEGACY);
+        opts.setImportMode(ImportMode.REPLACE);
+        extractVaultPackage("/test-packages/referenceable.zip", opts);
+
+        // 4. Verify UUID is preserved (not replaced)
+        node = admin.getNode("/tmp/referenceable");
+        String finalUuid = node.getIdentifier();
+        assertEquals("UUID should be preserved with LEGACY policy", currentUuid, finalUuid);
+        assertNotEquals("UUID should NOT be replaced with package UUID", UUID_REFERENCEABLE, finalUuid);
+    }
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
index bdab1ffb..212a73f3 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
@@ -906,8 +906,8 @@ public class DocViewImporter implements DocViewParserHandler {
                             } else {
                                 log.warn("To-be imported node and existing conflicting node have different parents. Will create new identifier for the former. ({})",
                                         newNodePath);
-                                preprocessedProperties.removeIf(p -> p.getName().equals(NameConstants.JCR_UUID) 
-                                        || p.getName().equals(NameConstants.JCR_BASEVERSION) 
+                                preprocessedProperties.removeIf(p -> p.getName().equals(NameConstants.JCR_UUID)
+                                        || p.getName().equals(NameConstants.JCR_BASEVERSION)
                                         || p.getName().equals(NameConstants.JCR_PREDECESSORS)
                                         || p.getName().equals(NameConstants.JCR_SUCCESSORS)
                                         || p.getName().equals(NameConstants.JCR_VERSIONHISTORY));
@@ -915,7 +915,11 @@ public class DocViewImporter implements DocViewParserHandler {
                         }
                     }
                 } catch (ItemNotFoundException e) {
-                    // ignore
+                    // no node with same ID present, but target node exists
+                    if (existingNode != null && idConflictPolicy == IdConflictPolicy.LEGACY && existingNode.isNodeType(JcrConstants.MIX_REFERENCEABLE)) {
+                        log.debug("IdConflictPolicy.LEGACY - removing Identifier " + identifier.get() + " from imported package at: " + docViewNode.getName());
+                        preprocessedProperties.removeIf(p -> p.getName().equals(NameConstants.JCR_UUID));
+                    }
                 }
             }
         }
@@ -1039,6 +1043,9 @@ public class DocViewImporter implements DocViewParserHandler {
                         newMixins.add(mixin);
                     }
                 }
+
+                boolean wasReferenceable = node.isNodeType(JcrConstants.MIX_REFERENCEABLE);
+
                 // remove mixins not in package (only for mode = replace)
                 if (importMode == ImportMode.REPLACE) {
                     for (NodeType mix : node.getMixinNodeTypes()) {
@@ -1049,12 +1056,20 @@ public class DocViewImporter implements DocViewParserHandler {
                                     || acHandling == AccessControlHandling.CLEAR
                                     || acHandling == AccessControlHandling.OVERWRITE) {
                                 vs.ensureCheckedOut();
-                                node.removeMixin(name);
+
+                                // can't remove a mixin which would remove the UUID in LEGACY Mode
+                                if (idConflictPolicy == idConflictPolicy.LEGACY && wasReferenceable && mix.isNodeType(JcrConstants.MIX_REFERENCEABLE)) {
+                                    log.debug("idConflictPolicy.LEGACY: not removing mixin " + mix.getName() + ", so that UUID can be preserved.");
+                                } else {
+                                    node.removeMixin(name);
+                                }
+
                                 updatedNode = node;
                             }
                         }
                     }
                 }
+
                 // add remaining mixins (for all import modes)
                 for (String mixin : newMixins) {
                     vs.ensureCheckedOut();
@@ -1080,7 +1095,7 @@ public class DocViewImporter implements DocViewParserHandler {
             }
             EffectiveNodeType effectiveNodeType = EffectiveNodeType.ofNode(node);
             Collection<DocViewProperty2> unprotectedProperties = removeProtectedProperties(ni.getProperties(), effectiveNodeType, node.getPath(), PROTECTED_PROPERTIES_CONSIDERED_FOR_UPDATED_NODES);
-            
+
             // add/modify properties contained in package
             if (setProperties(node, ni,unprotectedProperties, importMode == ImportMode.REPLACE|| importMode == ImportMode.UPDATE || importMode == ImportMode.UPDATE_PROPERTIES, vs)) {
                 updatedNode = node;