You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2005/11/24 17:09:00 UTC

svn commit: r348763 - in /incubator/jackrabbit/trunk/jackrabbit/src: main/config/ main/java/org/apache/jackrabbit/core/config/ test/java/org/apache/jackrabbit/core/config/

Author: stefan
Date: Thu Nov 24 08:08:43 2005
New Revision: 348763

URL: http://svn.apache.org/viewcvs?rev=348763&view=rev
Log:
added optional attribute (configRootPath) to <workspaces/> element in repository.xml;
if this attribute is present the virtual file system of the repository is used to read/write workspace configurations rather than the physical workspace home directory on disk (default behaviour)

Modified:
    incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd
    incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml?rev=348763&r1=348762&r2=348763&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml Thu Nov 24 08:08:43 2005
@@ -17,7 +17,9 @@
             in the JAAS config and the access manager
 
             a Workspaces element that specifies to the location of
-            workspaces root directory and the name of default workspace
+            workspaces root directory, the name of default workspace
+            and optionally the workspace configuration root directory
+            within the virtual repository file system
 
             a Workspace element that is used as a workspace configuration
             template; it is used to create the initial workspace if there's
@@ -41,7 +43,7 @@
 
     <!--
         the Security element specifies the name (appName attribute)
-        of the JAAS configuration app-entry for this repository. 
+        of the JAAS configuration app-entry for this repository.
 
         it also specifies the access manager to be used (AccessManager element).
     -->
@@ -66,7 +68,7 @@
       name CDATA #REQUIRED
       value CDATA #REQUIRED>
 
-     <!--
+    <!--
         the LoginModule element optionally specifies a JAAS login module to
         authenticate users. This feature allows the use of Jackrabbit in a
         non-JAAS environment.
@@ -76,17 +78,26 @@
       class CDATA #REQUIRED>
 
     <!--
-        the Workspaces element specifies the workspaces root directory
-        (rootPath attribute) and the name of the default workspace
-        (defaultWorkspace attribute).
+        the Workspaces element specifies the physical workspaces root directory
+        (rootPath attribute), the name of the default workspace
+        (defaultWorkspace attribute) and optionally the workspace configuration
+        root directory within the virtual repository file system (configRootPath
+        attribute).
 
         individual workspaces are configured through individual workspace.xml
-        files located in a subfolder each of the workspaces root directory.
+        files located in a subfolder each of either
+
+        a) the physical workspaces root directory
+
+        or, if configRootPath had been specified,
+
+        b) the configuration root directory within the virtual repository file system.
     -->
     <!ELEMENT Workspaces EMPTY>
     <!ATTLIST Workspaces
-      rootPath CDATA #REQUIRED
-      defaultWorkspace CDATA #REQUIRED>
+        rootPath CDATA #REQUIRED
+        defaultWorkspace CDATA #REQUIRED
+        configRootPath CDATA #IMPLIED>
 
     <!--
         the Workspace element serves as a workspace configuration template;

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java?rev=348763&r1=348762&r2=348763&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java Thu Nov 24 08:08:43 2005
@@ -97,6 +97,9 @@
     /** Name of the root path configuration attribute. */
     public static final String ROOT_PATH_ATTRIBUTE = "rootPath";
 
+    /** Name of the config root path configuration attribute. */
+    public static final String CONFIG_ROOT_PATH_ATTRIBUTE = "configRootPath";
+
     /** Name of the default workspace configuration attribute. */
     public static final String DEFAULT_WORKSPACE_ATTRIBUTE =
         "defaultWorkspace";
@@ -218,6 +221,10 @@
         Element workspaces = getElement(root, WORKSPACES_ELEMENT);
         String workspaceDirectory = replaceVariables(
                 getAttribute(workspaces, ROOT_PATH_ATTRIBUTE));
