Package org.modeshape.jcr.cache.change

Examples of org.modeshape.jcr.cache.change.RecordingChanges


        // print = true;
        printMessage("Received change sets: \n" + listener.receivedChangeSet);

        // The first change set should be from the auto-created (since with the MockConnectorWithChanges will fire
        // off the events *before* the 'session.save()' call above completes...
        RecordingChanges changes = listener.receivedChangeSet.get(0);
        Iterator<Change> iter = changes.iterator();
        assertNodeAdded(iter.next(), "/projection1/generated-out/testNode1");
        assertNodeAdded(iter.next(), "/projection1/generated-out/testNode1/child0");
        assertNodeAdded(iter.next(), "/projection1/generated-out/testNode1/child1");
        assertNodeAdded(iter.next(), "/projection1/generated-out/testNode1/child2");
        assertThat(iter.hasNext(), is(false));

        // The remaining change set is due to our manually-created node saved above ...
        changes = listener.receivedChangeSet.get(1);
        iter = changes.iterator();
        assertNodeAdded(iter.next(), "/projection1/generate/testNode1");
        assertPropertyAdded(iter.next(), "/projection1/generate/testNode1", "jcr:primaryType");
        assertThat(iter.hasNext(), is(false));
        changeBus.unregister(listener);

        // expect 2 change sets (1 for the node we're adding, and one for the auto-generated events) ...
        listener = new TestListener(2);
        changeBus.register(listener);

        // Remove the recently-added node ...
        session.refresh(false);
        node = session.getNode("/projection1/generate/testNode1");
        node.remove();
        session.save();

        // Wait until the listener gets some events ...
        listener.await();
        assertThat("Didn't receive changes after node removal!", listener.receivedChangeSet.size(), is(2));
        // print = true;
        printMessage("Received change sets: \n" + listener.receivedChangeSet);

        // The first change set should be from the automatic removal (since with the MockConnectorWithChanges will fire
        // off the events *before* the 'session.save()' call above completes...
        changes = listener.receivedChangeSet.get(0);
        iter = changes.iterator();
        assertNodeRemoved(iter.next(), "/projection1/generated-out/testNode1");
        assertThat(iter.hasNext(), is(false));

        // The remaining change set is due to our manually-created node saved above ...
        changes = listener.receivedChangeSet.get(1);
        iter = changes.iterator();
        assertNodeRemoved(iter.next(), "/projection1/generate/testNode1");
        assertThat(iter.hasNext(), is(false));
        changeBus.unregister(listener);
    }
View Full Code Here


                                      List<AbstractJcrNode> outputNodes,
                                      JcrSession outputSession,
                                      String sequencerName ) throws RepositoryException {

        final ExecutionContext context = outputSession.context();
        RecordingChanges sequencingChanges = new RecordingChanges(outputSession.sessionId(), context.getProcessId(),
                                                                  outputSession.getRepository().repositoryKey(),
                                                                  outputSession.workspaceName(), outputSession.getRepository()
                                                                                                              .journalId());
        Name primaryType = sequencedNode.getPrimaryTypeName();
        Set<Name> mixinTypes = sequencedNode.getMixinTypeNames();
        for (AbstractJcrNode outputNode : outputNodes) {

            sequencingChanges.nodeSequenced(sequencedNode.key(), sequencedNode.path(), primaryType, mixinTypes, outputNode.key(),
                                            outputNode.path(), work.getOutputPath(), work.getUserId(), work.getSelectedPath(),
                                            sequencerName, sequencedNode.node().isQueryable(outputSession.cache()));
        }
        sequencingChanges.freeze(outputSession.getUserID(), null, context.getValueFactories().getDateFactory().create());
        repository.changeBus().notify(sequencingChanges);
    }
