[dna-commits] DNA SVN: r1124 - in trunk: dna-jcr/src/main/java/org/jboss/dna/jcr and 2 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Wed Jul 22 13:32:10 EDT 2009


Author: rhauch
Date: 2009-07-22 13:32:10 -0400 (Wed, 22 Jul 2009)
New Revision: 1124

Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
   trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
   trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
Log:
DNA-392 All sessions in a repository should share the same /jcr:system content

Changed the JcrRepository implementation to have a new option that defines the source name (and optionally workspace name) where the /jcr:system branch is to be found.  If this option is not used, or if the supplied names do not correspond to a valid workspace in an existing source, a transient in-memory source will be used.  The existing JcrConfiguration and JcrEngine are already able to process any options, so they do not need to be changed to allow a configuration to define the /jcr:system branch location.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java	2009-07-21 20:53:05 UTC (rev 1123)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java	2009-07-22 17:32:10 UTC (rev 1124)
@@ -475,8 +475,10 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append(this.workspaceName);
-        sb.append('@');
+        if (this.workspaceName != null) {
+            sb.append(this.workspaceName);
+            sb.append('@');
+        }
         sb.append(this.sourceName);
         sb.append(" { ");
         boolean first = true;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-07-21 20:53:05 UTC (rev 1123)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java	2009-07-22 17:32:10 UTC (rev 1124)
@@ -99,6 +99,10 @@
     public static I18n typeNotFound;
     public static I18n supertypeNotFound;
 
+    public static I18n systemSourceNameOptionValueDoesNotReferenceExistingSource;
+    public static I18n systemSourceNameOptionValueDoesNotReferenceValidWorkspace;
+    public static I18n systemSourceNameOptionValueIsNotFormattedCorrectly;
+
     // Used in AbstractJcrNode#getAncestor
     public static I18n noNegativeDepth;
     public static I18n tooDeep;

Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-07-21 20:53:05 UTC (rev 1123)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java	2009-07-22 17:32:10 UTC (rev 1124)
@@ -45,9 +45,12 @@
 import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
+import net.jcip.annotations.Immutable;
 import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.text.Inflector;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Logger;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.Graph;
 import org.jboss.dna.graph.JaasSecurityContext;
