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;
private SortedMap<LdapName, Attributes> 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);
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);
+// }
}
}
}
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()) {
return null;
}
- static boolean isInitialized() {
+ private static boolean isInitialized() {
return Files.exists(deployConfigPath);
}
+ public boolean isFirstInit() {
+ return isFirstInit;
+ }
+
}
private Session session;
private Set<String> contentPaths = new TreeSet<>();
- private boolean inSystem = false;
+// private boolean inSystem = false;
public BackupContentHandler(Writer out, Session session) {
super();
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 {
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 {
@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) {
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]);
}
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;
import javax.jcr.Binary;
import javax.jcr.Node;
+import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
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;
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() {
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<String, String> headers = bundle.getHeaders();
- Manifest manifest = new Manifest();
- Enumeration<String> 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<String> 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<URL> 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<Set<String>> 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<String> 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<String> 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<Set<String>> contentPathsFuture) {
+ Set<String> 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);
}
}
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<String, String> headers = bundle.getHeaders();
+ Manifest manifest = new Manifest();
+ Enumeration<String> 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<URL> 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);
+
+ }
+
}
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;
@Override
public void run() {
Path workspaces = basePath.resolve(LogicalBackup.WORKSPACES_BASE);
- try (DirectoryStream<Path> 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<Path> workspaceDirs = Files.newDirectoryStream(workspaces)) {
+ dirs: for (Path workspacePath : workspaceDirs) {
+ String workspaceName = workspacePath.getFileName().toString();
+ try (DirectoryStream<Path> 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<Path> workspaceDirs = Files.newDirectoryStream(workspaces)) {
+ for (Path workspacePath : workspaceDirs) {
+ String workspaceName = workspacePath.getFileName().toString();
+ Session session = JcrUtils.loginOrCreateWorkspace(repository, workspaceName);
+ try (DirectoryStream<Path> 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) {