View Full Code Here

        assert sequencedNode != null;
        assert inputSession != null;
        Name primaryType = sequencedNode.getPrimaryTypeName();
        Set<Name> mixinTypes = sequencedNode.getMixinTypeNames();
        final ExecutionContext context = inputSession.context();
        RecordingChanges sequencingChanges = new RecordingChanges(inputSession.sessionId(), context.getProcessId(),
                                                                  inputSession.getRepository().repositoryKey(),
                                                                  inputSession.workspaceName(), inputSession.getRepository()
                                                                                                            .journalId());
        sequencingChanges.nodeSequencingFailure(sequencedNode.key(), sequencedNode.path(), primaryType, mixinTypes,
                                                work.getOutputPath(), work.getUserId(), work.getSelectedPath(), sequencerName,
                                                sequencedNode.node().isQueryable(inputSession.cache()), cause);
        repository.changeBus().notify(sequencingChanges);
    }
View Full Code Here

    protected final RecordingChanges changesFor( WorkspaceAndPath workspaceAndPath ) {
        return changesFor(workspaceAndPath.getWorkspaceName());
    }

    protected final RecordingChanges changesFor( String workspaceName ) {
        RecordingChanges changes = changesByWorkspace.get(workspaceName);
        if (changes == null) {
            changes = new RecordingChanges(sessionId, processId, repositoryKey, workspaceName, journalId);
            changesByWorkspace.put(workspaceName, changes);
        }
        return changes;
    }
View Full Code Here

            // And notify the others ...
            String userId = context.getSecurityContext().getUserName();
            Map<String, String> userData = context.getData();
            DateTime timestamp = context.getValueFactories().getDateFactory().create();
            RecordingChanges changes = new RecordingChanges(context.getId(), context.getProcessId(), this.getKey(), null,
                                                            sessionContext.journalId());
            changes.repositoryMetadataChanged();
            changes.freeze(userId, userData, timestamp);
            this.changeBus.notify(changes);
        }
    }
View Full Code Here

            // And notify the others ...
            String userId = context.getSecurityContext().getUserName();
            Map<String, String> userData = context.getData();
            DateTime timestamp = context.getValueFactories().getDateFactory().create();
            RecordingChanges changes = new RecordingChanges(context.getId(), context.getProcessId(), this.getKey(), null,
                                                            sessionContext.journalId());
            changes.workspaceAdded(name);
            changes.freeze(userId, userData, timestamp);
            this.changeBus.notify(changes);
        }
        return workspace(name);
    }
View Full Code Here

            // And notify the others - this notification will clear & close the WS cache via the local listener
            String userId = context.getSecurityContext().getUserName();
            Map<String, String> userData = context.getData();
            DateTime timestamp = context.getValueFactories().getDateFactory().create();
            RecordingChanges changes = new RecordingChanges(context.getId(), context.getProcessId(), this.getKey(), null,
                                                            sessionContext.journalId());
            changes.workspaceRemoved(name);
            changes.freeze(userId, userData, timestamp);
            this.changeBus.notify(changes);
            return true;
        }
        return false;
    }