+
+        String workspaceConfigDirectory =
+                getAttribute(workspaces, CONFIG_ROOT_PATH_ATTRIBUTE, null);
+
         String defaultWorkspace = replaceVariables(
                 getAttribute(workspaces, DEFAULT_WORKSPACE_ATTRIBUTE));
 
@@ -228,7 +235,8 @@
         VersioningConfig vc = parseVersioningConfig(root);
 
         return new RepositoryConfig(home, appName, amc, lmc, fsc,
-                workspaceDirectory, defaultWorkspace, template, vc, this);
+                workspaceDirectory, workspaceConfigDirectory, defaultWorkspace,
+                template, vc, this);
     }
 
     /**

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java?rev=348763&r1=348762&r2=348763&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java Thu Nov 24 08:08:43 2005
@@ -20,6 +20,12 @@
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.InputStream;
+import java.io.Reader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Writer;
+import java.io.OutputStreamWriter;
+import java.io.FileWriter;
 import java.net.URI;
 import java.util.Collection;
 import java.util.HashMap;
@@ -35,6 +41,8 @@
 import javax.xml.transform.stream.StreamResult;
 
 import org.apache.jackrabbit.core.fs.FileSystem;
+import org.apache.jackrabbit.core.fs.FileSystemException;
+import org.apache.jackrabbit.core.fs.FileSystemPathUtil;
 import org.w3c.dom.Element;
 import org.xml.sax.InputSource;
 
@@ -171,13 +179,22 @@
     private final ConfigurationParser parser;
 
     /**
-     * Workspace root directory. This directory contains a subdirectory for
-     * each workspace in this repository. Each workspace is configured by
-     * a workspace configuration file contained in the workspace subdirectory.
+     * Workspace physical root directory. This directory contains a subdirectory
+     * for each workspace in this repository, i.e. the physical workspace home
+     * directory. Each workspace is configured by a workspace configuration file
+     * either contained in the workspace home directory or, optionally, located
+     * in a subdirectory of {@link #workspaceConfigDirectory} within the
+     * repository file system if such has been specified.
      */
     private final String workspaceDirectory;
 
     /**
+     * Path to workspace configuration root directory within the
+     * repository file system or null if none was specified.
+     */
+    private final String workspaceConfigDirectory;
+
+    /**
      * The workspace configuration template. Used in creating new workspace
      * configuration files.
      */
@@ -198,14 +215,16 @@
      * @param lmc login module configuration (can be <code>null</code>)
      * @param fsc file system configuration
      * @param workspaceDirectory workspace root directory
