[dna-commits] DNA SVN: r115 - in trunk: dna-common/src/main/java/org/jboss/dna/common/component and 31 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Tue May 6 10:57:52 EDT 2008


Author: rhauch
Date: 2008-05-06 10:57:52 -0400 (Tue, 06 May 2008)
New Revision: 115

Added:
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerLibrary.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/AbstractSessionFactory.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleExecutionContext.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleSessionFactory.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/MockExecutionContext.java
   trunk/docs/examples/
   trunk/docs/examples/getting_started/
   trunk/docs/examples/getting_started/pom.xml
   trunk/docs/examples/getting_started/sequencers/
   trunk/docs/examples/getting_started/sequencers/.classpath
   trunk/docs/examples/getting_started/sequencers/.project
   trunk/docs/examples/getting_started/sequencers/pom.xml
   trunk/docs/examples/getting_started/sequencers/src/
   trunk/docs/examples/getting_started/sequencers/src/main/
   trunk/docs/examples/getting_started/sequencers/src/main/assembly/
   trunk/docs/examples/getting_started/sequencers/src/main/assembly/basic.xml
   trunk/docs/examples/getting_started/sequencers/src/main/config/
   trunk/docs/examples/getting_started/sequencers/src/main/config/run.sh
   trunk/docs/examples/getting_started/sequencers/src/main/java/
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/ConsoleInput.java
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/ImageInfo.java
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/SequencingClient.java
   trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/UserInterface.java
   trunk/docs/examples/getting_started/sequencers/src/main/resources/
   trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.gif
   trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.jpg
   trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.pict
   trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.png
   trunk/docs/examples/getting_started/sequencers/src/main/resources/jackrabbitConfig.xml
   trunk/docs/examples/getting_started/sequencers/src/main/resources/jackrabbitNodeTypes.cnd
   trunk/docs/examples/getting_started/sequencers/src/main/resources/log4j.properties
   trunk/docs/examples/getting_started/sequencers/src/test/
   trunk/docs/examples/getting_started/sequencers/src/test/java/
   trunk/docs/examples/getting_started/sequencers/src/test/java/org/
   trunk/docs/examples/getting_started/sequencers/src/test/java/org/jboss/
   trunk/docs/examples/getting_started/sequencers/src/test/java/org/jboss/example/
   trunk/docs/examples/getting_started/sequencers/src/test/java/org/jboss/example/dna/
   trunk/docs/examples/getting_started/sequencers/src/test/java/org/jboss/example/dna/sequencers/
   trunk/docs/examples/getting_started/sequencers/src/test/java/org/jboss/example/dna/sequencers/SequencingClientTest.java
   trunk/docs/getting_started/.project
Removed:
   trunk/dna-repository/src/main/java/org/jboss/dna/services/
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/SimpleExecutionContext.java
   trunk/docs/.project
Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/observation/ObservationService.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerPathExpression.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencingService.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/JcrTools.java
   trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/JndiSessionFactory.java
   trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencerPathExpressionTest.java
   trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencingServiceTest.java
   trunk/pom.xml
Log:
Added example, changed some of the implementation classes to be used more easily.

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/component/ComponentLibrary.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -202,7 +202,7 @@
             // Don't use ClassLoader.loadClass(String), as it doesn't properly initialize the class
             // (specifically static initializers may not be called)
             Class<?> componentClass = Class.forName(config.getComponentClassname(), true, classLoader);
-            newInstance = (ComponentType)componentClass.newInstance();
+            newInstance = doCreateInstance(componentClass);
             if (newInstance instanceof Component) {
                 ((Component<ConfigType>)newInstance).setConfiguration(config);
             }
@@ -216,6 +216,19 @@
     }
 
     /**
+     * Method that instantiates the supplied class. This method can be overridden by subclasses that may need to wrap or adapt the
+     * instance to be a ComponentType.
+     * @param componentClass
+     * @return
+     * @throws InstantiationException
+     * @throws IllegalAccessException
+     */
+    @SuppressWarnings( "unchecked" )
+    protected ComponentType doCreateInstance( Class<?> componentClass ) throws InstantiationException, IllegalAccessException {
+        return (ComponentType)componentClass.newInstance();
+    }
+
+    /**
      * Find the index for the matching {@link #configurations configuration} and {@link #sequencerInstances sequencer}.
      * @param config the configuration; may not be null
      * @return the index, or -1 if not found

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -30,85 +30,91 @@
  */
 public final class RepositoryI18n {
 
-	public static I18n invalidStateString;
-	public static I18n serviceShutdowAndMayNotBeStarted;
-	public static I18n serviceShutdowAndMayNotBePaused;
-	public static I18n unableToFindRepositoryInJndi;
-	public static I18n errorProcessingEvents;
-	public static I18n errorFindingPropertyNameInPropertyAddedEvent;
-	public static I18n errorFindingPropertyNameInPropertyChangedEvent;
-	public static I18n errorFindingPropertyNameInPropertyRemovedEvent;
+    public static I18n invalidStateString;
+    public static I18n serviceShutdowAndMayNotBeStarted;
+    public static I18n serviceShutdowAndMayNotBePaused;
+    public static I18n unableToFindRepositoryInJndi;
+    public static I18n unableToRegisterRepositoryInJndi;
+    public static I18n unableToUnregisterRepositoryInJndi;
+    public static I18n unableToRemoveRepository;
+    public static I18n unableToFindRepositoryWithName;
+    public static I18n errorProcessingEvents;
+    public static I18n errorFindingPropertyNameInPropertyAddedEvent;
+    public static I18n errorFindingPropertyNameInPropertyChangedEvent;
+    public static I18n errorFindingPropertyNameInPropertyRemovedEvent;
 
-	public static I18n unableToObtainJsr94RuleAdministrator;
-	public static I18n errorUsingJsr94RuleAdministrator;
-	public static I18n unableToObtainJsr94ServiceProvider;
-	public static I18n errorAddingOrUpdatingRuleSet;
-	public static I18n errorRollingBackRuleSetAfterUpdateFailed;
-	public static I18n errorReadingRulesAndProperties;
-	public static I18n errorDeregisteringRuleSetBeforeUpdatingIt;
-	public static I18n errorRecreatingRuleSet;
-	public static I18n errorRemovingRuleSet;
-	public static I18n errorRemovingRuleSetUponShutdown;
-	public static I18n unableToFindRuleSet;
-	public static I18n errorExecutingRuleSetWithGlobalsAndFacts;
-	public static I18n unableToBuildRuleSetRegularExpressionPattern;
+    public static I18n unableToObtainJsr94RuleAdministrator;
+    public static I18n errorUsingJsr94RuleAdministrator;
+    public static I18n unableToObtainJsr94ServiceProvider;
+    public static I18n errorAddingOrUpdatingRuleSet;
+    public static I18n errorRollingBackRuleSetAfterUpdateFailed;
+    public static I18n errorReadingRulesAndProperties;
+    public static I18n errorDeregisteringRuleSetBeforeUpdatingIt;
+    public static I18n errorRecreatingRuleSet;
+    public static I18n errorRemovingRuleSet;
+    public static I18n errorRemovingRuleSetUponShutdown;
+    public static I18n unableToFindRuleSet;
+    public static I18n errorExecutingRuleSetWithGlobalsAndFacts;
+    public static I18n unableToBuildRuleSetRegularExpressionPattern;
 
-	public static I18n errorObtainingSessionToRepositoryWorkspace;
-	public static I18n errorWritingProblemsOnRuleSet;
+    public static I18n errorObtainingSessionToRepositoryWorkspace;
+    public static I18n errorWritingProblemsOnRuleSet;
 
-	public static I18n sequencingServiceName;
-	public static I18n unableToChangeExecutionContextWhileRunning;
-	public static I18n unableToStartSequencingServiceWithoutExecutionContext;
-	public static I18n errorWhileSequencingNode;
-	public static I18n errorInRepositoryWhileSequencingNode;
-	public static I18n errorFindingSequencersToRunAgainstNode;
-	public static I18n errorInRepositoryWhileFindingSequencersToRunAgainstNode;
-	public static I18n executionContextHasBeenClosed;
-	public static I18n sequencerTask;
-	public static I18n sequencerSubtask;
-	public static I18n unableToFindPropertyForSequencing;
-	public static I18n sequencingPropertyOnNode;
-	public static I18n writingOutputSequencedFromPropertyOnNodes;
+    public static I18n sequencingServiceName;
+    public static I18n unableToChangeExecutionContextWhileRunning;
+    public static I18n unableToStartSequencingServiceWithoutExecutionContext;
+    public static I18n errorWhileSequencingNode;
+    public static I18n errorInRepositoryWhileSequencingNode;
+    public static I18n errorFindingSequencersToRunAgainstNode;
+    public static I18n errorInRepositoryWhileFindingSequencersToRunAgainstNode;
+    public static I18n executionContextHasBeenClosed;
+    public static I18n sequencerTask;
+    public static I18n sequencerSubtask;
+    public static I18n unableToFindPropertyForSequencing;
+    public static I18n sequencingPropertyOnNode;
+    public static I18n writingOutputSequencedFromPropertyOnNodes;
 
-	public static I18n errorReadingPropertiesFromContainerNode;
-	public static I18n requiredPropertyOnNodeWasExpectedToBeStringValue;
-	public static I18n optionalPropertyOnNodeWasExpectedToBeStringValue;
-	public static I18n requiredPropertyOnNodeWasExpectedToBeStringArrayValue;
-	public static I18n optionalPropertyOnNodeWasExpectedToBeStringArrayValue;
-	public static I18n requiredPropertyOnNodeCouldNotBeRead;
-	public static I18n optionalPropertyOnNodeCouldNotBeRead;
-	public static I18n requiredPropertyIsMissingFromNode;
-	public static I18n errorGettingRequiredPropertyFromNode;
-	public static I18n errorGettingOptionalPropertyFromNode;
-	public static I18n errorClosingBinaryStreamForPropertyFromNode;
-	public static I18n requiredNodeDoesNotExistRelativeToNode;
-	public static I18n errorGettingNodeRelativeToNode;
-	public static I18n unknownPropertyValueType;
+    public static I18n errorReadingPropertiesFromContainerNode;
+    public static I18n requiredPropertyOnNodeWasExpectedToBeStringValue;
+    public static I18n optionalPropertyOnNodeWasExpectedToBeStringValue;
+    public static I18n requiredPropertyOnNodeWasExpectedToBeStringArrayValue;
+    public static I18n optionalPropertyOnNodeWasExpectedToBeStringArrayValue;
+    public static I18n requiredPropertyOnNodeCouldNotBeRead;
+    public static I18n optionalPropertyOnNodeCouldNotBeRead;
+    public static I18n requiredPropertyIsMissingFromNode;
+    public static I18n errorGettingRequiredPropertyFromNode;
+    public static I18n errorGettingOptionalPropertyFromNode;
+    public static I18n errorClosingBinaryStreamForPropertyFromNode;
+    public static I18n requiredNodeDoesNotExistRelativeToNode;
+    public static I18n errorGettingNodeRelativeToNode;
+    public static I18n unknownPropertyValueType;
 
-	public static I18n pathExpressionIsInvalid;
-	public static I18n pathExpressionMayNotBeBlank;
-	public static I18n pathExpressionHasInvalidSelect;
-	public static I18n pathExpressionHasInvalidMatch;
+    public static I18n pathExpressionIsInvalid;
+    public static I18n pathExpressionMayNotBeBlank;
+    public static I18n pathExpressionHasInvalidSelect;
+    public static I18n pathExpressionHasInvalidMatch;
 
-	public static I18n invalidRepositoryNodePath;
+    public static I18n errorUnregisteringWorkspaceListenerWhileShuttingDownObservationService;
 
-	static {
-		try {
-			I18n.initialize(RepositoryI18n.class);
-		} catch (final Exception err) {
-			System.err.println(err);
-		}
-	}
+    public static I18n invalidRepositoryNodePath;
 
-	public static Set<Locale> getLocalizationProblemLocales() {
-		return I18n.getLocalizationProblemLocales(RepositoryI18n.class);
-	}
+    static {
+        try {
+            I18n.initialize(RepositoryI18n.class);
+        } catch (final Exception err) {
+            System.err.println(err);
+        }
+    }
 
-	public static Set<String> getLocalizationProblems() {
-		return I18n.getLocalizationProblems(RepositoryI18n.class);
-	}
+    public static Set<Locale> getLocalizationProblemLocales() {
+        return I18n.getLocalizationProblemLocales(RepositoryI18n.class);
+    }
 
-	public static Set<String> getLocalizationProblems( Locale locale ) {
-		return I18n.getLocalizationProblems(RepositoryI18n.class, locale);
-	}
+    public static Set<String> getLocalizationProblems() {
+        return I18n.getLocalizationProblems(RepositoryI18n.class);
+    }
+
+    public static Set<String> getLocalizationProblems( Locale locale ) {
+        return I18n.getLocalizationProblems(RepositoryI18n.class, locale);
+    }
 }

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/observation/ObservationService.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/observation/ObservationService.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/observation/ObservationService.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -109,6 +109,15 @@
         /**
          * {@inheritDoc}
          */
