You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2009/05/20 18:19:10 UTC

svn commit: r776753 - in /jackrabbit/branches/1.x: ./ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/ jackr...

Author: jukka
Date: Wed May 20 16:19:10 2009
New Revision: 776753

URL: http://svn.apache.org/viewvc?rev=776753&view=rev
Log:
1.x: Merged revisions 775833, 776256, 776356, 776362, 776373, 776310, 7776313 and 776332 (JCR-442 and JCR-2119)

Added:
    jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java
      - copied, changed from r775833, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java
    jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java
      - copied, changed from r775833, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java
Modified:
    jackrabbit/branches/1.x/   (props changed)
    jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
    jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java
    jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
    jackrabbit/branches/1.x/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java
    jackrabbit/branches/1.x/jackrabbit-standalone/src/main/java/org/apache/jackrabbit/standalone/Main.java

Propchange: jackrabbit/branches/1.x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed May 20 16:19:10 2009
@@ -1 +1 @@
-/jackrabbit/trunk:770143-773197,773525-773554,773584,773588,773828,773835-775756,775836,775840,775981,775986,776036,776321-776322,776357,776650-776693,776737
+/jackrabbit/trunk:770143-773197,773525-773554,773584,773588,773828,773835-775756,775833,775836,775840,775981,775986,776036,776256,776310,776313,776321-776322,776332,776356-776357,776362,776373,776650-776693,776737

Copied: jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java (from r775833, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java)
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java?p2=jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java&p1=jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java&r1=775833&r2=776753&rev=776753&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java (original)
+++ jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryCopier.java Wed May 20 16:19:10 2009
@@ -17,20 +17,15 @@
 package org.apache.jackrabbit.core;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
 
 import javax.jcr.NamespaceRegistry;
 import javax.jcr.RepositoryException;
 
-import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
 import org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
 import org.apache.jackrabbit.core.nodetype.NodeTypeDef;
@@ -39,125 +34,171 @@
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.version.VersionManagerImpl;
 import org.apache.jackrabbit.spi.Name;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+/**
+ * Tool for backing up or migrating the entire contents (workspaces,
+ * version histories, namespaces, node types, etc.) of a repository to
+ * a new repository. The target repository (if it exists) is overwritten.
+ *
+ * @since Apache Jackrabbit 1.6
+ */
 public class RepositoryCopier {
 
-    private final RepositoryImpl source;
-
-    private final RepositoryImpl target;
-
+    /**
+     * Logger instance
+     */
+    private static final Logger logger =
+        LoggerFactory.getLogger(RepositoryCopier.class);
+
+    /**
+     * Source repository configuration
+     */
+    private final RepositoryConfig sourceConfig;
+
+    /**
+     * Target repository configuration
+     */
+    private final RepositoryConfig targetConfig;
+
+    /**
+     * Creates a tool for copying the full contents of the source repository.
+     * The given source repository directory is expected to contain the
+     * repository configuration as a <code>repository.xml</code> file.
+     * The target repository directory should not already exist. It will be
+     * automatically created with default repository configuration.
+     *
+     * @param source source repository directory
+     * @param target target repository directory
+     * @throws RepositoryException if the repositories can not be accessed
+     * @throws IOException if the target repository can not be initialized
+     */
     public RepositoryCopier(File source, File target)
             throws RepositoryException, IOException {
-        if (!source.isDirectory()) {
-            throw new RepositoryException("Not a directory: " + source);
-        }
+        this(RepositoryConfig.create(source), RepositoryConfig.install(target));
+    }
 
-        File sx = new File(source, "repository.xml");
-        if (!sx.isFile()) {
-            throw new RepositoryException(
-                    "Not a repository directory: " + source);
-        }
+    /**
+     * Creates a tool for copying the full contents of the source repository
+     * to the given target repository. Any existing content in the target
+     * repository will be overwritten.
+     *
+     * @param source source repository configuration
+     * @param target target repository configuration
+     * @throws RepositoryException if the repositories can not be accessed
+     */
+    public RepositoryCopier(RepositoryConfig source, RepositoryConfig target)
+            throws RepositoryException {
+        sourceConfig = source;
+        targetConfig = target;
+    }
 
-        if (target.exists()) {
-            throw new RepositoryException("Target directory exists: " + target);
-        }
-        target.mkdirs();
+    /**
+     * Copies the full content from the source to the target repository.
+     * Note that both the source and the target repository must be closed
+     * during the copy operation as this method requires exclusive access
+     * to the repositories.
+     *
+     * @throws RepositoryException if the copy operation fails
+     */
+    public void copy() throws RepositoryException {
+        logger.info(
+                "Copying repository content from {} to {}",
+                sourceConfig.getHomeDir(), targetConfig.getHomeDir());
 
-        File tx = new File(target, "repository.xml");
-        OutputStream output = new FileOutputStream(tx);
+        RepositoryImpl source = RepositoryImpl.create(sourceConfig);
         try {
-            InputStream input =
-                RepositoryImpl.class.getResourceAsStream("repository.xml");
+            RepositoryImpl target = RepositoryImpl.create(targetConfig);
             try {
-                IOUtils.copy(input, output);
+                copyNamespaces(
+                        source.getNamespaceRegistry(),
+                        target.getNamespaceRegistry());
+                copyNodeTypes(
+                        source.getNodeTypeRegistry(),
+                        target.getNodeTypeRegistry());
+                copyVersionStore(
+                        source.getVersionManagerImpl(),
+                        target.getVersionManagerImpl());
+                copyWorkspaces(source, target);
+            } catch (InvalidNodeTypeDefException e) {
+                throw new RepositoryException("Failed to copy node types", e);
+            } catch (ItemStateException e) {
+                throw new RepositoryException("Failed to copy item states", e);
             } finally {
-                input.close();
+                target.shutdown();
+            }
+
+            // Remove index directories to force re-indexing on next startup
+            // TODO: There should be a cleaner way to do this
+            File targetDir = new File(targetConfig.getHomeDir());
+            File repoDir = new File(targetDir, "repository");
+            FileUtils.deleteQuietly(new File(repoDir, "index"));
+            File[] workspaces = new File(targetDir, "workspaces").listFiles();
+            for (int i = 0; workspaces != null && i < workspaces.length; i++) {
+                FileUtils.deleteQuietly(new File(workspaces[i], "index"));
             }
         } finally {
-            output.close();
+            source.shutdown();
         }
-
-        this.source = RepositoryImpl.create(
-                RepositoryConfig.create(sx.getPath(), source.getPath()));
-        this.target = RepositoryImpl.create(
-                RepositoryConfig.create(tx.getPath(), target.getPath()));
     }
 
-    public RepositoryCopier(RepositoryConfig source, RepositoryConfig target)
+    private void copyNamespaces(
+            NamespaceRegistry source, NamespaceRegistry target)
             throws RepositoryException {
-        this.source = RepositoryImpl.create(source);
-        this.target = RepositoryImpl.create(target);
-    }
+        logger.info("Copying registered namespaces");
 
-    public void copy() throws Exception {
-        System.out.println(
-                "Copying repository " + source.getConfig().getHomeDir());
-        copyNamespaces();
-        copyNodeTypes();
-        copyVersionStore();
-        copyWorkspaces();
-
-        target.shutdown();
-        source.shutdown();
-
-        System.out.println("  Done.");
-    }
-
-    private void copyNamespaces() throws RepositoryException {
-        NamespaceRegistry sourceRegistry = source.getNamespaceRegistry();
-        NamespaceRegistry targetRegistry = target.getNamespaceRegistry();
-        Set<String> existing = new HashSet<String>(Arrays.asList(
-                targetRegistry.getURIs()));
-        for (String uri : sourceRegistry.getURIs()) {
-            if (!existing.contains(uri)) {
+        Collection existing = Arrays.asList(target.getURIs());
+        String[] uris = source.getURIs();
+        for (int i = 0; i < uris.length; i++) {
+            if (!existing.contains(uris[i])) {
                 // TODO: what if the prefix is already taken?
-                targetRegistry.registerNamespace(
-                        sourceRegistry.getPrefix(uri), uri);
+                target.registerNamespace(source.getPrefix(uris[i]), uris[i]);
             }
         }
     }
 
-    private void copyNodeTypes()
+    private void copyNodeTypes(NodeTypeRegistry source, NodeTypeRegistry target)
             throws RepositoryException, InvalidNodeTypeDefException {
-        NodeTypeRegistry sourceRegistry = source.getNodeTypeRegistry();
-        NodeTypeRegistry targetRegistry = target.getNodeTypeRegistry();
-        Set<Name> existing = new HashSet<Name>(Arrays.asList(
-                targetRegistry.getRegisteredNodeTypes()));
-        Collection<NodeTypeDef> register = new ArrayList<NodeTypeDef>();
-        for (Name name : sourceRegistry.getRegisteredNodeTypes()) {
+        logger.info("Copying registered node types");
+
+        Collection existing = Arrays.asList(target.getRegisteredNodeTypes());
+        Collection register = new ArrayList();
+        Name[] names = source.getRegisteredNodeTypes();
+        for (int i = 0; i < names.length; i++) {
             // TODO: what about modified node types?
-            if (!existing.contains(name)) {
-                register.add(sourceRegistry.getNodeTypeDef(name));
+            if (!existing.contains(names[i])) {
+                register.add(source.getNodeTypeDef(names[i]));
             }
         }
-        targetRegistry.registerNodeTypes(register);
+        target.registerNodeTypes(register);
     }
 
-    private void copyVersionStore()
+    private void copyVersionStore(
+            VersionManagerImpl source, VersionManagerImpl target)
             throws RepositoryException, ItemStateException {
-        System.out.println("  Copying version histories...");
-        VersionManagerImpl sourceManager = source.getVersionManagerImpl();
-        VersionManagerImpl targetManager = target.getVersionManagerImpl();
+        logger.info("Copying version histories");
+
         PersistenceCopier copier = new PersistenceCopier(
-                sourceManager.getPersistenceManager(),
-                targetManager.getPersistenceManager());
+                source.getPersistenceManager(),
+                target.getPersistenceManager());
         copier.copy(RepositoryImpl.VERSION_STORAGE_NODE_ID);
     }
 
-    private void copyWorkspaces()
+    private void copyWorkspaces(RepositoryImpl source, RepositoryImpl target)
             throws RepositoryException, ItemStateException {
-        Set<String> existing = new HashSet<String>(Arrays.asList(
-                target.getWorkspaceNames()));
-        for (String name : source.getWorkspaceNames()) {
-            System.out.println("  Copying workspace " + name + "...");
+        Collection existing = Arrays.asList(target.getWorkspaceNames());
+        String[] names = source.getWorkspaceNames();
+        for (int i = 0; i < names.length; i++) {
+            logger.info("Copying workspace {}" , names[i]);
 
-            if (!existing.contains(name)) {
-                target.createWorkspace(name);
+            if (!existing.contains(names[i])) {
+                target.createWorkspace(names[i]);
             }
 
             PersistenceCopier copier = new PersistenceCopier(
-                    source.getWorkspaceInfo(name).getPersistenceManager(),
-                    target.getWorkspaceInfo(name).getPersistenceManager());
+                    source.getWorkspaceInfo(names[i]).getPersistenceManager(),
+                    target.getWorkspaceInfo(names[i]).getPersistenceManager());
             copier.excludeNode(RepositoryImpl.SYSTEM_ROOT_NODE_ID);
             copier.copy(RepositoryImpl.ROOT_NODE_ID);
         }

Modified: jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=776753&r1=776752&r2=776753&view=diff
==============================================================================
--- jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Wed May 20 16:19:10 2009
@@ -2321,4 +2321,9 @@
             return RepositoryImpl.this.getDataStore();
         }
     }
+
+    VersionManagerImpl getVersionManagerImpl() {
+        return vMgr;
+    }
+
 }

Modified: jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java?rev=776753&r1=776752&r2=776753&view=diff
==============================================================================
--- jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java (original)
+++ jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java Wed May 20 16:19:10 2009
@@ -17,10 +17,8 @@
 package org.apache.jackrabbit.core;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -34,7 +32,6 @@
 import javax.jcr.Value;
 
 import org.apache.commons.collections.map.ReferenceMap;
-import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.api.JackrabbitRepository;
 import org.apache.jackrabbit.core.config.ConfigurationException;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
@@ -58,11 +55,6 @@
         LoggerFactory.getLogger(TransientRepository.class);
 
     /**
-     * Resource path of the default repository configuration file.
-     */
-    private static final String DEFAULT_REPOSITORY_XML = "repository.xml";
-
-    /**
      * Name of the repository configuration file property.
      */
     private static final String CONF_PROPERTY =
@@ -92,7 +84,7 @@
     public interface RepositoryFactory {
 
         /**
-         * Creates and intializes a repository instance. The returned instance
+         * Creates and initializes a repository instance. The returned instance
          * will be used and finally shut down by the caller of this method.
          *
          * @return initialized repository instance
@@ -133,9 +125,8 @@
      * factory to initialize the underlying repository instances.
      *
      * @param factory repository factory
-     * @throws IOException if the static repository descriptors cannot be loaded
      */
-    public TransientRepository(RepositoryFactory factory) throws IOException {
+    public TransientRepository(RepositoryFactory factory) {
         this.factory = factory;
         this.repository = null;
         this.descriptors = new Properties();
@@ -143,12 +134,16 @@
         // FIXME: The current RepositoryImpl class does not allow static
         // access to the repository descriptors, so we need to load them
         // directly from the underlying property file.
-        InputStream in =
-            RepositoryImpl.class.getResourceAsStream("repository.properties");
         try {
-            descriptors.load(in);
-        } finally {
-            in.close();
+            InputStream in = RepositoryImpl.class.getResourceAsStream(
+                    "repository.properties");
+            try {
+                descriptors.load(in);
+            } finally {
+                in.close();
+            }
+        } catch (IOException e) {
+            logger.warn("Unable to load static repository descriptors", e);
         }
     }
 
@@ -187,6 +182,32 @@
     /**
      * Creates a transient repository proxy that will use the given repository
      * configuration file and home directory paths to initialize the underlying
+     * repository instances.
+     *
+     * @see #TransientRepository(File, File)
+     * @param config repository configuration file
+     * @param home repository home directory
+     * @throws IOException if the static repository descriptors cannot be loaded
+     */
+    public TransientRepository(String config, String home) {
+        this(new File(config), new File(home));
+    }
+
+    /**
+     * Creates a transient repository proxy based on the given repository
+     * home directory and the repository configuration file "repository.xml"
+     * contained in that directory.
+     *
+     * @since Apache Jackrabbit 1.6
+     * @param dir repository home directory
+     */
+    public TransientRepository(File dir) {
+        this(new File(dir, "repository.xml"), dir);
+    }
+
+    /**
+     * Creates a transient repository proxy that will use the given repository
+     * configuration file and home directory paths to initialize the underlying
      * repository instances. The repository configuration file is reloaded
      * whenever the repository is restarted, so it is safe to modify the
      * configuration when all sessions have been closed.
@@ -198,48 +219,22 @@
      * session starts. This is a convenience feature designed to reduce the
      * need for manual configuration.
      *
-     * @param config repository configuration file
-     * @param home repository home directory
-     * @throws IOException if the static repository descriptors cannot be loaded
+     * @since Apache Jackrabbit 1.6
+     * @param xml repository configuration file
+     * @param dir repository home directory
      */
-    public TransientRepository(final String config, final String home)
-            throws IOException {
+    public TransientRepository(final File xml, final File dir) {
         this(new RepositoryFactory() {
             public RepositoryImpl getRepository() throws RepositoryException {
                 try {
-                    // Make sure that the repository configuration file exists
-                    File configFile = new File(config);
-                    if (!configFile.exists()) {
-                        logger.info("Copying default configuration to " + config);
-                        OutputStream output = new FileOutputStream(configFile);
-                        try {
-                            InputStream input =
-                                TransientRepository.class.getResourceAsStream(
-                                        DEFAULT_REPOSITORY_XML);
-                            try {
-                                IOUtils.copy(input, output);
-                            } finally {
-                               input.close();
-                            }
-                        } finally {
-                            output.close();
-                        }
-                    }
-                    // Make sure that the repository home directory exists
-                    File homeDir = new File(home);
-                    if (!homeDir.exists()) {
-                        logger.info("Creating repository home directory " + home);
-                        homeDir.mkdirs();
-                    }
-                    // Load the configuration and create the repository
-                    RepositoryConfig rc = RepositoryConfig.create(config, home);
-                    return RepositoryImpl.create(rc);
+                    return RepositoryImpl.create(
+                            RepositoryConfig.install(xml, dir));
                 } catch (IOException e) {
                     throw new RepositoryException(
                             "Automatic repository configuration failed", e);
                 } catch (ConfigurationException e) {
                     throw new RepositoryException(
-                            "Invalid repository configuration: " + config, e);
+                            "Invalid repository configuration file: " + xml, e);
                 }
             }
         });

Modified: jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java?rev=776753&r1=776752&r2=776753&view=diff
==============================================================================
--- jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java (original)
+++ jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java Wed May 20 16:19:10 2009
@@ -16,7 +16,8 @@
  */
 package org.apache.jackrabbit.core.config;
 
-import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.IOUtils; 
+import org.apache.jackrabbit.core.RepositoryImpl;
 import org.apache.jackrabbit.core.data.DataStore;
 import org.apache.jackrabbit.core.data.DataStoreFactory;
 import org.apache.jackrabbit.core.fs.FileSystem;
@@ -41,10 +42,12 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.StringWriter;
@@ -71,10 +74,116 @@
     /** the default logger */
     private static Logger log = LoggerFactory.getLogger(RepositoryConfig.class);
 
+    /** Name of the default repository configuration file. */
+    private static final String REPOSITORY_XML = "repository.xml";
+
     /** Name of the workspace configuration file. */
     private static final String WORKSPACE_XML = "workspace.xml";
 
     /**
+     * Returns the configuration of a repository in a given repository
+     * directory. The repository configuration is read from a "repository.xml"
+     * file inside the repository directory.
+     * <p>
+     * The directory is created if it does not exist. If the repository
+     * configuration file does not exist, then it is created using the
+     * default Jackrabbit configuration settings.
+     *
+     * @since Apache Jackrabbit 1.6
+     * @param dir repository home directory
+     * @return repository configuration
+     * @throws ConfigurationException on configuration errors
+     */
+    public static RepositoryConfig install(File dir)
+            throws IOException, ConfigurationException {
+        return install(new File(dir, REPOSITORY_XML), dir);
+    }
+
+    /**
+     * Returns the configuration of a repository with the given configuration
+     * file and repository home directory.
+     * <p>
+     * The directory is created if it does not exist. If the repository
+     * configuration file does not exist, then it is created using the
+     * default Jackrabbit configuration settings.
+     *
+     * @since Apache Jackrabbit 1.6
+     * @param dir repository home directory
+     * @return repository configuration
+     * @throws ConfigurationException on configuration errors
+     */
+    public static RepositoryConfig install(File xml, File dir)
+            throws IOException, ConfigurationException {
+        if (!dir.exists()) {
+            log.info("Creating repository directory {}", dir);
+            dir.mkdirs();
+        }
+
+        if (!xml.exists()) {
+            log.info("Installing default repository configuration to {}", xml);
+            OutputStream output = new FileOutputStream(xml);
+            try {
+                InputStream input =
+                    RepositoryImpl.class.getResourceAsStream(REPOSITORY_XML);
+                try {
+                    IOUtils.copy(input, output);
+                } finally {
+                   input.close();
+                }
+            } finally {
+                output.close();
+            }
+        }
+
+        return create(xml, dir);
+    }
+
+    /**
+     * Returns the configuration of a repository in a given repository
+     * directory. The repository configuration is read from a "repository.xml"
+     * file inside the repository directory.
+     * <p>
+     * An exception is thrown if the directory does not exist or if
+     * the repository configuration file can not be read. 
+     *
+     * @since Apache Jackrabbit 1.6
+     * @param dir repository home directory
+     * @return repository configuration
+     * @throws ConfigurationException on configuration errors
+     */
+    public static RepositoryConfig create(File dir)
+            throws ConfigurationException {
+        return create(new File(dir, REPOSITORY_XML), dir);
+    }
+
+    /**
+     * Returns the configuration of a repository with the given configuration
+     * file and repository home directory.
+     * <p>
+     * An exception is thrown if the directory does not exist or if
+     * the repository configuration file can not be read. 
+     *
+     * @since Apache Jackrabbit 1.6
+     * @param dir repository home directory
+     * @return repository configuration
+     * @throws ConfigurationException on configuration errors
+     */
+    public static RepositoryConfig create(File xml, File dir)
+            throws ConfigurationException {
+        if (!dir.isDirectory()) {
+            throw new ConfigurationException(
+                    "Repository directory " + dir + " does not exist");
+        }
+
+        if (!xml.isFile()) {
+            throw new ConfigurationException(
+                    "Repository configuration file " + xml + " does not exist");
+        }
+
+        return create(new InputSource(xml.toURI().toString()), dir.getPath());
+    }
+
+    /**
      * Convenience method that wraps the configuration file name into an
      * {@link InputSource} and invokes the
      * {@link #create(InputSource, String)} method.

Copied: jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java (from r775833, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java)
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java?p2=jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java&p1=jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java&r1=775833&r2=776753&rev=776753&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java (original)
+++ jackrabbit/branches/1.x/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/PersistenceCopier.java Wed May 20 16:19:10 2009
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.core.persistence;
 
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
 
 import org.apache.jackrabbit.core.NodeId;
@@ -24,46 +25,95 @@
 import org.apache.jackrabbit.core.state.ChangeLog;
 import org.apache.jackrabbit.core.state.ChildNodeEntry;
 import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.core.state.PropertyState;
 import org.apache.jackrabbit.spi.Name;
 
 /**
- * 
+ * Tool for copying item states from one persistence manager to another.
+ * Used for backing up or migrating repository content.
+ *
+ * @since Apache Jackrabbit 1.6
  */
 public class PersistenceCopier {
 
+    /**
+     * Source persistence manager.
+     */
     private final PersistenceManager source;
 
+    /**
+     * Target persistence manager.
+     */
     private final PersistenceManager target;
 
-    private final Set<NodeId> exclude = new HashSet<NodeId>();
-
+    /**
+     * Identifiers of the nodes that have already been copied or that
+     * should explicitly not be copied. Used to avoid duplicate copies
+     * of shareable nodes and to avoid trying to copy "missing" nodes
+     * like the virtual "/jcr:system" node.
+     */
+    private final Set exclude = new HashSet();
+
+    /**
+     * Creates a tool for copying content from one persistence manager
+     * to another.
+     *
+     * @param source source persistence manager
+     * @param target target persistence manager
+     */
     public PersistenceCopier(
             PersistenceManager source, PersistenceManager target) {
         this.source = source;
         this.target = target;
     }
 
+    /**
+     * Explicitly exclude the identified node from being copied. Used for
+     * excluding virtual nodes like "/jcr:system" from the copy process.
+     *
+     * @param id identifier of the node to be excluded
+     */
     public void excludeNode(NodeId id) {
         exclude.add(id);
     }
 
+    /**
+     * Recursively copies the identified node and all its descendants.
+     * Explicitly excluded nodes and nodes that have already been copied
+     * are automatically skipped.
+     *
+     * @param id identifier of the node to be copied
+     * @throws ItemStateException if the copy operation fails
+     */
     public void copy(NodeId id) throws ItemStateException {
         if (!exclude.contains(id)) {
             NodeState node = source.load(id);
 
-            for (ChildNodeEntry entry : node.getChildNodeEntries()) {
+            Iterator iterator = node.getChildNodeEntries().iterator();
+            while (iterator.hasNext()) {
+                ChildNodeEntry entry = (ChildNodeEntry) iterator.next();
                 copy(entry.getId());
             }
 
             copy(node);
+            exclude.add(id);
         }
     }
 
+    /**
+     * Copies the given node state and all associated property states
+     * to the target persistence manager.
+     *
+     * @param sourceNode source node state
+     * @throws ItemStateException if the copy operation fails
+     */
     private void copy(NodeState sourceNode) throws ItemStateException {
         ChangeLog changes = new ChangeLog();
 
+        // Copy the node state
         NodeState targetNode = target.createNew(sourceNode.getNodeId());
         targetNode.setParentId(sourceNode.getParentId());
         targetNode.setDefinitionId(sourceNode.getDefinitionId());
@@ -77,7 +127,10 @@
             changes.added(targetNode);
         }
 
-        for (Name name : sourceNode.getPropertyNames()) {
+        // Copy all associated property states
+        Iterator iterator = sourceNode.getPropertyNames().iterator();
+        while (iterator.hasNext()) {
+            Name name = (Name) iterator.next();
             PropertyId id = new PropertyId(sourceNode.getNodeId(), name);
             PropertyState sourceState = source.load(id);
             PropertyState targetState = target.createNew(id);
@@ -93,6 +146,17 @@
             }
         }
 
+        // Copy all node references
+        NodeReferencesId refsId = new NodeReferencesId(sourceNode.getNodeId());
+        if (source.exists(refsId)) {
+            changes.modified(source.load(refsId));
+        } else if (target.exists(refsId)) {
+            NodeReferences references = target.load(refsId);
+            references.clearAllReferences();
+            changes.modified(references);
+        }
+
+        // Persist the copied states
         target.store(changes);
     }
 

Modified: jackrabbit/branches/1.x/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.x/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java?rev=776753&r1=776752&r2=776753&view=diff
==============================================================================
--- jackrabbit/branches/1.x/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java (original)
+++ jackrabbit/branches/1.x/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java Wed May 20 16:19:10 2009
@@ -16,16 +16,16 @@
  */
 package org.apache.jackrabbit.core.config;
 
-import junit.framework.TestCase;
+ import junit.framework.TestCase;
 import org.xml.sax.InputSource;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.input.ClosedInputStream;
 import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 
@@ -34,54 +34,58 @@
  */
 public class RepositoryConfigTest extends TestCase {
 
-    private static final String REPOSITORY_XML = "target/repository_for_test.xml";
-    private static final String REPOSITORY_HOME = "target/repository_for_test";
+    private static final File DIR =
+        new File("target", "RepositoryConfigTest");
 
-    private static void deleteAll(File file) {
-        if (file.exists()) {
-            if (file.isDirectory()) {
-                File[] children = file.listFiles();
-                for (int i = 0; i < children.length; i++) {
-                    deleteAll(children[i]);
-                }
-            }
-            file.delete();
-        }
-    }
+    private static final File XML =
+        new File(DIR, "repository.xml");
+
+    private RepositoryConfig config;
 
     /**
      * Sets up the test case by creating the repository home directory
      * and copying the repository configuration file in place.
      */
     protected void setUp() throws Exception {
-        // Create the repository directory
-        File home = new File(REPOSITORY_HOME);
-        home.mkdirs();
-
-        // Copy the repository configuration file in place
-        ClassLoader loader = getClass().getClassLoader();
-        InputStream input = loader.getResourceAsStream("org/apache/jackrabbit/core/repository.xml");
-        try {
-            OutputStream output = new FileOutputStream(REPOSITORY_XML);
-            try {
-                int n;
-                byte[] buffer = new byte[1024];
-                while ((n = input.read(buffer)) != -1) {
-                    output.write(buffer, 0, n);
-                }
-            } finally {
-                output.close();
-            }
-        } finally {
-            input.close();
-        }
+        config = RepositoryConfig.install(DIR);
     }
 
     protected void tearDown() {
-        File home = new File(REPOSITORY_HOME);
-        deleteAll(home);
-        File config = new File(REPOSITORY_XML);
-        config.delete();
+        FileUtils.deleteQuietly(DIR);
+    }
+
+    public void testCreateWithRepositoryDirectory() {
+        try {
+            RepositoryConfig.create(DIR);
+        } catch (ConfigurationException e) {
+            fail("Valid repository directory");
+        }
+
+        try {
+            RepositoryConfig.create(new File(DIR, "invalid-repo-dir"));
+            fail("Invalid repository directory");
+        } catch (ConfigurationException e) {
+        }
+    }
+
+    public void testCreateWithRepositoryConfigAndDirectory() {
+        try {
+            RepositoryConfig.create(XML, DIR);
+        } catch (ConfigurationException e) {
+            fail("Valid repository configuration and directory");
+        }
+
+        try {
+            RepositoryConfig.create(XML, new File(DIR, "invalid-repo-dir"));
+            fail("Invalid repository directory");
+        } catch (ConfigurationException e) {
+        }
+
+        try {
+            RepositoryConfig.create(new File(DIR, "invalid.xml"), DIR);
+            fail("Invalid repository configuration");
+        } catch (ConfigurationException e) {
+        }
     }
 
     /**
@@ -89,12 +93,15 @@
      */
     public void testRepositoryConfigCreateWithFileName() {
         try {
-            RepositoryConfig.create(REPOSITORY_XML, REPOSITORY_HOME);
+            RepositoryConfig.create(XML.getPath(), DIR.getPath());
         } catch (ConfigurationException e) {
             fail("Valid configuration file name");
         }
+
         try {
-            RepositoryConfig.create("invalid-config-file", REPOSITORY_HOME);
+            RepositoryConfig.create(
+                    new File(DIR, "invalid-config-file.xml").getPath(),
+                    DIR.getPath());
             fail("Invalid configuration file name");
         } catch (ConfigurationException e) {
         }
@@ -105,14 +112,23 @@
      */
     public void testRepositoryConfigCreateWithURI() throws URISyntaxException {
         try {
-            URI uri = new File(REPOSITORY_XML).toURI();
-            RepositoryConfig.create(uri, REPOSITORY_HOME);
+            RepositoryConfig.create(XML.toURI(), DIR.getPath());
         } catch (ConfigurationException e) {
             fail("Valid configuration URI");
         }
+
+        try {
+            RepositoryConfig.create(
+                    new File(DIR, "invalid-config-file.xml").toURI(),
+                    DIR.getPath());
+            fail("Invalid configuration URI");
+        } catch (ConfigurationException e) {
+        }
+
         try {
-            URI uri = new URI("invalid://config/uri");
-            RepositoryConfig.create(uri, REPOSITORY_HOME);
+            RepositoryConfig.create(
+                    new URI("invalid://config/uri"),
+                    DIR.getPath());
             fail("Invalid configuration URI");
         } catch (ConfigurationException e) {
         }
@@ -122,25 +138,33 @@
      * Tests that an input stream can be used for the configuration.
      */
     public void testRepositoryConfigCreateWithInputStream() throws IOException {
-        InputStream input = new FileInputStream(REPOSITORY_XML);
+        InputStream input = new FileInputStream(XML);
         try {
-            RepositoryConfig.create(input, REPOSITORY_HOME);
+            RepositoryConfig.create(input, DIR.getPath());
         } catch (ConfigurationException e) {
             fail("Valid configuration input stream");
         } finally {
             input.close();
         }
-        input = new InputStream() {
-            public int read() throws IOException {
-                throw new IOException("invalid input stream");
-            }
-        };
+
         try {
-            RepositoryConfig.create(input, REPOSITORY_HOME);
+            RepositoryConfig.create(
+                    new InputStream() {
+                        public int read() throws IOException {
+                            throw new IOException("invalid input stream");
+                        }
+                    },
+                    DIR.getPath());
+            fail("Invalid configuration input stream");
+        } catch (ConfigurationException e) {
+        }
+
+        try {
+            RepositoryConfig.create(
+                    new ClosedInputStream(),
+                    DIR.getPath());
             fail("Invalid configuration input stream");
         } catch (ConfigurationException e) {
-        } finally {
-            input.close();
         }
     }
 
@@ -149,16 +173,16 @@
      */
     public void testRepositoryConfigCreateWithInputSource() throws IOException {
         try {
-            URI uri = new File(REPOSITORY_XML).toURI();
-            InputSource source = new InputSource(uri.toString());
-            RepositoryConfig.create(source, REPOSITORY_HOME);
+            InputSource source = new InputSource(XML.toURI().toString());
+            RepositoryConfig.create(source, DIR.getPath());
         } catch (ConfigurationException e) {
             fail("Valid configuration input source with file URI");
         }
-        InputStream stream = new FileInputStream(REPOSITORY_XML);
+
+        InputStream stream = new FileInputStream(XML);
         try {
             InputSource source = new InputSource(stream);
-            RepositoryConfig.create(source, REPOSITORY_HOME);
+            RepositoryConfig.create(source, DIR.getPath());
         } catch (ConfigurationException e) {
             fail("Valid configuration input source with input stream");
         } finally {
@@ -170,18 +194,16 @@
      * Test that the repository configuration file is correctly parsed.
      */
     public void testRepositoryConfig() throws Exception {
-        RepositoryConfig config =
-            RepositoryConfig.create(REPOSITORY_XML, REPOSITORY_HOME);
-        assertEquals(REPOSITORY_HOME, config.getHomeDir());
+        assertEquals(DIR.getPath(), config.getHomeDir());
         assertEquals("default", config.getDefaultWorkspaceName());
         assertEquals(
-                new File(REPOSITORY_HOME, "workspaces").getPath(),
+                new File(DIR, "workspaces").getPath(),
                 new File(config.getWorkspacesConfigRootDir()).getPath());
-        assertEquals("Jackrabbit", config.getAppName());
         assertEquals("Jackrabbit", config.getSecurityConfig().getAppName());
 
         // SecurityManagerConfig
-        SecurityManagerConfig smc = config.getSecurityConfig().getSecurityManagerConfig();
+        SecurityManagerConfig smc =
+            config.getSecurityConfig().getSecurityManagerConfig();
         assertEquals(
                 "org.apache.jackrabbit.core.security.simple.SimpleSecurityManager",
                 smc.getClassName());
@@ -195,33 +217,30 @@
         }
 
         // AccessManagerConfig
-        AccessManagerConfig amc = config.getAccessManagerConfig();
-        amc = config.getSecurityConfig().getAccessManagerConfig();
+        AccessManagerConfig amc =
+            config.getSecurityConfig().getAccessManagerConfig();
         assertEquals(
                 "org.apache.jackrabbit.core.security.simple.SimpleAccessManager",
                 amc.getClassName());
         assertTrue(amc.getParameters().isEmpty());
 
         VersioningConfig vc = config.getVersioningConfig();
-        assertEquals(new File(REPOSITORY_HOME, "version"), vc.getHomeDir());
+        assertEquals(new File(DIR, "version"), vc.getHomeDir());
         assertEquals(
                 "org.apache.jackrabbit.core.persistence.bundle.DerbyPersistenceManager",
                 vc.getPersistenceManagerConfig().getClassName());
     }
 
     public void testInit() throws Exception {
-        RepositoryConfig.create(REPOSITORY_XML, REPOSITORY_HOME);
-        File workspaces_dir = new File(REPOSITORY_HOME, "workspaces");
+        File workspaces_dir = new File(DIR, "workspaces");
         File workspace_dir = new File(workspaces_dir, "default");
         File workspace_xml = new File(workspace_dir, "workspace.xml");
         assertTrue("Default workspace is created", workspace_xml.exists());
     }
 
     public void testCreateWorkspaceConfig() throws Exception {
-        RepositoryConfig config =
-            RepositoryConfig.create(REPOSITORY_XML, REPOSITORY_HOME);
-        config.createWorkspaceConfig("test-workspace", (StringBuffer)null);
-        File workspaces_dir = new File(REPOSITORY_HOME, "workspaces");
+        config.createWorkspaceConfig("test-workspace", (StringBuffer) null);
+        File workspaces_dir = new File(DIR, "workspaces");
         File workspace_dir = new File(workspaces_dir, "test-workspace");
         File workspace_xml = new File(workspace_dir, "workspace.xml");
         assertTrue(workspace_xml.exists());
@@ -229,9 +248,7 @@
 
     public void testCreateDuplicateWorkspaceConfig() throws Exception {
         try {
-            RepositoryConfig config =
-                RepositoryConfig.create(REPOSITORY_XML, REPOSITORY_HOME);
-            config.createWorkspaceConfig("default", (StringBuffer)null);
+            config.createWorkspaceConfig("default", (StringBuffer) null);
             fail("No exception thrown when creating a duplicate workspace");
         } catch (ConfigurationException e) {
             // test passed
@@ -247,10 +264,11 @@
 
         InputStream in = getClass().getResourceAsStream(
                 "/org/apache/jackrabbit/core/cluster/repository.xml");
-        RepositoryConfig config = RepositoryConfig.create(in, REPOSITORY_HOME);
+        RepositoryConfig config = RepositoryConfig.create(in, DIR.getPath());
 
         ClusterConfig clusterConfig = config.getClusterConfig();
         assertEquals(id, clusterConfig.getId());
         assertEquals(syncDelay, clusterConfig.getSyncDelay());
     }
+
 }

Modified: jackrabbit/branches/1.x/jackrabbit-standalone/src/main/java/org/apache/jackrabbit/standalone/Main.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.x/jackrabbit-standalone/src/main/java/org/apache/jackrabbit/standalone/Main.java?rev=776753&r1=776752&r2=776753&view=diff
==============================================================================
--- jackrabbit/branches/1.x/jackrabbit-standalone/src/main/java/org/apache/jackrabbit/standalone/Main.java (original)
+++ jackrabbit/branches/1.x/jackrabbit-standalone/src/main/java/org/apache/jackrabbit/standalone/Main.java Wed May 20 16:19:10 2009
@@ -19,7 +19,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.BindException;
 import java.net.URL;
 
 import org.apache.commons.cli.CommandLine;
@@ -28,6 +27,8 @@
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.core.RepositoryCopier;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
 import org.apache.jackrabbit.servlet.jackrabbit.JackrabbitRepositoryServlet;
 import org.apache.log4j.FileAppender;
 import org.apache.log4j.Layout;
@@ -70,6 +71,8 @@
         options.addOption("?", "help", false, "print this message");
         options.addOption("n", "notice", false, "print copyright notices");
         options.addOption("l", "license", false, "print license information");
+        options.addOption(
+                "b", "backup", false, "create a backup of the repository");
 
         options.addOption("q", "quiet", false, "disable console output");
         options.addOption("d", "debug", false, "enable debug logging");
@@ -79,6 +82,12 @@
         options.addOption("f", "file", true, "location of this jar file");
         options.addOption("r", "repo", true, "repository directory (jackrabbit)");
         options.addOption("c", "conf", true, "repository configuration file");
+        options.addOption(
+                "R", "backup-repo", true,
+                "backup repository directory (jackrabbit-backupN)");
+        options.addOption(
+                "C", "backup-conf", true,
+                "backup repository configuration file");
 
         command = new GnuParser().parse(options, args);
     }
@@ -118,32 +127,71 @@
             message("Writing log messages to " + log);
             prepareServerLog(log);
 
-            message("Starting the server...");
-            prepareWebapp(file, repository, tmp);
-            accessLog.setHandler(webapp);
-            prepareAccessLog(log);
-            server.setHandler(accessLog);
-            prepareConnector();
-            server.addConnector(connector);
-            prepareShutdown();
-
-            try {
-                server.start();
-
-                String host = connector.getHost();
-                if (host == null) {
-                    host = "localhost";
+            if (command.hasOption("backup")) {
+                backup(repository);
+            } else {
+                message("Starting the server...");
+                prepareWebapp(file, repository, tmp);
+                accessLog.setHandler(webapp);
+                prepareAccessLog(log);
+                server.setHandler(accessLog);
+                prepareConnector();
+                server.addConnector(connector);
+                prepareShutdown();
+
+                try {
+                    server.start();
+
+                    String host = connector.getHost();
+                    if (host == null) {
+                        host = "localhost";
+                    }
+                    message("Apache Jackrabbit is now running at "
+                            +"http://" + host + ":" + connector.getPort() + "/");
+                } catch (Throwable t) {
+                    System.err.println(
+                            "Unable to start the server: " + t.getMessage());
+                    System.exit(1);
                 }
-                message("Apache Jackrabbit is now running at "
-                        +"http://" + host + ":" + connector.getPort() + "/");
-            } catch (Throwable t) {
-                System.err.println(
-                        "Unable to start the server: " + t.getMessage());
-                System.exit(1);
             }
         }
     }
 
+    private void backup(File sourceDir) throws Exception {
+        RepositoryConfig source;
+        if (command.hasOption("conf")) {
+            source = RepositoryConfig.create(
+                    new File(command.getOptionValue("conf")), sourceDir);
+        } else {
+            source = RepositoryConfig.create(sourceDir);
+        }
+
+        File targetDir;
+        if (command.hasOption("backup-repo")) {
+            targetDir = new File(command.getOptionValue("backup-repo"));
+        } else {
+            int i = 1;
+            do {
+                targetDir = new File("jackrabbit-backup" + i++);
+            } while (targetDir.exists());
+        }
+
+        RepositoryConfig target;
+        if (command.hasOption("backup-conf")) {
+            target = RepositoryConfig.install(
+                    new File(command.getOptionValue("backup-conf")), targetDir);
+        } else {
+            target = RepositoryConfig.install(targetDir);
+        }
+
+        message("Creating a repository copy in " + targetDir);
+
+        RepositoryCopier copier = new RepositoryCopier(source, target);
+        copier.copy();
+
+        message("The repository has been successfully copied.");
+    }
+
     private void prepareServerLog(File log)
             throws IOException {
         Layout layout =