Author: rhauch
Date: 2009-04-01 18:27:52 -0400 (Wed, 01 Apr 2009)
New Revision: 799
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
Log:
DNA-194 Implement update JCR capability
Implemented Node.addNode(...) methods and Node.save(). Some enhancements to the Request
classes were required to allow the SessionCache to easily figure out whether a request
affected a particular branch. Also enabled one TCK unit test.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-04-01 16:10:43 UTC
(rev 798)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-04-01 22:27:52 UTC
(rev 799)
@@ -1925,10 +1925,26 @@
* @see Results
*/
public Batch batch() {
- return new Batch();
+ return new Batch(new LinkedList<Request>());
}
/**
+ * Begin a batch of requests to perform various operations, but specify the queue
where all accumulated requests should be
+ * placed. Use this approach when multiple operations are to be built and then
executed with one submission to the underlying
+ * {@link #getSourceName() repository source}. The {@link Results results} are not
available until the {@link Batch#execute()}
+ * method is invoked.
+ *
+ * @param queue the queue where all batched requests should be placed
+ * @return the batch object used to build and accumulate multiple requests and to
submit them all for processing at once.
+ * @see Batch#execute()
+ * @see Results
+ */
+ public Batch batch( LinkedList<Request> queue ) {
+ CheckArg.isNotNull(queue, "queue");
+ return new Batch(queue);
+ }
+
+ /**
* Interface for creating multiple requests to perform various operations. Note that
all the requests are accumulated until
* the {@link #execute()} method is called. The results of all the operations are
then available in the {@link Results} object
* returned by the {@link #execute()}.
@@ -1937,12 +1953,13 @@
*/
@Immutable
public final class Batch implements Executable<Node> {
- protected final CompositingRequestQueue requestQueue = new
CompositingRequestQueue();
+ protected final CompositingRequestQueue requestQueue;
protected final BatchConjunction nextRequests;
protected final String workspaceName;
protected boolean executed = false;
- /*package*/Batch() {
+ /*package*/Batch( LinkedList<Request> queue ) {
+ this.requestQueue = new CompositingRequestQueue(queue);
this.workspaceName = Graph.this.getCurrentWorkspaceName();
this.nextRequests = new BatchConjunction() {
public Batch and() {
@@ -4737,8 +4754,13 @@
*/
@NotThreadSafe
/*package*/class CompositingRequestQueue implements RequestQueue {
- private final LinkedList<Request> requests = new
LinkedList<Request>();
+ private final LinkedList<Request> requests;
+ CompositingRequestQueue( LinkedList<Request> queue ) {
+ assert queue != null;
+ this.requests = queue;
+ }
+
public Graph getGraph() {
return Graph.this;
}
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java 2009-04-01
22:27:52 UTC (rev 799)
@@ -0,0 +1,50 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.request;
+
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ *
+ */
+public interface ChangeRequest {
+
+ /**
+ * Determine if this request changes the branch at the given path.
+ *
+ * @param workspace the name of the workspace; may not be null
+ * @param path the path; may not be null
+ * @return true if this request changes a node under the given path
+ */
+ boolean changes( String workspace,
+ Path path );
+
+ /**
+ * Get the location of the top-most node that is to be changed by this request.
+ *
+ * @return the location changed by this request
+ */
+ Location changedLocation();
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-04-01
16:10:43 UTC (rev 798)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-04-01
22:27:52 UTC (rev 799)
@@ -28,6 +28,7 @@
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
/**
* Instruction that a branch be copied from one location into another. This request can
copy a branch in one workspace into
@@ -35,7 +36,7 @@
*
* @author Randall Hauch
*/
-public class CopyBranchRequest extends Request {
+public class CopyBranchRequest extends Request implements ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -246,6 +247,25 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String,
org.jboss.dna.graph.property.Path)
+ */
+ public boolean changes( String workspace,
+ Path path ) {
+ return this.intoWorkspace.equals(workspace) && into.hasPath() &&
into.getPath().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ public Location changedLocation() {
+ return into;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java 2009-04-01
16:10:43 UTC (rev 798)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java 2009-04-01
22:27:52 UTC (rev 799)
@@ -34,6 +34,7 @@
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.Property;
/**
@@ -41,7 +42,7 @@
*
* @author Randall Hauch
*/
-public class CreateNodeRequest extends Request implements Iterable<Property> {
+public class CreateNodeRequest extends Request implements Iterable<Property>,
ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -317,6 +318,25 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String,
org.jboss.dna.graph.property.Path)
+ */
+ public boolean changes( String workspace,
+ Path path ) {
+ return this.workspaceName.equals(workspace) && under.hasPath() &&
under.getPath().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ public Location changedLocation() {
+ return under;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java 2009-04-01
16:10:43 UTC (rev 798)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java 2009-04-01
22:27:52 UTC (rev 799)
@@ -26,13 +26,14 @@
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
/**
* Instruction that a branch be deleted.
*
* @author Randall Hauch
*/
-public class DeleteBranchRequest extends Request {
+public class DeleteBranchRequest extends Request implements ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -114,6 +115,25 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String,
org.jboss.dna.graph.property.Path)
+ */
+ public boolean changes( String workspace,
+ Path path ) {
+ return this.workspaceName.equals(workspace) && at.hasPath() &&
at.getPath().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ public Location changedLocation() {
+ return at;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java 2009-04-01
16:10:43 UTC (rev 798)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java 2009-04-01
22:27:52 UTC (rev 799)
@@ -34,7 +34,7 @@
*
* @author Randall Hauch
*/
-public class MoveBranchRequest extends Request {
+public class MoveBranchRequest extends Request implements ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -207,6 +207,25 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String,
org.jboss.dna.graph.property.Path)
+ */
+ public boolean changes( String workspace,
+ Path path ) {
+ return this.workspaceName.equals(workspace) && into.hasPath() &&
into.getPath().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ public Location changedLocation() {
+ return into;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java 2009-04-01
16:10:43 UTC (rev 798)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertiesRequest.java 2009-04-01
22:27:52 UTC (rev 799)
@@ -32,13 +32,14 @@
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
/**
* Instruction to remove properties from the node at the specified location.
*
* @author Randall Hauch
*/
-public class RemovePropertiesRequest extends Request implements Iterable<Name> {
+public class RemovePropertiesRequest extends Request implements Iterable<Name>,
ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -196,6 +197,25 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String,
org.jboss.dna.graph.property.Path)
+ */
+ public boolean changes( String workspace,
+ Path path ) {
+ return this.workspaceName.equals(workspace) && from.hasPath() &&
from.getPath().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ public Location changedLocation() {
+ return from;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java 2009-04-01
16:10:43 UTC (rev 798)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java 2009-04-01
22:27:52 UTC (rev 799)
@@ -35,7 +35,7 @@
*
* @author Randall Hauch
*/
-public class RenameNodeRequest extends Request {
+public class RenameNodeRequest extends Request implements ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -161,6 +161,25 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String,
org.jboss.dna.graph.property.Path)
+ */
+ public boolean changes( String workspace,
+ Path path ) {
+ return this.workspaceName.equals(workspace) && at.hasPath() &&
at.getPath().getParent().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ public Location changedLocation() {
+ return at;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java 2009-04-01
16:10:43 UTC (rev 798)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java 2009-04-01
22:27:52 UTC (rev 799)
@@ -35,6 +35,7 @@
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.Property;
/**
@@ -42,7 +43,7 @@
*
* @author Randall Hauch
*/
-public class UpdatePropertiesRequest extends Request implements Iterable<Property>
{
+public class UpdatePropertiesRequest extends Request implements Iterable<Property>,
ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -216,6 +217,16 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String,
org.jboss.dna.graph.property.Path)
+ */
+ public boolean changes( String workspace,
+ Path path ) {
+ return this.workspaceName.equals(workspace) && on.hasPath() &&
on.getPath().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@@ -234,6 +245,15 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ public Location changedLocation() {
+ return on;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#toString()
*/
@Override
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-04-01 16:10:43
UTC (rev 798)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-04-01 22:27:52
UTC (rev 799)
@@ -31,7 +31,9 @@
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
+import javax.jcr.InvalidItemStateException;
import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
@@ -44,10 +46,13 @@
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.version.Version;
+import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;
import net.jcip.annotations.Immutable;
import org.jboss.dna.common.i18n.I18n;
@@ -56,6 +61,7 @@
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.jcr.SessionCache.NodeEditor;
import org.jboss.dna.jcr.cache.ChildNode;
import org.jboss.dna.jcr.cache.Children;
import org.jboss.dna.jcr.cache.NodeInfo;
@@ -88,6 +94,30 @@
return cache.findNodeInfo(nodeUuid);
}
+ final NodeEditor editorForParent() throws RepositoryException {
+ try {
+ return cache.getEditorFor(nodeInfo().getParent());
+ } catch (ItemNotFoundException err) {
+ String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid,
cache.workspaceName());
+ throw new RepositoryException(msg);
+ } catch (InvalidItemStateException err) {
+ String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid,
cache.workspaceName());
+ throw new RepositoryException(msg);
+ }
+ }
+
+ final NodeEditor editor() throws RepositoryException {
+ try {
+ return cache.getEditorFor(nodeUuid);
+ } catch (ItemNotFoundException err) {
+ String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid,
cache.workspaceName());
+ throw new RepositoryException(msg);
+ } catch (InvalidItemStateException err) {
+ String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid,
cache.workspaceName());
+ throw new RepositoryException(msg);
+ }
+ }
+
@Override
Path path() throws RepositoryException {
return cache.getPathFor(nodeInfo());
@@ -380,7 +410,7 @@
int indexOfFirstSlash = relativePath.indexOf('/');
if (indexOfFirstSlash == 0) {
// Not a relative path ...
- throw new
IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
+ throw new
IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath,
"relativePath"));
}
if (indexOfFirstSlash != -1) {
// We know it's a relative path with more than one segment ...
@@ -411,7 +441,7 @@
int indexOfFirstSlash = relativePath.indexOf('/');
if (indexOfFirstSlash == 0) {
// Not a relative path ...
- throw new
IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
+ throw new
IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath,
"relativePath"));
}
if (indexOfFirstSlash != -1) {
Path path = pathFrom(relativePath).getNormalizedPath();
@@ -450,7 +480,7 @@
int indexOfFirstSlash = relativePath.indexOf('/');
if (indexOfFirstSlash == 0) {
// Not a relative path ...
- throw new
IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
+ throw new
IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath,
"relativePath"));
}
if (indexOfFirstSlash != -1) {
// We know it's a relative path with more than one segment ...
@@ -547,22 +577,86 @@
/**
* {@inheritDoc}
*
- * @throws UnsupportedOperationException always
* @see javax.jcr.Node#addNode(java.lang.String)
*/
- public final Node addNode( String relPath ) {
- throw new UnsupportedOperationException();
+ public final Node addNode( String relPath )
+ throws ItemExistsException, PathNotFoundException, VersionException,
ConstraintViolationException, LockException,
+ RepositoryException {
+ return addNode(relPath, null);
}
/**
* {@inheritDoc}
*
- * @throws UnsupportedOperationException always
* @see javax.jcr.Node#addNode(java.lang.String, java.lang.String)
*/
public final Node addNode( String relPath,
- String primaryNodeTypeName ) {
- throw new UnsupportedOperationException();
+ String primaryNodeTypeName )
+ throws ItemExistsException, PathNotFoundException, VersionException,
ConstraintViolationException, LockException,
+ RepositoryException {
+ // Determine the path ...
+ NodeEditor editor = null;
+ Path path = null;
+ try {
+ path = cache.pathFactory().create(relPath);
+ } catch (ValueFormatException e) {
+ throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath,
"relPath"));
+ }
+ if (path.size() == 0) {
+ throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath,
"relPath"));
+ }
+ if (path.getLastSegment().getIndex() > 1 || relPath.endsWith("]"))
{
+ throw new RepositoryException(JcrI18n.invalidPathParameter.text(relPath,
"relPath"));
+ }
+ if (path.size() != 1) {
+ // The only segment in the path is the child name ...
+ NodeInfo parentInfo = null;
+ Path parentPath = path.getParent();
+ try {
+ parentInfo = cache.findNodeInfo(nodeUuid, parentPath); // throws
PathNotFoundException
+ editor = cache.getEditorFor(parentInfo.getUuid());
+ } catch (PathNotFoundException e) {
+ // We're going to throw an exception ... the question is which one
...
+ if (parentPath.size() > 1) {
+ // Per the TCK, if relPath references a property, then we have to
throw a ConstraintViolationException
+ // So, if we can't find the parent, try for the parent's
parent and see if the last segment of the parent's
+ // path contains a property ...
+ Path grandparentPath = parentPath.getParent();
+ assert grandparentPath != null;
+ try {
+ NodeInfo grandparentInfo = cache.findNodeInfo(nodeUuid,
grandparentPath); // throws PathNotFoundException
+ if
(grandparentInfo.getProperty(parentPath.getLastSegment().getName()) != null) {
+ // Need to throw a ConstraintViolationException since the
request was to add a child to
+ // a property ...
+ throw new
ConstraintViolationException(JcrI18n.invalidPathParameter.text(relPath,
"relPath"));
+ }
+ } catch (PathNotFoundException e2) {
+ // eat, since the original exception is what we want ...
+ }
+ }
+ // Otherwise, just throw the PathNotFoundException ...
+ throw e;
+ }
+ } else {
+ assert path.size() == 1;
+ editor = editor();
+ }
+ Name childName = path.getLastSegment().getName();
+
+ // Determine the name for the primary node type
+ Name childPrimaryTypeName = null;
+ if (primaryNodeTypeName != null) {
+ try {
+ childPrimaryTypeName = cache.nameFactory().create(primaryNodeTypeName);
+ } catch (ValueFormatException e) {
+ throw new
RepositoryException(JcrI18n.invalidNodeTypeNameParameter.text(primaryNodeTypeName,
+
"primaryNodeTypeName"));
+ }
+ }
+
+ // Create the child ...
+ ChildNode child = editor.createChild(childName, null, childPrimaryTypeName);
+ return cache.findJcrNode(child.getUuid());
}
/**
@@ -1001,10 +1095,9 @@
/**
* {@inheritDoc}
*
- * @throws UnsupportedOperationException always
* @see javax.jcr.Item#save()
*/
- public void save() {
- throw new UnsupportedOperationException();
+ public void save() throws RepositoryException {
+ cache.save(nodeUuid);
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-04-01 16:10:43 UTC
(rev 798)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-04-01 22:27:52 UTC
(rev 799)
@@ -62,6 +62,7 @@
public static I18n invalidRelativePath;
public static I18n invalidPathParameter;
public static I18n invalidNamePattern;
+ public static I18n invalidNodeTypeNameParameter;
public static I18n noPrimaryItemNameDefinedOnPrimaryType;
public static I18n primaryItemNameForPrimaryTypeIsNotValid;
public static I18n primaryItemDoesNotExist;
@@ -75,6 +76,9 @@
public static I18n errorWhileFindingNodeWithPath;
public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
public static I18n missingNodeTypeForExistingNode;
+ public static I18n unableToCreateNodeWithPrimaryTypeThatDoesNotExist;
+ public static I18n unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition;
+ public static I18n unableToSaveNodeThatWasCreatedSincePreviousSave;
public static I18n unableToRemoveRootNode;
public static I18n unableToMoveNodeToBeChildOfDecendent;
@@ -100,7 +104,7 @@
// Query-related messages
public static I18n notStoredQuery;
public static I18n invalidQueryLanguage;
-
+
static {
try {
I18n.initialize(JcrI18n.class);
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java 2009-04-01 16:10:43 UTC
(rev 798)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java 2009-04-01 22:27:52 UTC
(rev 799)
@@ -24,7 +24,6 @@
package org.jboss.dna.jcr;
import java.util.UUID;
-import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
@@ -93,16 +92,7 @@
* @see javax.jcr.Item#remove()
*/
public void remove() throws RepositoryException {
- try {
- SessionCache.NodeEditor editor = cache.getEditorFor(nodeInfo().getParent());
- editor.destroyChild(nodeUuid);
- } catch (ItemNotFoundException err) {
- String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid,
cache.workspaceName());
- throw new RepositoryException(msg);
- } catch (InvalidItemStateException err) {
- String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid,
cache.workspaceName());
- throw new RepositoryException(msg);
- }
+ editorForParent().destroyChild(nodeUuid);
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-04-01 16:10:43
UTC (rev 798)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-04-01 22:27:52
UTC (rev 799)
@@ -27,6 +27,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -44,10 +45,12 @@
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.PropertyDefinition;
import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.i18n.I18n;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
import org.jboss.dna.graph.Location;
@@ -63,6 +66,8 @@
import org.jboss.dna.graph.property.ValueFactories;
import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.request.ChangeRequest;
+import org.jboss.dna.graph.request.Request;
import org.jboss.dna.jcr.cache.ChangedNodeInfo;
import org.jboss.dna.jcr.cache.ChildNode;
import org.jboss.dna.jcr.cache.Children;
@@ -141,7 +146,8 @@
protected final HashMap<UUID, ChangedNodeInfo> changedNodes;
protected final HashMap<UUID, NodeInfo> deletedNodes;
- private Graph.Batch operations;
+ private LinkedList<Request> requests;
+ protected Graph.Batch operations;
public SessionCache( JcrSession session,
String workspaceName,
@@ -174,6 +180,7 @@
this.deletedNodes = new HashMap<UUID, NodeInfo>();
// Create the batch operations ...
+ this.requests = new LinkedList<Request>();
this.operations = this.store.batch();
}
@@ -181,22 +188,26 @@
return session;
}
+ String workspaceName() {
+ return workspaceName;
+ }
+
ExecutionContext context() {
return context;
}
- String workspaceName() {
- return workspaceName;
+ PathFactory pathFactory() {
+ return pathFactory;
}
+ NameFactory nameFactory() {
+ return nameFactory;
+ }
+
JcrNodeTypeManager nodeTypes() {
return session.nodeTypeManager();
}
- final Graph.Batch operations() {
- return operations;
- }
-
/**
* Save any changes that have been accumulated by this session.
*
@@ -207,12 +218,17 @@
// Execute the batched operations ...
try {
operations.execute();
+ } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+ throw new PathNotFoundException(e.getLocalizedMessage(), e);
} catch (RuntimeException e) {
- throw new RepositoryException(e);
+ throw new RepositoryException(e.getLocalizedMessage(), e);
}
// Create a new batch for future operations ...
- operations = store.batch();
+ // LinkedList<Request> oldRequests = this.requests;
+ this.requests = new LinkedList<Request>();
+ operations = store.batch(this.requests);
+
// Remove all the cached items that have been changed or deleted ...
for (UUID changedUuid : changedNodes.keySet()) {
cachedNodes.remove(changedUuid);
@@ -226,6 +242,77 @@
}
}
+ /**
+ * Save any changes to the identified node or its decendants. The supplied node may
not have been deleted or created in this
+ * session since the last save operation.
+ *
+ * @param nodeUuid the UUID of the node that is to be saved; may not be null
+ * @throws RepositoryException if any error resulting while saving the changes to the
repository
+ */
+ public void save( UUID nodeUuid ) throws RepositoryException {
+ assert nodeUuid != null;
+ if (deletedNodes.containsKey(nodeUuid)) {
+ // This node was deleted in this session ...
+ throw new
InvalidItemStateException(JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid,
workspaceName()));
+ }
+
+ // The node must not have been created since the last save ...
+ if (!cachedNodes.containsKey(nodeUuid)) {
+ // It is not cached, which means it WAS created ...
+ Path path = getPathFor(nodeUuid);
+ throw new
RepositoryException(JcrI18n.unableToSaveNodeThatWasCreatedSincePreviousSave.text(path,
workspaceName()));
+ }
+
+ if (operations.isExecuteRequired()) {
+ // Find the path of the given node ...
+ Path path = getPathFor(nodeUuid);
+
+ // Remove all of the enqueued requests for this branch ...
+ LinkedList<Request> branchRequests = new LinkedList<Request>();
+ LinkedList<Request> nonBranchRequests = new
LinkedList<Request>();
+ Set<UUID> branchUuids = new HashSet<UUID>();
+ for (Request request : this.requests) {
+ assert request instanceof ChangeRequest;
+ ChangeRequest change = (ChangeRequest)request;
+ if (change.changes(workspaceName, path)) {
+ branchRequests.add(request);
+ // Record the UUID of the node being saved now ...
+ UUID changedUuid = change.changedLocation().getUuid();
+ assert changedUuid != null;
+ branchUuids.add(changedUuid);
+ } else {
+ nonBranchRequests.add(request);
+ }
+ }
+
+ if (branchRequests.isEmpty()) {
+ // None of the changes affected the branch given by the node ...
+ return;
+ }
+
+ // Now execute the branch ...
+ Graph.Batch branchBatch = store.batch(branchRequests);
+ try {
+ branchBatch.execute();
+
+ // Still have non-branch related requests that we haven't executed
...
+ this.requests = nonBranchRequests;
+ this.operations = store.batch(nonBranchRequests);
+
+ // Remove all the cached, changed or deleted items that were just saved
...
+ for (UUID changedUuid : branchUuids) {
+ cachedNodes.remove(changedUuid);
+ changedNodes.remove(changedUuid);
+ deletedNodes.remove(changedUuid);
+ }
+ } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+ throw new PathNotFoundException(e.getLocalizedMessage(), e);
+ } catch (RuntimeException e) {
+ throw new RepositoryException(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+
public JcrRootNode findJcrRootNode() throws RepositoryException {
return (JcrRootNode)findJcrNode(findNodeInfoForRoot().getUuid());
}
@@ -498,7 +585,7 @@
// Finally update the cached information and record the change ...
node.setProperty(newProperty, context().getValueFactories());
- operations().set(dnaProp).on(currentLocation);
+ operations.set(dnaProp).on(currentLocation);
}
/**
@@ -587,7 +674,7 @@
// Finally update the cached information and record the change ...
node.setProperty(newProperty, context().getValueFactories());
- operations().set(dnaProp).on(currentLocation);
+ operations.set(dnaProp).on(currentLocation);
}
/**
@@ -598,7 +685,7 @@
*/
public boolean removeProperty( Name name ) {
if (node.removeProperty(name) != null) {
- operations().remove(name).on(currentLocation);
+ operations.remove(name).on(currentLocation);
return true;
}
return false;
@@ -675,7 +762,7 @@
existingNodeInfo.setParent(node.getUuid());
// Now, record the operation to do this ...
- operations().move(existingNodeEditor.currentLocation).into(currentLocation);
+ operations.move(existingNodeEditor.currentLocation).into(currentLocation);
return newChild;
}
@@ -686,17 +773,16 @@
* @param name the name for the new child; may not be null
* @param desiredUuid the desired UUID, or null if the UUID for the child should
be generated automatically
* @param primaryTypeName the name of the primary type for the new node
- * @param nodeDefinitionId
* @return the representation of the newly-created child, which includes the
{@link ChildNode#getSnsIndex()
* same-name-sibling index}
* @throws InvalidItemStateException if the specified child has been marked for
deletion within this session
* @throws ConstraintViolationException if moving the node into this node
violates this node's definition
+ * @throws NoSuchNodeTypeException if the node type for the primary type could
not be found
* @throws RepositoryException if any other error occurs while reading
information from the repository
*/
public ChildNode createChild( Name name,
UUID desiredUuid,
- Name primaryTypeName,
- NodeDefinitionId nodeDefinitionId )
+ Name primaryTypeName )
throws InvalidItemStateException, ConstraintViolationException,
RepositoryException {
if (desiredUuid == null) desiredUuid = UUID.randomUUID();
@@ -708,10 +794,34 @@
primaryTypeName,
numSns,
true);
+ // Make sure there was a valid child node definition ...
if (definition == null) {
- throw new ConstraintViolationException();
+ Path pathForChild = pathFactory.create(getPathFor(node), name, numSns +
1);
+ String msg =
JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(pathForChild, workspaceName());
+ throw new ConstraintViolationException(msg);
}
+ // Find the primary type ...
+ JcrNodeType primaryType = null;
+ if (primaryTypeName != null) {
+ primaryType = nodeTypes().getNodeType(primaryTypeName);
+ if (primaryType == null) {
+ Path pathForChild = pathFactory.create(getPathFor(node), name, numSns
+ 1);
+ I18n msg =
JcrI18n.unableToCreateNodeWithPrimaryTypeThatDoesNotExist;
+ throw new NoSuchNodeTypeException(msg.text(primaryTypeName,
pathForChild, workspaceName()));
+ }
+ } else {
+ primaryType = (JcrNodeType)definition.getDefaultPrimaryType();
+ if (primaryType == null) {
+ // There is no default primary type ...
+ Path pathForChild = pathFactory.create(getPathFor(node), name, numSns
+ 1);
+ I18n msg =
JcrI18n.unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition;
+ String nodeTypeName = definition.getDeclaringNodeType().getName();
+ throw new NoSuchNodeTypeException(msg.text(definition.getName(),
nodeTypeName, pathForChild, workspaceName()));
+ }
+ }
+ primaryTypeName = primaryType.getInternalName();
+
ChildNode result = node.addChild(name, desiredUuid, pathFactory);
// ---------------------------------------------------------
@@ -723,7 +833,7 @@
// Create the properties ...
Map<Name, PropertyInfo> properties = new HashMap<Name,
PropertyInfo>();
Property primaryTypeProp = propertyFactory.create(JcrLexicon.PRIMARY_TYPE,
primaryTypeName);
- Property nodeDefinitionProp =
propertyFactory.create(DnaLexicon.NODE_DEFINITON, nodeDefinitionId.getString());
+ Property nodeDefinitionProp =
propertyFactory.create(DnaLexicon.NODE_DEFINITON, definition.getId().getString());
// Create the property info for the "jcr:primaryType" child
property ...
JcrPropertyDefinition primaryTypeDefn =
findBestPropertyDefintion(node.getPrimaryTypeName(),
@@ -759,10 +869,10 @@
// ---------------------------------------
// Now record the changes to the store ...
// ---------------------------------------
- Graph.Create<Graph.Batch> create =
operations().createUnder(currentLocation)
- .nodeNamed(name)
- .with(desiredUuid)
- .with(primaryTypeProp);
+ Graph.Create<Graph.Batch> create =
operations.createUnder(currentLocation)
+ .nodeNamed(name)
+ .with(desiredUuid)
+ .with(primaryTypeProp);
if (nodeDefnDefn != null) {
create = create.with(nodeDefinitionProp);
}
@@ -787,7 +897,7 @@
// Now make the request to the source ...
Path childPath = pathFactory.create(currentLocation.getPath(),
deleted.getSegment());
Location locationOfChild = Location.create(childPath, nodeUuid);
- operations().delete(locationOfChild);
+ operations.delete(locationOfChild);
return true;
}
return false;
@@ -917,6 +1027,7 @@
* @see #findNodeInfoForRoot()
*/
NodeInfo findNodeInfo( UUID uuid ) throws ItemNotFoundException,
InvalidItemStateException, RepositoryException {
+ assert uuid != null;
// See if we already have something in the cache ...
NodeInfo info = findNodeInfoInCache(uuid);
if (info == null) {
@@ -1326,16 +1437,16 @@
definition = nodeTypes().getNodeDefinition(id);
}
// Figure out the node definition for this node ...
- if (definition == null) {
- if (isRoot) {
- definition = nodeTypes().getRootNodeDefinition();
- } else {
- // We need the parent ...
- Path path = location.getPath();
- if (parentInfo == null) {
- Path parentPath = path.getParent();
- parentInfo = findNodeInfo(null, parentPath.getNormalizedPath());
- }
+ if (isRoot) {
+ if (definition == null) definition = nodeTypes().getRootNodeDefinition();
+ } else {
+ // We need the parent ...
+ Path path = location.getPath();
+ if (parentInfo == null) {
+ Path parentPath = path.getParent();
+ parentInfo = findNodeInfo(null, parentPath.getNormalizedPath());
+ }
+ if (definition == null) {
Name childName = path.getLastSegment().getName();
int numExistingChildrenWithSameName =
parentInfo.getChildren().getCountOfSameNameSiblingsWithName(childName);
definition =
nodeTypes().findChildNodeDefinition(parentInfo.getPrimaryTypeName(),
@@ -1344,11 +1455,11 @@
primaryTypeName,
numExistingChildrenWithSameName,
false);
- if (definition == null) {
- String msg =
JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspaceName);
- throw new RepositorySourceException(msg);
- }
}
+ if (definition == null) {
+ String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path,
workspaceName);
+ throw new RepositorySourceException(msg);
+ }
}
// ------------------------------------------------------
Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-04-01
16:10:43 UTC (rev 798)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-04-01
22:27:52 UTC (rev 799)
@@ -52,6 +52,7 @@
invalidRelativePath = "{0}" is not a valid relative path
invalidPathParameter = The "{1}" parameter value "{0}" was not a
valid path
invalidNamePattern = The "{1}" name pattern contained the '{0}'
character, which is not allowed in a name pattern
+invalidNodeTypeNameParameter = The "{1}" parameter value "{0}" was
not a valid node type name
noPrimaryItemNameDefinedOnPrimaryType = The primary type "{0}" for node
"{1}" in workspace "{2}" does not define a primary item name
primaryItemNameForPrimaryTypeIsNotValid = The primary type "{0}" for node
"{2}" in workspace "{3}" defines an invalid primary item name
("{1}")
primaryItemDoesNotExist = The node "{2}" in workspace "{3}" does not
have an item named "{1}" as defined by its primary type "{0}"
@@ -65,6 +66,9 @@
errorWhileFindingNodeWithPath = Error while finding the node "{0}" in workspace
"{1}"
nodeDefinitionCouldNotBeDeterminedForNode = Unable to determine a valid node definition
for the node "{0}" in workspace "{1}"
missingNodeTypeForExistingNode = Missing primary node type "{0}" for node {1}
in workspace "{2}"
+unableToCreateNodeWithPrimaryTypeThatDoesNotExist = Unable to create child
"{1}" in workspace "{2}" because the node type "{0}" does
not exist
+unableToCreateNodeWithNoDefaultPrimaryTypeOnChildNodeDefinition = Unable to create child
"{2}" in workspace "{3}" because the node definition "{0}"
on the "{1}" node type has no default primary type
+unableToSaveNodeThatWasCreatedSincePreviousSave = Unable to save node "{0}" in
workspace "{1}" because it was created since the last save
unableToRemoveRootNode = Unable to remove the root node in workspace "{1}"
unableToMoveNodeToBeChildOfDecendent = Node "{0}" in workspace "{2}"
cannot be moved under a decendant node ("{1}")
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java 2009-04-01 16:10:43 UTC
(rev 798)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java 2009-04-01 22:27:52 UTC
(rev 799)
@@ -35,6 +35,7 @@
import junit.framework.TestSuite;
import org.apache.jackrabbit.test.JCRTestSuite;
import org.apache.jackrabbit.test.RepositoryStub;
+import org.apache.jackrabbit.test.api.AddNodeTest;
import org.apache.jackrabbit.test.api.RepositoryLoginTest;
import org.jboss.dna.graph.DnaLexicon;
import org.jboss.dna.graph.ExecutionContext;
@@ -142,7 +143,7 @@
// See
https://jira.jboss.org/jira/browse/DNA-285
// level 2 tests
- // addTestSuite(AddNodeTest.class);
+ addTestSuite(AddNodeTest.class);
// addTestSuite(NamespaceRegistryTest.class);
// addTestSuite(ReferencesTest.class);
// addTestSuite(SessionTest.class);
Modified: trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties 2009-04-01 16:10:43 UTC
(rev 798)
+++ trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties 2009-04-01 22:27:52 UTC
(rev 799)
@@ -7,3 +7,5 @@
javax.jcr.tck.propertyname1=prop1
javax.jcr.tck.propertyname2=prop2
javax.jcr.tck.workspacename=
+javax.jcr.tck.nodetype=nt:unstructured
+javax.jcr.tck.nodetypenochildren=dna:namespace