+        @Override
+        protected void doShutdown( State fromState ) {
+            super.doShutdown(fromState);
+            shutdownService();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
         public boolean awaitTermination( long timeout, TimeUnit unit ) {
             return true;
         }
@@ -119,6 +128,7 @@
     private ProblemLog problemLog = new DefaultProblemLog();
     private final Statistics statistics = new Statistics();
     private final SessionFactory sessionFactory;
+    private final CopyOnWriteArrayList<WorkspaceListener> workspaceListeners = new CopyOnWriteArrayList<WorkspaceListener>();
     private final CopyOnWriteArrayList<EventListener> eventListeners = new CopyOnWriteArrayList<EventListener>();
     private final CopyOnWriteArrayList<NodeChangeListener> nodeChangeListeners = new CopyOnWriteArrayList<NodeChangeListener>();
     private final Administrator administrator = new Administrator();
@@ -200,6 +210,17 @@
         return this.nodeChangeListeners.remove(listener);
     }
 
+    protected void shutdownService() {
+        // Unregister all listeners ...
+        for (WorkspaceListener listener : this.workspaceListeners) {
+            try {
+                listener.unregister();
+            } catch (RepositoryException e) {
+                this.logger.error(RepositoryI18n.errorUnregisteringWorkspaceListenerWhileShuttingDownObservationService);
+            }
+        }
+    }
+
     /**
      * Monitor the supplied workspace for events of the given type on any node at or under the supplied path.
      * <p>
@@ -262,6 +283,7 @@
         throws RepositoryException {
         WorkspaceListener listener = new WorkspaceListener(repositoryWorkspaceName, eventTypes, absolutePath, isDeep, uuids, nodeTypeNames, noLocal);
         listener.register();
+        this.workspaceListeners.add(listener);
         return listener;
     }
 
@@ -315,6 +337,10 @@
         return monitor(repositoryWorkspaceName, WorkspaceListener.DEFAULT_ABSOLUTE_PATH, eventTypes, WorkspaceListener.DEFAULT_IS_DEEP, null, nodeTypeNames, WorkspaceListener.DEFAULT_NO_LOCAL);
     }
 
+    protected void unregisterListener( WorkspaceListener listener ) {
+        if (listener != null) this.workspaceListeners.remove(listener);
+    }
+
     /**
      * From section 2.8.8 of the JSR-170 specification:
      * <p>
@@ -564,6 +590,7 @@
                 this.session.logout();
             } finally {
                 this.session = null;
+                unregisterListener(this);
             }
             return this;
         }

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerLibrary.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerLibrary.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerLibrary.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,51 @@
+/*
+ * 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.repository.sequencers;
+
+import org.jboss.dna.common.component.ComponentLibrary;
+import org.jboss.dna.spi.sequencers.StreamSequencer;
+
+/**
+ * @author Randall Hauch
+ */
+public class SequencerLibrary extends ComponentLibrary<Sequencer, SequencerConfig> {
+
+    /**
+     * 
+     */
+    public SequencerLibrary() {
+        super();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Sequencer doCreateInstance( Class<?> componentClass ) throws InstantiationException, IllegalAccessException {
+        Object sequencerImpl = componentClass.newInstance();
+        if (sequencerImpl instanceof StreamSequencer) {
+            sequencerImpl = new StreamSequencerAdapter((StreamSequencer)sequencerImpl);
+        }
+        return (Sequencer)sequencerImpl;
+    }
+
+}

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerPathExpression.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerPathExpression.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencerPathExpression.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -265,8 +265,9 @@
         }
 
         // Order is important here
-        expression = expression.replaceAll("[*]", "[^/]*");
-        expression = expression.replaceAll("[/]{2,}", "(/[^/]*)*/");
+        expression = expression.replaceAll("[*]([^/])", "[^/$1]*$1");
+        expression = expression.replaceAll("(?<!\\[\\^/\\])[*]", "[^/]*");
+        expression = expression.replaceAll("[/]{2,}", "(?:/[^/]*)*/");
         return expression;
     }
 

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencingService.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencingService.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/sequencers/SequencingService.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -29,6 +29,7 @@
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
@@ -185,7 +186,7 @@
     }
 
     private ExecutionContext executionContext;
-    private ComponentLibrary<Sequencer, SequencerConfig> sequencerLibrary = new ComponentLibrary<Sequencer, SequencerConfig>();
+    private SequencerLibrary sequencerLibrary = new SequencerLibrary();
     private Selector sequencerSelector = DEFAULT_SEQUENCER_SELECTOR;
     private NodeFilter nodeFilter = DEFAULT_NODE_FILTER;
     private ExecutorService executorService;
