All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.modeshape.jcr.JcrSharedNodeCache Maven / Gradle / Ivy

There is a newer version: 5.4.1.Final
Show newest version
/*
 * ModeShape (http://www.modeshape.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.modeshape.jcr;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.modeshape.jcr.cache.CachedNode;
import org.modeshape.jcr.cache.NodeKey;
import org.modeshape.jcr.cache.SessionCache;
import org.modeshape.jcr.value.Path;

/**
 * A class that manages for a single {@link JcrSession} the various sets of shared nodes for each of the shareable nodes. Some
 * terminology:
 * 
    *
  • shareable node - A node that has the mix:shareable mixin, and which is capable of being shared.
  • *
  • shared node - The result of sharing a shareable node. Each shared node has its own name and location.
  • *
*

* The set of Node instances that represent the shared nodes for a given shareable node could be stored in the AbstractJcrNode * instance that is the shareable node. However, since the "mix:shareable" can be added or removed at any time, we can't create a * subclass of AbstractJcrNode to store this information, and we also don't want to store a null reference in every * AbstractJcrNode instance. *

*

* Therefore, this class is created lazily within the {@link JcrSession} and is used to maintain the Node instances for each of * the shared nodes for each shareable node. This cache is required so that clients always get the same Node instance. *

*/ final class JcrSharedNodeCache { private final ConcurrentMap sharedSets = new ConcurrentHashMap(); protected final JcrSession session; JcrSharedNodeCache( JcrSession session ) { this.session = session; } public JcrSession session() { return session; } /** * Get the {@link SharedSet} for the given shareable node. * * @param shareableNode the shareable node * @return the SharedSet; never null */ public SharedSet getSharedSet( AbstractJcrNode shareableNode ) { NodeKey shareableNodeKey = shareableNode.key(); SharedSet sharedSet = sharedSets.get(shareableNodeKey); if (sharedSet == null) { SharedSet newSharedSet = new SharedSet(shareableNode); sharedSet = sharedSets.putIfAbsent(shareableNodeKey, newSharedSet); if (sharedSet == null) sharedSet = newSharedSet; } return sharedSet; } /** * Signal that the supplied shareable node was destroyed. This method will remove the corresponding {@link SharedSet}. * * @param shareableNodeKey the key shareable node that was destroyed */ public void destroyed( NodeKey shareableNodeKey ) { sharedSets.remove(shareableNodeKey); } /** * Signal that the supplied node was removed from it's parent. This method will adjust the corresponding {@link SharedSet} * even if the removed node was the original shareable node. * * @param shareableNode the shareable node that was removed from its shared set * @throws RepositoryException if there is a problem */ public void removed( AbstractJcrNode shareableNode ) throws RepositoryException { NodeKey shareableNodeKey = shareableNode.key(); SharedSet sharedSet = getSharedSet(shareableNode); if (sharedSet.shareableNode == shareableNode) { // We're removing the original shareable node, so we need to recreate the SharedSet after // we figure out the new parent ... session().releaseCachedNode(shareableNode); AbstractJcrNode newShared = session().node(shareableNodeKey, null); SharedSet newSharedSet = new SharedSet(newShared); sharedSets.put(shareableNodeKey, newSharedSet); } else { assert shareableNode instanceof JcrSharedNode; sharedSet.remove((JcrSharedNode)shareableNode); } } /** * The set of shared nodes for a single shareable node. */ final class SharedSet { /** The sharable node */ protected final AbstractJcrNode shareableNode; /** The cache of Node instances that represent the shared nodes for a single shareable node. */ private final ConcurrentMap sharedNodesByParentKey = new ConcurrentHashMap(); protected SharedSet( AbstractJcrNode shareableNode ) { this.shareableNode = shareableNode; assert this.shareableNode != null; } /** * Get the NodeKey for the shareable node. * * @return the key; never null and always the same */ public final NodeKey key() { return shareableNode.key(); } /** * Get the {@link Session} to which this set belongs. * * @return the session instance; never null and always the same */ public final JcrSession session() { return JcrSharedNodeCache.this.session(); } /** * Get the {@link JcrSharedNode shared Node instance} that is a child of the specified parent. * * @param cachedNode the cached node; may not be null * @param parentKey the key of the parent node; may not b enull * @return the shared node instance; never null */ public AbstractJcrNode getSharedNode( CachedNode cachedNode, NodeKey parentKey ) { assert parentKey != null; try { NodeKey actualParentKey = shareableNode.parentKey(); assert actualParentKey != null; if (!actualParentKey.equals(parentKey)) { // Obtain the set of keys for all parents ... final SessionCache cache = session.cache(); Set additionalParents = cachedNode.getAdditionalParentKeys(cache); // And get the shared node for the parent key, first making sure the parent exists ... if (additionalParents.contains(parentKey) && session.nodeExists(parentKey)) { return getOrCreateSharedNode(parentKey); } } return shareableNode; } catch (RepositoryException e) { throw new RuntimeException(e); } } /** * Determine and return the first of any nodes in the shared set exist at a location that is at or below the supplied * path. * * @param path the path; may not be null * @return the node in the shared set that exists at or below the supplied path; or null if none of the nodes in the * shared set are at or below the supplied path * @throws ItemNotFoundException * @throws InvalidItemStateException * @throws RepositoryException */ public AbstractJcrNode getSharedNodeAtOrBelow( Path path ) throws RepositoryException, ItemNotFoundException, InvalidItemStateException { NodeIterator iter = getSharedNodes(); while (iter.hasNext()) { AbstractJcrNode shared = (AbstractJcrNode)iter.nextNode(); if (shared.path().isAtOrBelow(path)) return shared; } return null; } protected void remove( JcrSharedNode sharedNode ) { sharedNodesByParentKey.remove(sharedNode.parentKey()); } private JcrSharedNode getOrCreateSharedNode( NodeKey parentKey ) { assert parentKey != null; JcrSharedNode sharedNode = sharedNodesByParentKey.get(parentKey); if (sharedNode == null) { JcrSharedNode newShared = new JcrSharedNode(this, parentKey); sharedNode = sharedNodesByParentKey.putIfAbsent(parentKey, newShared); if (sharedNode == null) sharedNode = newShared; } return sharedNode; } /** * Get the number of shared nodes within this shared set. * * @return the number of shared nodes; always 1 or more * @throws RepositoryException if there's a problem getting the size */ public int getSize() throws RepositoryException { final SessionCache cache = session().cache(); Set additionalParents = shareableNode.node().getAdditionalParentKeys(cache); return additionalParents.size() + 1; } /** * Find all of the {@link javax.jcr.Node}s that make up the shared set. * * @return the iterator over the nodes in the node set; never null, but possibly empty if this is not shareable, or of * size 1 if the node is shareable but hasn't been shared * @throws RepositoryException if there is a problem finding the nodes in the set */ public NodeIterator getSharedNodes() throws RepositoryException { // Obtain the set of keys for all parents ... final SessionCache cache = session().cache(); Set additionalParents = shareableNode.node().getAdditionalParentKeys(cache); // Obtain the Node objects for each of these ... final NodeKey key = shareableNode.key(); final String workspaceKey = key.getWorkspaceKey(); Collection sharedNodes = new ArrayList(additionalParents.size() + 1); sharedNodes.add(shareableNode); // add the shareable node for (NodeKey parentKey : additionalParents) { if (!workspaceKey.equals(parentKey.getWorkspaceKey())) { // The parent node has to be in this workspace ... continue; } if (session.nodeExists(parentKey)) { sharedNodes.add(getOrCreateSharedNode(parentKey)); } } // Return an iterator ... return new JcrNodeListIterator(sharedNodes.iterator(), sharedNodes.size()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy