JCR repository backup
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 6 Feb 2013 16:22:45 +0000 (16:22 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 6 Feb 2013 16:22:45 +0000 (16:22 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@6071 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

lib/org.argeo.slc.lib.jcr/META-INF/spring/backup.xml
lib/org.argeo.slc.lib.jcr/pom.xml
lib/org.argeo.slc.lib.repo/pom.xml
runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/lib/jcr/JcrRepositoryBackup.java

index d4186fd2c1fea61ced8df8e978ed180652dbcc04..7f33f541735137c52d37588b360ec2774f84959f 100644 (file)
@@ -6,11 +6,13 @@
        http://www.argeo.org/schema/slc-flow http://www.argeo.org/schema/slc-flow-1.2.xsd">\r
 \r
        <flow:flow name="backup">\r
-               <description>Backups a JCR repository as XML</description>\r
+               <description>Backups a JCR repository</description>\r
                <flow:spec>\r
-                       <flow:primitive name="targetFile" value="file://${user.home}/node.zip" />\r
+                       <flow:primitive name="targetFile"\r
+                               value="file://${user.home}/.slc/backups/node" />\r
                        <flow:primitive name="sourceRepo" value="vm:///node/" />\r
                        <flow:primitive name="sourceWksp" value="" />\r
+                       <flow:primitive name="sourceDatastore" value="" />\r
                        <flow:primitive name="sourceUsername" value="${user.name}" />\r
                        <flow:primitive name="sourcePassword" type="password"\r
                                value="" />\r
@@ -20,6 +22,7 @@
                        <property name="targetFile" value="@{targetFile}" />\r
                        <property name="sourceRepo" value="@{sourceRepo}" />\r
                        <property name="sourceWksp" value="@{sourceWksp}" />\r
+                       <property name="sourceDatastore" value="@{sourceDatastore}" />\r
                        <property name="sourceUsername" value="@{sourceUsername}" />\r
                        <property name="sourcePassword" value="@{sourcePassword}" />\r
                        <property name="repositoryFactory" ref="repositoryFactory" />\r
index 4e7b17cd020b4b9cfc9520a087ad9680f32dfc92..3ce0ebb46b55baed6529290faffa91aae633f35a 100644 (file)
@@ -6,7 +6,7 @@
                <version>1.1.12-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
-       <artifactId>org.argeo.slc.lib.repo</artifactId>
-       <name>SLC Lib - Repository Utilities</name>
+       <artifactId>org.argeo.slc.lib.jcr</artifactId>
+       <name>SLC Lib - JCR Utilities</name>
        <description>Utilities to transfer, convert, manage software repositories</description>
 </project>
index 0a974be10a02e71900f8293dbc504bc6a86d2517..1e1bb44a56dfad4256c92a9a6ff5c5cf7b66baed 100644 (file)
@@ -7,7 +7,7 @@
                <version>1.1.12-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
-       <artifactId>org.argeo.slc.lib.jcr</artifactId>
+       <artifactId>org.argeo.slc.lib.repo</artifactId>
        <name>SLC Lib - Repository Utilities</name>
        <description>Utilities related to JCR repositories</description>
 </project>
index a6b7d28ca5cb72dcd064e8838da53275fc1718ee..c63bdf02672efddb0088da572e36a40fd4c2f364 100644 (file)
  */
 package org.argeo.slc.lib.jcr;
 
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.UUID;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
 
 import javax.jcr.Credentials;
 import javax.jcr.Node;
@@ -31,6 +33,8 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSelectInfo;
+import org.apache.commons.vfs.FileSelector;
 import org.apache.commons.vfs.FileSystemException;
 import org.apache.commons.vfs.FileSystemManager;
 import org.argeo.jcr.ArgeoJcrUtils;
@@ -42,6 +46,7 @@ public class JcrRepositoryBackup implements Runnable {
        private final static Log log = LogFactory.getLog(JcrRepositoryBackup.class);
 
        private String sourceRepo;
+       private String sourceDatastore;
        private String targetFile;
 
        private String sourceWksp;
@@ -53,11 +58,17 @@ public class JcrRepositoryBackup implements Runnable {
        private FileSystemManager fileSystemManager;
 
        public void run() {
-               FileObject archiveFo = null;
                Session sourceDefaultSession = null;
                try {
                        long begin = System.currentTimeMillis();
 
+                       FileObject archiveRoot = fileSystemManager.resolveFile(targetFile);
+                       archiveRoot.createFolder();
+
+                       String datastoreFolderName = "datastore";
+                       if (hasDatastore())
+                               backupDataStore(archiveRoot.resolveFile(datastoreFolderName));
+
                        Repository sourceRepository = ArgeoJcrUtils.getRepositoryByUri(
                                        repositoryFactory, sourceRepo);
                        Credentials sourceCredentials = null;
@@ -65,84 +76,132 @@ public class JcrRepositoryBackup implements Runnable {
                                sourceCredentials = new SimpleCredentials(sourceUsername,
                                                sourcePassword);
 
-                       archiveFo = fileSystemManager.resolveFile(targetFile);
-                       FileObject archiveRoot = fileSystemManager
-                                       .createFileSystem(archiveFo);
-
-                       Map<String, Exception> errors = new HashMap<String, Exception>();
                        sourceDefaultSession = sourceRepository.login(sourceCredentials);
                        for (String sourceWorkspaceName : sourceDefaultSession
                                        .getWorkspace().getAccessibleWorkspaceNames()) {
+                               if (Thread.interrupted()) {
+                                       log.error("Workspace backup interrupted");
+                                       Thread.currentThread().interrupt();
+                                       return;
+                               }
+
                                if (sourceWksp != null && !sourceWksp.trim().equals("")
                                                && !sourceWorkspaceName.equals(sourceWksp))
                                        continue;
-                               // if (sourceWorkspaceName.equals("security"))
-                               // continue;
-                               // if (sourceWorkspaceName.equals("localrepo"))
-                               // continue;
                                Session sourceSession = null;
-                               OutputStream out = null;
+                               JarOutputStream out = null;
+                               FileObject workspaceBackup = null;
                                try {
-                                       FileObject workspaceXml = archiveRoot
-                                                       .getChild(sourceWorkspaceName + ".xml");
-                                       out = workspaceXml.getContent().getOutputStream();
+                                       Manifest manifest = new Manifest();
+                                       manifest.getMainAttributes().put(
+                                                       Attributes.Name.MANIFEST_VERSION, "1.0");
+                                       manifest.getMainAttributes().putValue("Backup-UUID",
+                                                       UUID.randomUUID().toString());
+                                       manifest.getMainAttributes().putValue("Backup-Timestamp",
+                                                       Long.toString(System.currentTimeMillis()));
+                                       manifest.getMainAttributes().putValue(
+                                                       "Backup-JCR-Workspace", sourceWorkspaceName);
+                                       workspaceBackup = fileSystemManager.resolveFile(targetFile
+                                                       + "/" + sourceWorkspaceName + ".jar");
+
+                                       out = new JarOutputStream(workspaceBackup.getContent()
+                                                       .getOutputStream(), manifest);
                                        sourceSession = sourceRepository.login(sourceCredentials,
                                                        sourceWorkspaceName);
                                        backupWorkspace(sourceSession, out);
-                                       workspaceXml.close();
-                               } catch (Exception e) {
-                                       errors.put("Could not sync workspace "
-                                                       + sourceWorkspaceName, e);
                                } finally {
                                        JcrUtils.logoutQuietly(sourceSession);
                                        IOUtils.closeQuietly(out);
+                                       if (workspaceBackup != null)
+                                               workspaceBackup.close();
                                }
                        }
 
+                       // in case some binaries have been added during the backup
+                       if (hasDatastore())
+                               backupDataStore(archiveRoot.resolveFile(datastoreFolderName));
+
                        long duration = (System.currentTimeMillis() - begin) / 1000;// s
                        log.info("Backed-up " + sourceRepo + " in " + (duration / 60)
                                        + "min " + (duration % 60) + "s");
-
-                       if (errors.size() > 0) {
-                               throw new SlcException("Sync failed " + errors);
-                       }
                } catch (Exception e) {
                        throw new SlcException("Cannot backup " + sourceRepo, e);
                } finally {
                        JcrUtils.logoutQuietly(sourceDefaultSession);
-                       if (archiveFo != null)
-                               try {
-                                       archiveFo.close();
-                               } catch (FileSystemException e) {
-                                       // silent
-                               }
                }
        }
 
-       protected void backupWorkspace(Session sourceSession, OutputStream out) {
+       protected Boolean hasDatastore() {
+               return sourceDatastore != null && !sourceDatastore.trim().equals("");
+       }
+
+       protected void backupWorkspace(Session sourceSession, JarOutputStream out) {
                try {
-                       if (log.isDebugEnabled())
-                               log.debug("Syncing " + sourceSession.getWorkspace().getName()
+                       if (log.isTraceEnabled())
+                               log.trace("Backup " + sourceSession.getWorkspace().getName()
                                                + "...");
+                       Boolean skipBinaries = hasDatastore();
                        for (NodeIterator it = sourceSession.getRootNode().getNodes(); it
                                        .hasNext();) {
+                               if (Thread.interrupted()) {
+                                       log.error("Node backup interrupted");
+                                       Thread.currentThread().interrupt();
+                                       return;
+                               }
                                Node node = it.nextNode();
-                               if (node.getName().equals("jcr:system"))
-                                       continue;
-
-                               sourceSession
-                                               .exportSystemView(node.getPath(), out, true, false);
-                               if (log.isDebugEnabled())
-                                       log.debug(" " + node.getPath());
+                               JarEntry entry = new JarEntry(node.getPath());
+                               out.putNextEntry(entry);
+                               sourceSession.exportSystemView(node.getPath(), out,
+                                               skipBinaries, false);
+                               out.flush();
+                               out.closeEntry();
                        }
                        if (log.isDebugEnabled())
-                               log.debug("Synced " + sourceSession.getWorkspace().getName());
+                               log.debug("Backed up " + sourceSession.getWorkspace().getName());
                } catch (Exception e) {
                        throw new SlcException("Cannot backup "
                                        + sourceSession.getWorkspace().getName(), e);
                }
        }
 
+       protected void backupDataStore(final FileObject targetDatastore) {
+               try {
+                       targetDatastore.createFolder();
+                       final FileObject sourceDataStore = fileSystemManager
+                                       .resolveFile(sourceDatastore);
+                       if (log.isDebugEnabled())
+                               log.debug("Backup " + sourceDatastore);
+                       targetDatastore.copyFrom(sourceDataStore, new FileSelector() {
+                               public boolean traverseDescendents(FileSelectInfo fileInfo)
+                                               throws Exception {
+                                       return true;
+                               }
+
+                               public boolean includeFile(FileSelectInfo fileInfo)
+                                               throws Exception {
+                                       String relativeName = fileInfo
+                                                       .getFile()
+                                                       .getName()
+                                                       .getPath()
+                                                       .substring(
+                                                                       sourceDataStore.getName().getPath()
+                                                                                       .length());
+                                       FileObject target = targetDatastore
+                                                       .resolveFile(relativeName);
+                                       if (target.exists()) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
+                               }
+                       });
+                       if (log.isDebugEnabled())
+                               log.debug("Backed-up " + sourceDatastore);
+               } catch (FileSystemException e) {
+                       throw new SlcException("Cannot backup datastore", e);
+               }
+       }
+
        public void setSourceRepo(String sourceRepo) {
                this.sourceRepo = sourceRepo;
        }
@@ -167,4 +226,12 @@ public class JcrRepositoryBackup implements Runnable {
                this.fileSystemManager = fileSystemManager;
        }
 
+       public void setTargetFile(String targetFile) {
+               this.targetFile = targetFile;
+       }
+
+       public void setSourceDatastore(String sourceDatastore) {
+               this.sourceDatastore = sourceDatastore;
+       }
+
 }