View Full Code Here

        Map<String, String> userData = context.getData();
        final boolean acquireLock = false; // we already pre-locked all of the existing documents that we'll edit ...
        DateTime timestamp = context.getValueFactories().getDateFactory().create();
        String workspaceName = persistedCache.getWorkspaceName();
        String repositoryKey = persistedCache.getRepositoryKey();
        RecordingChanges changes = new RecordingChanges(context.getId(), context.getProcessId(), repositoryKey, workspaceName,
                                                        sessionContext().journalId());

        // Get the documentStore ...
        DocumentStore documentStore = persistedCache.documentStore();
        DocumentTranslator translator = persistedCache.translator();

        PathCache sessionPaths = new PathCache(this);
        PathCache workspacePaths = new PathCache(persistedCache);

        Set<NodeKey> removedNodes = null;
        Set<BinaryKey> unusedBinaryKeys = new HashSet<>();
        Set<BinaryKey> usedBinaryKeys = new HashSet<>();
        Set<NodeKey> renamedExternalNodes = new HashSet<>();
        for (NodeKey key : changedNodesInOrder) {
            SessionNode node = changedNodes.get(key);
            String keyStr = key.toString();
            boolean isExternal = !node.getKey().getSourceKey().equalsIgnoreCase(workspaceCache().getRootKey().getSourceKey());
            if (node == REMOVED) {
                // We need to read some information from the node before we remove it ...
                CachedNode persisted = persistedCache.getNode(key);
                if (persisted != null) {
                    // This was a persistent node, so we have to generate an event and deal with the remove ...
                    if (removedNodes == null) {
                        removedNodes = new HashSet<>();
                    }
                    Name primaryType = persisted.getPrimaryType(this);
                    Set<Name> mixinTypes = persisted.getMixinTypes(this);
                    Path path = workspacePaths.getPath(persisted);
                    boolean queryable = persisted.isQueryable(this);
                    changes.nodeRemoved(key, persisted.getParentKey(persistedCache), path, primaryType, mixinTypes, queryable);
                    removedNodes.add(key);

                    // if there were any referrer changes for the removed nodes, we need to process them
                    ReferrerChanges referrerChanges = referrerChangesForRemovedNodes.get(key);
                    if (referrerChanges != null) {
                        EditableDocument doc = documentStore.edit(keyStr, false, acquireLock);
                        if (doc != null) translator.changeReferrers(doc, referrerChanges);
                    }

                    // if the node had any binary properties, make sure we decrement the ref count of each
                    for (Iterator<Property> propertyIterator = persisted.getProperties(persistedCache); propertyIterator.hasNext();) {
                        Property property = propertyIterator.next();
                        if (property.isBinary()) {
                            Object value = property.isMultiple() ? Arrays.asList(property.getValuesAsArray()) : property.getFirstValue();
                            translator.decrementBinaryReferenceCount(value, unusedBinaryKeys, null);
                        }
                    }

                    // Note 1: Do not actually remove the document from the documentStore yet; see below (note 2)
                }
                // Otherwise, the removed node was created in the session (but not ever persisted),
                // so we don't have to do anything ...
            } else {
                // Get the primary and mixin type names; even though we're passing in the session, the two properties
                // should be there and shouldn't require a looking in the cache...
                Name primaryType = node.getPrimaryType(this);
                Set<Name> mixinTypes = node.getMixinTypes(this);
                boolean queryable = node.isQueryable(this);

                CachedNode persisted = null;
                Path newPath = sessionPaths.getPath(node);
                NodeKey newParent = node.newParent();
                EditableDocument doc = null;
                ChangedAdditionalParents additionalParents = node.additionalParents();

                if (node.isNew()) {
                    doc = Schematic.newDocument();
                    translator.setKey(doc, key);
                    translator.setParents(doc, newParent, null, additionalParents);
                    // Create an event ...
                    changes.nodeCreated(key, newParent, newPath, primaryType, mixinTypes, node.changedProperties(), queryable);
                } else {
                    doc = documentStore.edit(keyStr, true, acquireLock);
                    if (doc == null) {
                        if (isExternal && renamedExternalNodes.contains(key)) {
                            // this is a renamed external node which has been processed in the parent, so we can skip it
                            continue;
                        }
                        // Could not find the entry in the documentStore, which means it was deleted by someone else
                        // just moments before we got our transaction to save ...
                        throw new DocumentNotFoundException(keyStr);
                    }
                    if (newParent != null) {
                        persisted = persistedCache.getNode(key);
                        // The node has moved (either within the same parent or to another parent) ...
                        Path oldPath = workspacePaths.getPath(persisted);
                        NodeKey oldParentKey = persisted.getParentKey(persistedCache);
                        if (!oldParentKey.equals(newParent) || (additionalParents != null && !additionalParents.isEmpty())) {
                            translator.setParents(doc, node.newParent(), oldParentKey, additionalParents);
                        }
                        // We only want to fire the event if the node we're working with is in the same workspace as the current
                        // workspace. The node will be in a different workspace when it is linked or un-linked
                        // (e.g. shareable node or jcr:system).
                        String workspaceKey = node.getKey().getWorkspaceKey();
                        boolean isSameWorkspace = persistedCache.getWorkspaceKey().equalsIgnoreCase(workspaceKey);
                        if (isSameWorkspace) {
                            changes.nodeMoved(key, primaryType, mixinTypes, newParent, oldParentKey, newPath, oldPath, queryable);
                        }
                    } else if (additionalParents != null) {
                        // The node in another workspace has been linked to this workspace ...
                        translator.setParents(doc, null, null, additionalParents);
                    }

                    // Deal with mixin changes here (since for new nodes they're put into the properties) ...
                    MixinChanges mixinChanges = node.mixinChanges(false);
                    if (mixinChanges != null && !mixinChanges.isEmpty()) {
                        Property oldProperty = translator.getProperty(doc, JcrLexicon.MIXIN_TYPES);
                        translator.addPropertyValues(doc, JcrLexicon.MIXIN_TYPES, true, mixinChanges.getAdded(),
                                                     unusedBinaryKeys, usedBinaryKeys);
                        translator.removePropertyValues(doc, JcrLexicon.MIXIN_TYPES, mixinChanges.getRemoved(), unusedBinaryKeys,
                                                        usedBinaryKeys);
                        // the property was changed ...
                        Property newProperty = translator.getProperty(doc, JcrLexicon.MIXIN_TYPES);
                        if (oldProperty == null) {
                            changes.propertyAdded(key, primaryType, mixinTypes, newPath, newProperty, queryable);
                        } else if (newProperty == null) {
                            changes.propertyRemoved(key, primaryType, mixinTypes, newPath, oldProperty, queryable);
                        } else {
                            changes.propertyChanged(key, primaryType, mixinTypes, newPath, newProperty, oldProperty, queryable);
                        }
                    }
                }

                LockChange lockChange = node.getLockChange();
                if (lockChange != null) {
                    switch (lockChange) {
                        case LOCK_FOR_SESSION:
                        case LOCK_FOR_NON_SESSION:
                            // check is another session has already locked the document
                            if (translator.isLocked(doc)) {
                                throw new LockFailureException(key);
                            }
                            break;
                        case UNLOCK:
                            break;
                    }
                }

                // As we go through the removed and changed properties, we want to keep track of whether there are any
                // effective modifications to the persisted properties.
                boolean hasPropertyChanges = false;

                // Save the removed properties ...
                Set<Name> removedProperties = node.removedProperties();
                if (!removedProperties.isEmpty()) {
                    assert !node.isNew();
                    if (persisted == null) {
                        persisted = persistedCache.getNode(key);
                    }
                    for (Name name : removedProperties) {
                        Property oldProperty = translator.removeProperty(doc, name, unusedBinaryKeys, usedBinaryKeys);
                        if (oldProperty != null) {
                            // the property was removed ...
                            changes.propertyRemoved(key, primaryType, mixinTypes, newPath, oldProperty, queryable);
                            // and we know that there are modifications to the properties ...
                            hasPropertyChanges = true;
                        }
                    }
                }

                // Save the changes to the properties
                if (!node.changedProperties().isEmpty()) {
                    if (!node.isNew() && persisted == null) {
                        persisted = persistedCache.getNode(key);
                    }
                    for (Map.Entry<Name, Property> propEntry : node.changedProperties().entrySet()) {
                        Name name = propEntry.getKey();
                        Property prop = propEntry.getValue();
                        // Get the old property ...
                        Property oldProperty = persisted != null ? persisted.getProperty(name, persistedCache) : null;
                        translator.setProperty(doc, prop, unusedBinaryKeys, usedBinaryKeys);
                        if (oldProperty == null) {
                            // the property was created ...
                            changes.propertyAdded(key, primaryType, mixinTypes, newPath, prop, queryable);
                            // and we know that there are modifications to the properties ...
                            hasPropertyChanges = true;
                        } else if (hasPropertyChanges || !oldProperty.equals(prop)) {
                            // The 'hasPropertyChanges ||' in the above condition is what gives us the "slight optimization"
                            // mentioned in the longer comment above. This is noticeably more efficient (since the
                            // '!oldProperty.equals(prop)' has to be called for only some of the changes) and does result
                            // in correct indexing behavior, but the compromise is that some no-op property changes will
                            // result in a PROPERTY_CHANGE event. To remove all potential no-op PROPERTY CHANGE events,
                            // simply remove the 'hasPropertyChanges||' in the above condition.
                            // See MODE-1856 for details.

                            // the property was changed and is actually different than the persisted property ...
                            changes.propertyChanged(key, primaryType, mixinTypes, newPath, prop, oldProperty, queryable);
                            hasPropertyChanges = true;
                        }
                    }
                }

                // Save the change to the child references. Note that we only need to generate events for renames;
                // moves (to the same or another parent), removes, and inserts are all recorded as changes in the
                // child node, and events are generated handled when we process
                // the child node.
                ChangedChildren changedChildren = node.changedChildren();
                MutableChildReferences appended = node.appended(false);
                if ((changedChildren == null || changedChildren.isEmpty()) && (appended != null && !appended.isEmpty())) {
                    // Just appended children ...
                    translator.changeChildren(doc, changedChildren, appended);
                } else if (changedChildren != null && !changedChildren.isEmpty()) {
                    if (!changedChildren.getRemovals().isEmpty()) {
                        // This node is not being removed (or added), but it has removals, and we have to calculate the paths
                        // of the removed nodes before we actually change the child references of this node.
                        for (NodeKey removed : changedChildren.getRemovals()) {
                            CachedNode persistent = persistedCache.getNode(removed);
                            if (persistent != null) {
                                if (appended != null && appended.hasChild(persistent.getKey())) {
                                    // the same node has been both removed and appended => reordered at the end
                                    ChildReference appendedChildRef = node.getChildReferences(this).getChild(persistent.getKey());
                                    newPath = pathFactory().create(sessionPaths.getPath(node), appendedChildRef.getSegment());
                                    Path oldPath = workspacePaths.getPath(persistent);
                                    changes.nodeReordered(persistent.getKey(), primaryType, mixinTypes, node.getKey(), newPath,
                                                          oldPath, null, queryable);
                                }
                            }

                        }
                    }

                    // Now change the children ...
                    translator.changeChildren(doc, changedChildren, appended);

                    // Generate events for renames, as this is only captured in the parent node ...
                    Map<NodeKey, Name> newNames = changedChildren.getNewNames();
                    if (!newNames.isEmpty()) {
                        for (Map.Entry<NodeKey, Name> renameEntry : newNames.entrySet()) {
                            NodeKey renamedKey = renameEntry.getKey();
                            CachedNode oldRenamedNode = persistedCache.getNode(renamedKey);
                            if (oldRenamedNode == null) {
                                // The node was created in this session, so we can ignore this ...
                                continue;
                            }
                            Path renamedFromPath = workspacePaths.getPath(oldRenamedNode);
                            Path renamedToPath = pathFactory().create(renamedFromPath.getParent(), renameEntry.getValue());
                            changes.nodeRenamed(renamedKey, renamedToPath, renamedFromPath.getLastSegment(), primaryType,
                                                mixinTypes, queryable);
                            if (isExternal) {
                                renamedExternalNodes.add(renamedKey);
                            }
                        }
                    }

                    // generate reordering events for nodes which have not been reordered to the end
                    Map<NodeKey, SessionNode.Insertions> insertionsByBeforeKey = changedChildren.getInsertionsByBeforeKey();
                    for (SessionNode.Insertions insertion : insertionsByBeforeKey.values()) {
                        for (ChildReference insertedRef : insertion.inserted()) {
                            CachedNode insertedNodePersistent = persistedCache.getNode(insertedRef);
                            CachedNode insertedNode = getNode(insertedRef.getKey());
                            Path nodeNewPath = sessionPaths.getPath(insertedNode);
                            if (insertedNodePersistent != null) {
                                Path nodeOldPath = workspacePaths.getPath(insertedNodePersistent);
                                Path insertedBeforePath = null;
                                CachedNode insertedBeforeNode = persistedCache.getNode(insertion.insertedBefore());
                                if (insertedBeforeNode != null) {
                                    insertedBeforePath = workspacePaths.getPath(insertedBeforeNode);
                                    boolean isSnsReordering = nodeOldPath.getLastSegment().getName()
                                                                         .equals(insertedBeforePath.getLastSegment().getName());
                                    if (isSnsReordering) {
                                        nodeNewPath = insertedBeforePath;
                                    }
                                }
                                changes.nodeReordered(insertedRef.getKey(), insertedNode.getPrimaryType(this),
                                                      insertedNode.getMixinTypes(this), node.getKey(), nodeNewPath, nodeOldPath,
                                                      insertedBeforePath, queryable);

                            } else {
                                // if the node is new and reordered at the same time (most likely due to either a version restore
                                // or explicit reordering of transient nodes) there is no "old path"
                                CachedNode insertedBeforeNode = getNode(insertion.insertedBefore().getKey());
                                Path insertedBeforePath = sessionPaths.getPath(insertedBeforeNode);
                                changes.nodeReordered(insertedRef.getKey(), insertedNode.getPrimaryType(this),
                                                      insertedNode.getMixinTypes(this), node.getKey(), nodeNewPath, null,
                                                      insertedBeforePath, queryable);
                            }
                        }
                    }
                }

                ReferrerChanges referrerChanges = node.getReferrerChanges();
                boolean nodeChanged = false;
                if (referrerChanges != null && !referrerChanges.isEmpty()) {
                    translator.changeReferrers(doc, referrerChanges);
                    changes.nodeChanged(key, newPath, primaryType, mixinTypes, queryable);
                    nodeChanged = true;
                }

                // write the federated segments
                for (Map.Entry<String, String> federatedSegment : node.getAddedFederatedSegments().entrySet()) {
                    String externalNodeKey = federatedSegment.getKey();
                    String childName = federatedSegment.getValue();
                    translator.addFederatedSegment(doc, externalNodeKey, childName);
                    if (!nodeChanged) {
                        changes.nodeChanged(key, newPath, primaryType, mixinTypes, queryable);
                        nodeChanged = true;
                    }
                }
                Set<String> removedFederatedSegments = node.getRemovedFederatedSegments();
                if (!removedFederatedSegments.isEmpty()) {
                    translator.removeFederatedSegments(doc, node.getRemovedFederatedSegments());
                    if (!nodeChanged) {
                        changes.nodeChanged(key, newPath, primaryType, mixinTypes, queryable);
                        nodeChanged = true;
                    }
                }

                // write additional node "metadata", meaning various flags which have internal meaning
                if (!queryable) {
                    // we are only interested if the node is not queryable, as by default all nodes are queryable.
                    translator.setQueryable(doc, false);
                }

                if (node.isNew()) {
                    // We need to create the schematic entry for the new node ...
                    if (documentStore.storeDocument(keyStr, doc) != null) {
                        if (replacedNodes != null && replacedNodes.contains(key)) {
                            // Then a node is being removed and recreated with the same key ...
                            documentStore.localStore().put(keyStr, doc);
                        } else if (removedNodes != null && removedNodes.contains(key)) {
                            // Then a node is being removed and recreated with the same key ...
                            documentStore.localStore().put(keyStr, doc);
                            removedNodes.remove(key);
                        } else {
                            // We couldn't create the entry because one already existed ...
                            throw new DocumentAlreadyExistsException(keyStr);
                        }
                    }
                } else {
                    boolean externalNodeChanged = isExternal
                                                  && (hasPropertyChanges || node.hasNonPropertyChanges() || node.changedChildren()
                                                                                                                .renameCount() > 0);

                    // writable connectors *may* change their data in-place, so the update operation needs to be called only
                    // after the index changes have finished.
                    if (externalNodeChanged) {
                        // in the case of external nodes, only if there are changes should the update be called
                        documentStore.updateDocument(keyStr, doc, node);
                    }
                }

                // The above code doesn't properly generate events for newly linked or unlinked nodes (e.g., shareable nodes
                // in JCR), because NODE_ADDED or NODE_REMOVED events are generated based upon the creation or removal of the
                // child nodes, whereas linking and unlinking nodes don't result in creation/removal of nodes. Instead,
                // the linked/unlinked node is modified with the addition/removal of additional parents.
                //
                // NOTE that this happens somewhat rarely (as linked/shared nodes are used far less frequently) ...
                //
                if (additionalParents != null) {
                    // Generate NODE_ADDED events for each of the newly-added parents ...
                    for (NodeKey parentKey : additionalParents.getAdditions()) {
                        // Find the mutable parent node (if it exists) ...
                        SessionNode parent = this.changedNodes.get(parentKey);
                        if (parent != null) {
                            // Then the parent was changed in this session, so find the one-and-only child reference ...
                            ChildReference ref = parent.getChildReferences(this).getChild(key);
                            Path parentPath = sessionPaths.getPath(parent);
                            Path childPath = pathFactory().create(parentPath, ref.getSegment());
                            changes.nodeCreated(key, parentKey, childPath, primaryType, mixinTypes, null, queryable);
                        }
                    }
                    // Generate NODE_REMOVED events for each of the newly-removed parents ...
                    for (NodeKey parentKey : additionalParents.getRemovals()) {
                        // We need to read some information from the parent node before it was changed ...
                        CachedNode persistedParent = persistedCache.getNode(parentKey);
                        if (persistedParent != null) {
                            // Find the path to the removed child ...
                            ChildReference ref = persistedParent.getChildReferences(this).getChild(key);
                            if (ref != null) {
                                Path parentPath = workspacePaths.getPath(persistedParent);
                                Path childPath = pathFactory().create(parentPath, ref.getSegment());
                                changes.nodeRemoved(key, parentKey, childPath, primaryType, mixinTypes, queryable);
                            }
                        }
                    }
                }
            }
        }

        if (removedNodes != null) {
            assert !removedNodes.isEmpty();
            // we need to collect the referrers at the end only, so that other potential changes in references have been computed
            Set<NodeKey> referrers = new HashSet<NodeKey>();
            for (NodeKey removedKey : removedNodes) {
                // we need the current document from the documentStore, because this differs from what's persisted
                SchematicEntry entry = documentStore.get(removedKey.toString());
                if (entry != null) {
                    // The entry hasn't yet been removed by another (concurrent) session ...
                    Document doc = documentStore.get(removedKey.toString()).getContent();
                    referrers.addAll(translator.getReferrers(doc, ReferenceType.STRONG));
                }
            }
            // check referential integrity ...
            referrers.removeAll(removedNodes);

            if (!referrers.isEmpty()) {
                throw new ReferentialIntegrityException(removedNodes, referrers);
            }

            // Now remove all of the nodes from the documentStore.
            // Note 2: we do this last because the children are removed from their parent before the removal is handled above
            // (see Node 1), meaning getting the path and other information for removed nodes never would work properly.
            for (NodeKey removedKey : removedNodes) {
                documentStore.remove(removedKey.toString());
            }
        }

        if (!unusedBinaryKeys.isEmpty()) {
            // There are some binary values that are no longer referenced ...
            for (BinaryKey key : unusedBinaryKeys) {
                changes.binaryValueNoLongerUsed(key);
            }
        }

        if (!usedBinaryKeys.isEmpty()) {
            // There are some binary values which need to be marked as used ...
            for (BinaryKey key : usedBinaryKeys) {
                changes.binaryValueUsed(key);
            }
        }

        changes.setChangedNodes(changedNodes.keySet()); // don't need to make a copy
        changes.freeze(userId, userData, timestamp);
        return changes;
    }
View Full Code Here

TOP

Related Classes of org.modeshape.jcr.cache.change.RecordingChanges

Copyright © 2018 www.massapicom. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.