[dna-commits] DNA SVN: r317 - in trunk/connectors: dna-connector-inmemory and 21 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Mon Jun 30 17:32:40 EDT 2008


Author: rhauch
Date: 2008-06-30 17:32:40 -0400 (Mon, 30 Jun 2008)
New Revision: 317

Added:
   trunk/connectors/dna-connector-inmemory/
   trunk/connectors/dna-connector-inmemory/.classpath
   trunk/connectors/dna-connector-inmemory/.project
   trunk/connectors/dna-connector-inmemory/pom.xml
   trunk/connectors/dna-connector-inmemory/src/
   trunk/connectors/dna-connector-inmemory/src/main/
   trunk/connectors/dna-connector-inmemory/src/main/java/
   trunk/connectors/dna-connector-inmemory/src/main/java/org/
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.java
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryConnection.java
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositorySource.java
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/Node.java
   trunk/connectors/dna-connector-inmemory/src/main/resources/
   trunk/connectors/dna-connector-inmemory/src/main/resources/org/
   trunk/connectors/dna-connector-inmemory/src/main/resources/org/jboss/
   trunk/connectors/dna-connector-inmemory/src/main/resources/org/jboss/dna/
   trunk/connectors/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/
   trunk/connectors/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/
   trunk/connectors/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.properties
   trunk/connectors/dna-connector-inmemory/src/test/
   trunk/connectors/dna-connector-inmemory/src/test/java/
   trunk/connectors/dna-connector-inmemory/src/test/java/org/
   trunk/connectors/dna-connector-inmemory/src/test/java/org/jboss/
   trunk/connectors/dna-connector-inmemory/src/test/java/org/jboss/dna/
   trunk/connectors/dna-connector-inmemory/src/test/java/org/jboss/dna/connector/
   trunk/connectors/dna-connector-inmemory/src/test/java/org/jboss/dna/connector/inmemory/
   trunk/connectors/dna-connector-inmemory/src/test/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryTest.java
   trunk/connectors/dna-connector-inmemory/src/test/resources/
Log:
DNA-167 Simple in-memory connector: Created a simple in-memory repository and connector


Property changes on: trunk/connectors/dna-connector-inmemory
___________________________________________________________________
Name: svn:ignore
   + target


Added: trunk/connectors/dna-connector-inmemory/.classpath
===================================================================
--- trunk/connectors/dna-connector-inmemory/.classpath	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/.classpath	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry kind="src" path="src/main/resources"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

Added: trunk/connectors/dna-connector-inmemory/.project
===================================================================
--- trunk/connectors/dna-connector-inmemory/.project	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/.project	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dna-connector-inmemory</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>

