properties.put(NodeConstants.CN, dataModelName);
if (dataModelName.equals(NodeConstants.NODE))
properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
- LocalRepository localRepository = new LocalRepository(repository, dataModelName);
+ LocalRepository localRepository;
+ if (repository instanceof RepositoryImpl)
+ localRepository = new JackrabbitLocalRepository((RepositoryImpl) repository, dataModelName);
+ else
+ localRepository = new LocalRepository(repository, dataModelName);
bc.registerService(Repository.class, localRepository, properties);
if (log.isTraceEnabled())
log.trace("Published data model " + dataModelName);
--- /dev/null
+package org.argeo.cms.internal.kernel;
+
+import java.util.GregorianCalendar;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.api.JackrabbitValue;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.argeo.jcr.JcrUtils;
+
+class JackrabbitLocalRepository extends LocalRepository {
+ private final static Log log = LogFactory.getLog(JackrabbitLocalRepository.class);
+ private final static String MIX_ETAG = "mix:etag";
+ private final static String JCR_ETAG = "jcr:etag";
+
+ private Map<String, WorkspaceMonitor> workspaceMonitors = new TreeMap<>();
+
+ public JackrabbitLocalRepository(RepositoryImpl repository, String cn) {
+ super(repository, cn);
+ Session session = KernelUtils.openAdminSession(repository);
+ try {
+ for (String workspaceName : session.getWorkspace().getAccessibleWorkspaceNames()) {
+ addMonitor(workspaceName);
+ }
+ } catch (RepositoryException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ }
+ }
+
+ protected RepositoryImpl getJackrabbitrepository(String workspaceName) {
+ return (RepositoryImpl) getRepository(workspaceName);
+ }
+
+ @Override
+ protected synchronized void processNewSession(Session session, String workspaceName) {
+ String realWorkspaceName = session.getWorkspace().getName();
+ addMonitor(realWorkspaceName);
+ }
+
+ private void addMonitor(String realWorkspaceName) {
+ if (!workspaceMonitors.containsKey(realWorkspaceName)) {
+ try {
+ WorkspaceMonitor workspaceMonitor = new WorkspaceMonitor(getJackrabbitrepository(realWorkspaceName),
+ getCn(), realWorkspaceName);
+ workspaceMonitors.put(realWorkspaceName, workspaceMonitor);
+ workspaceMonitor.start();
+ if (log.isDebugEnabled())
+ log.debug("Registered " + workspaceMonitor.getName());
+ } catch (RepositoryException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ static String toEtag(Value v) {
+ JackrabbitValue value = (JackrabbitValue) v;
+ return '\"' + value.getContentIdentity() + '\"';
+
+ }
+
+ /** recursive */
+ static void setLastModified(Node node, Event event) throws RepositoryException {
+ GregorianCalendar calendar = new GregorianCalendar();
+ calendar.setTimeInMillis(event.getDate());
+ if (node.isNodeType(NodeType.NT_FOLDER)) {
+ node.addMixin(NodeType.MIX_LAST_MODIFIED);
+ }
+ if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
+ node.setProperty(Property.JCR_LAST_MODIFIED, calendar);
+ node.setProperty(Property.JCR_LAST_MODIFIED_BY, event.getUserID());
+ }
+ if (node.getDepth() == 0)
+ return;
+ Node parent = node.getParent();
+ setLastModified(parent, event);
+ }
+
+ static class WorkspaceMonitor extends Thread implements EventListener {
+ String workspaceName;
+ RepositoryImpl repositoryImpl;
+ Session session;
+
+ public WorkspaceMonitor(RepositoryImpl repositoryImpl, String cn, String workspaceName)
+ throws RepositoryException {
+ super("Monitor workspace " + workspaceName + " of repository " + cn);
+ this.workspaceName = workspaceName;
+ this.repositoryImpl = repositoryImpl;
+ }
+
+ public void run() {
+
+ session = KernelUtils.openAdminSession(repositoryImpl, workspaceName);
+ try {
+ String[] nodeTypes = { NodeType.NT_FILE, NodeType.MIX_LAST_MODIFIED };
+ session.getWorkspace().getObservationManager().addEventListener(this,
+ Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_CHANGED, "/", true, null, nodeTypes,
+ true);
+ while (save()) {
+
+ }
+
+ } catch (RepositoryException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ }
+ }
+
+ protected synchronized boolean save() {
+ try {
+ wait(100);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ if (!session.isLive())
+ return false;
+ try {
+ if (session.hasPendingChanges())
+ session.save();
+ } catch (RepositoryException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ @Override
+ public synchronized void onEvent(EventIterator events) {
+ events: while (events.hasNext()) {
+ Event event = events.nextEvent();
+// if (log.isDebugEnabled())
+// log.debug(event);
+ try {
+ if (event.getType() == Event.NODE_ADDED) {
+ Node node = session.getNode(event.getPath());
+ if (node.getParent().isNodeType(NodeType.NT_FILE)) {
+// Node contentNode = node.getNode(Node.JCR_CONTENT);
+ node.addMixin(NodeType.MIX_LAST_MODIFIED);
+ Property property = node.getProperty(Property.JCR_DATA);
+ String etag = toEtag(property.getValue());
+ node.setProperty(JCR_ETAG, etag);
+ setLastModified(node, event);
+// node.getSession().save();
+ if (log.isDebugEnabled())
+ log.debug("Node " + node.getPath() + ": " + event);
+ }
+ } else if (event.getType() == Event.NODE_REMOVED) {
+ String parentPath = JcrUtils.parentPath(event.getPath());
+ try {
+ Node parent = session.getNode(parentPath);
+ setLastModified(parent, event);
+
+ if (log.isDebugEnabled())
+ log.debug("Node removed from " + parent.getPath() + ": " + event);
+ } catch (ItemNotFoundException | PathNotFoundException e) {
+ continue events;
+ }
+ } else if (event.getType() == Event.PROPERTY_CHANGED) {
+ Property property = session.getProperty(event.getPath());
+ if (property.getName().equals("jcr:lastModified"))
+ continue events;
+ if (property.getType() == PropertyType.BINARY && property.getName().equals("jcr:data")
+ && property.getParent().isNodeType(NodeType.NT_UNSTRUCTURED)) {
+ String etag = toEtag(property.getValue());
+ property.getParent().setProperty(JCR_ETAG, etag);
+ }
+ setLastModified(property.getParent(), event);
+// property.getParent().getSession().save();
+ if (log.isDebugEnabled())
+ log.debug("Property " + property.getPath() + ": " + event);
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ notifyAll();
+
+ }
+
+ }
+}
throws LoginException, NoSuchWorkspaceException, RepositoryException {
Session session;
try {
- session = getRepository().login(credentials, workspaceName);
+ session = getRepository(workspaceName).login(credentials, workspaceName);
} catch (NoSuchWorkspaceException e) {
if (autocreateWorkspaces && workspaceName != null)
session = createWorkspaceAndLogsIn(credentials, workspaceName);
protected void processNewSession(Session session, String workspaceName) {
}
- /** Wraps access to the repository, making sure it is available. */
+ /**
+ * Wraps access to the repository, making sure it is available.
+ *
+ * @deprecated Use {@link #getDefaultRepository()} instead.
+ */
+ @Deprecated
protected synchronized Repository getRepository() {
- // if (repository == null) {
- // throw new ArgeoJcrException("No repository initialized."
- // + " Was the init() method called?"
- // + " The destroy() method should also"
- // + " be called on shutdown.");
- // }
+ return getDefaultRepository();
+ }
+
+ protected synchronized Repository getDefaultRepository() {
return repository;
}
+ protected synchronized Repository getRepository(String workspaceName) {
+ return getDefaultRepository();
+ }
+
/**
* Logs in to the default workspace, creates the required workspace, logs out,
* logs in to the required workspace.
throws RepositoryException {
if (workspaceName == null)
throw new ArgeoJcrException("No workspace specified.");
- Session session = getRepository().login(credentials);
+ Session session = getRepository(workspaceName).login(credentials);
session.getWorkspace().createWorkspace(workspaceName);
session.logout();
- return getRepository().login(credentials, workspaceName);
+ return getRepository(workspaceName).login(credentials, workspaceName);
}
public boolean isStandardDescriptor(String key) {
--- /dev/null
+package org.argeo.maintenance.backup;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.util.Base64;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.jcr.Binary;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class BackupContentHandler extends DefaultHandler {
+ final static int MAX_DEPTH = 1024;
+ final static String SV_NAMESPACE_URI = "http://www.jcp.org/jcr/sv/1.0";
+ // elements
+ final static String NODE = "node";
+ final static String PROPERTY = "property";
+ final static String VALUE = "value";
+ // attributes
+ final static String NAME = "name";
+ final static String MULTIPLE = "multiple";
+ final static String TYPE = "type";
+
+ // values
+ final static String BINARY = "Binary";
+ final static String JCR_CONTENT = "jcr:content";
+
+ private Writer out;
+ private Session session;
+ private Set<String> contentPaths = new TreeSet<>();
+
+ public BackupContentHandler(Writer out, Session session) {
+ super();
+ this.out = out;
+ this.session = session;
+ }
+
+ private int currentDepth = -1;
+ private String[] currentPath = new String[MAX_DEPTH];
+
+ private boolean currentPropertyIsMultiple = false;
+ private String currentEncoded = null;
+ private Base64.Encoder base64encore = Base64.getEncoder();
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ boolean isNode;
+ boolean isProperty;
+ switch (localName) {
+ case NODE:
+ isNode = true;
+ isProperty = false;
+ break;
+ case PROPERTY:
+ isNode = false;
+ isProperty = true;
+ break;
+ default:
+ isNode = false;
+ isProperty = false;
+ }
+
+ if (isNode) {
+ String nodeName = attributes.getValue(SV_NAMESPACE_URI, NAME);
+ currentDepth = currentDepth + 1;
+ if (currentDepth > 0)
+ currentPath[currentDepth - 1] = nodeName;
+// System.out.println(getCurrentPath() + " , depth=" + currentDepth);
+ }
+
+ if (SV_NAMESPACE_URI.equals(uri))
+ try {
+ out.write("<");
+ out.write(localName);
+ if (isProperty)
+ currentPropertyIsMultiple = false; // always reset
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String ns = attributes.getURI(i);
+ if (SV_NAMESPACE_URI.equals(ns)) {
+ String attrName = attributes.getLocalName(i);
+ String attrValue = attributes.getValue(i);
+ out.write(" ");
+ out.write(attrName);
+ out.write("=");
+ out.write("\"");
+ out.write(attrValue);
+ out.write("\"");
+ if (isProperty) {
+ if (MULTIPLE.equals(attrName))
+ currentPropertyIsMultiple = Boolean.parseBoolean(attrValue);
+ else if (TYPE.equals(attrName)) {
+ if (BINARY.equals(attrValue)) {
+ if (JCR_CONTENT.equals(getCurrentName())) {
+ contentPaths.add(getCurrentPath());
+ } else {
+ Binary binary = session.getNode(getCurrentPath()).getProperty(attrName)
+ .getBinary();
+ try (InputStream in = binary.getStream()) {
+ currentEncoded = base64encore.encodeToString(IOUtils.toByteArray(in));
+ } finally {
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (currentDepth == 0) {
+ out.write(" xmlns=\"" + SV_NAMESPACE_URI + "\"");
+ }
+ out.write(">");
+ if (isNode)
+ out.write("\n");
+ else if (isProperty && currentPropertyIsMultiple)
+ out.write("\n");
+ } catch (IOException | RepositoryException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ 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;
+ currentDepth = currentDepth - 1;
+ }
+ boolean isValue = localName.equals(VALUE);
+ if (SV_NAMESPACE_URI.equals(uri))
+ try {
+ if (isValue && currentEncoded != null) {
+ out.write(currentEncoded);
+ }
+ currentEncoded = null;
+ out.write("</");
+ out.write(localName);
+ out.write(">");
+ if (!isValue)
+ out.write("\n");
+ else {
+ if (currentPropertyIsMultiple)
+ out.write("\n");
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ try {
+ out.write(ch, start, length);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ protected String getCurrentName() {
+ assert currentDepth >= 0;
+ if (currentDepth == 0)
+ return "jcr:root";
+ return currentPath[currentDepth - 1];
+ }
+
+ protected String getCurrentPath() {
+ if (currentDepth == 0)
+ return "/";
+ StringBuilder sb = new StringBuilder("/");
+ for (int i = 0; i < currentDepth; i++) {
+ if (i != 0)
+ sb.append('/');
+ sb.append(currentPath[i]);
+ }
+ return sb.toString();
+ }
+
+ public Set<String> getContentPaths() {
+ return contentPaths;
+ }
+
+
+
+}
--- /dev/null
+package org.argeo.maintenance.backup;
+
+import java.io.BufferedWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipOutputStream;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.node.NodeUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.xml.sax.SAXException;
+
+public class LogicalBackup {
+ private final static Log log = LogFactory.getLog(LogicalBackup.class);
+
+ public final static String WORKSPACES_BASE = "workspaces/";
+ public final static String OSGI_BASE = "share/osgi/";
+ private final Repository repository;
+ private final BundleContext bundleContext;
+
+ private final ZipOutputStream zout;
+ private final Path basePath;
+
+ public LogicalBackup(BundleContext bundleContext, Repository repository, ZipOutputStream zout) {
+ this.repository = repository;
+ this.zout = zout;
+ this.basePath = null;
+ this.bundleContext = bundleContext;
+ }
+
+ public LogicalBackup(BundleContext bundleContext, Repository repository, Path basePath) {
+ this.repository = repository;
+ this.zout = null;
+ this.basePath = basePath;
+ this.bundleContext = bundleContext;
+ }
+
+ public void perform() throws RepositoryException, 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());
+ }
+ }
+ }
+ }
+
+ Session defaultSession = login(null);
+ try {
+ String[] workspaceNames = defaultSession.getWorkspace().getAccessibleWorkspaceNames();
+ workspaces: for (String workspaceName : workspaceNames) {
+ if ("security".equals(workspaceName))
+ continue workspaces;
+ perform(workspaceName);
+ }
+ } finally {
+ JcrUtils.logoutQuietly(defaultSession);
+ }
+
+ }
+
+ public void perform(String workspaceName) throws RepositoryException, IOException {
+ Session session = login(workspaceName);
+ try {
+ String relativePath = WORKSPACES_BASE + workspaceName + ".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 (PathNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (SAXException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (RepositoryException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ for (String path : contentHandler.getContentPaths()) {
+ Node contentNode = session.getNode(path);
+ Binary binary = contentNode.getProperty(Property.JCR_DATA).getBinary();
+ String fileRelativePath = WORKSPACES_BASE + workspaceName + contentNode.getParent().getPath();
+ try (InputStream in = binary.getStream(); OutputStream out = openOutputStream(fileRelativePath)) {
+ IOUtils.copy(in, out);
+ if (log.isDebugEnabled())
+ log.debug("Workspace " + workspaceName + ": file content exported to " + fileRelativePath);
+ } finally {
+
+ }
+
+ }
+
+// OutputStream xmlOut = openOutputStream(relativePath);
+// try {
+// session.exportSystemView("/", xmlOut, false, false);
+// } finally {
+// closeOutputStream(relativePath, xmlOut);
+// }
+
+ // TODO scan all binaries
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ }
+ }
+
+ protected OutputStream openOutputStream(String relativePath) throws IOException {
+ if (zout != null) {
+ ZipEntry entry = new ZipEntry(relativePath);
+ zout.putNextEntry(entry);
+ return zout;
+ } else if (basePath != null) {
+ Path targetPath = basePath.resolve(Paths.get(relativePath));
+ Files.createDirectories(targetPath.getParent());
+ return Files.newOutputStream(targetPath);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ protected void closeOutputStream(String relativePath, OutputStream out) throws IOException {
+ if (zout != null) {
+ zout.closeEntry();
+ } else if (basePath != null) {
+ out.close();
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ protected Session login(String workspaceName) {
+ return NodeUtils.openDataAdminSession(repository, workspaceName);
+ }
+}
--- /dev/null
+package org.argeo.maintenance.internal;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import javax.jcr.Repository;
+
+import org.argeo.maintenance.backup.LogicalBackup;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class Activator implements BundleActivator {
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ try {
+ Repository repository = context.getService(context.getServiceReference(Repository.class));
+ Path basePath = Paths.get(System.getProperty("user.dir"), "backup");
+ LogicalBackup backup = new LogicalBackup(context, repository, basePath);
+ backup.perform();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ // TODO Auto-generated method stub
+
+ }
+
+}
/bin/
/target/
+/configuration/
+/state/
+/data/
+/log4j.properties
--- /dev/null
+package org.argeo.osgi.boot.internal.springutil;
+
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Bootstrap {
+
+ public static void main(String[] args) {
+ try {
+ String configurationArea = "file:" + System.getProperty("user.dir") + "/state";
+ String instanceArea = "file:" + System.getProperty("user.dir") + "/data";
+ String log4jUrl = "file:" + System.getProperty("user.dir") + "/log4j.properties";
+
+ System.setProperty("org.osgi.service.http.port", "7070");
+ System.setProperty("log4j.configuration", log4jUrl);
+
+ System.setProperty("osgi.console", "2323");
+ Map<String, String> props = new HashMap<String, String>();
+ props.put("osgi.clean", "true");
+// props.put("osgi.console", "2323");
+ props.put("osgi.configuration.area", configurationArea);
+ props.put("osgi.instance.area", instanceArea);
+
+ System.setProperty("argeo.osgi.start.2.node",
+ "org.eclipse.equinox.console,org.eclipse.equinox.http.servlet,org.eclipse.equinox.ds,"
+ + "org.eclipse.equinox.metatype,org.eclipse.equinox.cm,org.eclipse.rap.rwt.osgi");
+ System.setProperty("argeo.osgi.start.3.node", "org.argeo.cms");
+
+ // URL osgiJar =
+ // Bootstrap.class.getClassLoader().getResource("/usr/share/osgi/boot/org.eclipse.org.jar");
+ URL osgiJar = new URL(
+ "file:///home/mbaudier/dev/git/apache2/argeo-commons/demo/exec/cms-e4-rap/backup/share/osgi/boot/org.eclipse.org.jar");
+ URL osgiBootJar = new URL(
+ "file:///home/mbaudier/dev/git/apache2/argeo-commons/demo/exec/cms-e4-rap/backup/share/osgi/boot/org.argeo.osgi.boot.jar");
+ URL[] jarUrls = { osgiJar };
+ try (URLClassLoader urlCl = new URLClassLoader(jarUrls)) {
+
+ // Class<?> factoryClass =
+ // urlCl.loadClass("/org/eclipse/osgi/launch/EquinoxFactory");
+ Class<?> factoryClass = urlCl.loadClass("org.eclipse.osgi.launch.EquinoxFactory");
+ Class<?> frameworkClass = urlCl.loadClass("org.osgi.framework.launch.Framework");
+ Class<?> bundleContextClass = urlCl.loadClass("org.osgi.framework.BundleContext");
+ Class<?> bundleClass = urlCl.loadClass("org.osgi.framework.Bundle");
+
+ Object factory = factoryClass.getConstructor().newInstance();
+ Method newFrameworkMethod = factoryClass.getMethod("newFramework", Map.class);
+ Object framework = newFrameworkMethod.invoke(factory, props);
+ Method startFramework = frameworkClass.getMethod("start", new Class[] {});
+ startFramework.invoke(framework);
+ Method getBundleContext = frameworkClass.getMethod("getBundleContext", new Class[] {});
+ Object bundleContext = getBundleContext.invoke(framework);
+ Class<?>[] installArgs = { String.class, InputStream.class };
+ Method install = bundleContextClass.getMethod("installBundle", installArgs);
+ Method startBundle = bundleClass.getMethod("start");
+ Method getSymbolicName = bundleClass.getMethod("getSymbolicName");
+
+ Path basePath = Paths.get(
+ "/home/mbaudier/dev/git/apache2/argeo-commons/demo/exec/cms-e4-rap/backup/share/osgi/boot/");
+ List<Object> bundles = new ArrayList<>();
+ for (Path p : Files.newDirectoryStream(basePath)) {
+ try (InputStream in = Files.newInputStream(p)) {
+ Object bundle = install.invoke(bundleContext, "file:" + p, in);
+ bundles.add(bundle);
+ System.out.println("Installed " + bundle);
+ } catch (Exception e) {
+ if (!p.getFileName().toString().startsWith("org.eclipse.osgi")) {
+ System.err.println(p);
+ e.printStackTrace();
+ }
+ }
+ }
+
+// for (Object bundle : bundles) {
+// try {
+// String symbolicName = getSymbolicName.invoke(bundle).toString();
+// startBundle.invoke(bundle);
+// } catch (Exception e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+
+ Object osgiBootBundle = install.invoke(bundleContext, osgiBootJar.toString(), osgiBootJar.openStream());
+ startBundle.invoke(osgiBootBundle);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+}