@@ -115,9 +118,23 @@
          * The {@link Configuration#getAppConfigurationEntry(String) JAAS application configuration name} that specifies which
          * login modules should be used to validate credentials.
          */
-        JAAS_LOGIN_CONFIG_NAME;
+        JAAS_LOGIN_CONFIG_NAME,
 
         /**
+         * The name of the source (and optionally the workspace in the source) where the "/jcr:system" branch should be stored.
+         * The format is "<code>name of workspace at name of source</code>", or simply "<code>name of source</code>" if the default
+         * workspace is to be used. If this option is not used, a transient in-memory source will be used.
+         * <p>
+         * Note that all leading and trailing whitespace is removed for both the source name and workspace name. Thus, a value of
+         * "<code>@</code>" implies a zero-length workspace name and zero-length source name.
+         * </p>
+         * <p>
+         * Also, any use of the '@' character in source and workspace names must be escaped with a preceding backslash.
+         * </p>
+         */
+        SYSTEM_SOURCE_NAME;
+
+        /**
          * Determine the option given the option name. This does more than {@link Option#valueOf(String)}, since this method first
          * tries to match the supplied string to the option's {@link Option#name() name}, then the uppercase version of the
          * supplied string to the option's name, and finally if the supplied string is a camel-case version of the name (e.g.,
@@ -180,7 +197,8 @@
     private final RepositoryConnectionFactory connectionFactory;
     private final RepositoryNodeTypeManager repositoryTypeManager;
     private final Map<Option, String> options;
-    private final RepositorySource systemSource;
+    private final String systemSourceName;
+    private final String systemWorkspaceName;
     private final Projection systemSourceProjection;
     private final FederatedRepositorySource federatedSource;
     private final Observer observer;
@@ -268,20 +286,63 @@
         // Initialize the observer, which receives events from all repository sources ...
         this.observer = new RepositoryObserver();
 
-        // Create the in-memory repository source that we'll use for the "/jcr:system" branch in this repository.
-        // All workspaces will be set up with a federation connector that projects this system repository into
-        // "/jcr:system", and all other content is projected to the repositories actual source (and workspace).
-        // (The federation connector refers to this configuration as an "offset mirror".)
-        InMemoryRepositorySource systemSource = new InMemoryRepositorySource();
-        String systemWorkspaceName = "jcr:system";
-        String systemSourceName = "jcr:system source";
-        systemSource.setName(systemSourceName);
-        systemSource.setDefaultWorkspaceName(systemWorkspaceName);
-        this.systemSource = systemSource;
-        this.connectionFactory = new ConnectionFactoryWithSystem(connectionFactory, this.systemSource);
+        // Set up the system source ...
+        String systemSourceNameValue = this.options.get(Option.SYSTEM_SOURCE_NAME);
+        String systemSourceName = null;
+        String systemWorkspaceName = null;
+        RepositoryConnectionFactory connectionFactoryWithSystem = connectionFactory;
+        if (systemSourceNameValue != null) {
+            // Find an existing source with the given name containing the named workspace ...
+            try {
+                SourceWorkspacePair pair = new SourceWorkspacePair(systemSourceNameValue);
+                // Look for a source with the given name ...
+                RepositoryConnection conn = connectionFactory.createConnection(pair.getSourceName());
+                if (conn != null) {
+                    // We found a source that we can use for the system ...
+                    systemSourceName = pair.getSourceName();
+                    if (pair.getWorkspaceName() != null) {
+                        // There should be the named workspace ...
+                        Graph temp = Graph.create(conn, executionContext);
+                        temp.useWorkspace(pair.getWorkspaceName());
+                        // found it ...
+                        systemWorkspaceName = pair.getWorkspaceName();
+                    }
+                } else {
+                    I18n msg = JcrI18n.systemSourceNameOptionValueDoesNotReferenceExistingSource;
+                    Logger.getLogger(getClass()).warn(msg, systemSourceNameValue, systemSourceName);
+                }
+            } catch (InvalidWorkspaceException e) {
+                // Bad workspace name ...
+                systemSourceName = null;
+                I18n msg = JcrI18n.systemSourceNameOptionValueDoesNotReferenceValidWorkspace;
+                Logger.getLogger(getClass()).warn(msg, systemSourceNameValue, systemSourceName);
+            } catch (IllegalArgumentException e) {
+                // Invalid format ...
+                systemSourceName = null;
+                I18n msg = JcrI18n.systemSourceNameOptionValueIsNotFormattedCorrectly;
+                Logger.getLogger(getClass()).warn(msg, systemSourceNameValue);
+            }
+        }
+        if (systemSourceName == null) {
+            // Create the in-memory repository source that we'll use for the "/jcr:system" branch in this repository.
+            // All workspaces will be set up with a federation connector that projects this system repository into
+            // "/jcr:system", and all other content is projected to the repositories actual source (and workspace).
+            // (The federation connector refers to this configuration as an "offset mirror".)
+            systemWorkspaceName = "jcr:system";
+            systemSourceName = "jcr:system source";
+            InMemoryRepositorySource transientSystemSource = new InMemoryRepositorySource();
+            transientSystemSource.setName(systemSourceName);
+            transientSystemSource.setDefaultWorkspaceName(systemWorkspaceName);
+            connectionFactoryWithSystem = new ConnectionFactoryWithSystem(connectionFactory, transientSystemSource);
+        }
+        this.systemWorkspaceName = systemWorkspaceName;
+        this.systemSourceName = systemSourceName;
+        this.connectionFactory = connectionFactoryWithSystem;
+        assert this.systemSourceName != null;
+        assert this.connectionFactory != null;
 
         // Set up the "/jcr:system" branch ...
-        Graph systemGraph = Graph.create(this.systemSource, executionContext);
+        Graph systemGraph = Graph.create(this.systemSourceName, this.connectionFactory, executionContext);
         systemGraph.useWorkspace(systemWorkspaceName);
         initializeSystemContent(systemGraph);
         this.sourceName = repositorySourceName;
@@ -353,8 +414,15 @@
     }
 
     Graph createSystemGraph() {
+        assert this.systemSourceName != null;
+        assert this.connectionFactory != null;
+        assert this.executionContext != null;
         // The default workspace should be the system workspace ...
-        return Graph.create(this.systemSource, this.executionContext);
+        Graph result = Graph.create(this.systemSourceName, this.connectionFactory, this.executionContext);
+        if (this.systemWorkspaceName != null) {
+            result.useWorkspace(systemWorkspaceName);
+        }
+        return result;
     }
 
     /**
@@ -462,7 +530,8 @@
      *         <li>provides neither a <code>getLoginContext()</code> nor a <code>getAccessControlContext()</code> method and is
      *         not an instance of {@code SimpleCredentials}.</li>
      *         <li>provides a <code>getLoginContext()</code> method that doesn't return a {@link LoginContext}.
-     *         <li>provides a <code>getLoginContext()</code> method that returns a <code>null</code> {@link LoginContext}.
+     *         <li>provides a <code>getLoginContext()</code> method that returns a <code>
+     *         null</code> {@link LoginContext}.
      *         <li>does not provide a <code>getLoginContext()</code> method, but provides a <code>getAccessControlContext()</code>
      *         method that doesn't return an {@link AccessControlContext}.
      *         <li>does not provide a <code>getLoginContext()</code> method, but provides a <code>getAccessControlContext()</code>
@@ -662,6 +731,72 @@
         }
     }
 
+    @Immutable
+    protected static class SourceWorkspacePair {
+        private final String sourceName;
+        private final String workspaceName;
+
+        protected SourceWorkspacePair( String sourceAndWorkspaceName ) {
+            assert sourceAndWorkspaceName != null;
+            sourceAndWorkspaceName = sourceAndWorkspaceName.trim();
+            assert sourceAndWorkspaceName.length() != 0;
+            sourceAndWorkspaceName = sourceAndWorkspaceName.trim();
+            // Look for the first '@' not preceded by a '\' ...
+            int maxIndex = sourceAndWorkspaceName.length() - 1;
+            int index = sourceAndWorkspaceName.indexOf('@');
+            while (index > 0 && index < maxIndex && sourceAndWorkspaceName.charAt(index - 1) == '\\') {
+                index = sourceAndWorkspaceName.indexOf('@', index + 1);
+            }
+            if (index > 0) {
+                // There is a workspace and source name ...
+                workspaceName = sourceAndWorkspaceName.substring(0, index).trim().replaceAll("\\\\@", "@");
+                if (index < maxIndex) sourceName = sourceAndWorkspaceName.substring(index + 1).trim().replaceAll("\\\\@", "@");
+                else throw new IllegalArgumentException("The source name is invalid");
+            } else if (index == 0) {
+                // The '@' was used, but the workspace is empty
+                if (sourceAndWorkspaceName.length() == 1) {
+                    sourceName = "";
+                } else {
+                    sourceName = sourceAndWorkspaceName.substring(1).trim().replaceAll("\\\\@", "@");
+                }
+                workspaceName = "";
+            } else {
+                // There is just a source name...
+                workspaceName = null;
+                sourceName = sourceAndWorkspaceName.replaceAll("\\\\@", "@");
+            }
+            assert this.sourceName != null;
+        }
+
+        /**
+         * @return sourceName
+         */
+        public String getSourceName() {
+            return sourceName;
+        }
+
+        /**
+         * @return workspaceName
+         */
+        public String getWorkspaceName() {
+            return workspaceName;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            if (sourceName == null) return "";
+            if (workspaceName != null) {
+                return workspaceName + '@' + sourceName;
+            }
+            return sourceName;
+        }
+    }
+
     protected class RepositoryObserver implements Observer {
         /**
          * {@inheritDoc}

Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-07-21 20:53:05 UTC (rev 1123)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties	2009-07-22 17:32:10 UTC (rev 1124)
@@ -97,6 +97,10 @@
 typeNotFound=No type exists with name "{0}"
 supertypeNotFound=Could not find type "{0}" which is a required supertype of type "{1}"
 
+systemSourceNameOptionValueDoesNotReferenceExistingSource = The JCR Repository 'SYSTEM_SOURCE_NAME' option value "{0}" references an invalid or non-existant source "{1}"
+systemSourceNameOptionValueDoesNotReferenceValidWorkspace = The JCR Repository 'SYSTEM_SOURCE_NAME' option value "{0}" references an invalid or non-existant workspace in the "{1}" source
+systemSourceNameOptionValueIsNotFormattedCorrectly = The JCR Repository 'SYSTEM_SOURCE_NAME' option value "{0}" is invalid or improperly formatted
+
 noNegativeDepth=Depth parameter ({0}) cannot be negative
 tooDeep=Depth parameter ({0}) cannot be greater than the result of getDepth() for this node
 

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-07-21 20:53:05 UTC (rev 1123)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java	2009-07-22 17:32:10 UTC (rev 1124)
@@ -350,6 +350,47 @@
         assertThat(session.getNamespaceURI("xmlns"), is("http://www.w3.org/2000/xmlns/"));
     }
 
+    @Test
+    public void shouldParseSourceNameOptionWithOnlySourceName() {
+        assertSourceWorkspacePair("source name", "source name", null);
+        assertSourceWorkspacePair(" \t source name \n ", "source name", null);
+        assertSourceWorkspacePair(" \t source \\@ name \n ", "source @ name", null);
+    }
+
+    @Test
+    public void shouldParseSourceNameOptionWithWorkspaceNameAndSourceName() {
+        assertSourceWorkspacePair("workspace at source", "source", "workspace");
+        assertSourceWorkspacePair(" \t workspace\t@ \t source \t \n", "source", "workspace");
+        assertSourceWorkspacePair(" \t workspace\\@ name \t@ \t source\\@name \t \n", "source at name", "workspace@ name");
+        assertSourceWorkspacePair("@ \t source \\@ name \n ", "source @ name", "");
+        assertSourceWorkspacePair("   @ \t source \\@ name \n ", "source @ name", "");
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldFailToParseSourceNameOptionThatHasZeroLengthSource() {
+        new JcrRepository.SourceWorkspacePair("workspace@");
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldFailToParseSourceNameOptionThatHasBlankSource() {
+        new JcrRepository.SourceWorkspacePair("workspace@  ");
+    }
+
+    @Test
+    public void shouldParseSourceNameOptionThatHasBlankSourceAndWorkspace() {
+        assertSourceWorkspacePair("@", "", "");
+        assertSourceWorkspacePair(" @ ", "", "");
+    }
+
+    protected void assertSourceWorkspacePair( String value,
+                                              String expectedSourceName,
+                                              String expectedWorkspaceName ) {
+        JcrRepository.SourceWorkspacePair pair = new JcrRepository.SourceWorkspacePair(value);
+        assertThat(pair, is(notNullValue()));
+        assertThat(pair.getSourceName(), is(expectedSourceName));
+        assertThat(pair.getWorkspaceName(), is(expectedWorkspaceName));
+    }
+
     protected JcrSession createSession() throws Exception {
         LoginContext login = new LoginContext("dna-jcr", new UserPasswordCallbackHandler("superuser", "superuser".toCharArray()));
         login.login();



More information about the dna-commits mailing list