Added: trunk/connectors/dna-connector-inmemory/pom.xml
===================================================================
--- trunk/connectors/dna-connector-inmemory/pom.xml	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/pom.xml	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.jboss.dna</groupId>
+    <artifactId>dna</artifactId>
+    <version>0.2-SNAPSHOT</version>
+    <relativePath>../..</relativePath>
+  </parent>
+  <!-- The groupId and version values are inherited from parent -->
+  <artifactId>dna-connector-inmemory</artifactId>
+  <packaging>jar</packaging>
+  <name>JBoss DNA Connector to a simple in-memory repository</name>
+  <description>JBoss DNA Connector that accesses an in-memory graph.</description>
+  <url>http://labs.jboss.org/dna</url>
+  
+  <properties>
+    <dna-version>0.2-SNAPSHOT</dna-version>
+  </properties>
+  <!--
+  Define the dependencies.  Note that all version and scopes default to those 
+  defined in the dependencyManagement section of the parent pom.
+  -->
+  <dependencies>
+    <!-- 
+    Common
+    -->
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-spi</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-common</artifactId>
+      <version>${dna-version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-spi</artifactId>
+      <version>${dna-version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <!-- 
+    Testing (note the scope)
+    -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+    </dependency>
+    <!-- 
+    Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) 
+    -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+    </dependency>
+    <!-- 
+    Java Concurrency in Practice annotations
+    -->
+  <dependency>
+    <groupId>net.jcip</groupId>
+    <artifactId>jcip-annotations</artifactId>
+  </dependency>
+  </dependencies>
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
\ No newline at end of file

Added: trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.java
===================================================================
--- trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.java	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.java	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.connector.inmemory;
+
+import java.util.Locale;
+import java.util.Set;
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ * @author Randall Hauch
+ */
+public final class InMemoryConnectorI18n {
+
+    public static I18n connectorName;
+    public static I18n nodeDoesNotExist;
+
+    static {
+        try {
+            I18n.initialize(InMemoryConnectorI18n.class);
+        } catch (final Exception err) {
+            System.err.println(err);
+        }
+    }
+
+    public static Set<Locale> getLocalizationProblemLocales() {
+        return I18n.getLocalizationProblemLocales(InMemoryConnectorI18n.class);
+    }
+
+    public static Set<String> getLocalizationProblems() {
+        return I18n.getLocalizationProblems(InMemoryConnectorI18n.class);
+    }
+
+    public static Set<String> getLocalizationProblems( Locale locale ) {
+        return I18n.getLocalizationProblems(InMemoryConnectorI18n.class, locale);
+    }
+}

Added: trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,266 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.connector.inmemory;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.util.ArgCheck;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
+
+/**
+ * @author Randall Hauch
+ */
+ at NotThreadSafe
+public class InMemoryRepository {
+
+    protected final ReadWriteLock lock = new ReentrantReadWriteLock();
+    private final String name;
+    private final UUID rootNodeUuid;
+    private final Map<UUID, Node> nodesByUuid = new HashMap<UUID, Node>();
+
+    public InMemoryRepository( String name,
+                               UUID rootNodeUUID ) {
+        ArgCheck.isNotNull(rootNodeUUID, "rootNodeUUID");
+        ArgCheck.isNotEmpty(name, "name");
+        this.name = name;
+        this.rootNodeUuid = rootNodeUUID;
+        // Create the root node ...
+        Node root = new Node(rootNodeUUID);
+        nodesByUuid.put(root.getUuid(), root);
+    }
+
+    /**
+     * @return lock
+     */
+    public ReadWriteLock getLock() {
+        return lock;
+    }
+
+    /**
+     * @return name
+     */
+    public String getName() {
+        return name;
+    }
+
+    public Node getRoot() {
+        return nodesByUuid.get(this.rootNodeUuid);
+    }
+
+    public Node getNode( UUID uuid ) {
+        assert uuid != null;
+        return nodesByUuid.get(uuid);
+    }
+
+    protected Map<UUID, Node> getNodesByUuid() {
+        return nodesByUuid;
+    }
+
+    public Node getNode( ExecutionEnvironment env,
+                         String path ) {
+        assert env != null;
+        assert path != null;
+        return getNode(env.getValueFactories().getPathFactory().create(path));
+    }
+
+    /**
+     * Find a node with the given path.
+     * 
+     * @param path the path to the node; may not be null
+     * @return the node with the path, or null if the node does not exist
+     */
+    public Node getNode( Path path ) {
+        assert path != null;
+        Node node = getRoot();
+        for (Path.Segment segment : path) {
+            Node desiredChild = null;
+            for (Node child : node.getChildren()) {
+                if (child == null) continue;
+                Path.Segment childName = child.getName();
+                if (childName == null) continue;
+                if (childName.equals(segment)) {
+                    desiredChild = child;
+                    break;
+                }
+            }
+            if (desiredChild != null) {
+                node = desiredChild;
+            } else {
+                return null;
+            }
+        }
+        return node;
+    }
+
+    protected UUID generateUuid() {
+        return UUID.randomUUID();
+    }
+
+    public void removeNode( ExecutionEnvironment env,
+                            Node node ) {
+        assert env != null;
+        assert node != null;
+        assert getRoot().equals(node) != true;
+        Node parent = node.getParent();
+        assert parent != null;
+        parent.getChildren().remove(node);
+        correctSameNameSiblingIndexes(env, parent, node.getName().getName());
+        removeUuidReference(node);
+    }
+
+    protected void removeUuidReference( Node node ) {
+        nodesByUuid.remove(node.getUuid());
+        for (Node child : node.getChildren()) {
+            removeUuidReference(child);
+        }
+    }
+
+    /**
+     * Create a node at the supplied path. The parent of the new node must already exist.
+     * 
+     * @param env the environment; may not be null
+     * @param pathToNewNode the path to the new node; may not be null
+     * @return the new node (or root if the path specified the root)
+     */
+    public Node createNode( ExecutionEnvironment env,
+                            String pathToNewNode ) {
+        assert env != null;
+        assert pathToNewNode != null;
+        Path path = env.getValueFactories().getPathFactory().create(pathToNewNode);
+        if (path.isRoot()) return getRoot();
+        Path parentPath = path.getAncestor();
+        Node parentNode = getNode(parentPath);
+        Name name = path.getLastSegment().getName();
+        return createNode(env, parentNode, name);
+    }
+
+    /**
+     * Create a new node with the supplied name, as a child of the supplied parent.
+     * 
+     * @param env the execution environment
+     * @param parentNode the parent node; may not be null
+     * @param name the name; may not be null
+     * @return the new node
+     */
+    public Node createNode( ExecutionEnvironment env,
+                            Node parentNode,
+                            Name name ) {
+        assert env != null;
+        assert name != null;
+        if (parentNode == null) parentNode = getRoot();
+        Node node = new Node(generateUuid());
+        nodesByUuid.put(node.getUuid(), node);
+        node.setParent(parentNode);
+        Path.Segment newName = env.getValueFactories().getPathFactory().createSegment(name);
+        node.setName(newName);
+        parentNode.getChildren().add(node);
+        correctSameNameSiblingIndexes(env, parentNode, name);
+        return node;
+    }
+
+    protected void correctSameNameSiblingIndexes( ExecutionEnvironment env,
+                                                  Node parentNode,
+                                                  Name name ) {
+        if (parentNode == null) return;
+        // Look for the highest existing index ...
+        List<Node> childrenWithSameNames = new LinkedList<Node>();
+        for (Node child : parentNode.getChildren()) {
+            if (child.getName().getName().equals(name)) childrenWithSameNames.add(child);
+        }
+        if (childrenWithSameNames.size() == 0) return;
+        if (childrenWithSameNames.size() == 1) {
+            Node childWithSameName = childrenWithSameNames.get(0);
+            Path.Segment newName = env.getValueFactories().getPathFactory().createSegment(name, Path.NO_INDEX);
+            childWithSameName.setName(newName);
+            return;
+        }
+        int index = 1;
+        for (Node childWithSameName : childrenWithSameNames) {
+            Path.Segment segment = childWithSameName.getName();
+            if (segment.getIndex() != index) {
+                Path.Segment newName = env.getValueFactories().getPathFactory().createSegment(name, index);
+                childWithSameName.setName(newName);
+            }
+            ++index;
+        }
+    }
+
+    /**
+     * Move the supplied node to the new parent. This method automatically removes the node from its existing parent, and also
+     * correctly adjusts the {@link Path.Segment#getIndex() index} to be correct in the new parent.
+     * 
+     * @param env
+     * @param node the node to be moved; may not be the {@link #getRoot() root}
+     * @param newParent the new parent; may not be the {@link #getRoot() root}
+     */
+    public void moveNode( ExecutionEnvironment env,
+                          Node node,
+                          Node newParent ) {
+        assert env != null;
+        assert newParent != null;
+        assert node != null;
+        assert getRoot().equals(newParent) != true;
+        assert getRoot().equals(node) != true;
+        Node oldParent = node.getParent();
+        if (oldParent != null) {
+            if (oldParent.equals(newParent)) return;
+            boolean removed = oldParent.getChildren().remove(node);
+            assert removed == true;
+            node.setParent(null);
+            correctSameNameSiblingIndexes(env, oldParent, node.getName().getName());
+        }
+        node.setParent(newParent);
+        newParent.getChildren().add(node);
+        correctSameNameSiblingIndexes(env, newParent, node.getName().getName());
+    }
+
+    public int copyNode( ExecutionEnvironment env,
+                         Node original,
+                         Node newParent,
+                         boolean recursive ) {
+        assert env != null;
+        assert original != null;
+        assert newParent != null;
+        // Get or create the new node ...
+        Node copy = createNode(env, newParent, original.getName().getName());
+
+        // Copy the properties ...
+        copy.getProperties().clear();
+        copy.getProperties().putAll(original.getProperties());
+        int numNodesCopied = 1;
+        if (recursive) {
+            // Loop over each child and call this method ...
+            for (Node child : original.getChildren()) {
+                numNodesCopied += copyNode(env, child, copy, true);
+            }
+        }
+        return numNodesCopied;
+    }
+}

Added: trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryConnection.java
===================================================================
--- trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryConnection.java	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryConnection.java	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,267 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.connector.inmemory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.spi.cache.CachePolicy;
+import org.jboss.dna.spi.graph.InvalidPathException;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.Path.Segment;
+import org.jboss.dna.spi.graph.commands.ActsAsUpdate;
+import org.jboss.dna.spi.graph.commands.ActsOnPath;
+import org.jboss.dna.spi.graph.commands.CompositeCommand;
+import org.jboss.dna.spi.graph.commands.CopyBranchCommand;
+import org.jboss.dna.spi.graph.commands.CopyNodeCommand;
+import org.jboss.dna.spi.graph.commands.CreateNodeCommand;
+import org.jboss.dna.spi.graph.commands.DeleteBranchCommand;
+import org.jboss.dna.spi.graph.commands.GetChildrenCommand;
+import org.jboss.dna.spi.graph.commands.GetPropertiesCommand;
+import org.jboss.dna.spi.graph.commands.GraphCommand;
+import org.jboss.dna.spi.graph.commands.MoveBranchCommand;
+import org.jboss.dna.spi.graph.commands.SetPropertiesCommand;
+import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
+import org.jboss.dna.spi.graph.connection.RepositoryConnection;
+import org.jboss.dna.spi.graph.connection.RepositorySourceListener;
+
+/**
+ * @author Randall Hauch
+ */
+public class InMemoryRepositoryConnection implements RepositoryConnection {
+
+    protected static final RepositorySourceListener NO_OP_LISTENER = new RepositorySourceListener() {
+
+        /**
+         * {@inheritDoc}
+         */
+        public void notify( String sourceName,
+                            Object... events ) {
+            // do nothing
+        }
+    };
+
+    private final InMemoryRepositorySource source;
+    private final InMemoryRepository content;
+    private RepositorySourceListener listener = NO_OP_LISTENER;
+
+    InMemoryRepositoryConnection( InMemoryRepositorySource source,
+                                  InMemoryRepository content ) {
+        assert source != null;
+        assert content != null;
+        this.source = source;
+        this.content = content;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getSourceName() {
+        return source.getName();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public CachePolicy getDefaultCachePolicy() {
+        return source.getDefaultCachePolicy();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public XAResource getXAResource() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean ping( long time,
+                         TimeUnit unit ) {
+        this.content.getRoot();
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setListener( RepositorySourceListener listener ) {
+        this.listener = listener != null ? listener : NO_OP_LISTENER;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void close() {
+        // do nothing
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void execute( ExecutionEnvironment env,
+                         GraphCommand... commands ) {
+        // Do any commands update/write?
+        Lock lock = this.content.getLock().readLock();
+        for (GraphCommand command : commands) {
+            if (command instanceof ActsAsUpdate) {
+                lock = this.content.getLock().writeLock();
+                break;
+            }
+        }
+
+        try {
+            // Obtain the lock ...
+            lock.lock();
+            // Now execute the commands ...
+            for (GraphCommand command : commands) {
+                executeCommand(env, command);
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * @param env
+     * @param command
+     */
+    protected void executeCommand( ExecutionEnvironment env,
+                                   GraphCommand command ) {
+        // This node reference is available for any command that extends ActsOnPath ...
+        Node node = null;
+
+        if (command instanceof CompositeCommand) {
+            CompositeCommand theCommand = (CompositeCommand)command;
+            for (GraphCommand containedCommand : theCommand) {
+                executeCommand(env, containedCommand);
+            }
+        }
+
+        // First, process the commands that create a new node ...
+        if (command instanceof CreateNodeCommand) {
+            CreateNodeCommand theCommand = (CreateNodeCommand)command;
+            Path path = theCommand.getPath();
+            Path parent = path.getAncestor();
+            // Look up the parent node, which must exist ...
+            Node parentNode = content.getNode(parent);
+            node = content.createNode(env, parentNode, path.getLastSegment().getName());
+            // Now add the properties to the supplied node ...
+            for (Property property : theCommand.getProperties()) {
+                Name propName = property.getName();
+                if (property.size() == 0) {
+                    node.getProperties().remove(propName);
+                    continue;
+                }
+                node.getProperties().put(propName, property);
+            }
+            assert node != null;
+        }
+
+        // Otherwise, check whether the command is applies to a path; all the remaining commands
+        // that do so expect the node to exist ...
+        else if (command instanceof ActsOnPath) {
+            ActsOnPath theCommand = (ActsOnPath)command;
+            Path path = theCommand.getPath();
+            // Look up the node with the supplied path ...
+            node = content.getNode(path);
+            if (node == null) throw new InvalidPathException(InMemoryConnectorI18n.nodeDoesNotExist.text(path));
+        }
+
+        if (command instanceof GetChildrenCommand) {
+            GetChildrenCommand theCommand = (GetChildrenCommand)command;
+            assert command instanceof ActsOnPath;
+            assert node != null;
+            // Get the names of the children ...
+            List<Node> children = node.getChildren();
+            List<Segment> childSegments = new ArrayList<Segment>(children.size());
+            for (Node child : children) {
+                childSegments.add(child.getName());
+            }
+            theCommand.setChildren(childSegments);
+
+        }
+        if (command instanceof GetPropertiesCommand) {
+            GetPropertiesCommand theCommand = (GetPropertiesCommand)command;
+            assert command instanceof ActsOnPath;
+            assert node != null;
+            for (Property property : node.getProperties().values()) {
+                theCommand.setProperty(property);
+            }
+        }
+        if (command instanceof SetPropertiesCommand) {
+            SetPropertiesCommand theCommand = (SetPropertiesCommand)command;
+            assert command instanceof ActsOnPath;
+            assert node != null;
+            // Now set (or remove) the properties to the supplied node ...
+            for (Property property : theCommand.getProperties()) {
+                Name propName = property.getName();
+                if (property.size() == 0) {
+                    node.getProperties().remove(propName);
+                    continue;
+                }
+                node.getProperties().put(propName, property);
+            }
+        }
+        if (command instanceof DeleteBranchCommand) {
+            assert command instanceof ActsOnPath;
+            assert node != null;
+            content.removeNode(env, node);
+        }
+        if (command instanceof CopyNodeCommand) {
+            CopyNodeCommand theCommand = (CopyNodeCommand)command;
+            boolean recursive = command instanceof CopyBranchCommand;
+            // Look up the new parent, which must exist ...
+            Path newPath = theCommand.getNewPath();
+            Node newParent = content.getNode(newPath.getAncestor());
+            if (newParent == null) {
+                throw new InvalidPathException(InMemoryConnectorI18n.nodeDoesNotExist.text(newPath.getAncestor()));
+            }
+            content.copyNode(env, node, newParent, recursive);
+        }
+        if (command instanceof MoveBranchCommand) {
+            MoveBranchCommand theCommand = (MoveBranchCommand)command;
+            assert command instanceof ActsOnPath;
+            assert node != null;
+            // Look up the new parent, which must exist ...
+            Path newPath = theCommand.getNewPath();
+            Node newParent = content.getNode(newPath.getAncestor());
+            if (newParent == null) {
+                throw new InvalidPathException(InMemoryConnectorI18n.nodeDoesNotExist.text(newPath.getAncestor()));
+            }
+            node.setParent(newParent);
+        }
+    }
+
+    /**
+     * @return listener
+     */
+    protected RepositorySourceListener getListener() {
+        return this.listener;
+    }
+
+}

Added: trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositorySource.java
===================================================================
--- trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositorySource.java	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositorySource.java	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,277 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.connector.inmemory;
+
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.naming.spi.ObjectFactory;
+import net.jcip.annotations.GuardedBy;
+import org.jboss.dna.common.util.ArgCheck;
+import org.jboss.dna.spi.cache.CachePolicy;
+import org.jboss.dna.spi.graph.connection.RepositoryConnection;
+import org.jboss.dna.spi.graph.connection.RepositorySource;
+
+/**
+ * @author Randall Hauch
+ */
+public class InMemoryRepositorySource implements RepositorySource, ObjectFactory {
+
+    /**
+     * The initial version is 1
+     */
+    private static final long serialVersionUID = 1L;
+
+    public static final String DEFAULT_UUID_PROPERTY_NAMESPACE = "http://www.jboss.org/dna/connector/jbosscache";
+    public static final String DEFAULT_UUID_PROPERTY_NAME = "uuid";
+
+    private static final ConcurrentMap<String, InMemoryRepositorySource> sources = new ConcurrentHashMap<String, InMemoryRepositorySource>();
+    private static final ReadWriteLock sourcesLock = new ReentrantReadWriteLock();
+
+    /**
+     * Get the names of the in-memory repository sources that are currently registered
+     * 
+     * @return the unmodifiable set of names
+     */
+    public static Set<String> getSourceNames() {
+        Lock lock = sourcesLock.readLock();
+        try {
+            lock.lock();
+            return Collections.unmodifiableSet(sources.keySet());
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Get the source with the supplied name.
+     * 
+     * @param name the name
+     * @return the source, or null if there is no source with the supplied name
+     */
+    public static InMemoryRepositorySource getSource( String name ) {
+        Lock lock = sourcesLock.readLock();
+        try {
+            lock.lock();
+            return sources.get(name);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    @GuardedBy( "sourcesLock" )
+    private String name;
+    @GuardedBy( "this" )
+    private String jndiName;
+    private final AtomicInteger retryLimit = new AtomicInteger(0);
+    private UUID rootNodeUuid = UUID.randomUUID();
+    private CachePolicy defaultCachePolicy;
+    private String configurationName;
+    private transient InMemoryRepository repository;
+
+    /**
+     * Create a repository source instance.
+     */
+    public InMemoryRepositorySource() {
+    }
+
+    /**
+     * Get the default cache policy for this source, or null if the global default cache policy should be used
+     * 
+     * @return the default cache policy, or null if this source has no explicit default cache policy
+     */
+    public CachePolicy getDefaultCachePolicy() {
+        return defaultCachePolicy;
+    }
+
+    /**
+     * @param defaultCachePolicy Sets defaultCachePolicy to the specified value.
+     */
+    public void setDefaultCachePolicy( CachePolicy defaultCachePolicy ) {
+        this.defaultCachePolicy = defaultCachePolicy;
+    }
+
+    /**
+     * @return rootNodeUuid
+     */
+    public UUID getRootNodeUuid() {
+        return this.rootNodeUuid;
+    }
+
+    /**
+     * @param rootNodeUuid Sets rootNodeUuid to the specified value.
+     */
+    public void setRootNodeUuid( UUID rootNodeUuid ) {
+        this.rootNodeUuid = rootNodeUuid != null ? rootNodeUuid : UUID.randomUUID();
+    }
+
+    /**
+     * If you use this to set a JNDI name, this source will be bound to that name using the default {@link InitialContext}. You
+     * can also do this manually if you have additional requirements.
+     * 
+     * @param name the JNDI name
+     * @throws NamingException if there is a problem registering this object
+     * @see #getJndiName()
+     */
+    public void setJndiName( String name ) throws NamingException {
+        setJndiName(name, null);
+    }
+
+    /**
+     * Register this source in JNDI under the supplied name using the supplied context. to set a JNDI name, this source will be
+     * bound to that name using the default {@link InitialContext}. You can also do this manually if you have additional
+     * requirements.
+     * 
+     * @param name the JNDI name, or null if this object is to no longer be registered
+     * @param context the JNDI context, or null if the {@link InitialContext} should be used
+     * @throws NamingException if there is a problem registering this object
+     * @see #getJndiName()
+     */
+    public synchronized void setJndiName( String name,
+                                          Context context ) throws NamingException {
+        ArgCheck.isNotNull(name, "name");
+        if (context == null) context = new InitialContext();
+
+        // First register in JNDI under the new name ...
+        if (name != null) {
+            context.bind(name, this);
+        }
+        // Second, unregister from JNDI if there is already a name ...
+        if (jndiName != null && !jndiName.equals(name)) {
+            context.unbind(jndiName);
+        }
+        // Record the new name ...
+        this.jndiName = name;
+    }
+
+    /**
+     * Gets the JNDI name this source is bound to. Only valid if you used setJNDIName to bind it.
+     * 
+     * @return the JNDI name, or null if it is not bound in JNDI
+     * @see #setJndiName(String)
+     */
+    public synchronized String getJndiName() {
+        return jndiName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getName() {
+        Lock lock = sourcesLock.readLock();
+        try {
+            lock.lock();
+            return this.name;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * @param name Sets name to the specified value.
+     * @return true if the name was changed, or false if an existing instance already exists with that name
+     */
+    public boolean setName( String name ) {
+        Lock lock = sourcesLock.writeLock();
+        try {
+            lock.lock();
+            // Determine if this name is allowed ...
+            if (sources.containsKey(name)) return false;
+
+            // Remove this object under its current name
+            if (this.name != null) {
+                sources.remove(this.name);
+            }
+            // Register this object under the new name
+            this.name = name;
+            sources.put(this.name, this);
+            return true;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getRetryLimit() {
+        return retryLimit.get();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setRetryLimit( int limit ) {
+        retryLimit.set(limit);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized RepositoryConnection getConnection() {
+        if (this.repository == null) {
+            repository = new InMemoryRepository(configurationName, this.rootNodeUuid);
+        }
+        return new InMemoryRepositoryConnection(this, this.repository);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Reference getReference() {
+        String className = getClass().getName();
+        String factoryClassName = className;
+        return new Reference(className, new StringRefAddr("DnaConnectorInMemoryRepositorySource", getName()), factoryClassName,
+                             null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object getObjectInstance( Object obj,
+                                     javax.naming.Name name,
+                                     Context nameCtx,
+                                     Hashtable<?, ?> environment ) {
+        if (obj instanceof Reference) {
+            Reference ref = (Reference)obj;
+            if (ref.getClassName().equals(getClass().getName())) {
+                RefAddr addr = ref.get("DnaConnectorInMemoryRepositorySource");
+                return InMemoryRepositorySource.getSource((String)addr.getContent());
+            }
+        }
+        return null;
+    }
+
+}

Added: trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/Node.java
===================================================================
--- trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/Node.java	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/Node.java	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,169 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.connector.inmemory;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.PropertyFactory;
+import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
+
+/**
+ * @author Randall Hauch
+ */
+ at NotThreadSafe
+public class Node {
+
+    private final UUID uuid;
+    private Node parent;
+    private Path.Segment name;
+    private final Map<Name, Property> properties = new HashMap<Name, Property>();
+    private final List<Node> children = new LinkedList<Node>();
+
+    public Node( UUID uuid ) {
+        assert uuid != null;
+        this.uuid = uuid;
+    }
+
+    /**
+     * @return uuid
+     */
+    public UUID getUuid() {
+        return uuid;
+    }
+
+    /**
+     * @return name
+     */
+    public Path.Segment getName() {
+        return name;
+    }
+
+    /**
+     * @param name Sets name to the specified value.
+     */
+    protected void setName( Path.Segment name ) {
+        this.name = name;
+    }
+
+    /**
+     * @return parent
+     */
+    public Node getParent() {
+        return parent;
+    }
+
+    /**
+     * @param parent Sets parent to the specified value.
+     */
+    protected void setParent( Node parent ) {
+        this.parent = parent;
+    }
+
+    /**
+     * @return children
+     */
+    protected List<Node> getChildren() {
+        return children;
+    }
+
+    /**
+     * @return properties
+     */
+    protected Map<Name, Property> getProperties() {
+        return properties;
+    }
+
+    public Node setProperty( Property property ) {
+        if (property != null) {
+            this.properties.put(property.getName(), property);
+        }
+        return this;
+    }
+
+    public Node setProperty( ExecutionEnvironment env,
+                             String name,
+                             Object... values ) {
+        PropertyFactory propertyFactory = env.getPropertyFactory();
+        Name propertyName = env.getValueFactories().getNameFactory().create(name);
+        return setProperty(propertyFactory.create(propertyName, values));
+    }
+
+    public Property getProperty( ExecutionEnvironment env,
+                                 String name ) {
+        Name propertyName = env.getValueFactories().getNameFactory().create(name);
+        return getProperty(propertyName);
+    }
+
+    public Property getProperty( Name name ) {
+        return this.properties.get(name);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return uuid.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof Node) {
+            Node that = (Node)obj;
+            if (!this.getUuid().equals(that.getUuid())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (this.name == null) {
+            sb.append("");
+        } else {
+            sb.append(this.name);
+        }
+        sb.append(" (").append(uuid).append(")");
+        return sb.toString();
+    }
+}

Added: trunk/connectors/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.properties
===================================================================
--- trunk/connectors/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.properties	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.properties	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,24 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2008, Red Hat Middleware LLC, and individual contributors
+# as indicated by the @author tags. See the copyright.txt file in the
+# distribution for a full listing of individual contributors.
+#
+# This is free software; you can redistribute it and/or modify it
+# 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.
+#
+# This software 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.
+#
+
+connectorName = In-Memory Connector
+nodeDoesNotExist = Could not find an existing node at {0}
\ No newline at end of file

Added: trunk/connectors/dna-connector-inmemory/src/test/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryTest.java
===================================================================
--- trunk/connectors/dna-connector-inmemory/src/test/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryTest.java	                        (rev 0)
+++ trunk/connectors/dna-connector-inmemory/src/test/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryTest.java	2008-06-30 21:32:40 UTC (rev 317)
@@ -0,0 +1,351 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.connector.inmemory;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.NameFactory;
+import org.jboss.dna.spi.graph.PathFactory;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.PropertyFactory;
+import org.jboss.dna.spi.graph.ValueFactories;
+import org.jboss.dna.spi.graph.ValueFactory;
+import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
+import org.jboss.dna.spi.graph.impl.BasicNamespaceRegistry;
+import org.jboss.dna.spi.graph.impl.BasicPropertyFactory;
+import org.jboss.dna.spi.graph.impl.StandardValueFactories;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class InMemoryRepositoryTest {
+
+    private InMemoryRepository repository;
+    private String name;
+    private UUID rootUuid;
+    private ValueFactories valueFactories;
+    private PathFactory pathFactory;
+    private NameFactory nameFactory;
+    private PropertyFactory propertyFactory;
+    @Mock
+    private ExecutionEnvironment env;
+
+    @Before
+    public void beforeEach() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        valueFactories = new StandardValueFactories(new BasicNamespaceRegistry());
+        pathFactory = valueFactories.getPathFactory();
+        nameFactory = valueFactories.getNameFactory();
+        propertyFactory = new BasicPropertyFactory(valueFactories);
+        name = "Test repository";
+        rootUuid = UUID.randomUUID();
+        repository = new InMemoryRepository(name, rootUuid);
+        stub(env.getValueFactories()).toReturn(valueFactories);
+        stub(env.getPropertyFactory()).toReturn(propertyFactory);
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAllowNullNameInConstructor() {
+        new InMemoryRepository(null, rootUuid);
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldNotAllowBlankNameInConstructor() {
+        new InMemoryRepository("  \t  ", rootUuid);
+    }
+
+    @Test
+    public void shouldHaveLock() {
+        assertThat(repository.getLock(), is(notNullValue()));
+    }
+
+    @Test
+    public void shouldHaveRootNodeAfterInstantiating() {
+        assertThat(repository.getRoot(), is(notNullValue()));
+    }
+
+    @Test
+    public void shouldHaveNameAfterInstantiating() {
+        assertThat(repository.getName(), is(name));
+    }
+
+    @Test
+    public void shouldHaveRootNodeWithRootUuid() {
+        assertThat(repository.getRoot().getUuid(), is(rootUuid));
+    }
+
+    @Test
+    public void shouldGenerateUuids() {
+        Set<UUID> uuids = new HashSet<UUID>();
+        for (int i = 0; i != 100; ++i) {
+            assertThat(uuids.add(repository.generateUuid()), is(true));
+        }
+    }
+
+    @Test( expected = AssertionError.class )
+    public void shouldNotAllowRootToBeRemoved() {
+        repository.removeNode(env, repository.getRoot());
+    }
+
+    @Test( expected = AssertionError.class )
+    public void shouldNotAllowRootToBeMoved() {
+        Node node = mock(Node.class);
+        repository.moveNode(env, repository.getRoot(), node);
+    }
+
+    @Test( expected = AssertionError.class )
+    public void shouldNotAllowNodeToBeMovedUsingNullEnvironment() {
+        Node node = mock(Node.class);
+        Node newParent = mock(Node.class);
+        repository.moveNode(null, node, newParent);
+    }
+
+    @Test( expected = AssertionError.class )
+    public void shouldNotAllowNullNodeToBeMoved() {
+        Node newParent = mock(Node.class);
+        repository.moveNode(env, null, newParent);
+    }
+
+    @Test( expected = AssertionError.class )
+    public void shouldNotAllowNodeToBeRemovedUsingNullEnvironment() {
+        Node node = mock(Node.class);
+        repository.removeNode(null, node);
+    }
+
+    @Test( expected = AssertionError.class )
+    public void shouldNotAllowNullNodeToBeRemoved() {
+        repository.removeNode(env, null);
+    }
+
+    @Test
+    public void shouldCreateNodesByPath() {
+        Name name_a = nameFactory.create("a");
+        Node node_a = repository.createNode(env, repository.getRoot(), name_a);
+        assertThat(node_a, is(notNullValue()));
+        assertThat(node_a.getParent(), is(repository.getRoot()));
+        assertThat(node_a.getName().getName(), is(name_a));
+        assertThat(node_a.getName().hasIndex(), is(false));
+
+        Name name_b = nameFactory.create("b");
+        Node node_b = repository.createNode(env, node_a, name_b);
+        assertThat(node_b, is(notNullValue()));
+        assertThat(node_b.getParent(), is(node_a));
+        assertThat(node_b.getName().getName(), is(name_b));
+        assertThat(node_b.getName().hasIndex(), is(false));
+
+        Name name_c = nameFactory.create("c");
+        Node node_c = repository.createNode(env, node_b, name_c);
+        assertThat(node_c, is(notNullValue()));
+        assertThat(node_c.getParent(), is(node_b));
+        assertThat(node_c.getName().getName(), is(name_c));
+        assertThat(node_c.getName().hasIndex(), is(false));
+
+        assertThat(repository.getNodesByUuid().size(), is(4));
+        assertThat(repository.getNode(pathFactory.create("/")), is(sameInstance(repository.getRoot())));
+        assertThat(repository.getNode(pathFactory.create("/a")), is(sameInstance(node_a)));
+        assertThat(repository.getNode(pathFactory.create("/a/b")), is(sameInstance(node_b)));
+        assertThat(repository.getNode(pathFactory.create("/a/b/c")), is(sameInstance(node_c)));
+    }
+
+    @Test
+    public void shouldNotFindNodesThatDoNotExist() {
+        Node node_a = repository.createNode(env, repository.getRoot(), nameFactory.create("a"));
+        Node node_b = repository.createNode(env, node_a, nameFactory.create("b"));
+        /*Node node_c =*/repository.createNode(env, node_b, nameFactory.create("c"));
+
+        assertThat(repository.getNodesByUuid().size(), is(4));
+        assertThat(repository.getNode(pathFactory.create("/a[1]")), is(nullValue()));
+        assertThat(repository.getNode(pathFactory.create("/d")), is(nullValue()));
+    }
+
+    @Test
+    public void shouldCorrectlyManageIndexesOfSiblingsWithSameNames() {
+        Name name_a1 = nameFactory.create("a");
+        Node node_a1 = repository.createNode(env, repository.getRoot(), name_a1);
+        assertThat(node_a1, is(notNullValue()));
+        assertThat(node_a1.getParent(), is(repository.getRoot()));
+        assertThat(node_a1.getName().getName(), is(name_a1));
+        assertThat(node_a1.getName().hasIndex(), is(false));
+
+        Name name_a2 = nameFactory.create("a");
+        Node node_a2 = repository.createNode(env, repository.getRoot(), name_a2);
+        assertThat(node_a2, is(notNullValue()));
+        assertThat(node_a2.getParent(), is(repository.getRoot()));
+        assertThat(node_a2.getName().getName(), is(name_a2));
+        assertThat(node_a2.getName().hasIndex(), is(true));
+        assertThat(node_a2.getName().getIndex(), is(2));
+
+        // node 1 should now have an index ...
+        assertThat(node_a1.getName().getIndex(), is(1));
+
+        // Add another node without the same name ...
+        Name name_b = nameFactory.create("b");
+        Node node_b = repository.createNode(env, repository.getRoot(), name_b);
+        assertThat(node_b, is(notNullValue()));
+        assertThat(node_b.getParent(), is(repository.getRoot()));
+        assertThat(node_b.getName().getName(), is(name_b));
+        assertThat(node_b.getName().hasIndex(), is(false));
+
+        // Add a third node with the same name ...
+        Name name_a3 = nameFactory.create("a");
+        Node node_a3 = repository.createNode(env, repository.getRoot(), name_a3);
+        assertThat(node_a3, is(notNullValue()));
+        assertThat(node_a3.getParent(), is(repository.getRoot()));
+        assertThat(node_a3.getName().getName(), is(name_a3));
+        assertThat(node_a3.getName().hasIndex(), is(true));
+        assertThat(node_a3.getName().getIndex(), is(3));
+
+        // Check the number of children ...
+        assertThat(repository.getRoot().getChildren().size(), is(4));
+        assertThat(repository.getRoot().getChildren(), hasItems(node_a1, node_a2, node_b, node_a3));
+        assertThat(repository.getNodesByUuid().size(), is(5));
+        assertThat(repository.getNode(pathFactory.create("/a[1]")), is(sameInstance(node_a1)));
+        assertThat(repository.getNode(pathFactory.create("/a[2]")), is(sameInstance(node_a2)));
+        assertThat(repository.getNode(pathFactory.create("/a[3]")), is(sameInstance(node_a3)));
+        assertThat(repository.getNode(pathFactory.create("/b")), is(sameInstance(node_b)));
+
+        // Removing a node with the same name will reduce the index ...
+        repository.removeNode(env, node_a2);
+        assertThat(repository.getRoot().getChildren().size(), is(3));
+        assertThat(repository.getRoot().getChildren(), hasItems(node_a1, node_b, node_a3));
+        assertThat(node_a1.getName().getIndex(), is(1));
+        assertThat(node_b.getName().hasIndex(), is(false));
+        assertThat(node_a3.getName().getIndex(), is(2));
+
+        // Removing a node with the same name will reduce the index ...
+        repository.removeNode(env, node_a1);
+        assertThat(repository.getRoot().getChildren().size(), is(2));
+        assertThat(repository.getRoot().getChildren(), hasItems(node_b, node_a3));
+        assertThat(node_b.getName().hasIndex(), is(false));
+        assertThat(node_a3.getName().hasIndex(), is(false));
+        assertThat(repository.getNodesByUuid().size(), is(3));
+    }
+
+    @Test
+    public void shouldMoveNodes() {
+        Node root = repository.getRoot();
+        Node node_a = repository.createNode(env, root, nameFactory.create("a"));
+        Node node_b = repository.createNode(env, node_a, nameFactory.create("b"));
+        Node node_c = repository.createNode(env, node_b, nameFactory.create("c"));
+        Node node_d = repository.createNode(env, root, nameFactory.create("d"));
+        Node node_e = repository.createNode(env, node_d, nameFactory.create("e"));
+        Node node_b2 = repository.createNode(env, node_d, nameFactory.create("b"));
+
+        assertThat(repository.getNodesByUuid().size(), is(7));
+        assertThat(repository.getNode(pathFactory.create("/")), is(sameInstance(repository.getRoot())));
+        assertThat(repository.getNode(pathFactory.create("/a")), is(sameInstance(node_a)));
+        assertThat(repository.getNode(pathFactory.create("/a/b")), is(sameInstance(node_b)));
+        assertThat(repository.getNode(pathFactory.create("/a/b/c")), is(sameInstance(node_c)));
+        assertThat(repository.getNode(pathFactory.create("/d")), is(sameInstance(node_d)));
+        assertThat(repository.getNode(pathFactory.create("/d/e")), is(sameInstance(node_e)));
+        assertThat(repository.getNode(pathFactory.create("/d/b")), is(sameInstance(node_b2)));
+
+        repository.moveNode(env, node_b, node_d);
+
+        assertThat(repository.getNode(pathFactory.create("/")), is(sameInstance(repository.getRoot())));
+        assertThat(repository.getNode(pathFactory.create("/a")), is(sameInstance(node_a)));
+        assertThat(repository.getNode(pathFactory.create("/d")), is(sameInstance(node_d)));
+        assertThat(repository.getNode(pathFactory.create("/d/e")), is(sameInstance(node_e)));
+        assertThat(repository.getNode(pathFactory.create("/d/b[1]")), is(sameInstance(node_b2)));
+        assertThat(repository.getNode(pathFactory.create("/d/b[2]")), is(sameInstance(node_b)));
+        assertThat(repository.getNode(pathFactory.create("/d/b[2]/c")), is(sameInstance(node_c)));
+
+        repository.moveNode(env, node_b, node_e);
+
+        assertThat(repository.getNode(pathFactory.create("/")), is(sameInstance(repository.getRoot())));
+        assertThat(repository.getNode(pathFactory.create("/a")), is(sameInstance(node_a)));
+        assertThat(repository.getNode(pathFactory.create("/d")), is(sameInstance(node_d)));
+        assertThat(repository.getNode(pathFactory.create("/d/e")), is(sameInstance(node_e)));
+        assertThat(repository.getNode(pathFactory.create("/d/e/b")), is(sameInstance(node_b)));
+        assertThat(repository.getNode(pathFactory.create("/d/e/b/c")), is(sameInstance(node_c)));
+        assertThat(repository.getNode(pathFactory.create("/d/b")), is(sameInstance(node_b2)));
+    }
+
+    @Test
+    public void shouldCopyNodes() {
+        Node root = repository.getRoot();
+        Node node_a = repository.createNode(env, root, nameFactory.create("a"));
+        Node node_b = repository.createNode(env, node_a, nameFactory.create("b"));
+        Node node_c = repository.createNode(env, node_b, nameFactory.create("c"));
+        Node node_d = repository.createNode(env, root, nameFactory.create("d"));
+        Node node_e = repository.createNode(env, node_d, nameFactory.create("e"));
+        Node node_b2 = repository.createNode(env, node_d, nameFactory.create("b"));
+
+        ValueFactory<String> stringFactory = valueFactories.getStringFactory();
+        Name propertyName = nameFactory.create("something");
+        Property property = propertyFactory.create(propertyName, stringFactory.create("Worth the wait"));
+        node_b.getProperties().put(propertyName, property);
+
+        assertThat(repository.getNodesByUuid().size(), is(7));
+        assertThat(repository.getNode(pathFactory.create("/")), is(sameInstance(repository.getRoot())));
+        assertThat(repository.getNode(pathFactory.create("/a")), is(sameInstance(node_a)));
+        assertThat(repository.getNode(pathFactory.create("/a/b")), is(sameInstance(node_b)));
+        assertThat(repository.getNode(pathFactory.create("/a/b/c")), is(sameInstance(node_c)));
+        assertThat(repository.getNode(pathFactory.create("/d")), is(sameInstance(node_d)));
+        assertThat(repository.getNode(pathFactory.create("/d/e")), is(sameInstance(node_e)));
+        assertThat(repository.getNode(pathFactory.create("/d/b")), is(sameInstance(node_b2)));
+
+        assertThat(repository.getNode(pathFactory.create("/a/b")).getProperties().get(propertyName), is(property));
+
+        repository.copyNode(env, node_b, node_d, true);
+
+        assertThat(repository.getNodesByUuid().size(), is(9));
+        assertThat(repository.getNode(pathFactory.create("/")), is(sameInstance(repository.getRoot())));
+        assertThat(repository.getNode(pathFactory.create("/a")), is(sameInstance(node_a)));
+        assertThat(repository.getNode(pathFactory.create("/a/b")), is(sameInstance(node_b)));
+        assertThat(repository.getNode(pathFactory.create("/a/b/c")), is(sameInstance(node_c)));
+        assertThat(repository.getNode(pathFactory.create("/d")), is(sameInstance(node_d)));
+        assertThat(repository.getNode(pathFactory.create("/d/e")), is(sameInstance(node_e)));
+        assertThat(repository.getNode(pathFactory.create("/d/b[1]")), is(sameInstance(node_b2)));
+        assertThat(repository.getNode(pathFactory.create("/d/b[2]")), is(notNullValue()));
+        assertThat(repository.getNode(pathFactory.create("/d/b[2]/c")), is(notNullValue()));
+
+        assertThat(repository.getNode(pathFactory.create("/a/b")).getProperties().get(propertyName), is(property));
+        assertThat(repository.getNode(pathFactory.create("/d/b[2]")).getProperties().get(propertyName), is(property));
+    }
+
+    @Test
+    public void shouldCreateRepositoryStructure() {
+        repository.createNode(env, "/a").setProperty(env, "name", "value").setProperty(env, "desc", "Some description");
+        repository.createNode(env, "/a/b").setProperty(env, "name", "value2").setProperty(env, "desc", "Some description 2");
+        assertThat(repository.getNode(env, "/a").getProperty(env, "name").getValuesAsArray(), is(new Object[] {"value"}));
+        assertThat(repository.getNode(env, "/a").getProperty(env, "desc").getValuesAsArray(),
+                   is(new Object[] {"Some description"}));
+        assertThat(repository.getNode(env, "/a/b").getProperty(env, "name").getValuesAsArray(), is(new Object[] {"value2"}));
+        assertThat(repository.getNode(env, "/a/b").getProperty(env, "desc").getValuesAsArray(),
+                   is(new Object[] {"Some description 2"}));
+    }
+}




More information about the dna-commits mailing list