From: Mathieu Baudier Date: Tue, 19 Jan 2021 08:31:55 +0000 (+0100) Subject: Improve logical backups. X-Git-Tag: argeo-commons-2.1.91~22 X-Git-Url: https://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=2606b4b145577c4767c37c464e3f517e49a98100 Improve logical backups. --- diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java index 57ce89809..2667d986a 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java @@ -295,13 +295,15 @@ public class CmsDeployment implements NodeDeployment { prepareDataModel(NodeConstants.NODE_REPOSITORY, deployedNodeRepository, publishAsLocalRepo); // init from backup - Path restorePath = Paths.get(System.getProperty("user.dir"), "restore"); - if (Files.exists(restorePath)) { - if (log.isDebugEnabled()) - log.debug("Found backup " + restorePath + ", restoring it..."); - LogicalRestore logicalRestore = new LogicalRestore(bc, deployedNodeRepository, restorePath); - KernelUtils.doAsDataAdmin(logicalRestore); - log.info("Restored backup from " + restorePath); + if (deployConfig.isFirstInit()) { + Path restorePath = Paths.get(System.getProperty("user.dir"), "restore"); + if (Files.exists(restorePath)) { + if (log.isDebugEnabled()) + log.debug("Found backup " + restorePath + ", restoring it..."); + LogicalRestore logicalRestore = new LogicalRestore(bc, deployedNodeRepository, restorePath); + KernelUtils.doAsDataAdmin(logicalRestore); + log.info("Restored backup from " + restorePath); + } } // init from repository diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java index 0a7d26584..228ccbb4a 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java @@ -20,7 +20,6 @@ import javax.naming.ldap.Rdn; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.api.NodeConstants; -import org.argeo.cms.CmsException; import org.argeo.naming.AttributesDictionary; import org.argeo.naming.LdifParser; import org.argeo.naming.LdifWriter; @@ -42,19 +41,20 @@ class DeployConfig implements ConfigurationListener { private SortedMap deployConfigs = new TreeMap<>(); private final DataModels dataModels; + private boolean isFirstInit = false; + public DeployConfig(ConfigurationAdmin configurationAdmin, DataModels dataModels, boolean isClean) { this.dataModels = dataModels; // ConfigurationAdmin configurationAdmin = // bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); try { - boolean isFirstInit = false; if (!isInitialized()) { // first init isFirstInit = true; firstInit(); } init(configurationAdmin, isClean, isFirstInit); } catch (IOException e) { - throw new CmsException("Could not init deploy configs", e); + throw new RuntimeException("Could not init deploy configs", e); } // FIXME check race conditions during initialization // bc.registerService(ConfigurationListener.class, this, null); @@ -108,15 +108,15 @@ class DeployConfig implements ConfigurationListener { LdapName userAdminFactoryName = serviceFactoryDn(NodeConstants.NODE_USER_ADMIN_PID); for (LdapName name : deployConfigs.keySet()) { if (name.startsWith(userAdminFactoryName) && !name.equals(userAdminFactoryName)) { - try { - Attributes attrs = deployConfigs.get(name); - String cn = name.getRdn(name.size() - 1).getValue().toString(); - if (!activeCns.contains(cn)) { - attrs.put(UserAdminConf.disabled.name(), "true"); - } - } catch (Exception e) { - throw new CmsException("Cannot disable user directory " + name, e); +// try { + Attributes attrs = deployConfigs.get(name); + String cn = name.getRdn(name.size() - 1).getValue().toString(); + if (!activeCns.contains(cn)) { + attrs.put(UserAdminConf.disabled.name(), "true"); } +// } catch (Exception e) { +// throw new CmsException("Cannot disable user directory " + name, e); +// } } } } @@ -186,7 +186,7 @@ class DeployConfig implements ConfigurationListener { deployConfigs = new LdifParser().read(in); } if (isClean) { - if(log.isDebugEnabled()) + if (log.isDebugEnabled()) log.debug("Clean state, loading from framework properties..."); setFromFrameworkProperties(isFirstInit); for (LdapName dn : deployConfigs.keySet()) { @@ -343,8 +343,12 @@ class DeployConfig implements ConfigurationListener { return null; } - static boolean isInitialized() { + private static boolean isInitialized() { return Files.exists(deployConfigPath); } + public boolean isFirstInit() { + return isFirstInit; + } + } diff --git a/org.argeo.maintenance/src/org/argeo/maintenance/backup/BackupContentHandler.java b/org.argeo.maintenance/src/org/argeo/maintenance/backup/BackupContentHandler.java index 745d39d1d..e29483e97 100644 --- a/org.argeo.maintenance/src/org/argeo/maintenance/backup/BackupContentHandler.java +++ b/org.argeo.maintenance/src/org/argeo/maintenance/backup/BackupContentHandler.java @@ -39,7 +39,7 @@ public class BackupContentHandler extends DefaultHandler { private Session session; private Set contentPaths = new TreeSet<>(); - private boolean inSystem = false; +// private boolean inSystem = false; public BackupContentHandler(Writer out, Session session) { super(); @@ -75,15 +75,15 @@ public class BackupContentHandler extends DefaultHandler { if (isNode) { String nodeName = attributes.getValue(SV_NAMESPACE_URI, NAME); currentDepth = currentDepth + 1; - if (currentDepth > 0) - currentPath[currentDepth - 1] = nodeName; +// if (currentDepth >= 0) + currentPath[currentDepth] = nodeName; // System.out.println(getCurrentPath() + " , depth=" + currentDepth); - if ("jcr:system".equals(nodeName)) { - inSystem = true; - } +// if ("jcr:system".equals(nodeName)) { +// inSystem = true; +// } } - if (inSystem) - return; +// if (inSystem) +// return; if (SV_NAMESPACE_URI.equals(uri)) try { @@ -143,20 +143,21 @@ public class BackupContentHandler extends DefaultHandler { public void endElement(String uri, String localName, String qName) throws SAXException { if (localName.equals(NODE)) { // System.out.println("endElement " + getCurrentPath() + " , depth=" + currentDepth); - if (currentDepth > 0) - currentPath[currentDepth - 1] = null; +// if (currentDepth > 0) + currentPath[currentDepth] = null; currentDepth = currentDepth - 1; - if (inSystem) { - // System.out.println("Skip " + getCurrentPath()+" , - // currentDepth="+currentDepth); - if (currentDepth == 0) { - inSystem = false; - return; - } - } + assert currentDepth >= 0; +// if (inSystem) { +// // System.out.println("Skip " + getCurrentPath()+" , +// // currentDepth="+currentDepth); +// if (currentDepth == 0) { +// inSystem = false; +// return; +// } +// } } - if (inSystem) - return; +// if (inSystem) +// return; boolean isValue = localName.equals(VALUE); if (SV_NAMESPACE_URI.equals(uri)) try { @@ -180,8 +181,8 @@ public class BackupContentHandler extends DefaultHandler { @Override public void characters(char[] ch, int start, int length) throws SAXException { - if (inSystem) - return; +// if (inSystem) +// return; try { out.write(ch, start, length); } catch (IOException e) { @@ -191,17 +192,17 @@ public class BackupContentHandler extends DefaultHandler { protected String getCurrentName() { assert currentDepth >= 0; - if (currentDepth == 0) - return "jcr:root"; - return currentPath[currentDepth - 1]; +// if (currentDepth == 0) +// return "jcr:root"; + return currentPath[currentDepth]; } protected String getCurrentPath() { - if (currentDepth == 0) - return "/"; - StringBuilder sb = new StringBuilder("/"); - for (int i = 0; i < currentDepth; i++) { - if (i != 0) +// if (currentDepth == 0) +// return "/"; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i <= currentDepth; i++) { +// if (i != 0) sb.append('/'); sb.append(currentPath[i]); } diff --git a/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java b/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java index 864de25be..4f7a2cfea 100644 --- a/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java +++ b/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java @@ -17,6 +17,13 @@ import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.ZipEntry; @@ -25,6 +32,7 @@ import java.util.zip.ZipOutputStream; import javax.jcr.Binary; import javax.jcr.Node; +import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.Repository; import javax.jcr.RepositoryException; @@ -37,6 +45,7 @@ import org.apache.commons.logging.LogFactory; import org.argeo.api.NodeConstants; import org.argeo.api.NodeUtils; import org.argeo.jackrabbit.client.ClientDavexRepositoryFactory; +import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrException; import org.argeo.jcr.JcrUtils; import org.osgi.framework.Bundle; @@ -53,26 +62,25 @@ public class LogicalBackup implements Runnable { private final static Log log = LogFactory.getLog(LogicalBackup.class); public final static String WORKSPACES_BASE = "workspaces/"; + public final static String FILES_BASE = "files/"; public final static String OSGI_BASE = "share/osgi/"; private final Repository repository; + private String defaultWorkspace; private final BundleContext bundleContext; private final ZipOutputStream zout; private final Path basePath; + private ExecutorService executorService; + public LogicalBackup(BundleContext bundleContext, Repository repository, Path basePath) { this.repository = repository; this.zout = null; this.basePath = basePath; this.bundleContext = bundleContext; - } -// public LogicalBackup(BundleContext bundleContext, Repository repository, ZipOutputStream zout) { -// this.repository = repository; -// this.zout = zout; -// this.basePath = null; -// this.bundleContext = bundleContext; -//} + executorService = Executors.newFixedThreadPool(3); + } @Override public void run() { @@ -80,143 +88,122 @@ public class LogicalBackup implements Runnable { log.info("Start logical backup to " + basePath); perform(); } catch (Exception e) { - e.printStackTrace(); + log.error("Unexpected exception when performing logical backup", e); throw new IllegalStateException("Logical backup failed", e); } } public void perform() throws RepositoryException, IOException { + long begin = System.currentTimeMillis(); // software backup if (bundleContext != null) - performSoftwareBackup(); + executorService.submit(() -> performSoftwareBackup(bundleContext)); // data backup Session defaultSession = login(null); + defaultWorkspace = defaultSession.getWorkspace().getName(); try { String[] workspaceNames = defaultSession.getWorkspace().getAccessibleWorkspaceNames(); workspaces: for (String workspaceName : workspaceNames) { if ("security".equals(workspaceName)) continue workspaces; - perform(workspaceName); + performDataBackup(workspaceName); } } finally { JcrUtils.logoutQuietly(defaultSession); + executorService.shutdown(); + try { + executorService.awaitTermination(24, TimeUnit.HOURS); + } catch (InterruptedException e) { + // silent + } } - + long duration = System.currentTimeMillis() - begin; + log.info("System logical backup completed in " + (duration / 60000) + "min " + (duration / 1000) + "s"); } - public void performSoftwareBackup() throws IOException { - for (Bundle bundle : bundleContext.getBundles()) { - String relativePath = OSGI_BASE + "boot/" + bundle.getSymbolicName() + ".jar"; - Dictionary headers = bundle.getHeaders(); - Manifest manifest = new Manifest(); - Enumeration headerKeys = headers.keys(); - while (headerKeys.hasMoreElements()) { - String headerKey = headerKeys.nextElement(); - String headerValue = headers.get(headerKey); - manifest.getMainAttributes().putValue(headerKey, headerValue); - } - try (JarOutputStream jarOut = new JarOutputStream(openOutputStream(relativePath), manifest)) { -// Enumeration entryPaths = bundle.getEntryPaths("/"); -// while (entryPaths.hasMoreElements()) { -// String entryPath = entryPaths.nextElement(); -// ZipEntry entry = new ZipEntry(entryPath); -// URL entryUrl = bundle.getEntry(entryPath); -// try (InputStream in = entryUrl.openStream()) { -// jarOut.putNextEntry(entry); -// IOUtils.copy(in, jarOut); -// jarOut.closeEntry(); -// } catch (FileNotFoundException e) { -// log.warn(entryPath); -// } -// } - Enumeration resourcePaths = bundle.findEntries("/", "*", true); - resources: while (resourcePaths.hasMoreElements()) { - URL entryUrl = resourcePaths.nextElement(); - String entryPath = entryUrl.getPath(); - if (entryPath.equals("")) - continue resources; - if (entryPath.endsWith("/")) - continue resources; - String entryName = entryPath.substring(1);// remove first '/' - if (entryUrl.getPath().equals("/META-INF/")) - continue resources; - if (entryUrl.getPath().equals("/META-INF/MANIFEST.MF")) - continue resources; - // dev - if (entryUrl.getPath().startsWith("/target")) - continue resources; - if (entryUrl.getPath().startsWith("/src")) - continue resources; - if (entryUrl.getPath().startsWith("/ext")) - continue resources; - - if (entryName.startsWith("bin/")) {// dev - entryName = entryName.substring("bin/".length()); - } - - ZipEntry entry = new ZipEntry(entryName); - try (InputStream in = entryUrl.openStream()) { - try { - jarOut.putNextEntry(entry); - } catch (ZipException e) {// duplicate - continue resources; - } - IOUtils.copy(in, jarOut); - jarOut.closeEntry(); -// log.info(entryUrl); - } catch (FileNotFoundException e) { - log.warn(entryUrl + ": " + e.getMessage()); - } - } + protected void performDataBackup(String workspaceName) throws RepositoryException, IOException { + Session session = login(workspaceName); + try { + nodes: for (NodeIterator nit = session.getRootNode().getNodes(); nit.hasNext();) { + Node nodeToExport = nit.nextNode(); + if ("jcr:system".equals(nodeToExport.getName()) && !workspaceName.equals(defaultWorkspace)) + continue nodes; + String nodePath = nodeToExport.getPath(); + Future> contentPathsFuture = executorService + .submit(() -> performNodeBackup(workspaceName, nodePath)); + executorService.submit(() -> performFilesBackup(workspaceName, contentPathsFuture)); } + } finally { + Jcr.logout(session); } - } - public void perform(String workspaceName) throws RepositoryException, IOException { + protected Set performNodeBackup(String workspaceName, String nodePath) { Session session = login(workspaceName); try { - String relativePath = WORKSPACES_BASE + workspaceName + ".xml"; + Node nodeToExport = session.getNode(nodePath); + String nodeName = nodeToExport.getName(); +// if (nodeName.startsWith("jcr:") || nodeName.startsWith("rep:")) +// continue nodes; +// // TODO make it more robust / configurable +// if (nodeName.equals("user")) +// continue nodes; + String relativePath = WORKSPACES_BASE + workspaceName + "/" + nodeName + ".xml"; OutputStream xmlOut = openOutputStream(relativePath); BackupContentHandler contentHandler; try (Writer writer = new BufferedWriter(new OutputStreamWriter(xmlOut, StandardCharsets.UTF_8))) { contentHandler = new BackupContentHandler(writer, session); - try { - session.exportSystemView("/", contentHandler, true, false); - if (log.isDebugEnabled()) - log.debug("Workspace " + workspaceName + ": metadata exported to " + relativePath); - } catch (SAXException e) { - throw new RuntimeException("Cannot perform backup of workspace " + workspaceName, e); - } catch (RepositoryException e) { - throw new JcrException("Cannot perform backup of workspace " + workspaceName, e); - } + session.exportSystemView(nodeToExport.getPath(), contentHandler, true, false); + if (log.isDebugEnabled()) + log.debug(workspaceName + ":/" + nodeName + " metadata exported to " + relativePath); } - for (String path : contentHandler.getContentPaths()) { + + // Files + Set contentPaths = contentHandler.getContentPaths(); + return contentPaths; + } catch (IOException | SAXException e) { + throw new RuntimeException("Cannot backup node " + workspaceName + ":" + nodePath, e); + } catch (RepositoryException e) { + throw new JcrException("Cannot backup node " + workspaceName + ":" + nodePath, e); + } finally { + Jcr.logout(session); + } + } + + protected void performFilesBackup(String workspaceName, Future> contentPathsFuture) { + Set contentPaths; + try { + contentPaths = contentPathsFuture.get(24, TimeUnit.HOURS); + } catch (InterruptedException | ExecutionException | TimeoutException e1) { + throw new RuntimeException("Cannot retrieve content paths for workspace " + workspaceName); + } + if (contentPaths == null || contentPaths.size() == 0) + return; + Session session = login(workspaceName); + try { + String workspacesFilesBasePath = FILES_BASE + workspaceName; + for (String path : contentPaths) { Node contentNode = session.getNode(path); Binary binary = contentNode.getProperty(Property.JCR_DATA).getBinary(); - String fileRelativePath = WORKSPACES_BASE + workspaceName + contentNode.getParent().getPath(); + String fileRelativePath = workspacesFilesBasePath + contentNode.getParent().getPath(); try (InputStream in = binary.getStream(); OutputStream out = openOutputStream(fileRelativePath)) { IOUtils.copy(in, out); if (log.isTraceEnabled()) log.trace("Workspace " + workspaceName + ": file content exported to " + fileRelativePath); } finally { - + JcrUtils.closeQuietly(binary); } - } - -// OutputStream xmlOut = openOutputStream(relativePath); -// try { -// session.exportSystemView("/", xmlOut, false, false); -// } finally { -// closeOutputStream(relativePath, xmlOut); -// } - - // TODO scan all binaries + if (log.isDebugEnabled()) + log.debug(workspaceName + ":" + contentPaths.size() + " files exported to " + workspacesFilesBasePath); + } catch (RepositoryException e) { + throw new JcrException("Cannot backup files from " + workspaceName + ":", e); + } catch (IOException e) { + throw new RuntimeException("Cannot backup files from " + workspaceName + ":", e); } finally { - JcrUtils.logoutQuietly(session); + Jcr.logout(session); } } @@ -286,4 +273,66 @@ public class LogicalBackup implements Runnable { return repositoryFactory.getRepository(params); } + public void performSoftwareBackup(BundleContext bundleContext) { + String bootBasePath = OSGI_BASE + "boot"; + Bundle[] bundles = bundleContext.getBundles(); + for (Bundle bundle : bundles) { + String relativePath = bootBasePath + "/" + bundle.getSymbolicName() + ".jar"; + Dictionary headers = bundle.getHeaders(); + Manifest manifest = new Manifest(); + Enumeration headerKeys = headers.keys(); + while (headerKeys.hasMoreElements()) { + String headerKey = headerKeys.nextElement(); + String headerValue = headers.get(headerKey); + manifest.getMainAttributes().putValue(headerKey, headerValue); + } + try (JarOutputStream jarOut = new JarOutputStream(openOutputStream(relativePath), manifest)) { + Enumeration resourcePaths = bundle.findEntries("/", "*", true); + resources: while (resourcePaths.hasMoreElements()) { + URL entryUrl = resourcePaths.nextElement(); + String entryPath = entryUrl.getPath(); + if (entryPath.equals("")) + continue resources; + if (entryPath.endsWith("/")) + continue resources; + String entryName = entryPath.substring(1);// remove first '/' + if (entryUrl.getPath().equals("/META-INF/")) + continue resources; + if (entryUrl.getPath().equals("/META-INF/MANIFEST.MF")) + continue resources; + // dev + if (entryUrl.getPath().startsWith("/target")) + continue resources; + if (entryUrl.getPath().startsWith("/src")) + continue resources; + if (entryUrl.getPath().startsWith("/ext")) + continue resources; + + if (entryName.startsWith("bin/")) {// dev + entryName = entryName.substring("bin/".length()); + } + + ZipEntry entry = new ZipEntry(entryName); + try (InputStream in = entryUrl.openStream()) { + try { + jarOut.putNextEntry(entry); + } catch (ZipException e) {// duplicate + continue resources; + } + IOUtils.copy(in, jarOut); + jarOut.closeEntry(); +// log.info(entryUrl); + } catch (FileNotFoundException e) { + log.warn(entryUrl + ": " + e.getMessage()); + } + } + } catch (IOException e1) { + throw new RuntimeException("Cannot export bundle " + bundle, e1); + } + } + if (log.isDebugEnabled()) + log.debug(bundles.length + " OSGi bundles exported to " + bootBasePath); + + } + } diff --git a/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalRestore.java b/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalRestore.java index b430af6e1..145c5bb3d 100644 --- a/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalRestore.java +++ b/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalRestore.java @@ -11,13 +11,17 @@ import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; -import org.apache.commons.io.FilenameUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrException; import org.argeo.jcr.JcrUtils; import org.osgi.framework.BundleContext; /** Restores a backup in the format defined by {@link LogicalBackup}. */ public class LogicalRestore implements Runnable { + private final static Log log = LogFactory.getLog(LogicalRestore.class); + private final Repository repository; private final BundleContext bundleContext; private final Path basePath; @@ -31,15 +35,48 @@ public class LogicalRestore implements Runnable { @Override public void run() { Path workspaces = basePath.resolve(LogicalBackup.WORKSPACES_BASE); - try (DirectoryStream xmls = Files.newDirectoryStream(workspaces, "*.xml")) { - for (Path workspacePath : xmls) { - String workspaceName = FilenameUtils.getBaseName(workspacePath.getFileName().toString()); - Session session = JcrUtils.loginOrCreateWorkspace(repository, workspaceName); - try (InputStream in = Files.newInputStream(workspacePath)) { - session.getWorkspace().importXML("/", in, - ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - } finally { - JcrUtils.logoutQuietly(session); + try { + // import jcr:system first + try (DirectoryStream workspaceDirs = Files.newDirectoryStream(workspaces)) { + dirs: for (Path workspacePath : workspaceDirs) { + String workspaceName = workspacePath.getFileName().toString(); + try (DirectoryStream xmls = Files.newDirectoryStream(workspacePath, "*.xml")) { + for (Path xml : xmls) { + if (xml.getFileName().toString().equals("jcr:system.xml")) { + Session session = JcrUtils.loginOrCreateWorkspace(repository, workspaceName); + try (InputStream in = Files.newInputStream(xml)) { + session.getWorkspace().importXML("/", in, + ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + if (log.isDebugEnabled()) + log.debug("Restored " + xml + " to workspace " + workspaceName); + break dirs; + } finally { + Jcr.logout(session); + } + } + } + } + } + } + // non-system content + try (DirectoryStream workspaceDirs = Files.newDirectoryStream(workspaces)) { + for (Path workspacePath : workspaceDirs) { + String workspaceName = workspacePath.getFileName().toString(); + Session session = JcrUtils.loginOrCreateWorkspace(repository, workspaceName); + try (DirectoryStream xmls = Files.newDirectoryStream(workspacePath, "*.xml")) { + xmls: for (Path xml : xmls) { + if (xml.getFileName().toString().equals("jcr:system.xml")) + continue xmls; + try (InputStream in = Files.newInputStream(xml)) { + session.getWorkspace().importXML("/", in, + ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + if (log.isDebugEnabled()) + log.debug("Restored " + xml + " to workspace " + workspaceName); + } + } + } finally { + Jcr.logout(session); + } } } } catch (IOException e) {