+     * @param workspaceConfigDirectory optional workspace configuration directory
      * @param defaultWorkspace name of the default workspace
      * @param vc versioning configuration
      * @param parser the ConfigurationParser that servers as config factory
      */
     RepositoryConfig(String home, String name,
             AccessManagerConfig amc, LoginModuleConfig lmc, FileSystemConfig fsc,
-            String workspaceDirectory, String defaultWorkspace,
-            Element template, VersioningConfig vc, ConfigurationParser parser) {
+            String workspaceDirectory, String workspaceConfigDirectory,
+            String defaultWorkspace, Element template, VersioningConfig vc,
+            ConfigurationParser parser) {
         this.workspaces = new HashMap();
         this.home = home;
         this.name = name;
@@ -213,6 +232,7 @@
         this.lmc = lmc;
         this.fsc = fsc;
         this.workspaceDirectory = workspaceDirectory;
+        this.workspaceConfigDirectory = workspaceConfigDirectory;
         this.defaultWorkspace = defaultWorkspace;
         this.template = template;
         this.vc = vc;
@@ -230,24 +250,54 @@
         fsc.init();
         vc.init();
 
-        // Get the workspace root directory (create it if not found)
+        // Get the physical workspace root directory (create it if not found)
         File directory = new File(workspaceDirectory);
         if (!directory.exists()) {
             directory.mkdirs();
         }
 
         // Get all workspace subdirectories
-        File[] files = directory.listFiles();
-        if (files == null) {
-            throw new ConfigurationException(
-                    "Invalid workspace root directory: " + workspaceDirectory);
-        }
+        if (workspaceConfigDirectory != null) {
+            // a configuration directoy had been specified; search for
+            // workspace configurations in virtual repository file system
+            // rather than in physical workspace root directory on disk
+            FileSystem fs = fsc.getFileSystem();
+            try {
+                if (!fs.exists(workspaceConfigDirectory)) {
+                    fs.createFolder(workspaceConfigDirectory);
+                } else {
+                    String[] dirNames = fs.listFolders(workspaceConfigDirectory);
+                    for (int i = 0; i < dirNames.length; i++) {
+                        String configDir = workspaceConfigDirectory
+                                + FileSystem.SEPARATOR + dirNames[i];
+                        WorkspaceConfig wc = loadWorkspaceConfig(fs, configDir);
+                        if (wc != null) {
+                            wc.init();
+                            addWorkspaceConfig(wc);
+                        }
+                    }
 
-        for (int i = 0; i < files.length; i++) {
-            WorkspaceConfig wc = loadWorkspaceConfig(files[i]);
-            if (wc != null) {
-                wc.init();
-                addWorkspaceConfig(wc);
+                }
+            } catch (FileSystemException e) {
+                throw new ConfigurationException(
+                        "error while loading workspace configurations from path "
+                        + workspaceConfigDirectory, e);
+            }
+        } else {
+            // search for workspace configurations in physical workspace root
+            // directory on disk
+            File[] files = directory.listFiles();
+            if (files == null) {
+                throw new ConfigurationException(
+                        "Invalid workspace root directory: " + workspaceDirectory);
+            }
+
+            for (int i = 0; i < files.length; i++) {
+                WorkspaceConfig wc = loadWorkspaceConfig(files[i]);
+                if (wc != null) {
+                    wc.init();
+                    addWorkspaceConfig(wc);
+                }
             }
         }
 
@@ -262,18 +312,18 @@
     }
 
     /**
-     * Attempts to load a workspace configuration from the given workspace
-     * subdirectory. If the directory contains a valid workspace configuration
-     * file, then the configuration is parsed and returned as a workspace
-     * configuration object. The returned configuration object has not been
-     * initialized.
+     * Attempts to load a workspace configuration from the given physical
+     * workspace subdirectory. If the directory contains a valid workspace
+     * configuration file, then the configuration is parsed and returned as a
+     * workspace configuration object. The returned configuration object has not
+     * been initialized.
      * <p>
      * This method returns <code>null</code>, if the given directory does
      * not exist or does not contain a workspace configuration file. If an
      * invalid configuration file is found, then a
      * {@link ConfigurationException ConfigurationException} is thrown.
      *
-     * @param directory workspace configuration directory
+     * @param directory physical workspace configuration directory on disk
      * @return workspace configuration
      * @throws ConfigurationException if the workspace configuration is invalid
      */
@@ -296,6 +346,60 @@
     }
 
     /**
+     * Attempts to load a workspace configuration from the given workspace
+     * subdirectory within the repository file system. If the directory contains
+     * a valid workspace configuration file, then the configuration is parsed
+     * and returned as a workspace configuration object. The returned
+     * configuration object has not been initialized.
+     * <p>
+     * This method returns <code>null</code>, if the given directory does
+     * not exist or does not contain a workspace configuration file. If an
+     * invalid configuration file is found, then a
+     * {@link ConfigurationException ConfigurationException} is thrown.
+     *
+     * @param fs virtual file system where to look for the configuration file
+     * @param configDir workspace configuration directory in virtual file system
+     * @return workspace configuration
+     * @throws ConfigurationException if the workspace configuration is invalid
+     */
+    private WorkspaceConfig loadWorkspaceConfig(FileSystem fs, String configDir)
+            throws ConfigurationException {
+        Reader configReader = null;
+        try {
+            String configPath = configDir + FileSystem.SEPARATOR + WORKSPACE_XML;
+            if (!fs.exists(configPath)) {
+                // no configuration file in this directory
+                return null;
+            }
+
+            configReader = new InputStreamReader(fs.getInputStream(configPath));
+            InputSource xml = new InputSource(configReader);
+            xml.setSystemId(configPath);
+
+            // the physical workspace home directory (TODO encode name?)
+            File homeDir = new File(
+                    workspaceDirectory, FileSystemPathUtil.getName(configDir));
+            if (!homeDir.exists()) {
+                homeDir.mkdir();
+            }
+            Properties variables = new Properties();
+            variables.setProperty(
+                    ConfigurationParser.WORKSPACE_HOME_VARIABLE,
+                    homeDir.getPath());
+            ConfigurationParser localParser = parser.createSubParser(variables);
+            return localParser.parseWorkspaceConfig(xml);
+        } catch (FileSystemException e) {
+            throw new ConfigurationException("Failed to load workspace configuration", e);
+        } finally {
+            if (configReader != null) {
+                try {
+                    configReader.close();
+                } catch (IOException ignore) {}
+            }
+        }
+    }
+
+    /**
      * Adds the given workspace configuration to the repository.
      *
      * @param wc workspace configuration
@@ -328,10 +432,12 @@
      */
     public synchronized WorkspaceConfig createWorkspaceConfig(String name)
             throws ConfigurationException {
-        // The workspace directory (TODO encode name?)
+
+        // The physical workspace home directory on disk (TODO encode name?)
         File directory = new File(workspaceDirectory, name);
 
-        // Create the directory, fail if it exists or cannot be created
+        // Create the physical workspace directory, fail if it exists
+        // or cannot be created
         if (!directory.mkdir()) {
             if (directory.exists()) {
                 throw new ConfigurationException(
@@ -342,28 +448,70 @@
             }
         }
 
-        // Create the workspace.xml file using the configuration template.
+        Writer configWriter;
+
+        // get a writer for the workspace configuration file
+        if (workspaceConfigDirectory != null) {
+            // a configuration directoy had been specified; create workspace
+            // configuration in virtual repository file system rather than
+            // on disk
+            FileSystem fs = fsc.getFileSystem();
+            String configDir = workspaceConfigDirectory
+                    + FileSystem.SEPARATOR + name;
+            String configFile = configDir + FileSystem.SEPARATOR + WORKSPACE_XML;
+            try {
+                // Create the directory
+                fs.createFolder(configDir);
+                configWriter = new OutputStreamWriter(
+                        fs.getOutputStream(configFile));
+            } catch (FileSystemException e) {
+                throw new ConfigurationException(
+                        "failed to create workspace configuration at path "
+                        + configFile, e);
+            }
+        } else {
+            File file = new File(directory, WORKSPACE_XML);
+            try {
+                configWriter = new FileWriter(file);
+            } catch (IOException e) {
+                throw new ConfigurationException(
+                        "failed to create workspace configuration at path "
+                        + file.getPath(), e);
+            }
+        }
+
+        // Create the workspace.xml file using the configuration template and
+        // the configuration writer.
         try {
             template.setAttribute("name", name);
-            File xml = new File(directory, WORKSPACE_XML);
 
             TransformerFactory factory = TransformerFactory.newInstance();
             Transformer transformer = factory.newTransformer();
             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-            // NOTE: The StreamResult instance below is constructed using the
-            // file path instead of the file object. See JCR-222 for details.
             transformer.transform(
-                    new DOMSource(template), new StreamResult(xml.getPath()));
+                    new DOMSource(template), new StreamResult(configWriter));
         } catch (TransformerConfigurationException e) {
             throw new ConfigurationException(
                     "Cannot create a workspace configuration writer", e);
         } catch (TransformerException e) {
             throw new ConfigurationException(
                     "Cannot create a workspace configuration file", e);
+        } finally {
+            try {
+                configWriter.close();
+            } catch (IOException ignore) {}
         }
 
         // Load the created workspace configuration.
-        WorkspaceConfig wc = loadWorkspaceConfig(directory);
+        WorkspaceConfig wc;
+        if (workspaceConfigDirectory != null) {
+            FileSystem fs = fsc.getFileSystem();
+            String configDir = workspaceConfigDirectory
+                    + FileSystem.SEPARATOR + name;
+            wc = loadWorkspaceConfig(fs, configDir);
+        } else {
+            wc = loadWorkspaceConfig(directory);
+        }
         if (wc != null) {
             wc.init();
             addWorkspaceConfig(wc);

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd?rev=348763&r1=348762&r2=348763&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd Thu Nov 24 08:08:43 2005
@@ -29,43 +29,91 @@
         used by the repository to persist global state such as
         registered namespaces, custom node types, etc..
 
+        a Security element that specifies the name of the app-entry
+        in the JAAS config and the access manager
+
         a Workspaces element that specifies to the location of
-        workspaces root directory and the name of default workspace
+        workspaces root directory, the name of default workspace
+        and optionally the workspace configuration root directory
+        within the virtual repository file system
 
         a Workspace element that is used as a workspace configuration
         template; it is used to create the initial workspace if there's
         no workspace yet and for creating additional workspaces through
         the api
+
+        a SearchIndex element that is used for configuring per workspace
+        Indexing-related settings
+
+        a Versioning element that is used for configuring
+        versioning-related settings
 -->
-<!ELEMENT Repository (FileSystem,Workspaces,Workspace,Versioning)>
+<!ELEMENT Repository (FileSystem,Security,Workspaces,Workspace,Versioning)>
 
 <!--
     a virtual file system
 -->
 <!ELEMENT FileSystem (param*)>
 <!ATTLIST FileSystem
-    class CDATA #REQUIRED>
+  class CDATA #REQUIRED>
+
+<!--
+    the Security element specifies the name (appName attribute)
+    of the JAAS configuration app-entry for this repository.
+
+    it also specifies the access manager to be used (AccessManager element).
+-->
+<!ELEMENT Security (AccessManager, LoginModule?)>
+<!ATTLIST Security
+  appName CDATA #REQUIRED>
+
+<!--
+    the AccessManager element configures the access manager to be used by
+    this repository instance; the class attribute specifies the FQN of the
+    class implementing the AccessManager interface
+-->
+<!ELEMENT AccessManager (param*)>
+<!ATTLIST AccessManager
+  class CDATA #REQUIRED>
 
 <!--
     generic parameter (name/value pair)
 -->
 <!ELEMENT param EMPTY>
 <!ATTLIST param
-    name CDATA #REQUIRED
-    value CDATA #REQUIRED>
+  name CDATA #REQUIRED
+  value CDATA #REQUIRED>
+
+<!--
+    the LoginModule element optionally specifies a JAAS login module to
+    authenticate users. This feature allows the use of Jackrabbit in a
+    non-JAAS environment.
+-->
+<!ELEMENT LoginModule (param*)>
+<!ATTLIST LoginModule
+  class CDATA #REQUIRED>
 
 <!--
-    the Workspaces element specifies the workspaces root directory
-    (rootPath attribute) and the name of the default workspace
-    (defaultWorkspace attribute).
+    the Workspaces element specifies the physical workspaces root directory
+    (rootPath attribute), the name of the default workspace
+    (defaultWorkspace attribute) and optionally the workspace configuration
+    root directory within the virtual repository file system (configRootPath
+    attribute).
 
     individual workspaces are configured through individual workspace.xml
-    files located in a subfolder each of the workspaces root directory.
+    files located in a subfolder each of either
+
+    a) the physical workspaces root directory
+
+    or, if configRootPath had been specified,
+
+    b) the configuration root directory within the virtual repository file system.
 -->
 <!ELEMENT Workspaces EMPTY>
 <!ATTLIST Workspaces
     rootPath CDATA #REQUIRED
-    defaultWorkspace CDATA #REQUIRED>
+    defaultWorkspace CDATA #REQUIRED
+    configRootPath CDATA #IMPLIED>
 
 <!--
     the Workspace element serves as a workspace configuration template;
@@ -74,28 +122,31 @@
 -->
 <!ELEMENT Workspace (FileSystem,PersistenceManager,SearchIndex?)>
 <!ATTLIST Workspace
-    name CDATA #REQUIRED>
+  name CDATA #REQUIRED>
 
 <!--
     the PersistenceManager element configures the persistence manager
     to be used for the workspace; the class attribute specifies the
-    FQN of the class implementing PersistenceManager interface
+    FQN of the class implementing the PersistenceManager interface
 -->
 <!ELEMENT PersistenceManager (param*)>
 <!ATTLIST PersistenceManager
-    class CDATA #REQUIRED>
+  class CDATA #REQUIRED>
 
 <!--
     the SearchIndex element specifies the locaction of the search index
-    (used by the QueryManager)
+    (used by the QueryHandler); the class attribute specifies the
+    FQN of the class implementing the QueryHandler interface.
 -->
-<!ELEMENT SearchIndex (param*,FileSystem)>
+<!ELEMENT SearchIndex (param*,FileSystem?)>
+<!ATTLIST SearchIndex
+  class CDATA #REQUIRED>
 
 <!--
-the Versioning element specifies the location and persistence manager
-that is used to store versions
-    -->
+    the Versioning element configures the persistence manager
+    to be used for persisting version state
+-->
 <!ELEMENT Versioning (FileSystem, PersistenceManager)>
 <!ATTLIST Versioning
-rootPath CDATA #REQUIRED
+  rootPath CDATA #REQUIRED
 >

Modified: incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml?rev=348763&r1=348762&r2=348763&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml Thu Nov 24 08:08:43 2005
@@ -17,7 +17,9 @@
             in the JAAS config and the access manager
 
             a Workspaces element that specifies to the location of
-            workspaces root directory and the name of default workspace
+            workspaces root directory, the name of default workspace
+            and optionally the workspace configuration root directory
+            within the virtual repository file system
 
             a Workspace element that is used as a workspace configuration
             template; it is used to create the initial workspace if there's
@@ -76,17 +78,26 @@
       class CDATA #REQUIRED>
 
     <!--
-        the Workspaces element specifies the workspaces root directory
-        (rootPath attribute) and the name of the default workspace
-        (defaultWorkspace attribute).
+        the Workspaces element specifies the physical workspaces root directory
+        (rootPath attribute), the name of the default workspace
+        (defaultWorkspace attribute) and optionally the workspace configuration
+        root directory within the virtual repository file system (configRootPath
+        attribute).
 
         individual workspaces are configured through individual workspace.xml
-        files located in a subfolder each of the workspaces root directory.
+        files located in a subfolder each of either
+
+        a) the physical workspaces root directory
+
+        or, if configRootPath had been specified,
+
+        b) the configuration root directory within the virtual repository file system.
     -->
     <!ELEMENT Workspaces EMPTY>
     <!ATTLIST Workspaces
-      rootPath CDATA #REQUIRED
-      defaultWorkspace CDATA #REQUIRED>
+        rootPath CDATA #REQUIRED
+        defaultWorkspace CDATA #REQUIRED
+        configRootPath CDATA #IMPLIED>
 
     <!--
         the Workspace element serves as a workspace configuration template;
@@ -111,7 +122,7 @@
         (used by the QueryHandler); the class attribute specifies the
         FQN of the class implementing the QueryHandler interface.
     -->
-    <!ELEMENT SearchIndex (param*,FileSystem)>
+    <!ELEMENT SearchIndex (param*,FileSystem?)>
     <!ATTLIST SearchIndex
       class CDATA #REQUIRED>