[dna-commits] DNA SVN: r515 - in trunk: dna-repository/src/main/java/org/jboss/dna/repository/sequencers/xml and 17 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Thu Sep 11 19:33:03 EDT 2008


Author: rhauch
Date: 2008-09-11 19:33:03 -0400 (Thu, 11 Sep 2008)
New Revision: 515

Added:
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/BasicExecutionContextFactory.java
   trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/ConsoleInput.java
   trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/UserInterface.java
   trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml
   trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml
   trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/
   trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java
   trunk/extensions/dna-connector-inmemory/src/test/resources/log4j.properties
Modified:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryImporter.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/xml/XmlSequencer.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/RepositoryImporterTest.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/RepositoryServiceTest.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/RepositoryConnectionPool.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicName.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicNamespaceRegistry.java
   trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicNameTest.java
   trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicNamespaceRegistryTest.java
   trunk/docs/examples/gettingstarted/repositories/pom.xml
   trunk/docs/examples/gettingstarted/repositories/src/main/assembly/basic.xml
   trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java
   trunk/docs/examples/gettingstarted/repositories/src/main/resources/log4j.properties
   trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.java
   trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java
   trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryConnection.java
   trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositorySource.java
   trunk/extensions/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.properties
Log:
DNA-222 - Create Getting Started example application for repositories
http://jira.jboss.com/jira/browse/DNA-222

Created a simple application (subproject under getting started examples) that allows a user to pick from a list of repositories so they can interactively navigate and explore them.  Currently, only a console user interface is implemented.  Several repositories are used: a configuration repository (which defines the others), two in-memory repositories ("Cars" and "Airplanes"), a federated repository ("Vehicles" that federates "Cars" and "Airplanes" below a "vehicles" root), and a cache repository used by "Vehicles".

Most of the unit testing is complete, and several issues have been fixed in other components.  However, there still are a few outstanding issues as well as testing of the interactive mode.  So, still a bit more work to do.

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryImporter.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryImporter.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryImporter.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -21,6 +21,7 @@
  */
 package org.jboss.dna.repository;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
@@ -33,11 +34,13 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.monitor.ProgressMonitor;
 import org.jboss.dna.common.monitor.SimpleProgressMonitor;
 import org.jboss.dna.common.util.ArgCheck;
 import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.repository.sequencers.xml.DnaXmlLexicon;
 import org.jboss.dna.repository.sequencers.xml.XmlSequencer;
 import org.jboss.dna.spi.ExecutionContext;
 import org.jboss.dna.spi.connector.RepositoryConnection;
