You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by dp...@apache.org on 2008/04/04 18:02:00 UTC
svn commit: r644747 - in /jackrabbit/trunk/jackrabbit-core/src:
main/java/org/apache/jackrabbit/core/NodeImpl.java
main/java/org/apache/jackrabbit/core/xml/SessionImporter.java
test/java/org/apache/jackrabbit/core/ShareableNodeTest.java
Author: dpfister
Date: Fri Apr 4 09:01:50 2008
New Revision: 644747
URL: http://svn.apache.org/viewvc?rev=644747&view=rev
Log:
JCR-1104 - JSR 283 support
- shareble nodes (work in progress)
- workspace and session import of conflicting shareable nodes
Modified:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=644747&r1=644746&r2=644747&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Fri Apr 4 09:01:50 2008
@@ -1962,7 +1962,110 @@
return node;
}
- //-----------------------------------------------------------------< Item >
+ /**
+ * Create a child node that is a clone of a shareable node.
+ *
+ * @param src shareable source node
+ * @param name name of new node
+ * @return child node
+ * @throws ItemExistsException if there already is a child node with the
+ * name given and the definition does not allow creating another one
+ * @throws VersionException if this node is not checked out
+ * @throws ConstraintViolationException if no definition is found in this
+ * node that would allow creating the child node
+ * @throws LockException if this node is locked
+ * @throws RepositoryException if some other error occurs
+ */
+ public synchronized NodeImpl clone(NodeImpl src, Name name)
+ throws ItemExistsException, VersionException,
+ ConstraintViolationException, LockException,
+ RepositoryException {
+
+ Path nodePath;
+ try {
+ nodePath = PathFactoryImpl.getInstance().create(getPrimaryPath(), name, true);
+ } catch (MalformedPathException e) {
+ // should never happen
+ String msg = "internal error: invalid path " + safeGetJCRPath();
+ log.debug(msg);
+ throw new RepositoryException(msg, e);
+ }
+
+ // (1) make sure that parent node is checked-out
+ if (!internalIsCheckedOut()) {
+ String msg = safeGetJCRPath()
+ + ": cannot add a child to a checked-in node";
+ log.debug(msg);
+ throw new VersionException(msg);
+ }
+
+ // (2) check lock status
+ checkLock();
+
+ // (3) check for name collisions
+ NodeDefinitionImpl def;
+ try {
+ def = getApplicableChildNodeDefinition(name, null);
+ } catch (RepositoryException re) {
+ String msg = "no definition found in parent node's node type for new node";
+ log.debug(msg);
+ throw new ConstraintViolationException(msg, re);
+ }
+ NodeState thisState = (NodeState) state;
+ NodeState.ChildNodeEntry cne = thisState.getChildNodeEntry(name, 1);
+ if (cne != null) {
+ // there's already a child node entry with that name;
+ // check same-name sibling setting of new node
+ if (!def.allowsSameNameSiblings()) {
+ throw new ItemExistsException(itemMgr.safeGetJCRPath(nodePath));
+ }
+ // check same-name sibling setting of existing node
+ NodeId newId = cne.getId();
+ if (!((NodeImpl) itemMgr.getItem(newId)).getDefinition().allowsSameNameSiblings()) {
+ throw new ItemExistsException(itemMgr.safeGetJCRPath(nodePath));
+ }
+ }
+
+ // (4) check protected flag of parent (i.e. this) node
+ if (definition.isProtected()) {
+ String msg = safeGetJCRPath() + ": cannot add a child to a protected node";
+ log.debug(msg);
+ throw new ConstraintViolationException(msg);
+ }
+
+ // (5) verify that source is shareable
+ if (!src.isShareable()) {
+ String msg = "Source node at " + src.safeGetJCRPath() + " is not shareable.";
+ log.debug(msg);
+ throw new RepositoryException(msg);
+ }
+
+ // (6) detect share cycle
+ NodeId srcId = src.getNodeId();
+ NodeId parentId = getNodeId();
+ HierarchyManager hierMgr = session.getHierarchyManager();
+ if (parentId.equals(srcId) || hierMgr.isAncestor(srcId, parentId)) {
+ String msg = "This would create a share cycle.";
+ log.debug(msg);
+ throw new RepositoryException(msg);
+ }
+
+ // (7) do clone operation (modify and store affected states)
+ if (!src.addShare(parentId)) {
+ String msg = "Adding a shareable node twice to the same parent is not supported.";
+ log.debug(msg);
+ throw new UnsupportedRepositoryOperationException(msg);
+ }
+
+ // (8) modify the state of 'this', i.e. the parent node
+ thisState = (NodeState) getOrCreateTransientItemState();
+ // add new child node entry
+ thisState.addChildNodeEntry(name, srcId);
+
+ return itemMgr.getNode(srcId, parentId);
+ }
+
+ // -----------------------------------------------------------------< Item >
/**
* {@inheritDoc}
*/
@@ -3119,6 +3222,26 @@
return ((NodeState) state).containsShare(parentId);
}
+ /**
+ * Add a parent to the shared set. This method does not check whether
+ * adding this parent would create a share cycle (and should therefore
+ * strictly be used internally), it does, however detect whether the given
+ * parent is already contained in the shared set.
+ *
+ * @return <code>true</code> if adding succeeded;
+ * <code>false</code> otherwise.
+ */
+ protected boolean addShare(NodeId parentId) throws RepositoryException {
+ // quickly verify whether the share is already contained before
+ // possibly changing the referenced state
+ NodeState state = (NodeState) this.state;
+ if (state.containsShare(parentId)) {
+ return false;
+ }
+ // now make modifications
+ state = (NodeState) getOrCreateTransientItemState();
+ return state.addShare(parentId);
+ }
/**
* {@inheritDoc}
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java?rev=644747&r1=644746&r2=644747&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java Fri Apr 4 09:01:50 2008
@@ -114,6 +114,11 @@
refTracker.mappedUUID(nodeInfo.getId().getUUID(), node.getNodeId().getUUID());
}
} else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW) {
+ // if conflicting node is shareable, then clone it
+ if (conflicting.isNodeType(NameConstants.MIX_SHAREABLE)) {
+ parent.clone(conflicting, nodeInfo.getName());
+ return null;
+ }
String msg = "a node with uuid " + nodeInfo.getId() + " already exists!";
log.debug(msg);
throw new ItemExistsException(msg);
@@ -224,6 +229,12 @@
if (conflicting != null) {
// resolve uuid conflict
node = resolveUUIDConflict(parent, conflicting, nodeInfo);
+ if (node == null) {
+ // no new node has been created, so skip this node
+ parents.push(null); // push null onto stack for skipped node
+ log.debug("skipping existing node " + nodeInfo.getName());
+ return;
+ }
} else {
// create new with given uuid
node = createNode(parent, nodeName, ntName, mixins, id);
Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java?rev=644747&r1=644746&r2=644747&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java Fri Apr 4 09:01:50 2008
@@ -485,9 +485,12 @@
}
/**
- * Verify import and export (6.13.14).
+ * Verify system view import via workspace (6.13.14). Export a system view
+ * containing a shareable node and verify, that reimporting underneath
+ * a different parent adds another member to the shared set and does not
+ * duplicate children nodes.
*/
- public void testImportExport() throws Exception {
+ public void testImportSystemViewCollision() throws Exception {
// setup parent nodes and first child
Node a1 = testRootNode.addNode("a1");
Node a2 = testRootNode.addNode("a2");
@@ -525,6 +528,179 @@
InputStream in = new FileInputStream(tmpFile);
try {
workspace.importXML(a3.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+ } finally {
+ in.close();
+ }
+
+ // verify there's another element in the shared set
+ Node[] shared = getSharedSet(b1);
+ assertEquals(3, shared.length);
+
+ // verify child c has not been duplicated
+ Node[] children = toArray(b1.getNodes());
+ assertEquals(1, children.length);
+ }
+
+ /**
+ * Verify document view import via workspace (6.13.14). Export a document
+ * view containing a shareable node and verify, that reimporting
+ * underneath a different parent adds another member to the shared set and
+ * does not duplicate children nodes.
+ */
+ public void testImportDocumentViewCollision() throws Exception {
+ // setup parent nodes and first child
+ Node a1 = testRootNode.addNode("a1");
+ Node a2 = testRootNode.addNode("a2");
+ Node a3 = testRootNode.addNode("a3");
+ Node b1 = a1.addNode("b1");
+ testRootNode.save();
+
+ // add mixin
+ b1.addMixin("mix:shareable");
+ b1.save();
+
+ // clone
+ Session session = b1.getSession();
+ Workspace workspace = session.getWorkspace();
+ workspace.clone(workspace.getName(), b1.getPath(),
+ a2.getPath() + "/b2", false);
+
+ // add child c to shareable nodes b1 & b2
+ b1.addNode("c");
+ b1.save();
+
+ // create temp file
+ File tmpFile = File.createTempFile("test", null);
+ tmpFile.deleteOnExit();
+
+ // export system view of /a1/b1
+ OutputStream out = new FileOutputStream(tmpFile);
+ try {
+ session.exportDocumentView(b1.getPath(), out, false, false);
+ } finally {
+ out.close();
+ }
+
+ // and import again underneath /a3
+ InputStream in = new FileInputStream(tmpFile);
+ try {
+ workspace.importXML(a3.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+ } finally {
+ in.close();
+ }
+
+ // verify there's another element in the shared set
+ Node[] shared = getSharedSet(b1);
+ assertEquals(3, shared.length);
+
+ // verify child c has not been duplicated
+ Node[] children = toArray(b1.getNodes());
+ assertEquals(1, children.length);
+ }
+
+ /**
+ * Verify system view import via session (6.13.14). Export a system view
+ * containing a shareable node and verify, that reimporting underneath
+ * a different parent adds another member to the shared set and does not
+ * duplicate children nodes.
+ */
+ public void testSessionImportSystemViewCollision() throws Exception {
+ // setup parent nodes and first child
+ Node a1 = testRootNode.addNode("a1");
+ Node a2 = testRootNode.addNode("a2");
+ Node a3 = testRootNode.addNode("a3");
+ Node b1 = a1.addNode("b1");
+ testRootNode.save();
+
+ // add mixin
+ b1.addMixin("mix:shareable");
+ b1.save();
+
+ // clone
+ Session session = b1.getSession();
+ Workspace workspace = session.getWorkspace();
+ workspace.clone(workspace.getName(), b1.getPath(),
+ a2.getPath() + "/b2", false);
+
+ // add child c to shareable nodes b1 & b2
+ b1.addNode("c");
+ b1.save();
+
+ // create temp file
+ File tmpFile = File.createTempFile("test", null);
+ tmpFile.deleteOnExit();
+
+ // export system view of /a1/b1
+ OutputStream out = new FileOutputStream(tmpFile);
+ try {
+ session.exportSystemView(b1.getPath(), out, false, false);
+ } finally {
+ out.close();
+ }
+
+ // and import again underneath /a3
+ InputStream in = new FileInputStream(tmpFile);
+ try {
+ session.importXML(a3.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+ session.save();
+ } finally {
+ in.close();
+ }
+
+ // verify there's another element in the shared set
+ Node[] shared = getSharedSet(b1);
+ assertEquals(3, shared.length);
+
+ // verify child c has not been duplicated
+ Node[] children = toArray(b1.getNodes());
+ assertEquals(1, children.length);
+ }
+
+ /**
+ * Verify document view import via session (6.13.14). Export a document
+ * view containing a shareable node and verify, that reimporting
+ * underneath a different parent adds another member to the shared set and
+ * does not duplicate children nodes.
+ */
+ public void testSessionImportDocumentViewCollision() throws Exception {
+ // setup parent nodes and first child
+ Node a1 = testRootNode.addNode("a1");
+ Node a2 = testRootNode.addNode("a2");
+ Node a3 = testRootNode.addNode("a3");
+ Node b1 = a1.addNode("b1");
+ testRootNode.save();
+
+ // add mixin
+ b1.addMixin("mix:shareable");
+ b1.save();
+
+ // clone
+ Session session = b1.getSession();
+ Workspace workspace = session.getWorkspace();
+ workspace.clone(workspace.getName(), b1.getPath(),
+ a2.getPath() + "/b2", false);
+
+ // add child c to shareable nodes b1 & b2
+ b1.addNode("c");
+ b1.save();
+
+ // create temp file
+ File tmpFile = File.createTempFile("test", null);
+ tmpFile.deleteOnExit();
+
+ // export system view of /a1/b1
+ OutputStream out = new FileOutputStream(tmpFile);
+ try {
+ session.exportSystemView(b1.getPath(), out, false, false);
+ } finally {
+ out.close();
+ }
+
+ // and import again underneath /a3
+ InputStream in = new FileInputStream(tmpFile);
+ try {
+ session.importXML(a3.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+ session.save();
} finally {
in.close();
}