@@ -431,12 +432,16 @@
         for (final NodeChange changedNode : changes) {
             // Only care about new nodes or nodes that have new/changed properies ...
             if (filter.accept(changedNode)) {
-                this.executorService.execute(new Runnable() {
+                try {
+                    this.executorService.execute(new Runnable() {
 
-                    public void run() {
-                        processChangedNode(changedNode);
-                    }
-                });
+                        public void run() {
+                            processChangedNode(changedNode);
+                        }
+                    });
+                } catch (RejectedExecutionException e) {
+                    // The executor service has been shut down, so do nothing with this set of changes
+                }
             }
         }
     }

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/AbstractSessionFactory.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/AbstractSessionFactory.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/AbstractSessionFactory.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,218 @@
+/*
+ * 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.repository.util;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.jcr.Credentials;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import org.jboss.dna.common.SystemFailureException;
+import org.jboss.dna.common.util.ArgCheck;
+
+/**
+ * A SessionFactory implementation that creates {@link Session} instances using {@link Repository} instances.
+ * <p>
+ * This factory using a naming convention where the name supplied to the {@link #createSession(String)} contains both the name of
+ * the repository and the name of the workspace. Typically, this is <i><code>repositoryName/workspaceName</code></i>, where
+ * <code>repositoryName</code> is the name under which the Repository instance was bound, and <code>workspaceName</code> is
+ * the name of the workspace. Note that this method looks for the last delimiter in the whole name to distinguish between the
+ * repository and workspace names.
+ * </p>
+ * <p>
+ * For example, if "<code>java:comp/env/repository/dataRepository/myWorkspace</code>" is passed to the
+ * {@link #createSession(String)} method, this factory will look for a {@link Repository} instance registered with the name "<code>java:comp/env/repository/dataRepository</code>"
+ * and use it to {@link Repository#login(String) create a session} to the workspace named "<code>myWorkspace</code>".
+ * </p>
+ * <p>
+ * By default, this factory creates an anonymous JCR session. To use sessions with specific {@link Credentials}, simply
+ * {@link #registerCredentials(String, Credentials) register} credentials for the appropriate repository/workspace name. For
+ * security reasons, it is not possible to retrieve the Credentials once registered with this factory.
+ * </p>
+ * @author Randall Hauch
+ */
+public abstract class AbstractSessionFactory implements SessionFactory {
+
+    protected static char[] DEFAULT_DELIMITERS = new char[] {'/'};
+
+    private final char[] workspaceDelims;
+    private final String workspaceDelimsRegexCharacterSet;
+    private final Map<String, Credentials> credentials = new ConcurrentHashMap<String, Credentials>();
+
+    /**
+     * Create an instance of the factory with the default delimiters.
+     */
+    public AbstractSessionFactory() {
+        this(null);
+    }
+
+    /**
+     * Create an instance of the factory by supplying naming context and the characters that may be used to delimit the workspace
+     * name from the repository name.
+     * @param workspaceDelimiters the delimiters, or null/empty if the default delimiter of '/' should be used.
+     * @throws IllegalArgumentException if the context parameter is null
+     */
+    public AbstractSessionFactory( char... workspaceDelimiters ) {
+        this.workspaceDelims = (workspaceDelimiters == null || workspaceDelimiters.length == 0) ? DEFAULT_DELIMITERS : workspaceDelimiters;
+        StringBuilder sb = new StringBuilder();
+        for (char delim : this.workspaceDelims) {
+            switch (delim) {
+                case '\\':
+                    sb.append("\\");
+                    break;
+                // case '[' : sb.append("\\["); break;
+                case ']':
+                    sb.append("\\]");
+                    break;
+                case '-':
+                    sb.append("\\-");
+                    break;
+                case '^':
+                    sb.append("\\^");
+                    break;
+                default:
+                    sb.append(delim);
+            }
+        }
+        this.workspaceDelimsRegexCharacterSet = sb.toString();
+    }
+
+    /**
+     * Convenience method to bind a repository in JNDI. Repository instances can be bound into JNDI using any technique, so this
+     * method need not be used. <i>Note that the name should not contain the workspace part.</i>
+     * @param name the name of the repository, without the workspace name component.
+     * @param repository the repository to be bound, or null if an existing repository should be unbound.
+     */
+    public void registerRepository( String name, Repository repository ) {
+        assert name != null;
+        // Remove all trailing delimiters ...
+        name = name.replaceAll("[" + this.workspaceDelimsRegexCharacterSet + "]+$", "");
+        if (repository != null) {
+            this.doRegisterRepository(name, repository);
+        } else {
+            this.doUnregisterRepository(name);
+        }
+    }
+
+    protected abstract void doRegisterRepository( String name, Repository repository ) throws SystemFailureException;
+
+    protected abstract void doUnregisterRepository( String name ) throws SystemFailureException;
+
+    protected abstract Repository findRegisteredRepository( String name ) throws SystemFailureException;
+
+    /**
+     * Register the credentials for the repository and workspace given by the supplied name, username and password. This is
+     * equivalent to calling <code>registerCredentials(name, new SimpleCredentials(username,password))</code>, although if
+     * <code>username</code> is null then this is equivalent to <code>registerCredentials(name,null)</code>.
+     * @param name the name of the repository and workspace
+     * @param username the username to use, or null if the existing credentials for the named workspace should be removed
+     * @param password the password, may be null or empty
+     * @return true if this overwrote existing credentials
+     * @see #registerCredentials(String, Credentials)
+     * @see #removeCredentials(String)
+     */
+    public boolean registerCredentials( String name, String username, char[] password ) {
+        if (password == null && username != null) password = new char[] {};
+        Credentials creds = username == null ? null : new SimpleCredentials(username, password);
+        return registerCredentials(name, creds);
+    }
+
+    /**
+     * Register the credentials to be used for the named repository and workspace. Use the same name as used to
+     * {@link #createSession(String) create sessions}.
+     * @param name the name of the repository and workspace
+     * @param credentials the credentials to use, or null if the existing credentials for the named workspace should be removed
+     * @return true if this overwrote existing credentials
+     * @see #registerCredentials(String, String, char[])
+     * @see #removeCredentials(String)
+     */
+    public boolean registerCredentials( String name, Credentials credentials ) {
+        boolean foundExisting = false;
+        name = name != null ? name.trim() : null;
+        if (credentials == null) {
+            foundExisting = this.credentials.remove(name) != null;
+        } else {
+            foundExisting = this.credentials.put(name, credentials) != null;
+        }
+        return foundExisting;
+    }
+
+    /**
+     * Remove any credentials associated with the named repository and workspace. This is equivalent to calling
+     * <code>registerCredentials(name,null)</code>.
+     * @param name the name of the repository and workspace
+     * @return true if existing credentials were found and removed, or false if no such credentials existed
+     * @see #registerCredentials(String, Credentials)
+     */
+    public boolean removeCredentials( String name ) {
+        return registerCredentials(name, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Session createSession( String name ) throws RepositoryException {
+        ArgCheck.isNotNull(name, "session name");
+        name = name.trim();
+        // Look up the Repository object in JNDI ...
+        String repositoryName = getRepositoryName(name);
+        Repository repository = findRegisteredRepository(repositoryName);
+
+        // Determine the name of the workspace ...
+        String workspaceName = getWorkspaceName(name);
+
+        // Look up any credentials, which may be null ...
+        Credentials creds = this.credentials.get(name);
+
+        // Create a session to the specified workspace and using the credentials (either or both may be null) ...
+        return repository.login(creds, workspaceName);
+    }
+
+    protected String getWorkspaceName( String name ) {
+        assert name != null;
+        int index = getIndexOfLastWorkspaceDelimiter(name);
+        if (index == -1) return null;
+        if ((index + 1) == name.length()) return null; // delim is the last character
+        return name.substring(index + 1);
+    }
+
+    protected String getRepositoryName( String name ) {
+        assert name != null;
+        int index = getIndexOfLastWorkspaceDelimiter(name);
+        if (index == -1) return name; // no delim
+        if ((index + 1) == name.length()) return name.substring(0, index); // delim as last character
+        return name.substring(0, index);
+    }
+
+    protected int getIndexOfLastWorkspaceDelimiter( String name ) {
+        int index = -1;
+        for (char delim : this.workspaceDelims) {
+            int i = name.lastIndexOf(delim);
+            index = Math.max(index, i);
+        }
+        return index;
+    }
+
+}

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/JcrTools.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/JcrTools.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/JcrTools.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -268,20 +268,33 @@
     }
 
     public Node findOrCreateNode( Session session, String path ) throws RepositoryException {
+        return findOrCreateNode(session, path, null, null);
+    }
+
+    public Node findOrCreateNode( Session session, String path, String nodeType ) throws RepositoryException {
+        return findOrCreateNode(session, path, nodeType, nodeType);
+    }
+
+    public Node findOrCreateNode( Session session, String path, String defaultNodeType, String finalNodeType ) throws RepositoryException {
         Node root = session.getRootNode();
+        return findOrCreateNode(session, root, path, defaultNodeType, finalNodeType);
+    }
+
+    public Node findOrCreateNode( Session session, Node parentNode, String path, String defaultNodeType, String finalNodeType ) throws RepositoryException {
         // Remove leading and trailing slashes ...
         String relPath = path.replaceAll("^/+", "").replaceAll("/+$", "");
 
         // Look for the node first ...
         try {
-            return root.getNode(relPath);
+            return parentNode.getNode(relPath);
         } catch (PathNotFoundException e) {
             // continue
         }
         // Create the node, which has to be done segment by segment ...
         String[] pathSegments = relPath.split("/");
-        Node node = root;
-        for (String pathSegment : pathSegments) {
+        Node node = parentNode;
+        for (int i = 0, len = pathSegments.length; i != len; ++i) {
+            String pathSegment = pathSegments[i];
             pathSegment = pathSegment.trim();
             if (pathSegment.length() == 0) continue;
             if (node.hasNode(pathSegment)) {
@@ -291,10 +304,24 @@
                 // Make sure there is no index on the final segment ...
                 String pathSegmentWithNoIndex = pathSegment.replaceAll("(\\[\\d+\\])+$", "");
                 // Create the node ...
-                node = node.addNode(pathSegmentWithNoIndex);
+                String nodeType = defaultNodeType;
+                if (i == len - 1 && finalNodeType != null) nodeType = finalNodeType;
+                if (nodeType != null) {
+                    node = node.addNode(pathSegmentWithNoIndex, nodeType);
+                } else {
+                    node = node.addNode(pathSegmentWithNoIndex);
+                }
             }
         }
         return node;
     }
 
+    public Node findOrCreateChild( Session session, Node parent, String name ) throws RepositoryException {
+        return findOrCreateChild(session, parent, name, null);
+    }
+
+    public Node findOrCreateChild( Session session, Node parent, String name, String nodeType ) throws RepositoryException {
+        return findOrCreateNode(session, parent, name, nodeType, nodeType);
+    }
+
 }

Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/JndiSessionFactory.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/JndiSessionFactory.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/JndiSessionFactory.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -22,13 +22,9 @@
 
 package org.jboss.dna.repository.util;
 
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import javax.jcr.Credentials;
 import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
@@ -58,14 +54,9 @@
  * </p>
  * @author Randall Hauch
  */
-public class JndiSessionFactory implements SessionFactory {
+public class JndiSessionFactory extends AbstractSessionFactory {
 
-    protected static char[] DEFAULT_DELIMITERS = new char[] {'/'};
-
-    private final char[] workspaceDelims;
-    private final String workspaceDelimsRegexCharacterSet;
     private final Context context;
-    private final Map<String, Credentials> credentials = new ConcurrentHashMap<String, Credentials>();
 
     /**
      * Create an instance of the factory by creating a new {@link InitialContext}. This is equivalent to calling
@@ -104,146 +95,44 @@
      * @throws IllegalArgumentException if the context parameter is null
      */
     public JndiSessionFactory( Context context, char... workspaceDelimiters ) {
+        super(workspaceDelimiters);
         ArgCheck.isNotNull(context, "initial context");
         this.context = context;
-        this.workspaceDelims = (workspaceDelimiters == null || workspaceDelimiters.length == 0) ? DEFAULT_DELIMITERS : workspaceDelimiters;
-        StringBuilder sb = new StringBuilder();
-        for (char delim : this.workspaceDelims) {
-            switch (delim) {
-                case '\\':
-                    sb.append("\\");
-                    break;
-                // case '[' : sb.append("\\["); break;
-                case ']':
-                    sb.append("\\]");
-                    break;
-                case '-':
-                    sb.append("\\-");
-                    break;
-                case '^':
-                    sb.append("\\^");
-                    break;
-                default:
-                    sb.append(delim);
-            }
-        }
-        this.workspaceDelimsRegexCharacterSet = sb.toString();
     }
 
     /**
-     * Convenience method to bind a repository in JNDI. Repository instances can be bound into JNDI using any technique, so this
-     * method need not be used. <i>Note that the name should not contain the workspace part.</i>
-     * @param name the name of the repository, without the workspace name component.
-     * @param repository the repository to be bound, or null if an existing repository should be unbound.
-     * @throws NamingException if there is a problem registering the supplied repository
+     * {@inheritDoc}
      */
-    public void registerRepository( String name, Repository repository ) throws NamingException {
-        assert name != null;
-        // Remove all trailing delimiters ...
-        name = name.replaceAll("[" + this.workspaceDelimsRegexCharacterSet + "]+$", "");
-        if (repository != null) {
+    @Override
+    protected void doRegisterRepository( String name, Repository repository ) throws SystemFailureException {
+        try {
             this.context.bind(name, repository);
-        } else {
-            this.context.unbind(name);
+        } catch (NamingException e) {
+            throw new SystemFailureException(RepositoryI18n.unableToRegisterRepositoryInJndi.text(name));
         }
     }
 
     /**
-     * Register the credentials for the repository and workspace given by the supplied name, username and password. This is
-     * equivalent to calling <code>registerCredentials(name, new SimpleCredentials(username,password))</code>, although if
-     * <code>username</code> is null then this is equivalent to <code>registerCredentials(name,null)</code>.
-     * @param name the name of the repository and workspace
-     * @param username the username to use, or null if the existing credentials for the named workspace should be removed
-     * @param password the password, may be null or empty
-     * @return true if this overwrote existing credentials
-     * @see #registerCredentials(String, Credentials)
-     * @see #removeCredentials(String)
+     * {@inheritDoc}
      */
-    public boolean registerCredentials( String name, String username, char[] password ) {
-        if (password == null && username != null) password = new char[] {};
-        Credentials creds = username == null ? null : new SimpleCredentials(username, password);
-        return registerCredentials(name, creds);
-    }
-
-    /**
-     * Register the credentials to be used for the named repository and workspace. Use the same name as used to
-     * {@link #createSession(String) create sessions}.
-     * @param name the name of the repository and workspace
-     * @param credentials the credentials to use, or null if the existing credentials for the named workspace should be removed
-     * @return true if this overwrote existing credentials
-     * @see #registerCredentials(String, String, char[])
-     * @see #removeCredentials(String)
-     */
-    public boolean registerCredentials( String name, Credentials credentials ) {
-        boolean foundExisting = false;
-        name = name != null ? name.trim() : null;
-        if (credentials == null) {
-            foundExisting = this.credentials.remove(name) != null;
-        } else {
-            foundExisting = this.credentials.put(name, credentials) != null;
+    @Override
+    protected void doUnregisterRepository( String name ) throws SystemFailureException {
+        try {
+            this.context.unbind(name);
+        } catch (NamingException e) {
+            throw new SystemFailureException(RepositoryI18n.unableToUnregisterRepositoryInJndi.text(name));
         }
-        return foundExisting;
     }
 
     /**
-     * Remove any credentials associated with the named repository and workspace. This is equivalent to calling
-     * <code>registerCredentials(name,null)</code>.
-     * @param name the name of the repository and workspace
-     * @return true if existing credentials were found and removed, or false if no such credentials existed
-     * @see #registerCredentials(String, Credentials)
-     */
-    public boolean removeCredentials( String name ) {
-        return registerCredentials(name, null);
-    }
-
-    /**
      * {@inheritDoc}
      */
-    public Session createSession( String name ) throws RepositoryException {
-        ArgCheck.isNotNull(name, "session name");
-        name = name.trim();
-        // Look up the Repository object in JNDI ...
-        String repositoryName = getRepositoryName(name);
-        Repository repository = null;
+    @Override
+    protected Repository findRegisteredRepository( String name ) throws SystemFailureException {
         try {
-            repository = (Repository)this.context.lookup(repositoryName);
-        } catch (NamingException err) {
-            throw new SystemFailureException(RepositoryI18n.unableToFindRepositoryInJndi.text(repositoryName));
+            return (Repository)this.context.lookup(name);
+        } catch (NamingException e) {
+            throw new SystemFailureException(RepositoryI18n.unableToFindRepositoryInJndi.text(name));
         }
-
-        // Determine the name of the workspace ...
-        String workspaceName = getWorkspaceName(name);
-
-        // Look up any credentials, which may be null ...
-        Credentials creds = this.credentials.get(name);
-
-        // Create a session to the specified workspace and using the credentials (either or both may be null) ...
-        return repository.login(creds, workspaceName);
     }
-
-    protected String getWorkspaceName( String name ) {
-        assert name != null;
-        int index = getIndexOfLastWorkspaceDelimiter(name);
-        if (index == -1) return null;
-        if ((index + 1) == name.length()) return null; // delim is the last character
-        return name.substring(index + 1);
-    }
-
-    protected String getRepositoryName( String name ) {
-        assert name != null;
-        int index = getIndexOfLastWorkspaceDelimiter(name);
-        if (index == -1) return name; // no delim
-        if ((index + 1) == name.length()) return name.substring(0, index); // delim as last character
-        return name.substring(0, index);
-    }
-
-    protected int getIndexOfLastWorkspaceDelimiter( String name ) {
-        int index = -1;
-        for (char delim : this.workspaceDelims) {
-            int i = name.lastIndexOf(delim);
-            index = Math.max(index, i);
-        }
-        return index;
-    }
-
 }

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleExecutionContext.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleExecutionContext.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleExecutionContext.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,49 @@
+/*
+ * 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.repository.util;
+
+/**
+ * @author Randall Hauch
+ */
+public class SimpleExecutionContext extends SimpleSessionFactory implements ExecutionContext {
+
+    private final JcrTools tools = new JcrTools();
+
+    public SimpleExecutionContext() {
+        super();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public SessionFactory getSessionFactory() {
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public JcrTools getTools() {
+        return this.tools;
+    }
+
+}

Added: trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleSessionFactory.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleSessionFactory.java	                        (rev 0)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/util/SimpleSessionFactory.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,106 @@
+/*
+ * 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.repository.util;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.jcr.Credentials;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.naming.InitialContext;
+import org.jboss.dna.common.SystemFailureException;
+import org.jboss.dna.repository.RepositoryI18n;
+
+/**
+ * A SessionFactory implementation that creates {@link Session} instances using {@link Repository} instances registered in JNDI.
+ * <p>
+ * This factory using a naming convention where the name supplied to the {@link #createSession(String)} contains both the name of
+ * the repository and the name of the workspace. Typically, this is <i><code>repositoryName/workspaceName</code></i>, where
+ * <code>repositoryName</code> is the JNDI name under which the Repository instance was bound, and <code>workspaceName</code>
+ * is the name of the workspace. Note that this method looks for the last delimiter in the whole name to distinguish between the
+ * repository and workspace names.
+ * </p>
+ * <p>
+ * For example, if "<code>java:comp/env/repository/dataRepository/myWorkspace</code>" is passed to the
+ * {@link #createSession(String)} method, this factory will look for a {@link Repository} instance registered in JDNI with the
+ * name "<code>java:comp/env/repository/dataRepository</code>" and use it to {@link Repository#login(String) create a session}
+ * to the workspace named "<code>myWorkspace</code>".
+ * </p>
+ * <p>
+ * By default, this factory creates an anonymous JCR session. To use sessions with specific {@link Credentials}, simply
+ * {@link #registerCredentials(String, Credentials) register} credentials for the appropriate repository/workspace name. For
+ * security reasons, it is not possible to retrieve the Credentials once registered with this factory.
+ * </p>
+ * @author Randall Hauch
+ */
+public class SimpleSessionFactory extends AbstractSessionFactory {
+
+    private final Map<String, Repository> repositories = new ConcurrentHashMap<String, Repository>();
+
+    /**
+     * Create an instance of the factory by creating a new {@link InitialContext}.
+     */
+    public SimpleSessionFactory() {
+        super();
+    }
+
+    /**
+     * Create an instance of the factory by supplying the characters that may be used to delimit the workspace name from the
+     * repository name.
+     * @param workspaceDelimiters the delimiters, or null/empty if the default delimiter of '/' should be used.
+     */
+    public SimpleSessionFactory( char... workspaceDelimiters ) {
+        super(workspaceDelimiters);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void doRegisterRepository( String name, Repository repository ) throws SystemFailureException {
+        this.repositories.put(name, repository);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void doUnregisterRepository( String name ) throws SystemFailureException {
+        if (this.repositories.remove(name) == null) {
+            throw new SystemFailureException(RepositoryI18n.unableToRemoveRepository.text(name));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Repository findRegisteredRepository( String name ) throws SystemFailureException {
+        Repository repository = this.repositories.get(name);
+        if (repository == null) {
+            throw new SystemFailureException(RepositoryI18n.unableToFindRepositoryWithName.text(name));
+        }
+        return repository;
+    }
+
+}

Modified: trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties
===================================================================
--- trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties	2008-05-06 14:57:52 UTC (rev 115)
@@ -2,6 +2,10 @@
 serviceShutdowAndMayNotBeStarted = The {0} has been shutdown and may not be (re)started
 serviceShutdowAndMayNotBePaused = The {0} has been shutdown and my not be paused
 unableToFindRepositoryInJndi = Unable to find a JCR repository in JNDI at '{0}'
+unableToRegisterRepositoryInJndi = Unable to register a JCR repository in JNDI at '{0}'
+unableToUnregisterRepositoryInJndi = Unable to unregister a JCR repository at JNDI at '{0}'
+unableToRemoveRepository = Unable to remove a JCR repository named '{0}'
+unableToFindRepositoryWithName = Unable to find a JCR repository named '{0}'
 errorProcessingEvents = Error processing events from {0}
 errorFindingPropertyNameInPropertyAddedEvent = Error finding the name of the added property in the event path {0}
 errorFindingPropertyNameInPropertyChangedEvent = Error finding the name of the changed property in the event path {0}
@@ -58,4 +62,6 @@
 pathExpressionHasInvalidSelect = Invalid select expression '{0}' in the path expression '{1}=>{2}'
 pathExpressionHasInvalidMatch = Invalid match expression '{0}' in the path expression '{1}=>{2}'
 
+errorUnregisteringWorkspaceListenerWhileShuttingDownObservationService = Error unregistering workspace listener while shutting down observation service
+
 invalidRepositoryNodePath = The repository node path '{0}' is not valid: {1}

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencerPathExpressionTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencerPathExpressionTest.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencerPathExpressionTest.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -177,9 +177,9 @@
         assertThat(expr.replaceXPathPatterns("/a/(b/c)[(d|e)/(f|g)/@something]"), is("/a/(b/c)/(d|e)/(f|g)/@something"));
 
         assertThat(expr.replaceXPathPatterns("/a/*/f"), is("/a/[^/]*/f"));
-        assertThat(expr.replaceXPathPatterns("/a//f"), is("/a(/[^/]*)*/f"));
-        assertThat(expr.replaceXPathPatterns("/a///f"), is("/a(/[^/]*)*/f"));
-        assertThat(expr.replaceXPathPatterns("/a/////f"), is("/a(/[^/]*)*/f"));
+        assertThat(expr.replaceXPathPatterns("/a//f"), is("/a(?:/[^/]*)*/f"));
+        assertThat(expr.replaceXPathPatterns("/a///f"), is("/a(?:/[^/]*)*/f"));
+        assertThat(expr.replaceXPathPatterns("/a/////f"), is("/a(?:/[^/]*)*/f"));
     }
 
     protected void assertNotMatches( SequencerPathExpression.Matcher matcher ) {
@@ -235,6 +235,21 @@
     }
 
     @Test
+    public void shouldMatchExpressionsWithFilenameLikeWildcardSelection() {
+        expr = SequencerPathExpression.compile("/a/*.txt[@something] => .");
+        assertMatches(expr.matcher("/a/b.txt/@something"), "/a/b.txt", "/a/b.txt");
+        assertNotMatches(expr.matcher("/a/b.tx/@something"));
+
+        expr = SequencerPathExpression.compile("/a/*.txt/c[@something] => .");
+        assertMatches(expr.matcher("/a/b.txt/c/@something"), "/a/b.txt/c", "/a/b.txt/c");
+        assertNotMatches(expr.matcher("/a/b.tx/c/@something"));
+
+        expr = SequencerPathExpression.compile("//*.txt[*]/c[@something] => .");
+        assertMatches(expr.matcher("/a/b.txt/c/@something"), "/a/b.txt/c", "/a/b.txt/c");
+        assertNotMatches(expr.matcher("/a/b.tx/c/@something"));
+    }
+
+    @Test
     public void shouldMatchExpressionsWithSegmentWildcardSelection() {
         expr = SequencerPathExpression.compile("/a//c[d/e/@something] => .");
         assertMatches(expr.matcher("/a/c/d/e/@something"), "/a/c", "/a/c");
@@ -353,4 +368,10 @@
         assertMatches(expr.matcher("/a/b/c/d/e/@something"), "/a/b/c", "/x/b/c/z");
     }
 
+    @Test
+    public void shouldMatchExpressionWithFilenamePatternAndChildProperty() {
+        expr = SequencerPathExpression.compile("//(*.(jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd))[*]/jcr:content[@jcr:data]=>/images/$1");
+        assertMatches(expr.matcher("/a/b/caution.png/jcr:content/@jcr:data"), "/a/b/caution.png/jcr:content", "/images/caution.png");
+    }
+
 }

Modified: trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencingServiceTest.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencingServiceTest.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/sequencers/SequencingServiceTest.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -39,7 +39,7 @@
 import org.jboss.dna.repository.sequencers.SequencingService;
 import org.jboss.dna.repository.services.ServiceAdministrator;
 import org.jboss.dna.repository.util.ExecutionContext;
-import org.jboss.dna.repository.util.SimpleExecutionContext;
+import org.jboss.dna.repository.util.MockExecutionContext;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -58,7 +58,7 @@
 
     @Before
     public void beforeEach() throws Exception {
-        this.executionContext = new SimpleExecutionContext(this, REPOSITORY_WORKSPACE_NAME);
+        this.executionContext = new MockExecutionContext(this, REPOSITORY_WORKSPACE_NAME);
         this.sequencingService = new SequencingService();
         this.sequencingService.setExecutionContext(this.executionContext);
         this.observationService = new ObservationService(this.executionContext.getSessionFactory());
@@ -69,6 +69,7 @@
     public void afterEach() throws Exception {
         super.shutdownRepository();
         this.sequencingService.getAdministrator().shutdown();
+        this.observationService.getAdministrator().shutdown();
     }
 
     @Test

Copied: trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/MockExecutionContext.java (from rev 110, trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/SimpleExecutionContext.java)
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/MockExecutionContext.java	                        (rev 0)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/MockExecutionContext.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,67 @@
+/*
+ * 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.repository.util;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.io.IOException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import org.jboss.dna.common.SystemFailureException;
+import org.jboss.dna.common.jcr.AbstractJcrRepositoryTest;
+import org.jboss.dna.repository.util.ExecutionContext;
+import org.jboss.dna.repository.util.JcrTools;
+import org.jboss.dna.repository.util.SessionFactory;
+
+/**
+ * @author Randall Hauch
+ */
+public class MockExecutionContext implements ExecutionContext {
+
+    private JcrTools tools = new JcrTools();
+    private SessionFactory sessionFactory;
+
+    public MockExecutionContext( final AbstractJcrRepositoryTest test, final String repositoryName ) {
+        this.sessionFactory = new SessionFactory() {
+
+            public Session createSession( String name ) throws RepositoryException {
+                assertThat(name, is(repositoryName));
+                try {
+                    return test.getRepository().login(test.getTestCredentials());
+                } catch (IOException e) {
+                    throw new SystemFailureException(e);
+                }
+            }
+        };
+    }
+
+    public SessionFactory getSessionFactory() {
+        return sessionFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public JcrTools getTools() {
+        return tools;
+    }
+}


Property changes on: trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/MockExecutionContext.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Deleted: trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/SimpleExecutionContext.java
===================================================================
--- trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/SimpleExecutionContext.java	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/dna-repository/src/test/java/org/jboss/dna/repository/util/SimpleExecutionContext.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -1,67 +0,0 @@
-/*
- * 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.repository.util;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
-import java.io.IOException;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import org.jboss.dna.common.SystemFailureException;
-import org.jboss.dna.common.jcr.AbstractJcrRepositoryTest;
-import org.jboss.dna.repository.util.ExecutionContext;
-import org.jboss.dna.repository.util.JcrTools;
-import org.jboss.dna.repository.util.SessionFactory;
-
-/**
- * @author Randall Hauch
- */
-public class SimpleExecutionContext implements ExecutionContext {
-
-    private JcrTools tools = new JcrTools();
-    private SessionFactory sessionFactory;
-
-    public SimpleExecutionContext( final AbstractJcrRepositoryTest test, final String repositoryName ) {
-        this.sessionFactory = new SessionFactory() {
-
-            public Session createSession( String name ) throws RepositoryException {
-                assertThat(name, is(repositoryName));
-                try {
-                    return test.getRepository().login(test.getTestCredentials());
-                } catch (IOException e) {
-                    throw new SystemFailureException(e);
-                }
-            }
-        };
-    }
-
-    public SessionFactory getSessionFactory() {
-        return sessionFactory;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public JcrTools getTools() {
-        return tools;
-    }
-}

Deleted: trunk/docs/.project
===================================================================
--- trunk/docs/.project	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/docs/.project	2008-05-06 14:57:52 UTC (rev 115)
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>docs</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-	</buildSpec>
-	<natures>
-	</natures>
-</projectDescription>

Added: trunk/docs/examples/getting_started/pom.xml
===================================================================
--- trunk/docs/examples/getting_started/pom.xml	                        (rev 0)
+++ trunk/docs/examples/getting_started/pom.xml	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,133 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.jboss.dna.examples</groupId>
+  <artifactId>getting_started</artifactId>
+  <packaging>pom</packaging>
+  <version>0.1</version>
+  <name>Getting Started examples</name>
+  <description></description>
+
+  <modules>
+    <module>sequencers</module>
+  </modules>
+
+  <properties>
+    <dna-version>0.1-SNAPSHOT</dna-version>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.jboss.dna</groupId>
+        <artifactId>dna-common</artifactId>
+        <version>${dna-version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.jboss.dna</groupId>
+        <artifactId>dna-spi</artifactId>
+        <version>${dna-version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.jboss.dna</groupId>
+        <artifactId>dna-repository</artifactId>
+        <version>${dna-version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.jboss.dna</groupId>
+        <artifactId>dna-maven-classloader</artifactId>
+        <version>${dna-version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.jboss.dna</groupId>
+        <artifactId>dna-sequencer-images</artifactId>
+        <version>${dna-version}</version>
+      </dependency>
+      <!-- Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) -->
+      <dependency>
+		<groupId>org.slf4j</groupId>
+		<artifactId>slf4j-api</artifactId>
+		<version>1.4.3</version>
+      </dependency>
+      <dependency>
+		<groupId>org.slf4j</groupId>
+		<artifactId>slf4j-log4j12</artifactId>
+		<version>1.4.3</version>
+      </dependency>
+	   <dependency>
+		<groupId>log4j</groupId>
+		<artifactId>log4j</artifactId>
+		<version>1.2.14</version>
+      </dependency>
+      <!-- Java Content Repository API -->
+      <dependency>
+        <groupId>javax.jcr</groupId>
+        <artifactId>jcr</artifactId>
+        <version>1.0.1</version>
+        <scope>compile</scope>
+      </dependency>
+      <!-- Apache Jackrabbit (JCR Implementation) -->
+      <dependency>
+        <groupId>org.apache.jackrabbit</groupId>
+        <artifactId>jackrabbit-api</artifactId>
+        <version>1.3.3</version>
+        <scope>compile</scope>
+        <!-- Exclude these since they are included in JDK 1.5 -->
+        <exclusions>
+          <exclusion>
+            <groupId>xml-apis</groupId>
+            <artifactId>xml-apis</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>xerces</groupId>
+            <artifactId>xercesImpl</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.jackrabbit</groupId>
+        <artifactId>jackrabbit-core</artifactId>
+        <version>1.3.3</version>
+        <scope>compile</scope>
+        <!-- Exclude these since they are included in JDK 1.5 -->
+        <exclusions>
+          <exclusion>
+            <groupId>xml-apis</groupId>
+            <artifactId>xml-apis</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>xerces</groupId>
+            <artifactId>xercesImpl</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+      <!-- Testing (note the scope) -->
+      <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>4.4</version>
+        <scope>test</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <build>
+    <plugins> 
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>2.0.2</version>
+        <configuration>
+          <source>1.5</source>
+          <target>1.5</target>
+          <debug>true</debug>
+          <showDeprecation>true</showDeprecation>
+          <showWarnings>true</showWarnings>
+          <optimize>true</optimize>
+        </configuration>
+      </plugin>  
+    </plugins>
+  </build>
+
+</project>


Property changes on: trunk/docs/examples/getting_started/sequencers
___________________________________________________________________
Name: svn:ignore
   + target


Added: trunk/docs/examples/getting_started/sequencers/.classpath
===================================================================
--- trunk/docs/examples/getting_started/sequencers/.classpath	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/.classpath	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry kind="src" path="src/main/resources"/>
+	<classpathentry kind="src" path="src/test/java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

Added: trunk/docs/examples/getting_started/sequencers/.project
===================================================================
--- trunk/docs/examples/getting_started/sequencers/.project	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/.project	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>docs-getting-started-examples-sequencers</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>

Added: trunk/docs/examples/getting_started/sequencers/pom.xml
===================================================================
--- trunk/docs/examples/getting_started/sequencers/pom.xml	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/pom.xml	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,95 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <parent>
+    <groupId>org.jboss.dna.examples</groupId>
+    <artifactId>getting_started</artifactId>
+    <version>0.1</version>
+    <relativePath>..</relativePath>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.jboss.dna.examples</groupId>
+  <artifactId>dna-example-sequencers</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>jar</packaging>
+  <name>Sequencer Examples</name>
+  <description>Examples that showcase how to use sequencers with a JCR repository.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-repository</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.dna</groupId>
+      <artifactId>dna-sequencer-images</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+    <!-- 
+    Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing) 
+    -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+    </dependency>
+    <!-- 
+      Java Content Repository API 
+    -->
+    <dependency>
+      <groupId>javax.jcr</groupId>
+      <artifactId>jcr</artifactId>
+    </dependency>
+    <!-- 
+      Apache Jackrabbit (JCR Implementation) for repository implementation
+    -->
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>jackrabbit-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>jackrabbit-core</artifactId>
+    </dependency>
+    <!-- 
+      Test cases use JUnit
+    -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/main/assembly/basic.xml</descriptor>
+          </descriptors>
+          <finalName>${pom.artifactId}</finalName>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>directory-inline</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
\ No newline at end of file

Added: trunk/docs/examples/getting_started/sequencers/src/main/assembly/basic.xml
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/assembly/basic.xml	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/assembly/basic.xml	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,57 @@
+<assembly>
+  <id>basic</id>
+  <formats>
+    <format>zip</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>target</directory>
+      <outputDirectory/>
+      <includes>
+        <include>*.jar</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>src/main/resources/</directory>
+      <outputDirectory/>
+      <includes>
+        <include>*.png</include>
+        <include>*.gif</include>
+        <include>*.jpg</include>
+        <include>*.pict</include>
+      </includes>
+    </fileSet>
+  </fileSets>
+  <files>
+    <file>
+      <source>src/main/config/run.sh</source>
+      <fileMode>0744</fileMode>
+    </file>
+    <file>
+      <source>src/main/resources/jackrabbitConfig.xml</source>
+      <fileMode>0644</fileMode>
+    </file>
+    <file>
+      <source>src/main/resources/jackrabbitNodeTypes.cnd</source>
+      <fileMode>0644</fileMode>
+    </file>
+    <file>
+      <source>src/main/resources/log4j.properties</source>
+      <fileMode>0644</fileMode>
+    </file>
+  </files>
+  <dependencySets>
+    <dependencySet>
+      <outputDirectory>/lib</outputDirectory>
+      <unpack>false</unpack>
+      <includes>
+        <!-- Include everything needed by the examples. -->
+        <include>*:jar:*</include>
+      </includes>
+      <!--excludes>
+        <exclude>*:sources</exclude>
+      </excludes-->
+    </dependencySet>
+  </dependencySets>
+</assembly>
\ No newline at end of file

Added: trunk/docs/examples/getting_started/sequencers/src/main/config/run.sh
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/config/run.sh	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/config/run.sh	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+java -Djava.ext.dirs=`pwd`/lib -cp .:dna-example-sequencers-0.1-SNAPSHOT.jar org.jboss.example.dna.sequencers.SequencingClient $1
\ No newline at end of file

Added: trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/ConsoleInput.java
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/ConsoleInput.java	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/ConsoleInput.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,200 @@
+/*
+ * 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.sequencers;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.repository.sequencers.SequencingService;
+
+/**
+ * @author Randall Hauch
+ */
+public class ConsoleInput implements UserInterface {
+
+    protected static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+
+    public ConsoleInput( final SequencingClient client ) {
+        try {
+            System.out.println();
+            System.out.print("Starting repository and sequencing service ... ");
+            client.startRepository();
+            System.out.print("done.\nStarting sequencing service ... ");
+            client.startDnaServices();
+            System.out.println("done.");
+            System.out.println();
+
+            System.out.println(getMenu());
+            Thread eventThread = new Thread(new Runnable() {
+
+                private boolean quit = false;
+
+                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 'u':
+                                        client.uploadFile();
+                                        break;
+                                    case 's':
+                                        client.search();
+                                        break;
+                                    case 'm':
+                                        System.out.println(getMenu());
+                                        break;
+                                    case 'p':
+                                        System.out.println(getStatistics(client.getStatistics()));
+                                        break;
+                                    case 'q':
+                                        quit = true;
+                                        break;
+                                    default:
+                                        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("Shutting down sequencing service ... ");
+                            client.shutdownDnaServices();
+                            System.out.print("done.\nShutting down repository ... ");
+                            client.shutdownRepository();
+                            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() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append("-----------------------------------\n");
+        buffer.append("Menu:\n");
+        buffer.append("\n");
+        buffer.append("u) Upload a file to the repository\n");
+        buffer.append("s) Search the repository using extracted metadata\n");
+        buffer.append("\n");
+        buffer.append("p) Print statistics\n");
+        buffer.append("\n");
+        buffer.append("m) Display menu\n");
+        buffer.append("q) Quit");
+        return buffer.toString();
+    }
+
+    public File getPathOfFileToUpload() throws IllegalArgumentException, IOException {
+        System.out.println("Please enter the file to upload:");
+        String path = in.readLine();
+        File file = new File(path);
+        if (!file.exists()) {
+            throw new IllegalArgumentException("The file \"" + file.getAbsolutePath() + "\" does not exist.");
+        }
+        if (!file.canRead()) {
+            throw new IllegalArgumentException("Unable to read \"" + file.getAbsolutePath() + "\".");
+        }
+        if (!file.isFile()) {
+            throw new IllegalArgumentException("Please specify a file.  The file \"" + file.getAbsolutePath() + "\" is a directory.");
+        }
+        return file;
+    }
+
+    public String getRepositoryPath( String defaultPath ) throws IllegalArgumentException, IOException {
+        if (defaultPath != null) defaultPath = defaultPath.trim();
+        if (defaultPath.length() == 0) defaultPath = null;
+        String displayDefaultPath = defaultPath == null ? "" : " [" + defaultPath.trim() + "]";
+        System.out.println("Please enter the repository path where the file should be placed" + displayDefaultPath + ":");
+        String path = in.readLine().trim();
+        if (path.length() == 0) {
+            if (defaultPath == null) {
+                throw new IllegalArgumentException("The path \"" + path + "\" is not valid.");
+            }
+            path = defaultPath;
+        }
+        return path;
+    }
+
+    public void displaySearchResults( List<ImageInfo> images ) {
+        System.out.println();
+        if (images.isEmpty()) {
+            System.out.println("No results were found.");
+            System.out.println();
+            return;
+        }
+        if (images.size() == 1) {
+            System.out.println("1 image was found:");
+        } else {
+            System.out.println("" + images.size() + " images were found:");
+        }
+        int counter = 1;
+        for (ImageInfo image : images) {
+            System.out.println(" Image " + counter++);
+            System.out.println("   Name: " + image.getName());
+            System.out.println("   Path: " + image.getPath());
+            for (Map.Entry<Object, Object> entry : image.getProperties().entrySet()) {
+                System.out.println("   " + entry.getKey() + ": " + entry.getValue());
+            }
+        }
+        System.out.println();
+    }
+
+    public String getStatistics( SequencingService.Statistics stats ) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("\n");
+        sb.append("# nodes sequenced: ").append(stats.getNumberOfNodesSequenced()).append("\n");
+        sb.append("# nodes skipped: ").append(stats.getNumberOfNodesSkipped()).append("\n");
+        sb.append("\n");
+        return sb.toString();
+    }
+}

Added: trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/ImageInfo.java
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/ImageInfo.java	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/ImageInfo.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,69 @@
+/*
+ * 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.sequencers;
+
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author Randall Hauch
+ */
+public class ImageInfo {
+
+    private final Properties properties = new Properties();
+    private final String name;
+    private final String path;
+
+    protected ImageInfo( String path, String name, Properties props ) {
+        this.name = name;
+        this.path = path;
+        if (props != null) this.properties.putAll(props);
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getPath() {
+        return this.path;
+    }
+
+    public Properties getProperties() {
+        return this.properties;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (Map.Entry<Object, Object> entry : this.properties.entrySet()) {
+            sb.append(entry.getKey()).append("=>").append(entry.getValue());
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+        }
+        return this.name + " (at " + this.path + ") with properties {" + sb.toString() + "}";
+    }
+
+}

Added: trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/SequencingClient.java
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/SequencingClient.java	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/SequencingClient.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,352 @@
+/*
+ * 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.sequencers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import javax.jcr.Credentials;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+import javax.jcr.observation.Event;
+import net.jcip.annotations.NotThreadSafe;
+import org.apache.jackrabbit.api.JackrabbitNodeTypeManager;
+import org.apache.jackrabbit.core.TransientRepository;
+import org.jboss.dna.common.SystemFailureException;
+import org.jboss.dna.repository.observation.ObservationService;
+import org.jboss.dna.repository.sequencers.SequencerConfig;
+import org.jboss.dna.repository.sequencers.SequencingService;
+import org.jboss.dna.repository.util.ExecutionContext;
+import org.jboss.dna.repository.util.JcrTools;
+import org.jboss.dna.repository.util.SimpleExecutionContext;
+
+/**
+ * @author Randall Hauch
+ */
+ at NotThreadSafe
+public class SequencingClient {
+
+    public static final String DEFAULT_JACKRABBIT_CONFIG_PATH = "jackrabbitConfig.xml";
+    public static final String DEFAULT_WORKING_DIRECTORY = "repositoryData";
+    public static final String DEFAULT_REPOSITORY_NAME = "repo";
+    public static final String DEFAULT_WORKSPACE_NAME = "default";
+    public static final String DEFAULT_USERNAME = "jsmith";
+    public static final char[] DEFAULT_PASSWORD = "secret".toCharArray();
+
+    public static void main( String[] args ) throws Exception {
+        SequencingClient client = new SequencingClient();
+        client.setRepositoryInformation(DEFAULT_REPOSITORY_NAME, DEFAULT_WORKSPACE_NAME, "jsmith", "secret".toCharArray());
+        client.setUserInterface(new ConsoleInput(client));
+    }
+
+    private String repositoryName;
+    private String workspaceName;
+    private String username;
+    private char[] password;
+    private String jackrabbitConfigPath;
+    private String workingDirectory;
+    private Session keepAliveSession;
+    private Repository repository;
+    private SequencingService sequencingService;
+    private ObservationService observationService;
+    private UserInterface userInterface;
+    private ExecutionContext executionContext;
+
+    public SequencingClient() {
+        setJackrabbitConfigPath(DEFAULT_JACKRABBIT_CONFIG_PATH);
+        setWorkingDirectory(DEFAULT_WORKING_DIRECTORY);
+        setRepositoryInformation(DEFAULT_REPOSITORY_NAME, DEFAULT_WORKSPACE_NAME, DEFAULT_USERNAME, DEFAULT_PASSWORD);
+    }
+
+    public void setUserInterface( UserInterface userInterface ) {
+        this.userInterface = userInterface;
+    }
+
+    protected void setWorkingDirectory( String workingDirectoryPath ) {
+        this.workingDirectory = workingDirectoryPath != null ? workingDirectoryPath : DEFAULT_WORKING_DIRECTORY;
+    }
+
+    protected void setJackrabbitConfigPath( String jackrabbitConfigPath ) {
+        this.jackrabbitConfigPath = jackrabbitConfigPath != null ? jackrabbitConfigPath : DEFAULT_JACKRABBIT_CONFIG_PATH;
+    }
+
+    protected void setRepositoryInformation( String repositoryName, String workspaceName, String username, char[] password ) {
+        if (this.repository != null) {
+            throw new IllegalArgumentException("Unable to set repository information when repository is already running");
+        }
+        this.repositoryName = repositoryName != null ? repositoryName : DEFAULT_REPOSITORY_NAME;
+        this.workspaceName = workspaceName != null ? workspaceName : DEFAULT_WORKSPACE_NAME;
+        this.username = username;
+        this.password = password;
+    }
+
+    public void startRepository() throws Exception {
+        if (this.repository == null) {
+            try {
+
+                // Load the Jackrabbit configuration ...
+                File configFile = new File(this.jackrabbitConfigPath);
+                if (!configFile.exists()) {
+                    throw new SystemFailureException("The Jackrabbit configuration file cannot be found at " + configFile.getAbsoluteFile());
+                }
+                if (!configFile.canRead()) {
+                    throw new SystemFailureException("Unable to read the Jackrabbit configuration file at " + configFile.getAbsoluteFile());
+                }
+                String pathToConfig = configFile.getAbsolutePath();
+
+                // Find the directory where the Jackrabbit repository data will be stored ...
+                File workingDirectory = new File(this.workingDirectory);
+                if (workingDirectory.exists()) {
+                    if (!workingDirectory.isDirectory()) {
+                        throw new SystemFailureException("Unable to create working directory at " + workingDirectory.getAbsolutePath());
+                    }
+                }
+                String workingDirectoryPath = workingDirectory.getAbsolutePath();
+
+                // Get the Jackrabbit custom node definition (CND) file ...
+                URL cndFile = Thread.currentThread().getContextClassLoader().getResource("jackrabbitNodeTypes.cnd");
+
+                // Create the Jackrabbit repository instance and establish a session to keep the repository alive ...
+                this.repository = new TransientRepository(pathToConfig, workingDirectoryPath);
+                if (this.username != null) {
+                    Credentials credentials = new SimpleCredentials(this.username, this.password);
+                    this.keepAliveSession = this.repository.login(credentials, this.workspaceName);
+                } else {
+                    this.keepAliveSession = this.repository.login();
+                }
+
+                try {
+                    // Register the node types (only valid the first time) ...
+                    JackrabbitNodeTypeManager mgr = (JackrabbitNodeTypeManager)this.keepAliveSession.getWorkspace().getNodeTypeManager();
+                    mgr.registerNodeTypes(cndFile.openStream(), JackrabbitNodeTypeManager.TEXT_X_JCR_CND);
+                } catch (RepositoryException e) {
+                    if (!e.getMessage().contains("already exists")) throw e;
+                }
+
+            } catch (Exception e) {
+                this.repository = null;
+                this.keepAliveSession = null;
+                throw e;
+            }
+        }
+    }
+
+    public void shutdownRepository() throws Exception {
+        if (this.repository != null) {
+            try {
+                this.keepAliveSession.logout();
+            } finally {
+                this.repository = null;
+                this.keepAliveSession = null;
+            }
+        }
+    }
+
+    public void startDnaServices() throws Exception {
+        if (this.repository == null) {
+            this.startRepository();
+        }
+        if (this.sequencingService != null) {
+            return;
+        }
+        // Create an execution context for the sequencing service.
+        // The repository instances and workspace names are registered, and that the service will reference.
+        // the repository ...
+        SimpleExecutionContext executionContext = new SimpleExecutionContext();
+        // Register the JCR repository ...
+        executionContext.registerRepository(this.repositoryName, this.repository);
+        if (this.username != null) {
+            Credentials credentials = new SimpleCredentials(this.username, this.password);
+            executionContext.registerCredentials(this.repositoryName + "/" + this.workspaceName, credentials);
+        }
+        this.executionContext = executionContext;
+
+        // Create the sequencing service ...
+        this.sequencingService = new SequencingService();
+        this.sequencingService.setExecutionContext(executionContext);
+
+        // Add the configuration for the image sequencer. This sequencer class should be on the thread's current context class
+        // loader, or if that's null the classloader that loaded the SequencingService class.
+        //
+        // The path expressions tell the service that this sequencer should be invoked on the "jcr:data" property
+        // on the "jcr:content" child node of any node uploaded to the repository whose name ends with one of the
+        // supported extensions, and it should place the output metadata in a node with the same name as the file
+        // but immediately below the "/images" node. Path expressions can be fairly complex, and can even
+        // specify that the generated information be placed in a different repository.
+        // 
+        // Sequencers can be added before or after the service is started.
+        String name = "Image Sequencer";
+        String desc = "Sequences image files to extract the characteristics of the image";
+        String classname = "org.jboss.dna.sequencer.images.ImageMetadataSequencer";
+        String[] classpath = null; // Use the current classpath
+        String[] pathExpressions = {"//(*.(jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd))[*]/jcr:content[@jcr:data] => /images/$1"};
+        SequencerConfig imageSequencerConfig = new SequencerConfig(name, desc, classname, classpath, pathExpressions);
+        this.sequencingService.addSequencer(imageSequencerConfig);
+
+        // Start up the sequencing service ...
+        this.sequencingService.getAdministrator().start();
+
+        // Register the sequencing service as a listener using the observation service ...
+        this.observationService = new ObservationService(this.executionContext.getSessionFactory());
+        this.observationService.getAdministrator().start();
+        this.observationService.addListener(this.sequencingService);
+        this.observationService.monitor(this.repositoryName + "/" + this.workspaceName, Event.NODE_ADDED | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED);
+    }
+
+    public void shutdownDnaServices() throws Exception {
+        if (this.sequencingService == null) return;
+
+        try {
+            // Shut down the service and wait until it's all shut down ...
+            this.sequencingService.getAdministrator().shutdown();
+            this.sequencingService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+
+            // Shut down the observation service ...
+            this.observationService.getAdministrator().shutdown();
+            this.observationService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+
+        } finally {
+            this.sequencingService = null;
+            this.observationService = null;
+        }
+    }
+
+    public SequencingService.Statistics getStatistics() {
+        return this.sequencingService.getStatistics();
+    }
+
+    public void uploadFile() throws Exception {
+        File file = this.userInterface.getPathOfFileToUpload();
+        String nodePath = this.userInterface.getRepositoryPath("/a/b/" + file.getName());
+        String mimeType = getMimeType(file);
+        uploadFile(new FileInputStream(file), nodePath, mimeType);
+    }
+
+    public void search() throws Exception {
+        List<ImageInfo> images = getImages();
+        // Display the search results ...
+        this.userInterface.displaySearchResults(images);
+    }
+
+    protected List<ImageInfo> getImages() throws Exception {
+        List<ImageInfo> images = new ArrayList<ImageInfo>();
+        Session session = createSession();
+        try {
+            // Find the image node ...
+            Node root = session.getRootNode();
+            if (root.hasNode("images")) {
+                Node imagesNode = root.getNode("images");
+
+                // Iterate over each child ...
+                for (NodeIterator iter = imagesNode.getNodes(); iter.hasNext();) {
+                    Node imageNode = iter.nextNode();
+                    String nodePath = imageNode.getPath();
+                    String nodeName = imageNode.getName();
+                    if (imageNode.hasNode("image:metadata")) {
+                        imageNode = imageNode.getNode("image:metadata");
+
+                        // Create a Properties object containing the properties for this node; ignore any children ...
+                        Properties props = new Properties();
+                        for (PropertyIterator propertyIter = imageNode.getProperties(); propertyIter.hasNext();) {
+                            Property property = propertyIter.nextProperty();
+                            String name = property.getName();
+                            String stringValue = null;
+                            if (property.getDefinition().isMultiple()) {
+                                StringBuilder sb = new StringBuilder();
+                                boolean first = true;
+                                for (Value value : property.getValues()) {
+                                    if (!first) {
+                                        sb.append(", ");
+                                        first = false;
+                                    }
+                                    sb.append(value.getString());
+                                }
+                                stringValue = sb.toString();
+                            } else {
+                                stringValue = property.getValue().getString();
+                            }
+                            props.put(name, stringValue);
+                        }
+                        // Create the image information object, and add it to the collection ...
+                        ImageInfo info = new ImageInfo(nodePath, nodeName, props);
+                        images.add(info);
+                    }
+                }
+            }
+        } finally {
+            session.logout();
+        }
+        return images;
+    }
+
+    protected Session createSession() throws RepositoryException {
+        return this.executionContext.getSessionFactory().createSession(this.repositoryName + "/" + this.workspaceName);
+    }
+
+    protected boolean uploadFile( InputStream content, String nodePath, String mimeType ) throws Exception {
+        Session session = createSession();
+        JcrTools tools = this.executionContext.getTools();
+        try {
+            // Create the node at the supplied path ...
+            Node node = tools.findOrCreateNode(session, nodePath, "nt:folder", "nt:file");
+
+            // Upload the file to that node ...
+            Node contentNode = tools.findOrCreateChild(session, node, "jcr:content", "nt:resource");
+            contentNode.setProperty("jcr:mimeType", mimeType);
+            contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
+            contentNode.setProperty("jcr:data", content);
+
+            // Save the session ...
+            session.save();
+        } finally {
+            session.logout();
+        }
+        return true;
+    }
+
+    protected String getMimeType( File file ) {
+        String extension = file.getName().toLowerCase();
+        if (extension.endsWith(".gif")) return "image/gif";
+        if (extension.endsWith(".png")) return "image/png";
+        if (extension.endsWith(".pict")) return "image/x-pict";
+        if (extension.endsWith(".bmp")) return "image/bmp";
+        if (extension.endsWith(".jpg")) return "image/jpeg";
+        if (extension.endsWith(".jpe")) return "image/jpeg";
+        if (extension.endsWith(".jpeg")) return "image/jpeg";
+        if (extension.endsWith(".ras")) return "image/x-cmu-raster";
+        throw new SystemFailureException("Unknown mime type for file " + file);
+    }
+
+}

Added: trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/UserInterface.java
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/UserInterface.java	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/java/org/jboss/example/dna/sequencers/UserInterface.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,38 @@
+/*
+ * 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.sequencers;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Randall Hauch
+ */
+public interface UserInterface {
+
+    public File getPathOfFileToUpload() throws IllegalArgumentException, IOException;
+
+    public String getRepositoryPath( String defaultPath ) throws IllegalArgumentException, IOException;
+
+    public void displaySearchResults( List<ImageInfo> images ) throws IOException;
+}

Added: trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.gif
===================================================================
(Binary files differ)


Property changes on: trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.jpg
===================================================================
(Binary files differ)


Property changes on: trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.jpg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.pict
===================================================================
(Binary files differ)


Property changes on: trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.pict
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.png
===================================================================
(Binary files differ)


Property changes on: trunk/docs/examples/getting_started/sequencers/src/main/resources/caution.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/docs/examples/getting_started/sequencers/src/main/resources/jackrabbitConfig.xml
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/resources/jackrabbitConfig.xml	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/resources/jackrabbitConfig.xml	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.2//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-1.2.dtd">
+<!-- Example Repository Configuration File -->
+<Repository>
+    <!--
+        virtual file system where the repository stores global state
+        (e.g. registered namespaces, custom node types, etc.)
+    -->
+    <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+        <param name="path" value="${rep.home}/repository"/>
+    </FileSystem>
+
+    <!--
+        security configuration
+    -->
+    <Security appName="Jackrabbit">
+        <!--
+            access manager:
+            class: FQN of class implementing the AccessManager interface
+        -->
+        <AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager">
+            <!-- <param name="config" value="${rep.home}/access.xml"/> -->
+        </AccessManager>
+
+        <LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule">
+           <!-- anonymous user name ('anonymous' is the default value) -->
+           <param name="anonymousId" value="anonymous"/>
+           <!--
+              default user name to be used instead of the anonymous user
+              when no login credentials are provided (unset by default)
+           -->
+           <!-- <param name="defaultUserId" value="superuser"/> -->
+        </LoginModule>
+    </Security>
+
+    <!--
+        location of workspaces root directory and name of default workspace
+    -->
+    <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>
+    <!--
+        workspace configuration template:
+        used to create the initial workspace if there's no workspace yet
+    -->
+    <Workspace name="Jackrabbit Core">
+        <!--
+            virtual file system of the workspace:
+            class: FQN of class implementing the FileSystem interface
+        -->
+        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+            <param name="path" value="${wsp.home}"/>
+        </FileSystem>
+        <!--
+            persistence manager of the workspace:
+            class: FQN of class implementing the PersistenceManager interface
+        -->
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
+          <param name="persistent" value="false"/>
+        </PersistenceManager>
+        <!--
+            Search index and the file system it uses.
+            class: FQN of class implementing the QueryHandler interface
+        -->
+        <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+            <param name="path" value="${wsp.home}/index"/>
+        </SearchIndex>
+    </Workspace>
+
+    <!--
+        Configures the versioning
+    -->
+    <Versioning rootPath="${rep.home}/version">
+        <!--
+            Configures the filesystem to use for versioning for the respective
+            persistence manager
+        -->
+        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+            <param name="path" value="${rep.home}/version" />
+        </FileSystem>
+
+        <!--
+            Configures the persistence manager to be used for persisting version state.
+            Please note that the current versioning implementation is based on
+            a 'normal' persistence manager, but this could change in future
+            implementations.
+        -->
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
+          <param name="persistent" value="false"/>
+        </PersistenceManager>
+    </Versioning>
+
+    <!--
+        Search index for content that is shared repository wide
+        (/jcr:system tree, contains mainly versions)
+    -->
+    <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+        <param name="path" value="${rep.home}/repository/index"/>
+    </SearchIndex>
+</Repository>

Added: trunk/docs/examples/getting_started/sequencers/src/main/resources/jackrabbitNodeTypes.cnd
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/resources/jackrabbitNodeTypes.cnd	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/resources/jackrabbitNodeTypes.cnd	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,37 @@
+<jcr='http://www.jcp.org/jcr/1.0'>
+<nt='http://www.jcp.org/jcr/nt/1.0'>
+<mix='http://www.jcp.org/jcr/mix/1.0'>
+<image='http://jboss.org/dna/images/1.0'>
+
+// ----------------------------------------------------------
+// JCR Pre-defined Mixin Types that are not loaded by default
+// ----------------------------------------------------------
+
+[mix:mimeType] mixin 
+  - jcr:mimeType (string)  
+  - jcr:encoding (string)
+
+
+// -------------------
+// DNA Image Sequencer
+// -------------------
+
+/**
+ * Marker mixin that defines the root of a Maven 2 repository.
+ */
+[image:metadata] > nt:unstructured
+  - image:mimeType (string)  
+  - image:encoding (string)
+  - image:formatName (string) mandatory
+  - image:width (long)
+  - image:height (long)
+  - image:bitsPerPixel (long)
+  - image:progressive (boolean)
+  - image:numberOfImages (long)
+  - image:physicalWidthDpi (long)
+  - image:physicalHeightDpi (long)
+  - image:physicalWidthInches (long)
+  - image:physicalHeightInches (long)
+
+
+

Added: trunk/docs/examples/getting_started/sequencers/src/main/resources/log4j.properties
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/main/resources/log4j.properties	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/main/resources/log4j.properties	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,17 @@
+# 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.tests.integration.jackrabbit.JackrabbitMySqlStressTest=DEBUG
+
+# Jackrabbit logging
+log4j.logger.org.apache.jackrabbit=WARN, stdout
+log4j.logger.org.apache.derby=INFO, stdout
+

Added: trunk/docs/examples/getting_started/sequencers/src/test/java/org/jboss/example/dna/sequencers/SequencingClientTest.java
===================================================================
--- trunk/docs/examples/getting_started/sequencers/src/test/java/org/jboss/example/dna/sequencers/SequencingClientTest.java	                        (rev 0)
+++ trunk/docs/examples/getting_started/sequencers/src/test/java/org/jboss/example/dna/sequencers/SequencingClientTest.java	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,89 @@
+/*
+ * 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.sequencers;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.net.URL;
+import java.util.List;
+import org.jboss.dna.common.util.FileUtil;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class SequencingClientTest {
+
+    private URL file;
+    private SequencingClient client;
+
+    @Before
+    public void beforeEach() throws Exception {
+        this.file = Thread.currentThread().getContextClassLoader().getResource("caution.png");
+        client = new SequencingClient();
+        client.setWorkingDirectory("target/repositoryData");
+        client.setJackrabbitConfigPath("src/main/resources/jackrabbitConfig.xml");
+        FileUtil.delete("target/repositoryData");
+    }
+
+    @After
+    public void afterEach() throws Exception {
+        client.shutdownDnaServices();
+        client.shutdownRepository();
+        FileUtil.delete("target/repositoryData");
+    }
+
+    @Test
+    public void shouldStartupAndShutdownRepository() throws Exception {
+        client.startRepository();
+        client.shutdownRepository();
+
+    }
+
+    @Test
+    public void shouldStartupAndShutdownRepositoryAndSequencingService() throws Exception {
+        client.startRepository();
+        client.startDnaServices();
+        client.shutdownDnaServices();
+        client.shutdownRepository();
+    }
+
+    @Test
+    public void shouldUploadFile() throws Exception {
+        client.startRepository();
+        client.startDnaServices();
+        client.uploadFile(file.openStream(), "/a/b/caution.png", "image/png");
+
+        // Let the sequencing start ...
+        Thread.sleep(1000);
+        client.shutdownDnaServices(); // this will block untill all processing has been done ...
+
+        List<ImageInfo> images = client.getImages();
+        assertThat(images.size(), is(1));
+        for (ImageInfo image : images) {
+            System.out.println("Image: " + image);
+        }
+    }
+
+}

Added: trunk/docs/getting_started/.project
===================================================================
--- trunk/docs/getting_started/.project	                        (rev 0)
+++ trunk/docs/getting_started/.project	2008-05-06 14:57:52 UTC (rev 115)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>docs-getting-started</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+</projectDescription>

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2008-04-30 19:30:18 UTC (rev 114)
+++ trunk/pom.xml	2008-05-06 14:57:52 UTC (rev 115)
@@ -33,6 +33,7 @@
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-compiler-plugin</artifactId>
+               <version>2.0.2</version>
 				<configuration>
 					<source>1.5</source>
 					<target>1.5</target>




More information about the dna-commits mailing list