@@ -69,27 +72,110 @@
  */
 public class RepositoryImporter {
 
+    public interface ImportSpecification {
+        /**
+         * Specify the location where the content is to be imported, and then perform the import. This is equivalent to calling
+         * <code>{@link #into(String, Path) into(sourceName,rootPath)}</code>.
+         * 
+         * @param sourceName the name of the source into which the content is to be imported
+         * @throws IllegalArgumentException if the <code>uri</code> or path are null
+         * @throws IOException if there is a problem reading the content
+         * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource
+         *         repository source}
+         */
+        void into( String sourceName ) throws IOException, RepositorySourceException;
+
+        /**
+         * Specify the location where the content is to be imported, and then perform the import.
+         * 
+         * @param sourceName the name of the source into which the content is to be imported
+         * @param pathInSource the path in the {@link RepositorySource repository source} named <code>sourceName</code> where the
+         *        content is to be written; may not be null
+         * @throws IllegalArgumentException if the <code>uri</code> or path are null
+         * @throws IOException if there is a problem reading the content
+         * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource
+         *         repository source}
+         */
+        void into( String sourceName,
+                   Path pathInSource ) throws IOException, RepositorySourceException;
+    }
+
+    @Immutable
+    protected abstract class ImportedContentUsingSequencer implements ImportSpecification {
+        private final StreamSequencer sequencer;
+
+        protected ImportedContentUsingSequencer( StreamSequencer sequencer ) {
+            this.sequencer = sequencer;
+        }
+
+        protected StreamSequencer getSequencer() {
+            return this.sequencer;
+        }
+
+        protected NodeConflictBehavior getConflictBehavior() {
+            return NodeConflictBehavior.UPDATE;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.RepositoryImporter.ImportSpecification#into(java.lang.String)
+         */
+        public void into( String sourceName ) throws IOException, RepositorySourceException {
+            Path root = getContext().getValueFactories().getPathFactory().createRootPath();
+            into(sourceName, root);
+        }
+    }
+
+    @Immutable
+    protected class UriImportedContent extends ImportedContentUsingSequencer {
+        private final URI uri;
+        private final String mimeType;
+
+        protected UriImportedContent( StreamSequencer sequencer,
+                                      URI uri,
+                                      String mimeType ) {
+            super(sequencer);
+            this.uri = uri;
+            this.mimeType = mimeType;
+        }
+
+        /**
+         * @return mimeType
+         */
+        public String getMimeType() {
+            return mimeType;
+        }
+
+        /**
+         * @return uri
+         */
+        public URI getUri() {
+            return uri;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.repository.RepositoryImporter.ImportSpecification#into(java.lang.String,
+         *      org.jboss.dna.spi.graph.Path)
+         */
+        public void into( String sourceName,
+                          Path pathInSource ) throws IOException, RepositorySourceException {
+            ArgCheck.isNotNull(sourceName, "sourceName");
+            ArgCheck.isNotNull(pathInSource, "pathInSource");
+            importWithSequencer(getSequencer(), uri, mimeType, sourceName, pathInSource, getConflictBehavior());
+        }
+    }
+
     private final RepositoryConnectionFactory sources;
-    private final String sourceName;
     private final ExecutionContext context;
 
-    public RepositoryImporter( RepositorySource source,
-                               ExecutionContext context ) {
-        ArgCheck.isNotNull(source, "source");
-        ArgCheck.isNotNull(context, "context");
-        this.sources = new SingleRepositorySourceConnectionFactory(source);
-        this.sourceName = source.getName();
-        this.context = context;
-    }
-
     public RepositoryImporter( RepositoryConnectionFactory sources,
-                               String sourceName,
                                ExecutionContext context ) {
         ArgCheck.isNotNull(sources, "sources");
-        ArgCheck.isNotEmpty(sourceName, "sourceName");
         ArgCheck.isNotNull(context, "context");
         this.sources = sources;
-        this.sourceName = sourceName;
         this.context = context;
     }
 
@@ -103,23 +189,68 @@
     }
 
     /**
+     * Import the content from the XML file at the supplied URI, specifying on the returned {@link ImportSpecification} where the
+     * content is to be imported.
+     * 
+     * @param uri the URI where the importer can read the content that is to be imported
+     * @return the object that should be used to specify into which the content is to be imported
+     * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
+     */
+    public ImportSpecification importXml( URI uri ) {
+        ArgCheck.isNotNull(uri, "uri");
+
+        // Create the sequencer ...
+        StreamSequencer sequencer = new XmlSequencer();
+        return new UriImportedContent(sequencer, uri, "text/xml");
+    }
+
+    /**
+     * Import the content from the XML file at the supplied file location, specifying on the returned {@link ImportSpecification}
+     * where the content is to be imported.
+     * 
+     * @param pathToFile the path to the XML file that should be imported.
+     * @return the object that should be used to specify into which the content is to be imported
+     * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
+     */
+    public ImportSpecification importXml( String pathToFile ) {
+        ArgCheck.isNotNull(pathToFile, "pathToFile");
+        return importXml(new File(pathToFile).toURI());
+    }
+
+    /**
+     * Import the content from the supplied XML file, specifying on the returned {@link ImportSpecification} where the content is
+     * to be imported.
+     * 
+     * @param file the XML file that should be imported.
+     * @return the object that should be used to specify into which the content is to be imported
+     * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
+     */
+    public ImportSpecification importXml( File file ) {
+        ArgCheck.isNotNull(file, "file");
+        return importXml(file.toURI());
+    }
+
+    /**
      * Read the content from the supplied URI and import into the repository at the supplied location.
      * 
      * @param uri the URI where the importer can read the content that is to be imported
-     * @param destinationPathInSource
+     * @param sourceName the name of the source into which the content is to be imported
+     * @param destinationPathInSource the path in the {@link RepositorySource repository source} where the content is to be
+     *        written; may not be null
      * @throws IllegalArgumentException if the <code>uri</code> or destination path are null
      * @throws IOException if there is a problem reading the content
      * @throws RepositorySourceException if there is a problem while writing the content to the {@link RepositorySource repository
      *         source}
      */
     public void importXml( URI uri,
+                           String sourceName,
                            Path destinationPathInSource ) throws IOException, RepositorySourceException {
         ArgCheck.isNotNull(uri, "uri");
         ArgCheck.isNotNull(destinationPathInSource, "destinationPathInSource");
 
         // Create the sequencer ...
         StreamSequencer sequencer = new XmlSequencer();
-        importWithSequencer(sequencer, uri, "text/xml", destinationPathInSource, NodeConflictBehavior.UPDATE);
+        importWithSequencer(sequencer, uri, "text/xml", sourceName, destinationPathInSource, NodeConflictBehavior.UPDATE);
     }
 
     /**
@@ -129,6 +260,7 @@
      * @param sequencer the sequencer that should be used; may not be null
      * @param contentUri the URI where the content can be found; may not be null
      * @param mimeType the MIME type for the content; may not be null
+     * @param sourceName the name of the source into which the content is to be imported
      * @param destinationPathInSource the path in the {@link RepositorySource repository source} where the content is to be
      *        written; may not be null
      * @param conflictBehavior the behavior when a node is to be created when an existing node already exists; defaults to
@@ -140,11 +272,13 @@
     protected void importWithSequencer( StreamSequencer sequencer,
                                         URI contentUri,
                                         String mimeType,
+                                        String sourceName,
                                         Path destinationPathInSource,
                                         NodeConflictBehavior conflictBehavior ) throws IOException, RepositorySourceException {
         assert sequencer != null;
         assert contentUri != null;
         assert mimeType != null;
+        assert sourceName != null;
         assert destinationPathInSource != null;
         conflictBehavior = conflictBehavior != null ? conflictBehavior : NodeConflictBehavior.UPDATE;
 
@@ -183,7 +317,11 @@
         // Now execute the commands against the repository ...
         RepositoryConnection connection = null;
         try {
-            connection = sources.createConnection(this.sourceName);
+            connection = sources.createConnection(sourceName);
+            if (connection == null) {
+                I18n msg = RepositoryI18n.unableToFindRepositorySourceWithName;
+                throw new RepositorySourceException(msg.text(sourceName));
+            }
             connection.execute(context, commands);
         } finally {
             if (connection != null) {
@@ -237,6 +375,8 @@
         private final Map<Path, BasicCreateNodeCommand> createNodeCommands = new HashMap<Path, BasicCreateNodeCommand>();
         private final NodeConflictBehavior conflictBehavior;
         private final Path destinationPath;
+        private final NameFactory nameFactory;
+        private final Name primaryTypeName;
 
         protected ImporterCommands( Path destinationPath,
                                     NodeConflictBehavior conflictBehavior ) {
@@ -244,6 +384,8 @@
             ArgCheck.isNotNull(conflictBehavior, "conflictBehavior");
             this.conflictBehavior = conflictBehavior;
             this.destinationPath = destinationPath;
+            this.nameFactory = getContext().getValueFactories().getNameFactory();
+            this.primaryTypeName = this.nameFactory.create("jcr:primaryType");
         }
 
         /**
@@ -287,6 +429,11 @@
         public void setProperty( Path nodePath,
                                  Name propertyName,
                                  Object... values ) {
+            // Ignore the property value if the "jcr:primaryType" is "dnaxml:document" ...
+            if (this.primaryTypeName.equals(propertyName) && values.length == 1) {
+                Name typeName = this.nameFactory.create(values[0]);
+                if (DnaXmlLexicon.DOCUMENT.equals(typeName)) return;
+            }
             PathFactory pathFactory = getFactories().getPathFactory();
             if (nodePath.isAbsolute()) nodePath.relativeTo(pathFactory.createRootPath());
             nodePath = pathFactory.create(destinationPath, nodePath).getNormalizedPath();

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryService.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -134,7 +134,7 @@
      * @param sources the source manager
      * @param configurationSourceName the name of the {@link RepositorySource} that is the configuration repository
      * @param pathToConfigurationRoot the path of the node in the configuration source repository that should be treated by this
-     *        service as the root of the service's configuration; if null, then "/" is used
+     *        service as the root of the service's configuration; if null, then "/dna:system" is used
      * @param context the execution context in which this service should run
      * @param classLoaderFactory the class loader factory used to instantiate {@link RepositorySource} instances; may be null if
      *        this instance should use a default factory that attempts to load classes first from the
@@ -150,7 +150,7 @@
         ArgCheck.isNotNull(configurationSourceName, "configurationSourceName");
         ArgCheck.isNotNull(sources, "sources");
         ArgCheck.isNotNull(context, "context");
-        if (pathToConfigurationRoot == null) pathToConfigurationRoot = context.getValueFactories().getPathFactory().createRootPath();
+        if (pathToConfigurationRoot == null) pathToConfigurationRoot = context.getValueFactories().getPathFactory().create("/dna:system");
         this.sources = sources;
         this.pathToConfigurationRoot = pathToConfigurationRoot;
         this.configurationSourceName = configurationSourceName;
@@ -245,6 +245,8 @@
                 }
             } catch (Throwable err) {
                 throw new FederationException(RepositoryI18n.errorStartingRepositoryService.text());
+            } finally {
+                executor.close();
             }
             this.started.set(true);
         }

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/xml/XmlSequencer.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/xml/XmlSequencer.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/xml/XmlSequencer.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -568,8 +568,21 @@
                                   String name,
                                   Attributes attributes ) throws SAXException {
             stopIfCancelled();
-            startElement(nameFactory.create(name));
-            output.setProperty(path, getPrimaryTypeName(), getDefaultPrimaryType());
+            // Look for the "jcr:name" attribute, and use that if it's there
+            Name type = getDefaultPrimaryType();
+            Name nameObj = nameFactory.create(name);
+            for (int ndx = 0, len = attributes.getLength(); ndx < len; ++ndx) {
+                String ns = attributes.getURI(ndx);
+                String attrLocalName = attributes.getLocalName(ndx);
+                Object value = attributes.getValue(ndx);
+                String jcrNsUri = context.getNamespaceRegistry().getNamespaceForPrefix("jcr");
+                if (jcrNsUri != null && jcrNsUri.equals(ns) && attrLocalName.equals("name")) {
+                    nameObj = nameFactory.create(value);
+                    break;
+                }
+            }
+            startElement(nameObj);
+            output.setProperty(path, getPrimaryTypeName(), type);
             // Output this element's attributes using the attribute's namespace, if supplied, or the current namespace in scope.
             String inheritedNs = nsStack.getFirst();
             for (int ndx = 0, len = attributes.getLength(); ndx < len; ++ndx) {
@@ -580,6 +593,9 @@
                 if (jcrNsUri != null && jcrNsUri.equals(ns) && attrLocalName.equals("primaryType")) {
                     value = nameFactory.create(value);
                 }
+                if (jcrNsUri != null && jcrNsUri.equals(ns) && attrLocalName.equals("name")) {
+                    continue;
+                }
                 output.setProperty(path, nameFactory.create(ns.length() == 0 ? inheritedNs : ns, attrLocalName), value);
             }
             updateProgress();

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/RepositoryImporterTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/RepositoryImporterTest.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/RepositoryImporterTest.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -83,7 +83,7 @@
         context.getNamespaceRegistry().register("nt", "http://www.jcp.org/jcr/nt/1.0");
         sourceName = "sourceA";
         destinationPath = context.getValueFactories().getPathFactory().create("/a/b");
-        importer = new RepositoryImporter(sources, sourceName, context);
+        importer = new RepositoryImporter(sources, context);
         connection = new MockRepositoryConnection();
         stub(sources.createConnection(sourceName)).toReturn(connection);
     }
@@ -91,10 +91,11 @@
     @Test
     public void shouldImportXmlContentAndGenerateTheCorrectCommands() throws Exception {
         System.out.println(xmlContent);
-        importer.importXml(xmlContent, destinationPath); // writes commands as CompositeCommand to 'lastExecutedCommand'
+        importer.importXml(xmlContent).into(sourceName, destinationPath); // writes commands as CompositeCommand to
+        // 'lastExecutedCommand'
         assertThat(lastExecutedCommand, is(instanceOf(CompositeCommand.class)));
         Iterator<GraphCommand> iter = ((CompositeCommand)lastExecutedCommand).iterator();
-        assertCreateNode(iter, "/a/b/", "jcr:primaryType={http://www.jboss.org/dna/xml/1.0}document");
+        // assertCreateNode(iter, "/a/b/", "jcr:primaryType={http://www.jboss.org/dna/xml/1.0}document");
         assertCreateNode(iter, "/a/b/dnaxml:comment[1]", "any properties"); // jcr:primaryType and dnaxml:commentContent
         assertCreateNode(iter, "/a/b/dna:system[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
         assertCreateNode(iter, "/a/b/dna:system[1]/dnaxml:comment[1]", "any properties");

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/RepositoryServiceTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/RepositoryServiceTest.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/RepositoryServiceTest.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -45,6 +45,7 @@
 import org.jboss.dna.spi.connector.RepositorySource;
 import org.jboss.dna.spi.connector.SimpleRepository;
 import org.jboss.dna.spi.connector.SimpleRepositorySource;
+import org.jboss.dna.spi.graph.Path;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -62,6 +63,7 @@
     private SimpleRepositorySource configRepositorySource;
     private RepositoryConnection configRepositoryConnection;
     private ExecutionContext context;
+    private Path root;
     @Mock
     private RepositorySourceManager sources;
 
@@ -77,6 +79,7 @@
         configRepositorySource.setName(configSourceName);
         configRepositoryConnection = configRepositorySource.getConnection();
         stub(sources.createConnection(configSourceName)).toReturn(configRepositoryConnection);
+        root = context.getValueFactories().getPathFactory().createRootPath();
         service = new RepositoryService(sources, configSourceName, context, null);
     }
 
@@ -150,7 +153,7 @@
         sources.addSource(configRepositorySource);
         assertThat(sources.getSources(), hasItems((RepositorySource)configRepositorySource));
         assertThat(sources.getSources().size(), is(1));
-        service = new RepositoryService(sources, configSourceName, context, null);
+        service = new RepositoryService(sources, configSourceName, root, context, null);
 
         // Set up the configuration repository to contain 3 sources ...
         final String className = SimpleRepositorySource.class.getName();

Added: trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/BasicExecutionContextFactory.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/BasicExecutionContextFactory.java	                        (rev 0)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/BasicExecutionContextFactory.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -0,0 +1,166 @@
+/*
+ * 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.spi.connector;
+
+import java.security.AccessControlContext;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import org.jboss.dna.spi.ExecutionContext;
+import org.jboss.dna.spi.ExecutionContextFactory;
+import org.jboss.dna.spi.graph.NamespaceRegistry;
+import org.jboss.dna.spi.graph.impl.BasicNamespaceRegistry;
+
+/**
+ * Basic implementation of a {@link ExecutionContextFactory} that returns {@link BasicExecutionContext basic}
+ * {@link ExecutionContext}s.
+ * 
+ * @author Randall Hauch
+ */
+public class BasicExecutionContextFactory implements ExecutionContextFactory {
+
+    private final NamespaceRegistry defaultNamespaces = new BasicNamespaceRegistry();
+
+    /**
+     * Create a new instance of this factory.
+     */
+    public BasicExecutionContextFactory() {
+        defaultNamespaces.register("jcr", "http://www.jcp.org/jcr/1.0");
+        defaultNamespaces.register("mix", "http://www.jcp.org/jcr/mix/1.0");
+        defaultNamespaces.register("nt", "http://www.jcp.org/jcr/nt/1.0");
+        defaultNamespaces.register("dna", "http://www.jboss.org/dna");
+        defaultNamespaces.register("dnadtd", "http://www.jboss.org/dna/dtd/1.0");
+        defaultNamespaces.register("dnaxml", "http://www.jboss.org/dna/xml/1.0");
+    }
+
+    /**
+     * Create a new instance of this factory.
+     * 
+     * @param defaultNamespaceUri the URI of the namespace that should be used with names that have no specified namespace prefix
+     * @throws IllegalArgumentException if the URI is null
+     */
+    public BasicExecutionContextFactory( String defaultNamespaceUri ) {
+        this();
+        defaultNamespaces.register("", defaultNamespaceUri);
+    }
+
+    /**
+     * @return defaultNamespaces
+     */
+    public NamespaceRegistry getDefaultNamespaces() {
+        return defaultNamespaces;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.spi.ExecutionContextFactory#create()
+     */
+    public ExecutionContext create() {
+        ExecutionContext context = new BasicExecutionContext();
+        initialize(context);
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.spi.ExecutionContextFactory#create(java.security.AccessControlContext)
+     */
+    public ExecutionContext create( AccessControlContext accessControlContext ) {
+        ExecutionContext context = new BasicExecutionContext(accessControlContext);
+        initialize(context);
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.spi.ExecutionContextFactory#create(javax.security.auth.login.LoginContext)
+     */
+    public ExecutionContext create( LoginContext loginContext ) {
+        ExecutionContext context = new BasicExecutionContext(loginContext);
+        initialize(context);
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.spi.ExecutionContextFactory#create(java.lang.String)
+     */
+    public ExecutionContext create( String name ) throws LoginException {
+        LoginContext loginContext = new LoginContext(name);
+        ExecutionContext context = new BasicExecutionContext(loginContext);
+        initialize(context);
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.spi.ExecutionContextFactory#create(java.lang.String, javax.security.auth.Subject)
+     */
+    public ExecutionContext create( String name,
+                                    Subject subject ) throws LoginException {
+        LoginContext loginContext = new LoginContext(name, subject);
+        ExecutionContext context = new BasicExecutionContext(loginContext);
+        initialize(context);
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.spi.ExecutionContextFactory#create(java.lang.String, javax.security.auth.callback.CallbackHandler)
+     */
+    public ExecutionContext create( String name,
+                                    CallbackHandler callbackHandler ) throws LoginException {
+        LoginContext loginContext = new LoginContext(name, callbackHandler);
+        ExecutionContext context = new BasicExecutionContext(loginContext);
+        initialize(context);
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.spi.ExecutionContextFactory#create(java.lang.String, javax.security.auth.Subject,
+     *      javax.security.auth.callback.CallbackHandler)
+     */
+    public ExecutionContext create( String name,
+                                    Subject subject,
+                                    CallbackHandler callbackHandler ) throws LoginException {
+        LoginContext loginContext = new LoginContext(name, subject, callbackHandler);
+        ExecutionContext context = new BasicExecutionContext(loginContext);
+        initialize(context);
+        return context;
+    }
+
+    protected synchronized void initialize( ExecutionContext context ) {
+        for (String uri : this.defaultNamespaces.getRegisteredNamespaceUris()) {
+            String prefix = this.defaultNamespaces.getPrefixForNamespaceUri(uri, false);
+            context.getNamespaceRegistry().register(prefix, uri);
+        }
+    }
+}


Property changes on: trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/BasicExecutionContextFactory.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/RepositoryConnectionPool.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/RepositoryConnectionPool.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/connector/RepositoryConnectionPool.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -638,19 +638,22 @@
      */
     public boolean awaitTermination( long timeout,
                                      TimeUnit unit ) throws InterruptedException {
+        this.logger.trace("Awaiting termination");
         long nanos = unit.toNanos(timeout);
         final ReentrantLock mainLock = this.mainLock;
         try {
             mainLock.lock();
             for (;;) {
-                // this.logger.debug("---> Run state = {}; condition = {}", runState, termination);
+                // this.logger.trace("---> Run state = {0}; condition = {1}, {2} open", runState, termination, poolSize);
                 if (runState == TERMINATED) return true;
                 if (nanos <= 0) return false;
                 nanos = termination.awaitNanos(nanos);
-                // this.logger.debug("---> Done waiting: run state = {}; condition = {}", runState, termination);
+                //this.logger.trace("---> Done waiting: run state = {0}; condition = {1}, {2} open",runState,termination,poolSize)
+                // ;
             }
         } finally {
             mainLock.unlock();
+            this.logger.trace("Finished awaiting termination");
         }
     }
 
@@ -798,7 +801,7 @@
         }
         // Close the connection if we're supposed to (do it outside of the main lock)...
         if (wrapperToClose != null) {
-            closeConnection(wrapper);
+            closeConnection(wrapperToClose);
         }
     }
 
@@ -853,7 +856,7 @@
         RepositoryConnection original = wrapper.getOriginal();
         assert original != null;
         try {
-            this.logger.debug("Closing repository connection to {0}", getSourceName());
+            this.logger.debug("Closing repository connection to {0} ({1} open connections remain)", getSourceName(), poolSize);
             original.close();
         } finally {
             final ReentrantLock mainLock = this.mainLock;
@@ -862,7 +865,7 @@
                 // No matter what reduce the pool size count
                 --this.poolSize;
                 // And if shutting down and this was the last connection being used...
-                if (this.runState == SHUTDOWN && this.poolSize <= 0) {
+                if ((runState == SHUTDOWN || runState == STOP) && this.poolSize <= 0) {
                     // then signal anybody that has called "awaitTermination(...)"
                     this.logger.trace("Signalling termination of repository connection pool for {0}", getSourceName());
                     this.runState = TERMINATED;
@@ -890,7 +893,7 @@
         }
         int numClosed = extraConnections.size();
         this.poolSize -= numClosed;
-        this.logger.trace("Drained {0} unused connections", numClosed);
+        this.logger.trace("Drained {0} unused connections ({1} open connections remain)", numClosed, poolSize);
         return numClosed;
     }
 

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicName.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicName.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicName.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -31,125 +31,125 @@
 
 /**
  * A basic implementation of {@link Name}.
- *
+ * 
  * @author Randall Hauch
  * @author John Verhaeg
  */
 @Immutable
 public class BasicName implements Name {
 
-	/**
+    /**
      */
     private static final long serialVersionUID = -1737537720336990144L;
     private final String namespaceUri;
-	private final String localName;
-	private final int hc;
+    private final String localName;
+    private final int hc;
 
-	public BasicName( String namespaceUri,
-	                  String localName ) {
-		ArgCheck.isNotEmpty(localName, "localName");
-		this.namespaceUri = namespaceUri != null ? namespaceUri.trim() : "";
-		this.localName = localName != null ? localName.trim() : "";
-		this.hc = HashCode.compute(this.namespaceUri, this.localName);
-	}
+    public BasicName( String namespaceUri,
+                      String localName ) {
+        ArgCheck.isNotEmpty(localName, "localName");
+        this.namespaceUri = namespaceUri != null ? namespaceUri.trim() : "";
+        this.localName = localName != null ? localName.trim() : "";
+        this.hc = HashCode.compute(this.namespaceUri, this.localName);
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	public String getLocalName() {
-		return this.localName;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    public String getLocalName() {
+        return this.localName;
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	public String getNamespaceUri() {
-		return this.namespaceUri;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    public String getNamespaceUri() {
+        return this.namespaceUri;
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	public String getString() {
-		return getString(Path.DEFAULT_ENCODER);
-	}
+    /**
+     * {@inheritDoc}
+     */
+    public String getString() {
+        return getString(Path.DEFAULT_ENCODER);
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	public String getString( TextEncoder encoder ) {
-		if (this.getNamespaceUri().length() == 0) {
-			if (this.getLocalName().equals(Path.SELF)) return Path.SELF;
-			if (this.getLocalName().equals(Path.PARENT)) return Path.PARENT;
-		}
-		if (encoder == null) encoder = Path.DEFAULT_ENCODER;
-		return "{" + encoder.encode(this.namespaceUri) + "}" + encoder.encode(this.localName);
-	}
+    /**
+     * {@inheritDoc}
+     */
+    public String getString( TextEncoder encoder ) {
+        if (this.getNamespaceUri().length() == 0) {
+            if (this.getLocalName().equals(Path.SELF)) return Path.SELF;
+            if (this.getLocalName().equals(Path.PARENT)) return Path.PARENT;
+        }
+        if (encoder == null) encoder = Path.DEFAULT_ENCODER;
+        return "{" + encoder.encode(this.namespaceUri) + "}" + encoder.encode(this.localName);
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	public String getString( NamespaceRegistry namespaceRegistry ) {
-		ArgCheck.isNotNull(namespaceRegistry, "namespaceRegistry");
-		String prefix = namespaceRegistry.getPrefixForNamespaceUri(this.namespaceUri, true);
-		if (prefix != null) {
-			return prefix + ":" + this.localName;
-		}
-		return this.localName;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    public String getString( NamespaceRegistry namespaceRegistry ) {
+        ArgCheck.isNotNull(namespaceRegistry, "namespaceRegistry");
+        String prefix = namespaceRegistry.getPrefixForNamespaceUri(this.namespaceUri, true);
+        if (prefix != null && prefix.length() != 0) {
+            return prefix + ":" + this.localName;
+        }
+        return this.localName;
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	public String getString( NamespaceRegistry namespaceRegistry,
-	                         TextEncoder encoder ) {
-		ArgCheck.isNotNull(namespaceRegistry, "namespaceRegistry");
-		String prefix = namespaceRegistry.getPrefixForNamespaceUri(this.namespaceUri, true);
-		if (prefix != null && prefix.length() != 0) {
-			return encoder.encode(prefix) + ":" + encoder.encode(this.localName);
-		}
-		return this.localName;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    public String getString( NamespaceRegistry namespaceRegistry,
+                             TextEncoder encoder ) {
+        ArgCheck.isNotNull(namespaceRegistry, "namespaceRegistry");
+        String prefix = namespaceRegistry.getPrefixForNamespaceUri(this.namespaceUri, true);
+        if (prefix != null && prefix.length() != 0) {
+            return encoder.encode(prefix) + ":" + encoder.encode(this.localName);
+        }
+        return this.localName;
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	public int compareTo( Name that ) {
-		if (that == this) return 0;
-		int diff = this.getNamespaceUri().compareTo(that.getNamespaceUri());
-		if (diff != 0) return diff;
-		diff = this.getLocalName().compareTo(that.getLocalName());
-		return diff;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    public int compareTo( Name that ) {
+        if (that == this) return 0;
+        int diff = this.getNamespaceUri().compareTo(that.getNamespaceUri());
+        if (diff != 0) return diff;
+        diff = this.getLocalName().compareTo(that.getLocalName());
+        return diff;
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int hashCode() {
-		return this.hc;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return this.hc;
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean equals( Object obj ) {
-		if (obj == this) return true;
-		if (obj instanceof Name) {
-			Name that = (Name)obj;
-			if (!this.getNamespaceUri().equals(that.getNamespaceUri())) return false;
-			return this.getLocalName().equals(that.getLocalName());
-		}
-		return false;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof Name) {
+            Name that = (Name)obj;
+            if (!this.getNamespaceUri().equals(that.getNamespaceUri())) return false;
+            return this.getLocalName().equals(that.getLocalName());
+        }
+        return false;
+    }
 
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String toString() {
-		return "{" + this.namespaceUri + "}" + this.localName;
-	}
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return "{" + this.namespaceUri + "}" + this.localName;
+    }
 
 }

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicNamespaceRegistry.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicNamespaceRegistry.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicNamespaceRegistry.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -109,7 +109,8 @@
     /**
      * {@inheritDoc}
      */
-    public String getPrefixForNamespaceUri( String namespaceUri, boolean generateIfMissing ) {
+    public String getPrefixForNamespaceUri( String namespaceUri,
+                                            boolean generateIfMissing ) {
         ArgCheck.isNotNull(namespaceUri, "namespaceUri");
         String prefix = null;
         Lock lock = this.registryLock.readLock();
@@ -169,7 +170,8 @@
     /**
      * {@inheritDoc}
      */
-    public String register( String prefix, String namespaceUri ) {
+    public String register( String prefix,
+                            String namespaceUri ) {
         ArgCheck.isNotNull(namespaceUri, "namespaceUri");
         String previousNamespaceForPrefix = null;
         namespaceUri = namespaceUri.trim();
@@ -180,9 +182,12 @@
             prefix = prefix.trim();
             previousNamespaceForPrefix = this.namespacesByPrefix.put(prefix, namespaceUri);
             String previousPrefix = this.prefixesByNamespace.put(namespaceUri, prefix);
-            if (previousPrefix != null) {
+            if (previousPrefix != null && !previousPrefix.equals(prefix)) {
                 this.namespacesByPrefix.remove(previousPrefix);
             }
+            if (previousNamespaceForPrefix != null && !previousNamespaceForPrefix.equals(namespaceUri)) {
+                this.prefixesByNamespace.remove(previousNamespaceForPrefix);
+            }
         } finally {
             lock.unlock();
         }

Modified: trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicNameTest.java
===================================================================
--- trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicNameTest.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicNameTest.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -155,4 +155,18 @@
         assertThat(result, is("dna:some%3aname%3awith%3acolons"));
     }
 
+    @Test
+    public void shouldNotIncludeNamespacePrefixOrColonInResultFromGetStringWithNamespaceRegistry() {
+        validNamespaceUri = namespaceRegistry.getDefaultNamespaceUri();
+        name = new BasicName(validNamespaceUri, validLocalName);
+        String result = name.getString(namespaceRegistry, encoder);
+        assertThat(result, is(validLocalName));
+        result = name.getString(namespaceRegistry); // default encoder
+        assertThat(result, is(validLocalName));
+
+        validLocalName = "some:name:with:colons";
+        name = new BasicName(validNamespaceUri, validLocalName);
+        result = name.getString(namespaceRegistry, encoder);
+        assertThat(result, is("some:name:with:colons"));
+    }
 }

Modified: trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicNamespaceRegistryTest.java
===================================================================
--- trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicNamespaceRegistryTest.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicNamespaceRegistryTest.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -25,6 +25,7 @@
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.hamcrest.core.IsNull.nullValue;
 import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItem;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -122,6 +123,9 @@
         namespaceRegistry.register(validPrefix1, validNamespaceUri1);
         namespaceRegistry.register("", validNamespaceUri2);
         assertThat(namespaceRegistry.getDefaultNamespaceUri(), is(validNamespaceUri2));
+        assertThat(namespaceRegistry.getNamespaceForPrefix(""), is(validNamespaceUri2));
+        assertThat(namespaceRegistry.getRegisteredNamespaceUris(), hasItem(validNamespaceUri2));
+        assertThat(namespaceRegistry.getPrefixForNamespaceUri(validNamespaceUri2, false), is(""));
     }
 
     @Test
@@ -144,4 +148,36 @@
         assertThat(namespaceRegistry.isRegisteredNamespaceUri(validNamespaceUri2), is(true));
     }
 
+    @Test
+    public void shouldBeAbleToCopyNamespacesToAnotherRegistry() {
+        namespaceRegistry.register(validPrefix1, validNamespaceUri1);
+        namespaceRegistry.register(validPrefix2, validNamespaceUri2);
+        namespaceRegistry.register("", validNamespaceUri3);
+        assertThat(namespaceRegistry.isRegisteredNamespaceUri(validNamespaceUri1), is(true));
+        assertThat(namespaceRegistry.isRegisteredNamespaceUri(validNamespaceUri2), is(true));
+        assertThat(namespaceRegistry.isRegisteredNamespaceUri(validNamespaceUri3), is(true));
+        assertThat(namespaceRegistry.getPrefixForNamespaceUri(validNamespaceUri1, false), is(validPrefix1));
+        assertThat(namespaceRegistry.getPrefixForNamespaceUri(validNamespaceUri2, false), is(validPrefix2));
+        assertThat(namespaceRegistry.getPrefixForNamespaceUri(validNamespaceUri3, false), is(""));
+        assertThat(namespaceRegistry.getNamespaceForPrefix(validPrefix1), is(validNamespaceUri1));
+        assertThat(namespaceRegistry.getNamespaceForPrefix(validPrefix2), is(validNamespaceUri2));
+        assertThat(namespaceRegistry.getNamespaceForPrefix(""), is(validNamespaceUri3));
+
+        BasicNamespaceRegistry newRegistry = new BasicNamespaceRegistry();
+        for (String uri : this.namespaceRegistry.getRegisteredNamespaceUris()) {
+            String prefix = this.namespaceRegistry.getPrefixForNamespaceUri(uri, false);
+            newRegistry.register(prefix, uri);
+        }
+        assertThat(newRegistry.isRegisteredNamespaceUri(validNamespaceUri1), is(true));
+        assertThat(newRegistry.isRegisteredNamespaceUri(validNamespaceUri2), is(true));
+        assertThat(newRegistry.isRegisteredNamespaceUri(validNamespaceUri3), is(true));
+        assertThat(newRegistry.getPrefixForNamespaceUri(validNamespaceUri1, false), is(validPrefix1));
+        assertThat(newRegistry.getPrefixForNamespaceUri(validNamespaceUri2, false), is(validPrefix2));
+        assertThat(newRegistry.getPrefixForNamespaceUri(validNamespaceUri3, false), is(""));
+        assertThat(newRegistry.getNamespaceForPrefix(validPrefix1), is(validNamespaceUri1));
+        assertThat(newRegistry.getNamespaceForPrefix(validPrefix2), is(validNamespaceUri2));
+        assertThat(newRegistry.getNamespaceForPrefix(""), is(validNamespaceUri3));
+
+    }
+
 }

Modified: trunk/docs/examples/gettingstarted/repositories/pom.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/pom.xml	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/docs/examples/gettingstarted/repositories/pom.xml	2008-09-11 23:33:03 UTC (rev 515)
@@ -50,6 +50,12 @@
       <version>${dna-version}</version>
       <scope>runtime</scope>
     </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-connector-federation</artifactId>
+      <version>${dna-version}</version>
+      <scope>runtime</scope>
+    </dependency>
     <!-- 
     Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) 
     -->
@@ -80,6 +86,11 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>

Modified: trunk/docs/examples/gettingstarted/repositories/src/main/assembly/basic.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/assembly/basic.xml	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/assembly/basic.xml	2008-09-11 23:33:03 UTC (rev 515)
@@ -16,7 +16,7 @@
       <directory>src/main/resources/</directory>
       <outputDirectory/>
       <includes>
-        <include>*.*</include>
+        <include>*.xml</include>
       </includes>
     </fileSet>
   </fileSets>

Added: trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/ConsoleInput.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/ConsoleInput.java	                        (rev 0)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/ConsoleInput.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -0,0 +1,243 @@
+/*
+ * 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.example.dna.repository;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.common.util.StringUtil;
+
+/**
+ * @author Randall Hauch
+ */
+public class ConsoleInput implements UserInterface {
+
+    protected static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+
+    private final RepositoryClient repositoryClient;
+    private final Map<Integer, String> selectionToSourceName = new HashMap<Integer, String>();
+
+    public ConsoleInput( final RepositoryClient client,
+                         final String[] args ) {
+        this.repositoryClient = client;
+        try {
+            System.out.println();
+            System.out.print("Starting repositories ... ");
+            client.startRepositories();
+            System.out.println("done.");
+            System.out.println();
+
+            System.out.println(getMenu());
+            Thread eventThread = new Thread(new Runnable() {
+
+                private boolean quit = false;
+
+                @SuppressWarnings( "synthetic-access" )
+                public void run() {
+                    try {
+                        while (!quit) {
+                            System.out.print(">");
+                            try {
+                                String input = in.readLine();
+                                if (input.length() != 1) {
+                                    System.out.println("Please enter a valid option.");
+                                    continue;
+                                }
+
+                                char option = input.charAt(0);
+                                switch (option) {
+                                    case '?':
+                                    case 'h':
+                                        System.out.println(getMenu());
+                                        break;
+                                    case 'q':
+                                        quit = true;
+                                        break;
+                                    default:
+                                        try {
+                                            int selection = Integer.parseInt("" + option);
+                                            String sourceName = selectionToSourceName.get(selection);
+                                            navigate(sourceName);
+                                        } catch (NumberFormatException e) {
+                                            System.out.println("Invalid option.");
+                                            break;
+                                        }
+                                }
+                            } catch (NumberFormatException e) {
+                                System.out.println("Invalid integer " + e.getMessage());
+                            } catch (IllegalArgumentException e) {
+                                System.out.println(e.getMessage());
+                            } catch (IOException e) {
+                                e.printStackTrace();
+                            } catch (Throwable e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    } finally {
+                        try {
+                            // Terminate ...
+                            System.out.println();
+                            System.out.print("done.\nShutting down repositories and services ... ");
+                            client.shutdown();
+                            System.out.print("done.");
+                            System.out.println();
+                            System.out.println();
+                        } catch (Exception err) {
+                            System.out.println("Error shutting down sequencing service and repository: "
+                                               + err.getLocalizedMessage());
+                            err.printStackTrace(System.err);
+                        }
+                    }
+                }
+            });
+
+            eventThread.start();
+        } catch (Exception err) {
+            System.out.println("Error: " + err.getLocalizedMessage());
+            err.printStackTrace(System.err);
+        }
+    }
+
+    protected String getMenu() {
+        selectionToSourceName.clear();
+        StringBuilder buffer = new StringBuilder();
+        buffer.append("-----------------------------------\n");
+        buffer.append("Menu:\n");
+        buffer.append("\n");
+        buffer.append(" Select a repository to view:\n");
+        int selection = 1;
+        for (String sourceName : this.repositoryClient.getNamesOfRepositories()) {
+            selectionToSourceName.put(selection, sourceName);
+            buffer.append("  " + selection + ") " + sourceName + "\n");
+        }
+        buffer.append(" or\n");
+        buffer.append("  ?) Show this menu\n");
+        buffer.append("  q) Quit");
+        return buffer.toString();
+    }
+
+    protected void navigate( String sourceName ) {
+        String currentPath = "/";
+        while (true) {
+
+            // Ask for the command ...
+            System.out.print("> ");
+            try {
+                String input = in.readLine().trim();
+                if (input.length() == 0) {
+                    continue;
+                }
+
+                if ("?".equals(input) || "help".equals(input) || "h".equals(input)) {
+                    System.out.println("  Enter a command:");
+                    System.out.println("      pwd          print the current node's path");
+                    System.out.println("      ls [path]    to list the details of the node at the specified absolute or relative path");
+                    System.out.println("                   (or the current path if none is supplied)");
+                    System.out.println("      cd path      to change to the node at the specified absolute or relative path");
+                    System.out.println("      exit         to exit this repository and return to the main menu");
+                    System.out.println("  and press return:");
+                } else if ("pwd".equals(input)) {
+                    System.out.println(" " + currentPath);
+                } else if ("exit".equals(input)) {
+                    return;
+                } else if (input.startsWith("ls")) {
+                    input = input.substring("ls".length()).trim();
+                    String path = currentPath;
+                    if (input.length() != 0) path = input;
+                    displayNode(sourceName, path);
+                } else if (input.startsWith("cd ")) {
+                    input = input.substring("cd ".length()).trim();
+                    if (input.length() == 0) continue;
+                    // Check to see if the new path exists ...
+                    if (!repositoryClient.getNodeInfo(sourceName, input, null, null)) {
+                        System.out.println(" \"" + input + "\" does not exist");
+                    } else {
+                        currentPath = input;
+                    }
+                }
+            } catch (Throwable e) {
+                displayError(" processing your command", e);
+            }
+        }
+
+    }
+
+    protected void displayNode( String sourceName,
+                                String path ) {
+        Map<String, Object[]> properties = new HashMap<String, Object[]>();
+        List<String> children = new ArrayList<String>();
+        try {
+            repositoryClient.getNodeInfo(sourceName, path, properties, children);
+        } catch (Throwable t) {
+            displayError(" displaying node \"" + path + "\"", t);
+        }
+
+        System.out.println("  Path:" + path);
+        System.out.println("  Properties:");
+        int maxLength = 0;
+        for (String propertyName : properties.keySet()) {
+            maxLength = Math.max(maxLength, propertyName.length());
+        }
+        for (Map.Entry<String, Object[]> property : properties.entrySet()) {
+            String name = property.getKey();
+            name = StringUtil.justifyLeft(name, maxLength, ' ');
+            Object[] values = property.getValue();
+            String valueStr = StringUtil.readableString(values);
+            if (values.length == 1) StringUtil.readableString(values[0]);
+            System.out.println("     " + name + " = " + valueStr);
+        }
+        System.out.println("  Children:");
+        for (String childName : children) {
+            System.out.println("     " + childName);
+        }
+        System.out.println();
+    }
+
+    protected void displayError( String activity,
+                                 Throwable t ) {
+        System.err.println();
+        System.err.println("There has been an error" + activity);
+        System.err.println("  " + t.getMessage());
+        t.printStackTrace(System.err);
+        System.err.println();
+        System.err.println("Press any key to continue:");
+        try {
+            in.readLine();
+        } catch (IOException err) {
+            err.printStackTrace(System.err);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.example.dna.repository.UserInterface#getLocationOfRepositoryFiles()
+     */
+    public String getLocationOfRepositoryFiles() {
+        return new File("").getAbsolutePath();
+    }
+}


Property changes on: trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/ConsoleInput.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/RepositoryClient.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -21,17 +21,284 @@
  */
 package org.jboss.example.dna.repository;
 
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.jcr.Credentials;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PropertyIterator;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.callback.PasswordCallback;
+import org.jboss.dna.common.component.ClassLoaderFactory;
+import org.jboss.dna.common.component.StandardClassLoaderFactory;
+import org.jboss.dna.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.jcr.JcrRepository;
+import org.jboss.dna.repository.RepositoryImporter;
+import org.jboss.dna.repository.RepositoryService;
+import org.jboss.dna.repository.RepositorySourceManager;
+import org.jboss.dna.spi.ExecutionContext;
+import org.jboss.dna.spi.ExecutionContextFactory;
+import org.jboss.dna.spi.connector.BasicExecutionContextFactory;
+import org.jboss.dna.spi.connector.RepositoryConnection;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.PathFactory;
+import org.jboss.dna.spi.graph.PathNotFoundException;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.commands.impl.BasicGetNodeCommand;
+
 /**
- * 
  * @author Randall Hauch
  */
 public class RepositoryClient {
 
+    public static final String INMEMORY_REPOSITORY_SOURCE_CLASSNAME = "org.jboss.dna.connector.inmemory.InMemoryRepositorySource";
+    public static final String JAAS_LOGIN_CONTEXT_NAME = "dna-repository-example";
+
     /**
      * @param args
      */
     public static void main( String[] args ) {
+        RepositoryClient client = new RepositoryClient();
+        for (String arg : args) {
+            arg = arg.trim();
+            if (arg.equals("--api=jcr")) client.setApi(Api.JCR);
+            if (arg.equals("--api=dna")) client.setApi(Api.DNA);
+            if (arg.startsWith("--user=") && arg.length() > 7) client.setUsername(arg.substring(7).trim());
+        }
+        client.setUserInterface(new ConsoleInput(client, args));
+    }
 
+    public enum Api {
+        JCR,
+        DNA;
     }
 
+    private ClassLoaderFactory classLoaderFactory;
+    private RepositorySourceManager sources;
+    private ExecutionContextFactory contextFactory;
+    private RepositoryService repositoryService;
+    private Api api = Api.DNA;
+    private String username = null;
+    private char[] password = null;
+    private UserInterface userInterface;
+
+    /**
+     * @param userInterface Sets userInterface to the specified value.
+     */
+    public void setUserInterface( UserInterface userInterface ) {
+        this.userInterface = userInterface;
+    }
+
+    /**
+     * Set the API that this client should use to interact with the repositories.
+     * 
+     * @param api The API that should be used
+     */
+    public void setApi( Api api ) {
+        this.api = api != null ? api : Api.DNA;
+    }
+
+    /**
+     * Set the username that this client should use.
+     * 
+     * @param username the new username, or null if no username should be used.
+     */
+    public void setUsername( String username ) {
+        this.username = username;
+        this.password = null;
+    }
+
+    /**
+     * Clear any cached password.
+     */
+    public void clearPassword() {
+        password = null;
+    }
+
+    /**
+     * Method used internally to obtain the password, which is obtained from the user (only once per process) if needed
+     * 
+     * @return the user's password, or null if no username is known
+     */
+    private char[] getPassword() {
+        if (username == null) return null;
+        if (password == null) {
+            PasswordCallback callback = new PasswordCallback("Password:", false);
+            password = callback.getPassword();
+        }
+        return password;
+    }
+
+    /**
+     * Start up the repositories. This method creates the necessary components and services, and initializes the in-memory
+     * repositories.
+     * 
+     * @throws IOException if there is a problem initializing the repositories from the files.
+     */
+    public void startRepositories() throws IOException {
+        if (repositoryService != null) return; // already started
+
+        // Create the class loader factory, which for this example will simply use this class' class loader...
+        classLoaderFactory = new StandardClassLoaderFactory();
+
+        // Create the factory for execution contexts.
+        contextFactory = new BasicExecutionContextFactory();
+
+        // Create the execution context that we'll use for the services. If we'd want to use JAAS, we'd create the context
+        // by supply LoginContext, AccessControlContext, or even Subject with CallbackHandlers. But no JAAS in this example.
+        ExecutionContext context = contextFactory.create();
+
+        // Create the manager for the RepositorySource instances ...
+        sources = new RepositorySourceManager();
+
+        // Load into the source manager the repository source for the configuration repository ...
+        InMemoryRepositorySource configSource = new InMemoryRepositorySource();
+        configSource.setName("Configuration");
+        sources.addSource(configSource);
+
+        // For this example, we're using a couple of in-memory repositories (including one for the configuration repository).
+        // Normally, these would exist already and would simply be accessed. But in this example, we're going to
+        // populate these repositories here by importing from files. First do the configuration repository ...
+        String location = this.userInterface.getLocationOfRepositoryFiles();
+        RepositoryImporter importer = new RepositoryImporter(sources, context);
+        importer.importXml(location + "/configRepository.xml").into(configSource.getName());
+
+        // Now instantiate the Repository Service ...
+        repositoryService = new RepositoryService(sources, configSource.getName(), context, classLoaderFactory);
+        repositoryService.getAdministrator().start();
+
+        // Now import the conten for two of the other in-memory repositories ...
+        importer.importXml(location + "/cars.xml").into("Cars");
+        importer.importXml(location + "/aircraft.xml").into("Aircraft");
+    }
+
+    /**
+     * Get the names of the repositories.
+     * 
+     * @return the repository names
+     */
+    public Collection<String> getNamesOfRepositories() {
+        return sources.getSourceNames();
+    }
+
+    /**
+     * Shut down the components and services and blocking until all resources have been released.
+     * 
+     * @throws InterruptedException if the thread was interrupted before completing the shutdown.
+     */
+    public void shutdown() throws InterruptedException {
+        if (repositoryService == null) return;
+        try {
+            // Shut down the various services ...
+            repositoryService.getAdministrator().shutdown();
+
+            // Shut down the manager of the RepositorySource instances, waiting until all connections are closed
+            sources.getAdministrator().shutdown();
+            sources.getAdministrator().awaitTermination(1, TimeUnit.SECONDS);
+        } finally {
+            repositoryService = null;
+            sources = null;
+        }
+    }
+
+    /**
+     * Get the information about a node, using the {@link #setApi(Api) API} method.
+     * 
+     * @param sourceName the name of the repository source
+     * @param pathToNode the path to the node in the repository that is to be retrieved
+     * @param properties the map into which the property values will be placed; may be null if the properties are not to be
+     *        retrieved
+     * @param children the collection into which the child names should be placed; may be null if the children are not to be
+     *        retrieved
+     * @return true if the node was found, or false if it was not
+     * @throws Throwable
+     */
+    public boolean getNodeInfo( String sourceName,
+                                String pathToNode,
+                                Map<String, Object[]> properties,
+                                List<String> children ) throws Throwable {
+        switch (api) {
+            case JCR: {
+                JcrRepository jcrRepository = new JcrRepository(contextFactory, sources);
+                Session session = null;
+                if (username != null) {
+                    Credentials credentials = new SimpleCredentials(username, getPassword());
+                    session = jcrRepository.login(credentials, sourceName);
+                } else {
+                    session = jcrRepository.login(sourceName);
+                }
+                try {
+                    // Get the node by path ...
+                    Node root = session.getRootNode();
+                    Node node = root.getNode(pathToNode);
+
+                    // Now populate the properties and children ...
+                    if (properties != null) {
+                        for (PropertyIterator iter = node.getProperties(); iter.hasNext();) {
+                            javax.jcr.Property property = iter.nextProperty();
+                            properties.put(property.getName(), property.getValues());
+                        }
+                    }
+                    if (children != null) {
+                        for (NodeIterator iter = node.getNodes(); iter.hasNext();) {
+                            javax.jcr.Node child = iter.nextNode();
+                            children.add(child.getName());
+                        }
+                    }
+                } catch (javax.jcr.PathNotFoundException e) {
+                    return false;
+                } finally {
+                    if (session != null) session.logout();
+                }
+                break;
+            }
+            case DNA: {
+                ExecutionContext context = null;
+                if (username != null) {
+                    context = contextFactory.create(JAAS_LOGIN_CONTEXT_NAME);
+                } else {
+                    context = contextFactory.create();
+                }
+                PathFactory pathFactory = context.getValueFactories().getPathFactory();
+
+                // Get the node submitting a graph command to a repository connection.
+                // (Using commands is a little verbose, but we'll be introducing in the next release
+                // an API that is much easier and concise.)
+                Path path = pathToNode != null ? pathFactory.create(pathToNode) : pathFactory.createRootPath();
+                BasicGetNodeCommand command = new BasicGetNodeCommand(path);
+                RepositoryConnection connection = sources.createConnection(sourceName);
+                try {
+                    connection.execute(context, command);
+                } finally {
+                    if (connection != null) connection.close();
+                }
+
+                // Check whether there's been an error ...
+                if (command.hasError()) {
+                    if (command.getError() instanceof PathNotFoundException) return false;
+                    throw command.getError();
+                }
+
+                // Now populate the properties and children ...
+                if (properties != null) {
+                    for (Property property : command.getProperties()) {
+                        String name = property.getName().getString(context.getNamespaceRegistry());
+                        properties.put(name, property.getValuesAsArray());
+                    }
+                }
+                if (children != null) {
+                    for (Path.Segment child : command.getChildren()) {
+                        String name = child.getString(context.getNamespaceRegistry());
+                        children.add(name);
+                    }
+                }
+                break;
+            }
+        }
+        return true;
+    }
 }

Added: trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/UserInterface.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/UserInterface.java	                        (rev 0)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/UserInterface.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -0,0 +1,31 @@
+/*
+ * 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.example.dna.repository;
+
+/**
+ * @author Randall Hauch
+ */
+public interface UserInterface {
+
+    String getLocationOfRepositoryFiles();
+
+}


Property changes on: trunk/docs/examples/gettingstarted/repositories/src/main/java/org/jboss/example/dna/repository/UserInterface.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml	                        (rev 0)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml	2008-09-11 23:33:03 UTC (rev 515)
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ JBoss, Home of Professional Open Source.
+  ~
+  ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+  ~ indicated by the @author tags or express copyright attribution
+  ~ statements applied by the authors.  All third-party contributions are
+  ~ distributed under license by Red Hat Middleware LLC.
+  ~
+  ~ This copyrighted material is made available to anyone wishing to use, modify,
+  ~ copy, or redistribute it subject to the terms and conditions of the GNU
+  ~ Lesser General Public License, as published by the Free Software Foundation.
+  ~
+  ~ This program 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 distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<Aircraft xmlns:jcr="http://www.jcp.org/jcr/1.0">
+    <Business>
+        <aircraft jcr:name="Gulfstream V" maker="Gulfstream" model="G-V" introduced="1995" range="5800nm" cruiseSpeed="488kt" crew="2" emptyWeight="46200lb" url="http://en.wikipedia.org/wiki/Gulfstream_V"/>
+        <aircraft jcr:name="Learjet 45" maker="Learjet" model="LJ45" introduced="1995" numberBuilt="264+" crew="2" emptyWeight="13695lb" range="2120nm" cruiseSpeed="457kt" url="http://en.wikipedia.org/wiki/Learjet_45"/>
+    </Business>
+    <Commercial>
+        <aircraft jcr:name="Boeing 777" maker="Boeing" model="777-200LR" introduced="1995" numberBuilt="731+" maxRange="7500nm" emptyWeight="326000lb" cruiseSpeed="560mph" url="http://en.wikipedia.org/wiki/Boeing_777"/>
+        <aircraft jcr:name="Boeing 767" maker="Boeing" model="767-200" introduced="1982" numberBuilt="966+" maxRange="3950nm" emptyWeight="176650lb" cruiseSpeed="530mph"  url="http://en.wikipedia.org/wiki/Boeing_767"/>
+        <aircraft jcr:name="Boeing 787" maker="Boeing" model="787-3" introduced="2009" range="3050nm" emptyWeight="223000lb" cruiseSpeed="561mph" url="http://en.wikipedia.org/wiki/Boeing_787"/>
+        <aircraft jcr:name="Boeing 757" maker="Boeing" model="757-200" introduced="1983" numberBuilt="1050" range="3900nm" maxWeight="255000lb" cruiseSpeed="530mph" url="http://en.wikipedia.org/wiki/Boeing_757"/>
+        <aircraft jcr:name="Airbus A380" maker="Airbus" model="A380-800" introduced="2007" numberBuilt="18" range="8200nm" maxWeight="1235000lb" cruiseSpeed="647mph" url="http://en.wikipedia.org/wiki/Airbus_a380"/>
+        <aircraft jcr:name="Airbus A340" maker="Airbus" model="A340-200" introduced="1993" numberBuilt="354" range="8000nm" maxWeight="606300lb" cruiseSpeed="557mph" url="http://en.wikipedia.org/wiki/Airbus_A-340"/>
+        <aircraft jcr:name="Airbus A310" maker="Airbus" model="A310-200" introduced="1983" numberBuilt="255" cruiseSpeed="850km/h" emptyWeight="176312lb" range="3670nm" url="http://en.wikipedia.org/wiki/Airbus_A-310"/>
+        <aircraft jcr:name="Embraer " maker="Embraer" model="ERJ170-200" introduced="2004" range="3334km" cruiseSpeed="481kt" emptyWeight="21810kg" url="http://en.wikipedia.org/wiki/EMBRAER_170"/>
+    </Commercial>
+    <Vintage>
+        <aircraft jcr:name="Fokker Trimotor" maker="Fokker" model="F.VII" introduced="1925" cruiseSpeed="170km/h" emptyWeight="3050kg" crew="2" url="http://en.wikipedia.org/wiki/Fokker_trimotor"/>
+        <aircraft jcr:name="P-38 Lightning" maker="Lockheed" model="P-38" designedBy="Kelly Johnson" introduced="1941" numberBuilt="10037" rateOfClimb="4750ft/min" range="1300mi" emptyWeight="12780lb" crew="1" url="http://en.wikipedia.org/wiki/P-38_Lightning"/>
+        <aircraft jcr:name="A6M Zero" maker="Mitsubishi" model="A6M" designedBy="Jiro Horikoshi" introduced="1940" numberBuilt="11000" crew="1" emptyWeight="3704lb" serviceCeiling="33000ft" maxSpeed="331mph" range="1929mi" rateOfClimb="3100ft/min" url="http://en.wikipedia.org/wiki/A6M_Zero"/>
+        <aircraft jcr:name="Bf 109" maker="Messerschmitt" model="Bf 109" introduced="1937" url="http://en.wikipedia.org/wiki/BF_109"/>
+        <aircraft jcr:name="Wright Flyer" maker="Wright Brothers" introduced="1903" range="852ft" maxSpeed="30mph" emptyWeight="605lb" crew="1"/>
+    </Vintage>
+    <Homebuilt>
+        <aircraft jcr:name="Long-EZ" maker="Rutan Aircraft Factory" model="61" emptyWeight="760lb" fuelCapacity="200L" maxSpeed="185kt" since="1976" range="1200nm" url="http://en.wikipedia.org/wiki/Rutan_Long-EZ"/>
+        <aircraft jcr:name="Cirrus VK-30" maker="Cirrus Design" model="VK-30" emptyWeight="2400lb" maxLoad="1200lb" maxSpeed="250mph" rateOfClimb="1500ft/min" range="1300mi" url="http://en.wikipedia.org/wiki/Cirrus_VK-30"/>
+        <aircraft jcr:name="Van's RV-4" maker="Van's Aircraft" model="RV-4" introduced="1980" emptyWeight="905lb" maxLoad="500lb" maxSpeed="200mph" rateOfClimb="2450ft/min" range="725mi" url="http://en.wikipedia.org/wiki/Van%27s_Aircraft_RV-4"/>
+    </Homebuilt>
+</Aircraft>
\ No newline at end of file


Property changes on: trunk/docs/examples/gettingstarted/repositories/src/main/resources/aircraft.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml	                        (rev 0)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml	2008-09-11 23:33:03 UTC (rev 515)
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ JBoss, Home of Professional Open Source.
+  ~
+  ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+  ~ indicated by the @author tags or express copyright attribution
+  ~ statements applied by the authors.  All third-party contributions are
+  ~ distributed under license by Red Hat Middleware LLC.
+  ~
+  ~ This copyrighted material is made available to anyone wishing to use, modify,
+  ~ copy, or redistribute it subject to the terms and conditions of the GNU
+  ~ Lesser General Public License, as published by the Free Software Foundation.
+  ~
+  ~ This program 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 distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<Cars xmlns:jcr="http://www.jcp.org/jcr/1.0">
+    <Hybrid>
+        <car jcr:name="Toyota Prius" maker="Toyota" model="Prius" year="2008" msrp="$21,500" userRating="4.2" valueRating="5" mpgCity="48" mpgHighway="45"/>
+        <car jcr:name="Toyota Highlander" maker="Toyota" model="Highlander" year="2008" msrp="$34,200" userRating="4" valueRating="5" mpgCity="27" mpgHighway="25"/>
+        <car jcr:name="Nissan Altima" maker="Nissan" model="Altima" year="2008" msrp="$18,260" mpgCity="23" mpgHighway="32"/>
+    </Hybrid>
+    <Sports>
+        <car jcr:name="Aston Martin DB9" maker="Aston Martin" model="DB9" year="2008" msrp="$171,600" userRating="5" mpgCity="12" mpgHighway="19" lengthInInches="185.5" wheelbaseInInches="108.0" engine="5,935 cc 5.9 liters V 12"/>
+        <car jcr:name="Infiniti G37" maker="Infiniti" model="G37" year="2008" msrp="$34,900" userRating="3.5" valueRating="4" mpgCity="18" mpgHighway="24" />
+    </Sports>
+    <Luxury>
+        <car jcr:name="Cadillac DTS" maker="Cadillac" model="DTS" year="2008" engine="3.6-liter V6" userRating="0"/>
+        <car jcr:name="Bentley Continental" maker="Bentley" model="Continental" year="2008" msrp="$170,990" mpgCity="10" mpgHighway="17" />
+        <car jcr:name="Lexus IS350" maker="Lexus" model="IS350" year="2008" msrp="$36,305" mpgCity="18" mpgHighway="25" userRating="4" valueRating="5" />
+    </Luxury>
+    <Utility>
+        <car jcr:name="Land Rover LR2" maker="Land Rover" model="LR2" year="2008" msrp="$33,985" userRating="4.5" valueRating="5" mpgCity="16" mpgHighway="23" />
+        <car jcr:name="Land Rover LR3" maker="Land Rover" model="LR3" year="2008" msrp="$48,525" userRating="5" valueRating="2" mpgCity="12" mpgHighway="17" />
+        <car jcr:name="Hummer H3" maker="Hummer" model="H3" year="2008" msrp="$30,595" userRating="3.5" valueRating="4" mpgCity="13" mpgHighway="16" />
+        <car jcr:name="Ford F-150" maker="Ford" model="F-150" year="2008" msrp="$23,910" userRating="4" valueRating="1" mpgCity="14" mpgHighway="20" />
+    </Utility>
+</Cars>
\ No newline at end of file


Property changes on: trunk/docs/examples/gettingstarted/repositories/src/main/resources/cars.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml	                        (rev 0)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml	2008-09-11 23:33:03 UTC (rev 515)
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ JBoss, Home of Professional Open Source.
+  ~
+  ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+  ~ indicated by the @author tags or express copyright attribution
+  ~ statements applied by the authors.  All third-party contributions are
+  ~ distributed under license by Red Hat Middleware LLC.
+  ~
+  ~ This copyrighted material is made available to anyone wishing to use, modify,
+  ~ copy, or redistribute it subject to the terms and conditions of the GNU
+  ~ Lesser General Public License, as published by the Free Software Foundation.
+  ~
+  ~ This program 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 distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<dna:system xmlns:dna="http://www.jboss.org/dna" xmlns:jcr="http://www.jcp.org/jcr/1.0">
+    <!-- Define the sources from which content is made available -->
+    <dna:sources>
+        <sourceA name="Cars" dna:classname="org.jboss.dna.connector.inmemory.InMemoryRepositorySource" retryLimit="3" />
+        <sourceB name="Aircraft" dna:classname="org.jboss.dna.connector.inmemory.InMemoryRepositorySource" />
+        <sourceC name="Vehicles" dna:classname="org.jboss.dna.connector.federation.FederatedRepositorySource" configurationSourceName="Configuration" configurationSourcePath="/dna:system/dna:federatedRepositories/Vehicles" />
+        <sourceD name="Cache" dna:classname="org.jboss.dna.connector.inmemory.InMemoryRepositorySource" />
+    </dna:sources>
+    <dna:federatedRepositories>
+        <Vehicles>
+            <!-- This section defines from where the content of the 'Vehicles' federated repository is obtained -->
+            <dna:federation dna:timeToCache="100000">
+                <dna:cache>
+                    <!-- Define how the content in the 'Cache' source is to map to the federated cache -->
+                    <Cache dna:projectionRules="/ => /" />
+                </dna:cache>
+                <dna:projections>
+                    <!-- Define how the content in the difference sources maps to the federated/unified repository.
+                         This example puts the 'Cars' and 'Aircraft' content underneath '/vehicles', but the
+                         'Configuration' content (which is defined by this file) will appear under '/'. -->
+                    <Cars dna:projectionRules="/vehicles => /" />
+                    <Aircraft dna:projectionRules="/vehicles => /" />
+                    <Configuration dna:projectionRules="/ => /" />
+                </dna:projections>
+            </dna:federation>
+        </Vehicles>
+    </dna:federatedRepositories>
+</dna:system>
\ No newline at end of file


Property changes on: trunk/docs/examples/gettingstarted/repositories/src/main/resources/configRepository.xml
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/docs/examples/gettingstarted/repositories/src/main/resources/log4j.properties
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/main/resources/log4j.properties	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/docs/examples/gettingstarted/repositories/src/main/resources/log4j.properties	2008-09-11 23:33:03 UTC (rev 515)
@@ -9,4 +9,9 @@
 
 # Set up the default logging to be INFO level, then override specific units
 log4j.logger.org.jboss.dna=INFO
+#log4j.logger.org.jboss.dna.repository.sequencers=TRACE
+#log4j.logger.org.jboss.dna.sequencer=DEBUG
+#log4j.logger.org.jboss.dna.connector=TRACE
+#log4j.logger.org.jboss.dna.spi.connector.RepositoryConnectionPool=TRACE
+#log4j.logger.org.jboss.dna.spi=DEBUG
 

Added: trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java
===================================================================
--- trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java	                        (rev 0)
+++ trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -0,0 +1,158 @@
+/*
+ * 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.example.dna.repository;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.IsCollectionContaining.hasItems;
+import static org.mockito.Mockito.stub;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.common.util.StringUtil;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class RepositoryClientTest {
+
+    private RepositoryClient client;
+    private Map<String, Object[]> properties;
+    private List<String> children;
+    @Mock
+    private UserInterface userInterface;
+
+    @Before
+    public void beforeEach() {
+        MockitoAnnotations.initMocks(this);
+        properties = new HashMap<String, Object[]>();
+        children = new ArrayList<String>();
+        client = new RepositoryClient();
+        client.setUserInterface(userInterface);
+        stub(userInterface.getLocationOfRepositoryFiles()).toReturn(new File("src/main/resources").getAbsolutePath());
+    }
+
+    @After
+    public void afterEach() throws Exception {
+        client.shutdown();
+    }
+
+    protected void assertProperty( String propertyName,
+                                   Object... values ) {
+        Object[] actualValues = properties.get(propertyName);
+        assertThat(actualValues, is(values));
+    }
+
+    @Test
+    public void shouldStartupWithoutError() throws Exception {
+        client.startRepositories();
+        assertThat(client.getNamesOfRepositories(), hasItems("Aircraft", "Cars", "Configuration", "Vehicles", "Cache"));
+    }
+
+    @Test
+    public void shouldStartupWithoutErrorMoreThanOnce() throws Exception {
+        client.startRepositories();
+        assertThat(client.getNamesOfRepositories(), hasItems("Aircraft", "Cars", "Configuration", "Vehicles", "Cache"));
+    }
+
+    @Ignore
+    @Test
+    public void shouldHaveContentFromConfigurationRepository() throws Throwable {
+        client.startRepositories();
+        assertThat(client.getNodeInfo("Configuration", "/dna:system", properties, children), is(true));
+        assertThat(children, hasItems("dna:sources", "dna:federatedRepositories"));
+        assertThat(properties.containsKey("jcr:primaryType"), is(true));
+        assertThat(properties.containsKey("dna:uuid"), is(true));
+        assertThat(properties.size(), is(2));
+
+        properties.clear();
+        children.clear();
+        assertThat(client.getNodeInfo("Configuration", "/dna:system/dna:sources", properties, children), is(true));
+        assertThat(children, hasItems("sourceA", "sourceB", "sourceC", "sourceD"));
+        assertThat(properties.containsKey("jcr:primaryType"), is(true));
+        assertThat(properties.containsKey("dna:uuid"), is(true));
+        assertThat(properties.size(), is(2));
+
+        properties.clear();
+        children.clear();
+        assertThat(client.getNodeInfo("Configuration", "/dna:system/dna:sources/sourceA", properties, children), is(true));
+        assertThat(children.size(), is(0));
+        System.out.println(StringUtil.readableString(properties));
+        assertThat(properties.containsKey("jcr:primaryType"), is(true));
+        assertThat(properties.containsKey("dna:uuid"), is(true));
+        assertProperty("dna:classname", "org.jboss.dna.connector.inmemory.InMemoryRepositorySource");
+        assertProperty("name", "Cars");
+        assertProperty("retryLimit", "3");
+        assertThat(properties.size(), is(4));
+    }
+
+    @Test
+    public void shouldLoadCarsRepository() throws Throwable {
+        client.startRepositories();
+        assertThat(client.getNodeInfo("Cars", "/Cars", properties, children), is(true));
+        assertThat(children, hasItems("Hybrid", "Sports", "Luxury", "Utility"));
+        assertThat(properties.containsKey("jcr:primaryType"), is(true));
+        assertThat(properties.containsKey("dna:uuid"), is(true));
+        assertThat(properties.size(), is(2));
+
+        properties.clear();
+        children.clear();
+        assertThat(client.getNodeInfo("Cars", "/Cars/Hybrid", properties, children), is(true));
+        assertThat(children, hasItems("Toyota Prius", "Toyota Highlander", "Nissan Altima"));
+        assertThat(properties.containsKey("jcr:primaryType"), is(true));
+        assertThat(properties.containsKey("dna:uuid"), is(true));
+        assertThat(properties.size(), is(2));
+
+        properties.clear();
+        children.clear();
+        assertThat(client.getNodeInfo("Cars", "/Cars/Sports/Aston Martin DB9", properties, children), is(true));
+        assertThat(children.size(), is(0));
+        assertThat(properties.containsKey("jcr:primaryType"), is(true));
+        assertThat(properties.containsKey("dna:uuid"), is(true));
+        assertProperty("maker", "Aston Martin");
+        assertProperty("maker", "Aston Martin");
+        assertProperty("model", "DB9");
+        assertProperty("year", "2008");
+        assertProperty("msrp", "$171,600");
+        assertProperty("userRating", "5");
+        assertProperty("mpgCity", "12");
+        assertProperty("mpgHighway", "19");
+        assertProperty("lengthInInches", "185.5");
+        assertProperty("wheelbaseInInches", "108.0");
+        assertProperty("engine", "5,935 cc 5.9 liters V 12");
+        assertThat(properties.size(), is(12));
+    }
+
+    @Test
+    public void shouldReturnNullForNonExistantNode() throws Throwable {
+        client.startRepositories();
+        assertThat(client.getNodeInfo("Cars", "/Cars/Sports/Non Existant Car", properties, children), is(false));
+    }
+}


Property changes on: trunk/docs/examples/gettingstarted/repositories/src/test/java/org/jboss/example/dna/repository/RepositoryClientTest.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.java
===================================================================
--- trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -32,6 +32,7 @@
 
     public static I18n connectorName;
     public static I18n nodeDoesNotExist;
+    public static I18n errorSerializingCachePolicyInSource;
 
     static {
         try {

Modified: trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -44,6 +44,7 @@
 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.RecordBranchCommand;
 import org.jboss.dna.spi.graph.commands.SetPropertiesCommand;
@@ -344,12 +345,15 @@
         @Override
         public void execute( CreateNodeCommand command ) {
             Path path = command.getPath();
-            Path parent = path.getParent();
-            // Look up the parent node, which must exist ...
-            Node parentNode = getNode(parent);
-            if (parentNode == null) {
-                Path lowestExisting = getLowestExistingPath(parent);
-                throw new PathNotFoundException(path, lowestExisting, InMemoryConnectorI18n.nodeDoesNotExist.text(parent));
+            Node parentNode = null;
+            if (!path.isRoot()) {
+                Path parent = path.getParent();
+                // Look up the parent node, which must exist ...
+                parentNode = getNode(parent);
+                if (parentNode == null) {
+                    Path lowestExisting = getLowestExistingPath(parent);
+                    throw new PathNotFoundException(path, lowestExisting, InMemoryConnectorI18n.nodeDoesNotExist.text(parent));
+                }
             }
             UUID uuid = null;
             for (Property property : command.getProperties()) {
@@ -376,6 +380,7 @@
         @Override
         public void execute( GetChildrenCommand command ) {
             Node node = getTargetNode(command);
+            if (node == null) return;
             // Get the names of the children ...
             List<Node> children = node.getChildren();
             for (Node child : children) {
@@ -387,6 +392,7 @@
         @Override
         public void execute( GetPropertiesCommand command ) {
             Node node = getTargetNode(command);
+            if (node == null) return;
             for (Property property : node.getProperties().values()) {
                 command.setProperty(property);
             }
@@ -396,6 +402,7 @@
         @Override
         public void execute( SetPropertiesCommand command ) {
             Node node = getTargetNode(command);
+            if (node == null) return;
             // Now set (or remove) the properties to the supplied node ...
             for (Property property : command.getProperties()) {
                 Name propName = property.getName();
@@ -412,12 +419,14 @@
         @Override
         public void execute( DeleteBranchCommand command ) {
             Node node = getTargetNode(command);
+            if (node == null) return;
             removeNode(getExecutionContext(), node);
         }
 
         @Override
         public void execute( CopyNodeCommand command ) {
             Node node = getTargetNode(command);
+            if (node == null) return;
             // Look up the new parent, which must exist ...
             Path newPath = command.getNewPath();
             Node newParent = getNode(newPath.getParent());
@@ -427,6 +436,7 @@
         @Override
         public void execute( CopyBranchCommand command ) {
             Node node = getTargetNode(command);
+            if (node == null) return;
             // Look up the new parent, which must exist ...
             Path newPath = command.getNewPath();
             Node newParent = getNode(newPath.getParent());
@@ -436,6 +446,7 @@
         @Override
         public void execute( MoveBranchCommand command ) {
             Node node = getTargetNode(command);
+            if (node == null) return;
             // Look up the new parent, which must exist ...
             Path newPath = command.getNewPath();
             Node newParent = getNode(newPath.getParent());
@@ -445,6 +456,7 @@
         @Override
         public void execute( RecordBranchCommand command ) {
             Node node = getTargetNode(command);
+            if (node == null) return;
             recordNode(command, node);
         }
 
@@ -456,15 +468,17 @@
             }
         }
 
-        protected Node getTargetNode( ActsOnPath command ) {
+        protected <T extends ActsOnPath & GraphCommand> Node getTargetNode( T command ) {
             Path path = command.getPath();
             // Look up the node with the supplied path ...
             Node node = InMemoryRepository.this.getNode(path);
             if (node == null) {
                 Path lowestExisting = getLowestExistingPath(path);
-                throw new PathNotFoundException(path, lowestExisting, InMemoryConnectorI18n.nodeDoesNotExist.text(path));
+                command.setError(new PathNotFoundException(path, lowestExisting,
+                                                           InMemoryConnectorI18n.nodeDoesNotExist.text(path)));
+                return null;
             }
-            return null;
+            return node;
         }
 
     }

Modified: trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryConnection.java
===================================================================
--- trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryConnection.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositoryConnection.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -24,6 +24,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import javax.transaction.xa.XAResource;
+import org.jboss.dna.common.stats.Stopwatch;
+import org.jboss.dna.common.util.Logger;
 import org.jboss.dna.spi.ExecutionContext;
 import org.jboss.dna.spi.cache.CachePolicy;
 import org.jboss.dna.spi.connector.RepositoryConnection;
@@ -112,6 +114,12 @@
      */
     public void execute( ExecutionContext context,
                          GraphCommand... commands ) throws RepositorySourceException {
+        Logger logger = context.getLogger(getClass());
+        Stopwatch sw = null;
+        if (logger.isTraceEnabled()) {
+            sw = new Stopwatch();
+            sw.start();
+        }
         // Do any commands update/write?
         Lock lock = this.content.getLock().readLock();
         for (GraphCommand command : commands) {
@@ -131,6 +139,11 @@
         } finally {
             lock.unlock();
         }
+        if (logger.isTraceEnabled()) {
+            assert sw != null;
+            sw.stop();
+            logger.trace("InMemoryRepositoryConnection.execute(...) took " + sw.getTotalDuration());
+        }
     }
 
     protected InMemoryRepository getContent() {

Modified: trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositorySource.java
===================================================================
--- trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositorySource.java	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepositorySource.java	2008-09-11 23:33:03 UTC (rev 515)
@@ -21,16 +21,18 @@
  */
 package org.jboss.dna.connector.inmemory;
 
-import java.util.Collections;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
-import java.util.Set;
+import java.util.Map;
 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.BinaryRefAddr;
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
@@ -39,6 +41,7 @@
 import javax.naming.StringRefAddr;
 import javax.naming.spi.ObjectFactory;
 import net.jcip.annotations.GuardedBy;
+import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.ArgCheck;
 import org.jboss.dna.spi.cache.CachePolicy;
 import org.jboss.dna.spi.connector.RepositoryConnection;
@@ -61,40 +64,12 @@
      */
     public static final int DEFAULT_RETRY_LIMIT = 0;
 
-    private static final ConcurrentMap<String, InMemoryRepositorySource> sources = new ConcurrentHashMap<String, InMemoryRepositorySource>();
-    private static final ReadWriteLock sourcesLock = new ReentrantReadWriteLock();
+    protected static final String ROOT_NODE_UUID = "rootNodeUuid";
+    protected static final String SOURCE_NAME = "sourceName";
+    protected static final String DEFAULT_CACHE_POLICY = "defaultCachePolicy";
+    protected static final String JNDI_NAME = "jndiName";
+    protected static final String RETRY_LIMIT = "retryLimit";
 
-    /**
-     * 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" )
@@ -212,37 +187,14 @@
      * {@inheritDoc}
      */
     public String getName() {
-        Lock lock = sourcesLock.readLock();
-        try {
-            lock.lock();
-            return this.name;
-        } finally {
-            lock.unlock();
-        }
+        return this.name;
     }
 
     /**
      * @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();
-        }
+    public void setName( String name ) {
+        this.name = name;
     }
 
     /**
@@ -260,11 +212,34 @@
     /**
      * {@inheritDoc}
      */
-    public Reference getReference() {
+    public synchronized Reference getReference() {
         String className = getClass().getName();
-        String factoryClassName = className;
-        return new Reference(className, new StringRefAddr("DnaConnectorInMemoryRepositorySource", getName()), factoryClassName,
-                             null);
+        String factoryClassName = this.getClass().getName();
+        Reference ref = new Reference(className, factoryClassName, null);
+
+        if (getName() != null) {
+            ref.add(new StringRefAddr(SOURCE_NAME, getName()));
+        }
+        if (getRootNodeUuid() != null) {
+            ref.add(new StringRefAddr(ROOT_NODE_UUID, getRootNodeUuid().toString()));
+        }
+        if (getJndiName() != null) {
+            ref.add(new StringRefAddr(JNDI_NAME, getJndiName()));
+        }
+        if (getDefaultCachePolicy() != null) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            CachePolicy policy = getDefaultCachePolicy();
+            try {
+                ObjectOutputStream oos = new ObjectOutputStream(baos);
+                oos.writeObject(policy);
+                ref.add(new BinaryRefAddr(DEFAULT_CACHE_POLICY, baos.toByteArray()));
+            } catch (IOException e) {
+                I18n msg = InMemoryConnectorI18n.errorSerializingCachePolicyInSource;
+                throw new RepositorySourceException(getName(), msg.text(policy.getClass().getName(), getName()), e);
+            }
+        }
+        ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit())));
+        return ref;
     }
 
     /**
@@ -273,13 +248,45 @@
     public Object getObjectInstance( Object obj,
                                      javax.naming.Name name,
                                      Context nameCtx,
-                                     Hashtable<?, ?> environment ) {
+                                     Hashtable<?, ?> environment ) throws Exception {
         if (obj instanceof Reference) {
+            Map<String, Object> values = new HashMap<String, Object>();
             Reference ref = (Reference)obj;
-            if (ref.getClassName().equals(getClass().getName())) {
-                RefAddr addr = ref.get("DnaConnectorInMemoryRepositorySource");
-                return InMemoryRepositorySource.getSource((String)addr.getContent());
+            Enumeration<?> en = ref.getAll();
+            while (en.hasMoreElements()) {
+                RefAddr subref = (RefAddr)en.nextElement();
+                if (subref instanceof StringRefAddr) {
+                    String key = subref.getType();
+                    Object value = subref.getContent();
+                    if (value != null) values.put(key, value.toString());
+                } else if (subref instanceof BinaryRefAddr) {
+                    String key = subref.getType();
+                    Object value = subref.getContent();
+                    if (value instanceof byte[]) {
+                        // Deserialize ...
+                        ByteArrayInputStream bais = new ByteArrayInputStream((byte[])value);
+                        ObjectInputStream ois = new ObjectInputStream(bais);
+                        value = ois.readObject();
+                        values.put(key, value);
+                    }
+                }
             }
+            String sourceName = (String)values.get(SOURCE_NAME);
+            String rootNodeUuidString = (String)values.get(ROOT_NODE_UUID);
+            String jndiName = (String)values.get(JNDI_NAME);
+            Object defaultCachePolicy = values.get(DEFAULT_CACHE_POLICY);
+            String retryLimit = (String)values.get(RETRY_LIMIT);
+
+            // Create the source instance ...
+            InMemoryRepositorySource source = new InMemoryRepositorySource();
+            if (sourceName != null) source.setName(sourceName);
+            if (rootNodeUuidString != null) source.setRootNodeUuid(UUID.fromString(rootNodeUuidString));
+            if (jndiName != null) source.setJndiName(jndiName);
+            if (defaultCachePolicy instanceof CachePolicy) {
+                source.setDefaultCachePolicy((CachePolicy)defaultCachePolicy);
+            }
+            if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
+            return source;
         }
         return null;
     }

Modified: trunk/extensions/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.properties
===================================================================
--- trunk/extensions/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.properties	2008-09-10 18:05:54 UTC (rev 514)
+++ trunk/extensions/dna-connector-inmemory/src/main/resources/org/jboss/dna/connector/inmemory/InMemoryConnectorI18n.properties	2008-09-11 23:33:03 UTC (rev 515)
@@ -21,4 +21,5 @@
 #
 
 connectorName = In-Memory Connector
-nodeDoesNotExist = Could not find an existing node at {0}
\ No newline at end of file
+nodeDoesNotExist = Could not find an existing node at {0}
+errorSerializingCachePolicyInSource = Error serializing a {0} instance owned by the {1} InMemoryRepositorySource

Added: trunk/extensions/dna-connector-inmemory/src/test/resources/log4j.properties
===================================================================
--- trunk/extensions/dna-connector-inmemory/src/test/resources/log4j.properties	                        (rev 0)
+++ trunk/extensions/dna-connector-inmemory/src/test/resources/log4j.properties	2008-09-11 23:33:03 UTC (rev 515)
@@ -0,0 +1,20 @@
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %m%n
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Set up the default logging to be INFO level, then override specific units
+log4j.logger.org.jboss.dna=INFO
+#log4j.logger.org.jboss.dna.repository.sequencers=TRACE
+#log4j.logger.org.jboss.dna.sequencer=DEBUG
+#log4j.logger.org.jboss.dna.connector=TRACE
+#log4j.logger.org.jboss.dna.spi=DEBUG
+
+# Jackrabbit logging
+log4j.logger.org.apache.jackrabbit=WARN, stdout
+log4j.logger.org.apache.derby=INFO, stdout
+


Property changes on: trunk/extensions/dna-connector-inmemory/src/test/resources/log4j.properties
___________________________________________________________________
Name: svn:mime-type
   + text/plain




More information about the dna-commits mailing list