From: Mathieu Baudier Date: Fri, 13 May 2022 05:53:43 +0000 (+0200) Subject: JCR as an ACR backend X-Git-Tag: v2.3.10~233 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=c615307d7b87bcb260d8a9f402c6e0a880862f38;p=lgpl%2Fargeo-commons.git JCR as an ACR backend --- diff --git a/eclipse/org.argeo.cms.swt/.project b/eclipse/org.argeo.cms.swt/.project index 082112e6d..8ac021b59 100644 --- a/eclipse/org.argeo.cms.swt/.project +++ b/eclipse/org.argeo.cms.swt/.project @@ -20,6 +20,11 @@ + + org.eclipse.pde.ds.core.builder + + + org.eclipse.pde.PluginNature diff --git a/eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml.xml b/eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml.xml new file mode 100644 index 000000000..4f2a405d5 --- /dev/null +++ b/eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/eclipse/org.argeo.cms.swt/bnd.bnd b/eclipse/org.argeo.cms.swt/bnd.bnd index 29f820a4c..a162ea4ae 100644 --- a/eclipse/org.argeo.cms.swt/bnd.bnd +++ b/eclipse/org.argeo.cms.swt/bnd.bnd @@ -5,4 +5,7 @@ javax.servlet.*;version="[3,5)",\ * Bundle-ActivationPolicy: lazy - \ No newline at end of file + +Service-Component: \ +OSGI-INF/cmsUserApp.xml.xml + \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/build.properties b/eclipse/org.argeo.cms.swt/build.properties index 0e0438744..606619784 100644 --- a/eclipse/org.argeo.cms.swt/build.properties +++ b/eclipse/org.argeo.cms.swt/build.properties @@ -1,5 +1,5 @@ -source.. = src/ output.. = bin/ bin.includes = META-INF/,\ - . - \ No newline at end of file + .,\ + OSGI-INF/cmsUserApp.xml.xml +source.. = src/ diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java new file mode 100644 index 000000000..75fa19749 --- /dev/null +++ b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java @@ -0,0 +1,17 @@ +package org.argeo.cms.swt; + +import org.argeo.api.cms.CmsUi; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; + +/** A basic {@link CmsUi}, based on an SWT {@link Composite}. */ +public class CmsSwtUi extends Composite implements CmsUi { + + private static final long serialVersionUID = -107939076610406448L; + + public CmsSwtUi(Composite parent, int style) { + super(parent, style); + setLayout(new GridLayout()); + } + +} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/AcrContentTreeView.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/AcrContentTreeView.java new file mode 100644 index 000000000..c6d738020 --- /dev/null +++ b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/AcrContentTreeView.java @@ -0,0 +1,130 @@ +package org.argeo.cms.swt.acr; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import javax.xml.namespace.QName; + +import org.argeo.api.acr.Content; +import org.argeo.cms.acr.fs.FsContentProvider; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; + +public class AcrContentTreeView extends Composite { + private Tree tree; + private Table table; + private Content rootContent; + + private Content selected; + + public AcrContentTreeView(Composite parent, int style, Content content) { + super(parent, style); + this.rootContent = content; + this.selected = rootContent; + setLayout(new GridLayout(2, false)); + initTree(); + GridData treeGd = CmsSwtUtils.fillHeight(); + treeGd.widthHint = 300; + tree.setLayoutData(treeGd); + initTable(); + + table.setLayoutData(CmsSwtUtils.fillAll()); + } + + protected void initTree() { + tree = new Tree(this, 0); + for (Content c : rootContent) { + TreeItem root = new TreeItem(tree, 0); + root.setText(c.getName().toString()); + root.setData(c); + new TreeItem(root, 0); + } + tree.addListener(SWT.Expand, event -> { + final TreeItem root = (TreeItem) event.item; + TreeItem[] items = root.getItems(); + for (TreeItem item : items) { + if (item.getData() != null) + return; + item.dispose(); + } + Content content = (Content) root.getData(); + for (Content c : content) { + TreeItem item = new TreeItem(root, 0); + item.setText(c.getName().toString()); + item.setData(c); + boolean hasChildren = true; + if (hasChildren) { + new TreeItem(item, 0); + } + } + }); + tree.addListener(SWT.Selection, event -> { + TreeItem item = (TreeItem) event.item; + selected = (Content) item.getData(); + refreshTable(); + }); + } + + protected void initTable() { + table = new Table(this, 0); + table.setLinesVisible(true); + table.setHeaderVisible(true); + TableColumn keyCol = new TableColumn(table, SWT.NONE); + keyCol.setText("Attribute"); + keyCol.setWidth(200); + TableColumn valueCol = new TableColumn(table, SWT.NONE); + valueCol.setText("Value"); + keyCol.setWidth(300); + refreshTable(); + } + + protected void refreshTable() { + for (TableItem item : table.getItems()) { + item.dispose(); + } + for (QName key : selected.keySet()) { + TableItem item = new TableItem(table, 0); + item.setText(0, key.toString()); + Object value = selected.get(key); + item.setText(1, value.toString()); + } + table.getColumn(0).pack(); + table.getColumn(1).pack(); + } + + public static void main(String[] args) { + Path basePath; + if (args.length > 0) { + basePath = Paths.get(args[0]); + } else { + basePath = Paths.get(System.getProperty("user.home")); + } + + final Display display = new Display(); + final Shell shell = new Shell(display); + shell.setText(basePath.toString()); + shell.setLayout(new FillLayout()); + + FsContentProvider contentSession = new FsContentProvider(basePath, true); +// GcrContentTreeView treeView = new GcrContentTreeView(shell, 0, contentSession.get("/")); + + shell.setSize(shell.computeSize(800, 600)); + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + } +} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java new file mode 100644 index 000000000..46166b859 --- /dev/null +++ b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java @@ -0,0 +1,11 @@ +package org.argeo.cms.swt.acr; + +import org.argeo.api.acr.Content; +import org.argeo.api.cms.MvcProvider; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +@FunctionalInterface +public interface SwtUiProvider extends MvcProvider { + +} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java new file mode 100644 index 000000000..8bb3cc765 --- /dev/null +++ b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java @@ -0,0 +1,68 @@ +package org.argeo.cms.swt.app; + +import java.util.HashSet; +import java.util.Set; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentRepository; +import org.argeo.api.cms.CmsContext; +import org.argeo.api.cms.CmsUi; +import org.argeo.api.cms.CmsView; +import org.argeo.cms.AbstractCmsApp; +import org.argeo.cms.swt.CmsSwtUi; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.acr.AcrContentTreeView; +import org.argeo.cms.swt.auth.CmsLogin; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; + +public class CmsUserApp extends AbstractCmsApp { + private CmsContext cmsContext; + private ContentRepository contentRepository; + + @Override + public Set getUiNames() { + Set uiNames = new HashSet<>(); + uiNames.add("login"); + uiNames.add("data"); + return uiNames; + } + + @Override + public CmsUi initUi(Object uiParent) { + Composite parent = (Composite) uiParent; + String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null; + CmsSwtUi cmsUi = new CmsSwtUi(parent, SWT.NONE); + if ("login".equals(uiName)) { + CmsView cmsView = CmsSwtUtils.getCmsView(cmsUi); + CmsLogin cmsLogin = new CmsLogin(cmsView, cmsContext); + cmsLogin.createUi(cmsUi); + + } else if ("data".equals(uiName)) { + Content rootContent = contentRepository.get().get("/"); + AcrContentTreeView view = new AcrContentTreeView(cmsUi, 0, rootContent); + view.setLayoutData(CmsSwtUtils.fillAll()); + + } + return cmsUi; + } + + @Override + public void refreshUi(CmsUi cmsUi, String state) { + } + + @Override + public void setState(CmsUi cmsUi, String state) { + // TODO Auto-generated method stub + + } + + public void setContentRepository(ContentRepository contentRepository) { + this.contentRepository = contentRepository; + } + + public void setCmsContext(CmsContext cmsContext) { + this.cmsContext = cmsContext; + } + +} \ No newline at end of file diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/gcr/GcrContentTreeView.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/gcr/GcrContentTreeView.java deleted file mode 100644 index 4039f2baa..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/gcr/GcrContentTreeView.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.argeo.cms.swt.gcr; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import javax.xml.namespace.QName; - -import org.argeo.api.acr.Content; -import org.argeo.cms.acr.fs.FsContentProvider; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TableItem; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeItem; - -public class GcrContentTreeView extends Composite { - private Tree tree; - private Table table; - private Content rootContent; - - private Content selected; - - public GcrContentTreeView(Composite parent, int style, Content content) { - super(parent, style); - this.rootContent = content; - this.selected = rootContent; - setLayout(new GridLayout(2, false)); - initTree(); - GridData treeGd = CmsSwtUtils.fillHeight(); - treeGd.widthHint = 300; - tree.setLayoutData(treeGd); - initTable(); - - table.setLayoutData(CmsSwtUtils.fillAll()); - } - - protected void initTree() { - tree = new Tree(this, 0); - for (Content c : rootContent) { - TreeItem root = new TreeItem(tree, 0); - root.setText(c.getName().toString()); - root.setData(c); - new TreeItem(root, 0); - } - tree.addListener(SWT.Expand, event -> { - final TreeItem root = (TreeItem) event.item; - TreeItem[] items = root.getItems(); - for (TreeItem item : items) { - if (item.getData() != null) - return; - item.dispose(); - } - Content content = (Content) root.getData(); - for (Content c : content) { - TreeItem item = new TreeItem(root, 0); - item.setText(c.getName().toString()); - item.setData(c); - boolean hasChildren = true; - if (hasChildren) { - new TreeItem(item, 0); - } - } - }); - tree.addListener(SWT.Selection, event -> { - TreeItem item = (TreeItem) event.item; - selected = (Content) item.getData(); - refreshTable(); - }); - } - - protected void initTable() { - table = new Table(this, 0); - table.setLinesVisible(true); - table.setHeaderVisible(true); - TableColumn keyCol = new TableColumn(table, SWT.NONE); - keyCol.setText("Attribute"); - keyCol.setWidth(200); - TableColumn valueCol = new TableColumn(table, SWT.NONE); - valueCol.setText("Value"); - keyCol.setWidth(300); - refreshTable(); - } - - protected void refreshTable() { - for (TableItem item : table.getItems()) { - item.dispose(); - } - for (QName key : selected.keySet()) { - TableItem item = new TableItem(table, 0); - item.setText(0, key.toString()); - Object value = selected.get(key); - item.setText(1, value.toString()); - } - table.getColumn(0).pack(); - table.getColumn(1).pack(); - } - - public static void main(String[] args) { - Path basePath; - if (args.length > 0) { - basePath = Paths.get(args[0]); - } else { - basePath = Paths.get(System.getProperty("user.home")); - } - - final Display display = new Display(); - final Shell shell = new Shell(display); - shell.setText(basePath.toString()); - shell.setLayout(new FillLayout()); - - FsContentProvider contentSession = new FsContentProvider(basePath); -// GcrContentTreeView treeView = new GcrContentTreeView(shell, 0, contentSession.get("/")); - - shell.setSize(shell.computeSize(800, 600)); - shell.open(); - while (!shell.isDisposed()) { - if (!display.readAndDispatch()) - display.sleep(); - } - display.dispose(); - } -} diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/gcr/SwtUiProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/gcr/SwtUiProvider.java deleted file mode 100644 index 8109c40ac..000000000 --- a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/gcr/SwtUiProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.argeo.cms.swt.gcr; - -import org.argeo.api.acr.Content; -import org.argeo.api.cms.MvcProvider; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -@FunctionalInterface -public interface SwtUiProvider extends MvcProvider { - -} diff --git a/jcr/org.argeo.cms.jcr/OSGI-INF/jcrContentProvider.xml b/jcr/org.argeo.cms.jcr/OSGI-INF/jcrContentProvider.xml new file mode 100644 index 000000000..eb15b4c81 --- /dev/null +++ b/jcr/org.argeo.cms.jcr/OSGI-INF/jcrContentProvider.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/jcr/org.argeo.cms.jcr/bnd.bnd b/jcr/org.argeo.cms.jcr/bnd.bnd index 065f0d15d..b53745a6a 100644 --- a/jcr/org.argeo.cms.jcr/bnd.bnd +++ b/jcr/org.argeo.cms.jcr/bnd.bnd @@ -37,4 +37,5 @@ OSGI-INF/jcrDeployment.xml,\ OSGI-INF/jcrServletContext.xml,\ OSGI-INF/dataServletContext.xml,\ OSGI-INF/filesServletContext.xml,\ -OSGI-INF/filesServlet.xml +OSGI-INF/filesServlet.xml,\ +OSGI-INF/jcrContentProvider.xml diff --git a/jcr/org.argeo.cms.jcr/build.properties b/jcr/org.argeo.cms.jcr/build.properties index 2b99dd7ba..859c241fe 100644 --- a/jcr/org.argeo.cms.jcr/build.properties +++ b/jcr/org.argeo.cms.jcr/build.properties @@ -1,10 +1,8 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ - OSGI-INF/ + OSGI-INF/,\ + OSGI-INF/jcrContentProvider.xml source.. = src/ -additional.bundles = \ -org.apache.jackrabbit.data, \ -org.apache.jackrabbit.spi.commons,\ - - \ No newline at end of file +additional.bundles = org.apache.jackrabbit.data,\ + org.apache.jackrabbit.spi.commons diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java index 04c5d2d8c..94fe7cf58 100644 --- a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java @@ -1,8 +1,12 @@ package org.argeo.cms.jcr.acr; +import java.util.ArrayList; import java.util.Calendar; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Optional; +import java.util.Set; import javax.jcr.Node; import javax.jcr.NodeIterator; @@ -15,11 +19,13 @@ import javax.jcr.nodetype.NodeType; import javax.xml.namespace.QName; import org.argeo.api.acr.Content; +import org.argeo.api.acr.NamespaceUtils; import org.argeo.api.acr.spi.AbstractContent; import org.argeo.api.acr.spi.ProvidedSession; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrException; +/** A JCR {@link Node} accessed as {@link Content}. */ public class JcrContent extends AbstractContent { private Node jcrNode; @@ -34,7 +40,11 @@ public class JcrContent extends AbstractContent { @Override public QName getName() { - return session.parsePrefixedName(Jcr.getName(jcrNode)); + String name = Jcr.getName(jcrNode); + if (name.equals("")) {// root + name = Jcr.getWorkspaceName(jcrNode); + } + return NamespaceUtils.parsePrefixedName(provider, name); } @Override @@ -56,18 +66,32 @@ public class JcrContent extends AbstractContent { @Override protected Iterable keys() { - return new Iterable() { - - @Override - public Iterator iterator() { - try { - PropertyIterator propertyIterator = jcrNode.getProperties(); - return new JcrKeyIterator(provider, propertyIterator); - } catch (RepositoryException e) { - throw new JcrException("Cannot retrive properties from " + jcrNode, e); - } + try { + Set keys = new HashSet<>(); + properties: for (PropertyIterator propertyIterator = jcrNode.getProperties(); propertyIterator.hasNext();) { + Property property = propertyIterator.nextProperty(); + // TODO convert standard names + // TODO skip technical properties + QName name = NamespaceUtils.parsePrefixedName(provider, property.getName()); + keys.add(name); } - }; + return keys; + } catch (RepositoryException e) { + throw new JcrException("Cannot list properties of " + jcrNode, e); + } + +// return new Iterable() { +// +// @Override +// public Iterator iterator() { +// try { +// PropertyIterator propertyIterator = jcrNode.getProperties(); +// return new JcrKeyIterator(provider, propertyIterator); +// } catch (RepositoryException e) { +// throw new JcrException("Cannot retrive properties from " + jcrNode, e); +// } +// } +// }; } public Node getJcrNode() { @@ -77,29 +101,43 @@ public class JcrContent extends AbstractContent { /** Cast to a standard Java object. */ static Object get(Node node, String property) { try { - Value value = node.getProperty(property).getValue(); - switch (value.getType()) { - case PropertyType.STRING: - return value.getString(); - case PropertyType.DOUBLE: - return (Double) value.getDouble(); - case PropertyType.LONG: - return (Long) value.getLong(); - case PropertyType.BOOLEAN: - return (Boolean) value.getBoolean(); - case PropertyType.DATE: - Calendar calendar = value.getDate(); - return calendar.toInstant(); - case PropertyType.BINARY: - throw new IllegalArgumentException("Binary is not supported as an attribute"); - default: - return value.getString(); + Property p = node.getProperty(property); + if (p.isMultiple()) { + Value[] values = p.getValues(); + List lst = new ArrayList<>(); + for (Value value : values) { + lst.add(convertSingleValue(value)); + } + return lst; + } else { + Value value = node.getProperty(property).getValue(); + return convertSingleValue(value); } } catch (RepositoryException e) { throw new JcrException("Cannot cast value from " + property + " of node " + node, e); } } + static Object convertSingleValue(Value value) throws RepositoryException { + switch (value.getType()) { + case PropertyType.STRING: + return value.getString(); + case PropertyType.DOUBLE: + return (Double) value.getDouble(); + case PropertyType.LONG: + return (Long) value.getLong(); + case PropertyType.BOOLEAN: + return (Boolean) value.getBoolean(); + case PropertyType.DATE: + Calendar calendar = value.getDate(); + return calendar.toInstant(); + case PropertyType.BINARY: + throw new IllegalArgumentException("Binary is not supported as an attribute"); + default: + return value.getString(); + } + } + class JcrContentIterator implements Iterator { private final NodeIterator nodeIterator; // we keep track in order to be able to delete it @@ -191,7 +229,7 @@ public class JcrContent extends AbstractContent { try { property = propertyIterator.nextProperty(); // TODO map standard property names - return session.parsePrefixedName(property.getName()); + return NamespaceUtils.parsePrefixedName(provider, property.getName()); } catch (RepositoryException e) { throw new JcrException("Cannot retrieve property " + property, null); } diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java index ef8e375d0..fc4a61bf9 100644 --- a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java @@ -1,29 +1,37 @@ package org.argeo.cms.jcr.acr; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; +import javax.jcr.Node; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.xml.namespace.NamespaceContext; import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentUtils; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedSession; import org.argeo.cms.jcr.CmsJcrUtils; import org.argeo.jcr.JcrException; import org.argeo.jcr.JcrUtils; +/** A JCR workspace accessed as an {@link ContentProvider}. */ public class JcrContentProvider implements ContentProvider, NamespaceContext { private Repository jcrRepository; private Session adminSession; - public void init() { + private Map sessionAdapters = Collections.synchronizedMap(new HashMap<>()); + + public void start() { adminSession = CmsJcrUtils.openDataAdminSession(jcrRepository, null); } - public void destroy() { + public void stop() { JcrUtils.logoutQuietly(adminSession); } @@ -32,9 +40,26 @@ public class JcrContentProvider implements ContentProvider, NamespaceContext { } @Override - public Content get(ProvidedSession session, String mountPath, String relativePath) { - // TODO Auto-generated method stub - return null; + public Content get(ProvidedSession contentSession, String mountPath, String relativePath) { + String workspace = ContentUtils.getParentPath(mountPath)[1]; + JcrSessionAdapter sessionAdapter = sessionAdapters.get(contentSession); + if (sessionAdapter == null) { + final JcrSessionAdapter newSessionAdapter = new JcrSessionAdapter(jcrRepository, + contentSession.getSubject()); + sessionAdapters.put(contentSession, newSessionAdapter); + contentSession.onClose().thenAccept((s) -> newSessionAdapter.close()); + sessionAdapter = newSessionAdapter; + } + + Session jcrSession = sessionAdapter.getSession(workspace); + String jcrPath = "/" + relativePath; + try { + Node node = jcrSession.getNode(jcrPath); + return new JcrContent(contentSession, this, node); + } catch (RepositoryException e) { + throw new JcrException("Cannot get JCR content '" + jcrPath + ", mounted from '" + mountPath + + "' with JCR session " + jcrSession, e); + } } /* diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrSessionAdapter.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrSessionAdapter.java new file mode 100644 index 000000000..10b243bcb --- /dev/null +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrSessionAdapter.java @@ -0,0 +1,69 @@ +package org.argeo.cms.jcr.acr; + +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.security.auth.Subject; + +import org.argeo.jcr.JcrUtils; + +/** Manages JCR {@link Session} in an ACR context. */ +class JcrSessionAdapter { + private Repository repository; + private Subject subject; + + private Map> threadSessions = Collections.synchronizedMap(new HashMap<>()); + + private boolean closed = false; + + public JcrSessionAdapter(Repository repository, Subject subject) { + this.repository = repository; + this.subject = subject; + } + + public synchronized void close() { + for (Map sessions : threadSessions.values()) { + for (Session session : sessions.values()) { + JcrUtils.logoutQuietly(session); + } + sessions.clear(); + } + threadSessions.clear(); + closed = true; + } + + public synchronized Session getSession(String workspace) { + if (closed) + throw new IllegalStateException("JCR session adapter is closed."); + + Thread currentThread = Thread.currentThread(); + + Map threadSession = threadSessions.get(currentThread); + if (threadSession == null) { + threadSession = new HashMap<>(); + threadSessions.put(currentThread, threadSession); + } + + Session session = threadSession.get(workspace); + if (session == null) { + session = Subject.doAs(subject, (PrivilegedAction) () -> { + try { + Session sess = repository.login(workspace); + return sess; + } catch (RepositoryException e) { + throw new IllegalStateException("Cannot log in to " + workspace, e); + } + }); + threadSession.put(workspace, session); + } + return session; + } + +} diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java b/org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java index 341a3e297..1fee92e39 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java @@ -48,7 +48,7 @@ public class ContentName extends QName { private static String checkPrefix(NamespaceContext nsContext, String namespaceURI) { Objects.requireNonNull(nsContext, "Namespace context cannot be null"); Objects.requireNonNull(namespaceURI, "Namespace URI cannot be null"); - String prefix = nsContext.getNamespaceURI(namespaceURI); + String prefix = nsContext.getPrefix(namespaceURI); if (prefix == null) throw new IllegalStateException("No prefix found for " + namespaceURI + " from context " + nsContext); return prefix; diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/ContentNameSupplier.java b/org.argeo.api.acr/src/org/argeo/api/acr/ContentNameSupplier.java index e3c721fef..f7900afd3 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/ContentNameSupplier.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/ContentNameSupplier.java @@ -45,7 +45,7 @@ public interface ContentNameSupplier extends Supplier, NamespaceCon @Override default String getNamespaceURI(String prefix) { - String namespaceURI = getStandardNamespaceURI(prefix); + String namespaceURI = NamespaceUtils.getStandardNamespaceURI(prefix); if (namespaceURI != null) return namespaceURI; if (prefix.equals(getDefaultPrefix())) @@ -55,7 +55,7 @@ public interface ContentNameSupplier extends Supplier, NamespaceCon @Override default String getPrefix(String namespaceURI) { - String prefix = getStandardPrefix(namespaceURI); + String prefix = NamespaceUtils.getStandardPrefix(namespaceURI); if (prefix != null) return prefix; if (namespaceURI.equals(getNamespaceURI())) @@ -65,7 +65,7 @@ public interface ContentNameSupplier extends Supplier, NamespaceCon @Override default Iterator getPrefixes(String namespaceURI) { - Iterator it = getStandardPrefixes(namespaceURI); + Iterator it = NamespaceUtils.getStandardPrefixes(namespaceURI); if (it != null) return it; if (namespaceURI.equals(getNamespaceURI())) @@ -73,34 +73,4 @@ public interface ContentNameSupplier extends Supplier, NamespaceCon return Collections.emptyIterator(); } - /* - * DEFAULT NAMESPACE CONTEXT OPERATIONS as specified in NamespaceContext - */ - static String getStandardPrefix(String namespaceURI) { - if (namespaceURI == null) - throw new IllegalArgumentException("Namespace URI cannot be null"); - if (XMLConstants.XML_NS_URI.equals(namespaceURI)) - return XMLConstants.XML_NS_PREFIX; - else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)) - return XMLConstants.XMLNS_ATTRIBUTE; - return null; - } - - static Iterator getStandardPrefixes(String namespaceURI) { - String prefix = ContentNameSupplier.getStandardPrefix(namespaceURI); - if (prefix == null) - return null; - return Collections.singleton(prefix).iterator(); - } - - static String getStandardNamespaceURI(String prefix) { - if (prefix == null) - throw new IllegalArgumentException("Prefix cannot be null"); - if (XMLConstants.XML_NS_PREFIX.equals(prefix)) - return XMLConstants.XML_NS_URI; - else if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) - return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; - return null; - } - } diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java b/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java index 215bb9e22..e3a09f603 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java @@ -1,12 +1,9 @@ package org.argeo.api.acr; import java.util.Locale; -import java.util.Objects; import javax.security.auth.Subject; -import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; -import javax.xml.namespace.QName; public interface ContentSession extends NamespaceContext { Subject getSubject(); @@ -15,35 +12,4 @@ public interface ContentSession extends NamespaceContext { Content get(String path); - /* - * NAMESPACE CONTEXT - */ - - default ContentName parsePrefixedName(String nameWithPrefix) { - Objects.requireNonNull(nameWithPrefix, "Name cannot be null"); - if (nameWithPrefix.charAt(0) == '{') { - return new ContentName(QName.valueOf(nameWithPrefix), this); - } - int index = nameWithPrefix.indexOf(':'); - if (index < 0) { - return new ContentName(nameWithPrefix); - } - String prefix = nameWithPrefix.substring(0, index); - // TODO deal with empty name? - String localName = nameWithPrefix.substring(index + 1); - String namespaceURI = getNamespaceURI(prefix); - if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) - throw new IllegalStateException("Prefix " + prefix + " is unbound."); - return new ContentName(namespaceURI, localName, prefix); - } - - default String toPrefixedName(QName name) { - if (XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI())) - return name.getLocalPart(); - String prefix = getPrefix(name.getNamespaceURI()); - if (prefix == null) - throw new IllegalStateException("Namespace " + name.getNamespaceURI() + " is unbound."); - return prefix + ":" + name.getLocalPart(); - } - } diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/ContentUtils.java b/org.argeo.api.acr/src/org/argeo/api/acr/ContentUtils.java index 2036c8622..5d1f59920 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/ContentUtils.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/ContentUtils.java @@ -66,12 +66,25 @@ public class ContentUtils { } - public static boolean isString(T t) { - return t instanceof String; +// public static boolean isString(T t) { +// return t instanceof String; +// } + + /** + * Split a path (with '/' separator) in an array of length 2, the first part + * being the parent path (which could be either absolute or relative), the + * second one being the last segment, (guaranteed to be with '/'). + */ + public static String[] getParentPath(String path) { + int parentIndex = path.lastIndexOf('/'); + // TODO make it more robust + return new String[] { parentIndex != 0 ? path.substring(0, parentIndex) : "/", + path.substring(parentIndex + 1) }; } /** Singleton. */ private ContentUtils() { } + } diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/CrName.java b/org.argeo.api.acr/src/org/argeo/api/acr/CrName.java index 099be9fda..7e089e8b2 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/CrName.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/CrName.java @@ -12,6 +12,7 @@ public enum CrName implements ContentNameSupplier { * ATTRIBUTES */ UUID, // the UUID of a content + MOUNT, /* * ATTRIBUTES FROM FILE SEMANTICS diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java b/org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java new file mode 100644 index 000000000..a1b4062aa --- /dev/null +++ b/org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java @@ -0,0 +1,108 @@ +package org.argeo.api.acr; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; + +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; + +public class NamespaceUtils { + + public static ContentName parsePrefixedName(NamespaceContext nameSpaceContext, String nameWithPrefix) { + Objects.requireNonNull(nameWithPrefix, "Name cannot be null"); + if (nameWithPrefix.charAt(0) == '{') { + return new ContentName(QName.valueOf(nameWithPrefix), nameSpaceContext); + } + int index = nameWithPrefix.indexOf(':'); + if (index < 0) { + return new ContentName(nameWithPrefix); + } + String prefix = nameWithPrefix.substring(0, index); + // TODO deal with empty name? + String localName = nameWithPrefix.substring(index + 1); + String namespaceURI = nameSpaceContext.getNamespaceURI(prefix); + if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) + throw new IllegalStateException("Prefix " + prefix + " is unbound."); + return new ContentName(namespaceURI, localName, prefix); + } + + public static String toPrefixedName(NamespaceContext nameSpaceContext, QName name) { + if (XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI())) + return name.getLocalPart(); + String prefix = nameSpaceContext.getPrefix(name.getNamespaceURI()); + if (prefix == null) + throw new IllegalStateException("Namespace " + name.getNamespaceURI() + " is unbound."); + return prefix + ":" + name.getLocalPart(); + } + + /** singleton */ + private NamespaceUtils() { + } + + /* + * DEFAULT NAMESPACE CONTEXT OPERATIONS as specified in NamespaceContext + */ + public static String getStandardPrefix(String namespaceURI) { + if (namespaceURI == null) + throw new IllegalArgumentException("Namespace URI cannot be null"); + if (XMLConstants.XML_NS_URI.equals(namespaceURI)) + return XMLConstants.XML_NS_PREFIX; + else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)) + return XMLConstants.XMLNS_ATTRIBUTE; + return null; + } + + public static Iterator getStandardPrefixes(String namespaceURI) { + String prefix = getStandardPrefix(namespaceURI); + if (prefix == null) + return null; + return Collections.singleton(prefix).iterator(); + } + + public static String getStandardNamespaceURI(String prefix) { + if (prefix == null) + throw new IllegalArgumentException("Prefix cannot be null"); + if (XMLConstants.XML_NS_PREFIX.equals(prefix)) + return XMLConstants.XML_NS_URI; + else if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) + return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; + return null; + } + + public static String getNamespaceURI(Function mapping, String prefix) { + String namespaceURI = NamespaceUtils.getStandardNamespaceURI(prefix); + if (namespaceURI != null) + return namespaceURI; + if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) + return XMLConstants.NULL_NS_URI; + namespaceURI = mapping.apply(prefix); + if (namespaceURI != null) + return namespaceURI; + return XMLConstants.NULL_NS_URI; + } + + public static String getPrefix(Function mapping, String namespaceURI) { + String prefix = NamespaceUtils.getStandardPrefix(namespaceURI); + if (prefix != null) + return prefix; + if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) + return XMLConstants.DEFAULT_NS_PREFIX; + return mapping.apply(namespaceURI); + } + + public static Iterator getPrefixes(Function> mapping, String namespaceURI) { + Iterator standard = NamespaceUtils.getStandardPrefixes(namespaceURI); + if (standard != null) + return standard; + if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) + return Collections.singleton(XMLConstants.DEFAULT_NS_PREFIX).iterator(); + Set prefixes = mapping.apply(namespaceURI); + assert prefixes != null; + return prefixes.iterator(); + } + +} diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java index d83cf49c9..9d2215f65 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java @@ -1,9 +1,30 @@ package org.argeo.api.acr.spi; +import java.util.Iterator; + +import javax.xml.namespace.NamespaceContext; + import org.argeo.api.acr.Content; -public interface ContentProvider { +public interface ContentProvider extends NamespaceContext { Content get(ProvidedSession session, String mountPath, String relativePath); + /* + * NAMESPACE CONTEXT + */ + @Override + default String getPrefix(String namespaceURI) { + Iterator prefixes = getPrefixes(namespaceURI); + return prefixes.hasNext() ? prefixes.next() : null; + } + +// default ContentName parsePrefixedName(String nameWithPrefix) { +// return NamespaceUtils.parsePrefixedName(this, nameWithPrefix); +// } +// +// default String toPrefixedName(QName name) { +// return NamespaceUtils.toPrefixedName(this, name); +// } + } diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java index f90d67475..60f64def8 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java @@ -1,68 +1,74 @@ package org.argeo.api.acr.spi; -import java.util.Collections; import java.util.Iterator; -import java.util.Set; +import java.util.concurrent.CompletionStage; -import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; -import org.argeo.api.acr.ContentNameSupplier; import org.argeo.api.acr.ContentSession; -public interface ProvidedSession extends ContentSession, NamespaceContext { +public interface ProvidedSession extends ContentSession { ProvidedRepository getRepository(); + CompletionStage onClose(); + /* * NAMESPACE CONTEXT */ - /** @return the bound namespace or null if not found */ - String findNamespace(String prefix); - - // TODO find the default prefix? - Set findPrefixes(String namespaceURI); - - /** To be overridden for optimisation, as it will be called a lot */ - default String findPrefix(String namespaceURI) { - Set prefixes = findPrefixes(namespaceURI); - if (prefixes.isEmpty()) - return null; - return prefixes.iterator().next(); - } - - @Override - default String getNamespaceURI(String prefix) { - String namespaceURI = ContentNameSupplier.getStandardNamespaceURI(prefix); - if (namespaceURI != null) - return namespaceURI; - if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) - return XMLConstants.NULL_NS_URI; - namespaceURI = findNamespace(prefix); - if (namespaceURI != null) - return namespaceURI; - return XMLConstants.NULL_NS_URI; - } @Override default String getPrefix(String namespaceURI) { - String prefix = ContentNameSupplier.getStandardPrefix(namespaceURI); - if (prefix != null) - return prefix; - if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) - return XMLConstants.DEFAULT_NS_PREFIX; - return findPrefix(namespaceURI); + Iterator prefixes = getPrefixes(namespaceURI); + return prefixes.hasNext() ? prefixes.next() : null; } - @Override - default Iterator getPrefixes(String namespaceURI) { - Iterator standard = ContentNameSupplier.getStandardPrefixes(namespaceURI); - if (standard != null) - return standard; - if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) - return Collections.singleton(XMLConstants.DEFAULT_NS_PREFIX).iterator(); - Set prefixes = findPrefixes(namespaceURI); - assert prefixes != null; - return prefixes.iterator(); - } +// /** @return the bound namespace or null if not found */ +// String findNamespace(String prefix); +// +// // TODO find the default prefix? +// Set findPrefixes(String namespaceURI); +// +// /** To be overridden for optimisation, as it will be called a lot */ +// default String findPrefix(String namespaceURI) { +// Set prefixes = findPrefixes(namespaceURI); +// if (prefixes.isEmpty()) +// return null; +// return prefixes.iterator().next(); +// } + +// @Override +// default String getNamespaceURI(String prefix) { +// String namespaceURI = NamespaceUtils.getStandardNamespaceURI(prefix); +// if (namespaceURI != null) +// return namespaceURI; +// if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) +// return XMLConstants.NULL_NS_URI; +// namespaceURI = findNamespace(prefix); +// if (namespaceURI != null) +// return namespaceURI; +// return XMLConstants.NULL_NS_URI; +// } +// +// @Override +// default String getPrefix(String namespaceURI) { +// String prefix = NamespaceUtils.getStandardPrefix(namespaceURI); +// if (prefix != null) +// return prefix; +// if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) +// return XMLConstants.DEFAULT_NS_PREFIX; +// return findPrefix(namespaceURI); +// } +// +// @Override +// default Iterator getPrefixes(String namespaceURI) { +// Iterator standard = NamespaceUtils.getStandardPrefixes(namespaceURI); +// if (standard != null) +// return standard; +// if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) +// return Collections.singleton(XMLConstants.DEFAULT_NS_PREFIX).iterator(); +// Set prefixes = findPrefixes(namespaceURI); +// assert prefixes != null; +// return prefixes.iterator(); +// } } diff --git a/org.argeo.cms/OSGI-INF/acrContentRepository.xml b/org.argeo.cms/OSGI-INF/acrContentRepository.xml new file mode 100644 index 000000000..281005dd3 --- /dev/null +++ b/org.argeo.cms/OSGI-INF/acrContentRepository.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd index e75adcdc3..da3354059 100644 --- a/org.argeo.cms/bnd.bnd +++ b/org.argeo.cms/bnd.bnd @@ -13,6 +13,7 @@ OSGI-INF/simpleTransactionManager.xml,\ OSGI-INF/nodeUserAdmin.xml,\ OSGI-INF/cmsUserManager.xml,\ OSGI-INF/deployConfig.xml,\ +OSGI-INF/acrContentRepository.xml,\ OSGI-INF/cmsDeployment.xml,\ OSGI-INF/cmsContext.xml,\ diff --git a/org.argeo.cms/build.properties b/org.argeo.cms/build.properties index db86d95a5..d1480bf5d 100644 --- a/org.argeo.cms/build.properties +++ b/org.argeo.cms/build.properties @@ -8,5 +8,6 @@ bin.includes = META-INF/,\ OSGI-INF/nodeUserAdmin.xml,\ OSGI-INF/deployConfig.xml,\ OSGI-INF/cmsDeployment.xml,\ - OSGI-INF/cmsContext.xml + OSGI-INF/cmsContext.xml,\ + OSGI-INF/acrContentRepository.xml source.. = src/ diff --git a/org.argeo.cms/src/org/argeo/cms/AbstractCmsApp.java b/org.argeo.cms/src/org/argeo/cms/AbstractCmsApp.java index a7049a4f4..7bf2c96d5 100644 --- a/org.argeo.cms/src/org/argeo/cms/AbstractCmsApp.java +++ b/org.argeo.cms/src/org/argeo/cms/AbstractCmsApp.java @@ -16,7 +16,10 @@ public abstract class AbstractCmsApp implements CmsApp { private List cmsAppListeners = new ArrayList<>(); - protected abstract String getThemeId(String uiName); + /** To be overridden in order to provide themes. */ + protected String getThemeId(String uiName) { + return null; + } @Override public CmsTheme getTheme(String uiName) { @@ -35,7 +38,7 @@ public abstract class AbstractCmsApp implements CmsApp { String themeId = getThemeId(uiName); if ("org.eclipse.rap.rwt.theme.Default".equals(themeId)) continue uiNames; - if (!themes.containsKey(themeId)) { + if (themeId != null && !themes.containsKey(themeId)) { themeMissing = true; break uiNames; } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java index 9cd8bd22d..6d17ea87d 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java @@ -1,38 +1,97 @@ package org.argeo.cms.acr; -import java.security.AccessController; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.NavigableMap; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.validation.Schema; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentSession; +import org.argeo.api.acr.ContentUtils; import org.argeo.api.acr.CrName; +import org.argeo.api.acr.NamespaceUtils; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedRepository; import org.argeo.api.acr.spi.ProvidedSession; +import org.argeo.api.cms.CmsAuth; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.CmsSession; +import org.argeo.cms.acr.xml.DomContentProvider; +import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.internal.runtime.CmsContextImpl; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +/** + * Base implementation of a {@link ProvidedRepository} integrated with a CMS. + */ public class CmsContentRepository implements ProvidedRepository { + private final static CmsLog log = CmsLog.getLog(CmsContentRepository.class); + private NavigableMap partitions = new TreeMap<>(); // TODO synchronize ? private NavigableMap prefixes = new TreeMap<>(); + private Schema schema; + + private CmsContentSession systemSession; + + private Map userSessions = Collections.synchronizedMap(new HashMap<>()); + public CmsContentRepository() { prefixes.put(CrName.CR_DEFAULT_PREFIX, CrName.CR_NAMESPACE_URI); prefixes.put("basic", CrName.CR_NAMESPACE_URI); prefixes.put("owner", CrName.CR_NAMESPACE_URI); prefixes.put("posix", CrName.CR_NAMESPACE_URI); + + systemSession = newSystemSession(); } - public void start() { + protected CmsContentSession newSystemSession() { + LoginContext loginContext; + try { + loginContext = new LoginContext(CmsAuth.DATA_ADMIN.getLoginContextName()); + loginContext.login(); + } catch (LoginException e1) { + throw new RuntimeException("Could not login as data admin", e1); + } finally { + } + return new CmsContentSession(loginContext.getSubject(), Locale.getDefault()); + } + public void start() { } public void stop() { @@ -50,12 +109,31 @@ public class CmsContentRepository implements ProvidedRepository { @Override public ContentSession get(Locale locale) { - Subject subject = Subject.getSubject(AccessController.getContext()); - return new CmsContentSession(subject, locale); + // Subject subject = Subject.getSubject(AccessController.getContext()); + CmsSession cmsSession = CurrentUser.getCmsSession(); + CmsContentSession contentSession = userSessions.get(cmsSession); + if (contentSession == null) { + final CmsContentSession newContentSession = new CmsContentSession(cmsSession.getSubject(), locale); + cmsSession.addOnCloseCallback((c) -> { + newContentSession.close(); + userSessions.remove(cmsSession); + }); + contentSession = newContentSession; + } + return contentSession; } public void addProvider(String base, ContentProvider provider) { partitions.put(base, provider); + if ("/".equals(base))// root + return; + String[] parentPath = ContentUtils.getParentPath(base); + Content parent = systemSession.get(parentPath[0]); + Content mount = parent.add(parentPath[1]); + // TODO use a boolean + // ContentName name = new ContentName(CrName.MOUNT.getNamespaceURI(), + // CrName.MOUNT.name(), systemSession); + mount.put(CrName.MOUNT.get(), "true"); } public void registerPrefix(String prefix, String namespaceURI) { @@ -69,6 +147,68 @@ public class CmsContentRepository implements ProvidedRepository { // do nothing if same namespace is already registered } + /* + * FACTORIES + */ + public void initRootContentProvider(Path path) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setXIncludeAware(true); + // factory.setSchema(schema); + + DocumentBuilder dBuilder = factory.newDocumentBuilder(); + dBuilder.setErrorHandler(new ErrorHandler() { + + @Override + public void warning(SAXParseException exception) throws SAXException { + } + + @Override + public void fatalError(SAXParseException exception) throws SAXException { + } + + @Override + public void error(SAXParseException exception) throws SAXException { + log.error(exception); + + } + }); + + Document document; + if (Files.exists(path)) { + InputSource inputSource = new InputSource(path.toAbsolutePath().toUri().toString()); + inputSource.setEncoding(StandardCharsets.UTF_8.name()); + // TODO public id as well? + document = dBuilder.parse(inputSource); + } else { + document = dBuilder.newDocument(); +// Element root = document.createElementNS(CrName.ROOT.getNamespaceURI(), +// CrName.ROOT.get().toPrefixedString()); + Element root = document.createElement(CrName.ROOT.get().toPrefixedString()); + // root.setAttribute("xmlns", ""); + root.setAttribute("xmlns:" + CrName.CR_DEFAULT_PREFIX, CrName.CR_NAMESPACE_URI); + document.appendChild(root); + + // write it + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(document); + try (Writer writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) { + StreamResult result = new StreamResult(writer); + transformer.transform(source, result); + } + } + + DomContentProvider contentProvider = new DomContentProvider(document); + addProvider("/", contentProvider); + } catch (DOMException | ParserConfigurationException | SAXException | IOException + | TransformerFactoryConfigurationError | TransformerException e) { + throw new IllegalStateException("Cannot init ACR root " + path, e); + } + + } + /* * NAMESPACE CONTEXT */ @@ -81,11 +221,22 @@ public class CmsContentRepository implements ProvidedRepository { private Subject subject; private Locale locale; + private CompletableFuture closed = new CompletableFuture<>(); + public CmsContentSession(Subject subject, Locale locale) { this.subject = subject; this.locale = locale; } + public void close() { + closed.complete(this); + } + + @Override + public CompletionStage onClose() { + return closed.minimalCompletionStage(); + } + @Override public Content get(String path) { Map.Entry entry = partitions.floorEntry(path); @@ -115,24 +266,35 @@ public class CmsContentRepository implements ProvidedRepository { */ @Override - public String findNamespace(String prefix) { - return prefixes.get(prefix); + public String getNamespaceURI(String prefix) { + return NamespaceUtils.getNamespaceURI((p) -> prefixes.get(p), prefix); } @Override - public Set findPrefixes(String namespaceURI) { - Set res = prefixes.entrySet().stream().filter(e -> e.getValue().equals(namespaceURI)) - .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()); - - return res; + public Iterator getPrefixes(String namespaceURI) { + return NamespaceUtils.getPrefixes((ns) -> prefixes.entrySet().stream().filter(e -> e.getValue().equals(ns)) + .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()), namespaceURI); } - @Override - public String findPrefix(String namespaceURI) { - if (CrName.CR_NAMESPACE_URI.equals(namespaceURI) && prefixes.containsKey(CrName.CR_DEFAULT_PREFIX)) - return CrName.CR_DEFAULT_PREFIX; - return ProvidedSession.super.findPrefix(namespaceURI); - } +// @Override +// public String findNamespace(String prefix) { +// return prefixes.get(prefix); +// } +// +// @Override +// public Set findPrefixes(String namespaceURI) { +// Set res = prefixes.entrySet().stream().filter(e -> e.getValue().equals(namespaceURI)) +// .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()); +// +// return res; +// } +// +// @Override +// public String findPrefix(String namespaceURI) { +// if (CrName.CR_NAMESPACE_URI.equals(namespaceURI) && prefixes.containsKey(CrName.CR_DEFAULT_PREFIX)) +// return CrName.CR_DEFAULT_PREFIX; +// return ProvidedSession.super.findPrefix(namespaceURI); +// } } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java index bfcd0118d..b8bbddb50 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java @@ -22,13 +22,14 @@ import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentName; import org.argeo.api.acr.ContentResourceException; import org.argeo.api.acr.CrName; +import org.argeo.api.acr.NamespaceUtils; import org.argeo.api.acr.spi.AbstractContent; import org.argeo.api.acr.spi.ProvidedContent; import org.argeo.api.acr.spi.ProvidedSession; import org.argeo.util.FsUtils; public class FsContent extends AbstractContent implements ProvidedContent { - private final static String USER_ = "user:"; + final static String USER_ = "user:"; private static final Map BASIC_KEYS; private static final Map POSIX_KEYS; @@ -59,8 +60,10 @@ public class FsContent extends AbstractContent implements ProvidedContent { // TODO check file names with ':' ? if (isRoot) this.name = CrName.ROOT.get(); - else - this.name = session.parsePrefixedName(path.getFileName().toString()); + else { + QName providerName = NamespaceUtils.parsePrefixedName(provider, path.getFileName().toString()); + this.name = new ContentName(providerName, session); + } } protected FsContent(FsContent context, Path path) { @@ -118,7 +121,9 @@ public class FsContent extends AbstractContent implements ProvidedContent { if (udfav != null) { try { for (String name : udfav.list()) { - result.add(session.parsePrefixedName(name)); + QName providerName = NamespaceUtils.parsePrefixedName(provider, name); + QName sessionName = new ContentName(providerName, session); + result.add(sessionName); } } catch (IOException e) { throw new ContentResourceException("Cannot list attributes for " + path, e); @@ -131,7 +136,7 @@ public class FsContent extends AbstractContent implements ProvidedContent { protected void removeAttr(QName key) { UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); try { - udfav.delete(session.toPrefixedName(key)); + udfav.delete(NamespaceUtils.toPrefixedName(provider, key)); } catch (IOException e) { throw new ContentResourceException("Cannot delete attribute " + key + " for " + path, e); } @@ -143,7 +148,7 @@ public class FsContent extends AbstractContent implements ProvidedContent { UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); ByteBuffer bb = ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8)); try { - int size = udfav.write(session.toPrefixedName(key), bb); + int size = udfav.write(NamespaceUtils.toPrefixedName(provider, key), bb); } catch (IOException e) { throw new ContentResourceException("Cannot delete attribute " + key + " for " + path, e); } @@ -154,7 +159,7 @@ public class FsContent extends AbstractContent implements ProvidedContent { if (POSIX_KEYS.containsKey(key)) return POSIX_KEYS.get(key); else - return USER_ + session.toPrefixedName(key); + return USER_ + NamespaceUtils.toPrefixedName(provider, key); } /* @@ -176,7 +181,7 @@ public class FsContent extends AbstractContent implements ProvidedContent { @Override public Content add(QName name, QName... classes) { try { - Path newPath = path.resolve(session.toPrefixedName(name)); + Path newPath = path.resolve(NamespaceUtils.toPrefixedName(provider, name)); if (ContentName.contains(classes, CrName.COLLECTION.get())) Files.createDirectory(newPath); else diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java index 99ed3a8ca..65bdd343e 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java @@ -1,25 +1,77 @@ package org.argeo.cms.acr.fs; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.UserDefinedFileAttributeView; +import java.util.Iterator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.stream.Collectors; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentResourceException; +import org.argeo.api.acr.CrName; +import org.argeo.api.acr.NamespaceUtils; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedSession; +/** Access a file system as a {@link ContentProvider}. */ public class FsContentProvider implements ContentProvider { + final static String XMLNS_ = "xmlns:"; + private final Path rootPath; + private final boolean isRoot; + + private NavigableMap prefixes = new TreeMap<>(); - public FsContentProvider(Path rootPath) { - super(); + public FsContentProvider(Path rootPath, boolean isRoot) { this.rootPath = rootPath; + this.isRoot = isRoot; + initNamespaces(); + } + + protected void initNamespaces() { + try { + UserDefinedFileAttributeView udfav = Files.getFileAttributeView(rootPath, + UserDefinedFileAttributeView.class); + for (String name : udfav.list()) { + if (name.startsWith(XMLNS_)) { + ByteBuffer buf = ByteBuffer.allocate(udfav.size(name)); + udfav.read(name, buf); + buf.flip(); + String namespace = StandardCharsets.UTF_8.decode(buf).toString(); + String prefix = name.substring(XMLNS_.length()); + prefixes.put(prefix, namespace); + } + } + + // defaults + addDefaultNamespace(udfav, CrName.CR_DEFAULT_PREFIX, CrName.CR_NAMESPACE_URI); + addDefaultNamespace(udfav, "basic", CrName.CR_NAMESPACE_URI); + addDefaultNamespace(udfav, "owner", CrName.CR_NAMESPACE_URI); + addDefaultNamespace(udfav, "posix", CrName.CR_NAMESPACE_URI); + } catch (IOException e) { + throw new RuntimeException("Cannot read namespaces from " + rootPath, e); + } + + } + + protected void addDefaultNamespace(UserDefinedFileAttributeView udfav, String prefix, String namespace) + throws IOException { + if (!prefixes.containsKey(prefix)) { + ByteBuffer bb = ByteBuffer.wrap(namespace.getBytes(StandardCharsets.UTF_8)); + int size = udfav.write(XMLNS_ + prefix, bb); + prefixes.put(prefix, namespace); + } } boolean isRoot(Path path) { try { - return Files.isSameFile(rootPath, path); + return isRoot && Files.isSameFile(rootPath, path); } catch (IOException e) { throw new ContentResourceException(e); } @@ -29,4 +81,20 @@ public class FsContentProvider implements ContentProvider { public Content get(ProvidedSession session, String mountPath, String relativePath) { return new FsContent(session, this, rootPath.resolve(relativePath)); } + + /* + * NAMESPACE CONTEXT + */ + + @Override + public String getNamespaceURI(String prefix) { + return NamespaceUtils.getNamespaceURI((p) -> prefixes.get(p), prefix); + } + + @Override + public Iterator getPrefixes(String namespaceURI) { + return NamespaceUtils.getPrefixes((ns) -> prefixes.entrySet().stream().filter(e -> e.getValue().equals(ns)) + .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()), namespaceURI); + } + } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java index 626f582e5..5cb458362 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java @@ -122,8 +122,6 @@ public class DomContent extends AbstractContent implements ProvidedContent { value.toString()); return previous; } - - @Override public boolean hasText() { @@ -169,7 +167,7 @@ public class DomContent extends AbstractContent implements ProvidedContent { @Override public Iterator iterator() { NodeList nodeList = element.getChildNodes(); - return new ElementIterator(session, provider, nodeList); + return new ElementIterator(this, session, provider, nodeList); } @Override @@ -177,6 +175,8 @@ public class DomContent extends AbstractContent implements ProvidedContent { Node parent = element.getParentNode(); if (parent == null) return null; + if (parent instanceof Document) + return null; if (!(parent instanceof Element)) throw new IllegalStateException("Parent is not an element"); return new DomContent(this, (Element) parent); diff --git a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java index c5fde8d7c..efca3effd 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java @@ -6,6 +6,8 @@ import java.util.Iterator; import java.util.List; import javax.xml.namespace.NamespaceContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; @@ -13,11 +15,15 @@ import javax.xml.xpath.XPathFactory; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentNotFoundException; +import org.argeo.api.acr.CrName; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedSession; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; public class DomContentProvider implements ContentProvider, NamespaceContext { private Document document; @@ -96,4 +102,5 @@ public class DomContentProvider implements ContentProvider, NamespaceContext { return Collections.unmodifiableList(res).iterator(); } + } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/xml/ElementIterator.java b/org.argeo.cms/src/org/argeo/cms/acr/xml/ElementIterator.java index 3b07081e4..1206fa86a 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/xml/ElementIterator.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/xml/ElementIterator.java @@ -4,12 +4,14 @@ import java.util.Iterator; import java.util.NoSuchElementException; import org.argeo.api.acr.Content; +import org.argeo.api.acr.CrName; import org.argeo.api.acr.spi.ProvidedSession; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; class ElementIterator implements Iterator { + private DomContent parent; private final ProvidedSession session; private final DomContentProvider provider; private final NodeList nodeList; @@ -18,7 +20,8 @@ class ElementIterator implements Iterator { private final int length; private Element nextElement = null; - public ElementIterator(ProvidedSession session, DomContentProvider provider, NodeList nodeList) { + public ElementIterator(DomContent parent, ProvidedSession session, DomContentProvider provider, NodeList nodeList) { + this.parent = parent; this.session = session; this.provider = provider; this.nodeList = nodeList; @@ -48,7 +51,15 @@ class ElementIterator implements Iterator { public Content next() { if (nextElement == null) throw new NoSuchElementException(); - DomContent result = new DomContent(session, provider, nextElement); + Content result; + String isMount = nextElement.getAttributeNS(CrName.CR_NAMESPACE_URI, CrName.MOUNT.get().getLocalPart()); + if (isMount.equals("true")) { + result = session.get(parent.getPath() + '/' + nextElement.getTagName()); + } + + else { + result = new DomContent(session, provider, nextElement); + } currentIndex++; nextElement = findNext(); return result; diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java index 85a482464..86a748325 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java @@ -121,7 +121,7 @@ public final class CurrentUser { return username == null || username.equalsIgnoreCase(CmsConstants.ROLE_ANONYMOUS); } - public CmsSession getCmsSession() { + public static CmsSession getCmsSession() { Subject subject = currentSubject(); CmsSessionId cmsSessionId = subject.getPrivateCredentials(CmsSessionId.class).iterator().next(); return CmsSessionImpl.getByUuid(cmsSessionId.getUuid()); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java index 4ffa03a63..44a2866f8 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java @@ -13,116 +13,19 @@ import org.osgi.service.http.HttpService; /** Implementation of a CMS deployment. */ public class CmsDeploymentImpl implements CmsDeployment { private final CmsLog log = CmsLog.getLog(getClass()); -// private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); - -// private Long availableSince; // Readiness -// private boolean nodeAvailable = false; -// private boolean userAdminAvailable = false; private boolean httpExpected = false; -// private boolean httpAvailable = false; private HttpService httpService; private CmsState cmsState; private DeployConfig deployConfig; - public CmsDeploymentImpl() { -// ServiceReference nodeStateSr = bc.getServiceReference(NodeState.class); -// if (nodeStateSr == null) -// throw new CmsException("No node state available"); - -// NodeState nodeState = bc.getService(nodeStateSr); -// cleanState = nodeState.isClean(); - -// nodeHttp = new NodeHttp(); - initTrackers(); - } - - private void initTrackers() { -// ServiceTracker httpSt = new ServiceTracker(bc, HttpService.class, null) { -// -// @Override -// public HttpService addingService(ServiceReference sr) { -// httpAvailable = true; -// Object httpPort = sr.getProperty("http.port"); -// Object httpsPort = sr.getProperty("https.port"); -// log.info(httpPortsMsg(httpPort, httpsPort)); -// checkReadiness(); -// return super.addingService(sr); -// } -// }; -// // httpSt.open(); -// KernelUtils.asyncOpen(httpSt); - -// ServiceTracker userAdminSt = new ServiceTracker(bc, UserAdmin.class, null) { -// @Override -// public UserAdmin addingService(ServiceReference reference) { -// UserAdmin userAdmin = super.addingService(reference); -// addStandardSystemRoles(userAdmin); -// userAdminAvailable = true; -// checkReadiness(); -// return userAdmin; -// } -// }; -// // userAdminSt.open(); -// KernelUtils.asyncOpen(userAdminSt); - -// ServiceTracker confAdminSt = new ServiceTracker(bc, -// ConfigurationAdmin.class, null) { -// @Override -// public ConfigurationAdmin addingService(ServiceReference reference) { -// ConfigurationAdmin configurationAdmin = bc.getService(reference); -//// boolean isClean; -//// try { -//// Configuration[] confs = configurationAdmin -//// .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")"); -//// isClean = confs == null || confs.length == 0; -//// } catch (Exception e) { -//// throw new IllegalStateException("Cannot analyse clean state", e); -//// } -// deployConfig = new DeployConfig(configurationAdmin, isClean); -// Activator.registerService(CmsDeployment.class, CmsDeploymentImpl.this, null); -//// JcrInitUtils.addToDeployment(CmsDeployment.this); -// httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null; -// try { -// Configuration[] configs = configurationAdmin -// .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")"); -// -// boolean hasDomain = false; -// for (Configuration config : configs) { -// Object realm = config.getProperties().get(UserAdminConf.realm.name()); -// if (realm != null) { -// log.debug("Found realm: " + realm); -// hasDomain = true; -// } -// } -// if (hasDomain) { -// loadIpaJaasConfiguration(); -// } -// } catch (Exception e) { -// throw new IllegalStateException("Cannot initialize config", e); -// } -// return super.addingService(reference); -// } -// }; -// // confAdminSt.open(); -// KernelUtils.asyncOpen(confAdminSt); - } - public void start() { httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null; if (deployConfig.hasDomain()) { loadIpaJaasConfiguration(); } - -// while (!isHttpAvailableOrNotExpected()) { -// try { -// Thread.sleep(100); -// } catch (InterruptedException e) { -// log.error("Interrupted while waiting for http"); -// } -// } } public void addFactoryDeployConfig(String factoryPid, Dictionary props) { @@ -139,30 +42,6 @@ public class CmsDeploymentImpl implements CmsDeployment { return deployConfig.getProps(factoryPid, cn); } -// private void addStandardSystemRoles(UserAdmin userAdmin) { -// // we assume UserTransaction is already available (TODO make it more robust) -// WorkTransaction userTransaction = bc.getService(bc.getServiceReference(WorkTransaction.class)); -// try { -// userTransaction.begin(); -// Role adminRole = userAdmin.getRole(CmsConstants.ROLE_ADMIN); -// if (adminRole == null) { -// adminRole = userAdmin.createRole(CmsConstants.ROLE_ADMIN, Role.GROUP); -// } -// if (userAdmin.getRole(CmsConstants.ROLE_USER_ADMIN) == null) { -// Group userAdminRole = (Group) userAdmin.createRole(CmsConstants.ROLE_USER_ADMIN, Role.GROUP); -// userAdminRole.addMember(adminRole); -// } -// userTransaction.commit(); -// } catch (Exception e) { -// try { -// userTransaction.rollback(); -// } catch (Exception e1) { -// // silent -// } -// throw new IllegalStateException("Cannot add standard system roles", e); -// } -// } - public boolean isHttpAvailableOrNotExpected() { return (httpExpected ? httpService != null : true); } @@ -177,18 +56,8 @@ public class CmsDeploymentImpl implements CmsDeployment { } public void stop() { -// if (nodeHttp != null) -// nodeHttp.destroy(); - -// try { -// JettyConfigurator.stopServer(KernelConstants.DEFAULT_JETTY_SERVER); -// } catch (Exception e) { -// log.error("Cannot stop default Jetty server.", e); -// } - if (deployConfig != null) { deployConfig.save(); - // new Thread(() -> deployConfig.save(), "Save Argeo Deploy Config").start(); } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java new file mode 100644 index 000000000..6392fdaf3 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java @@ -0,0 +1,47 @@ +package org.argeo.cms.internal.runtime; + +import java.nio.file.Path; +import java.util.Map; + +import org.argeo.api.acr.spi.ContentProvider; +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsState; +import org.argeo.cms.acr.CmsContentRepository; +import org.argeo.cms.acr.fs.FsContentProvider; +import org.argeo.util.LangUtils; + +public class DeployedContentRepository extends CmsContentRepository { + private final static String ROOT_XML = "root.xml"; + private final static String ACR_MOUNT_PATH = "acr.mount.path"; + + private CmsState cmsState; + + @Override + public void start() { + super.start(); + Path rootXml = KernelUtils.getOsgiInstancePath(KernelConstants.DIR_NODE).resolve(ROOT_XML); + initRootContentProvider(rootXml); + + Path srvPath = KernelUtils.getOsgiInstancePath(CmsConstants.SRV_WORKSPACE); + FsContentProvider srvContentProvider = new FsContentProvider(srvPath, false); + addProvider("/" + CmsConstants.SRV_WORKSPACE, srvContentProvider); + } + + @Override + public void stop() { + super.stop(); + } + + public void addContentProvider(ContentProvider provider, Map properties) { + String base = LangUtils.get(properties, ACR_MOUNT_PATH); + addProvider(base, provider); + } + + public void removeContentProvider(ContentProvider provider, Map properties) { + } + + public void setCmsState(CmsState cmsState) { + this.cmsState = cmsState; + } + +} diff --git a/rap/org.argeo.cms.ui.rap/build.properties b/rap/org.argeo.cms.ui.rap/build.properties index 7d543b56b..2416e522a 100644 --- a/rap/org.argeo.cms.ui.rap/build.properties +++ b/rap/org.argeo.cms.ui.rap/build.properties @@ -3,3 +3,5 @@ bin.includes = META-INF/,\ .,\ OSGI-INF/cmsWebAppFactory.xml source.. = src/ +additional.bundles = org.argeo.ext.slf4j,\ + org.slf4j.api diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java index 4008b49eb..68fd803b9 100644 --- a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java +++ b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java @@ -7,9 +7,9 @@ import java.util.Set; import org.argeo.api.cms.CmsApp; import org.argeo.api.cms.CmsAppListener; +import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsTheme; import org.argeo.api.cms.CmsView; -import org.argeo.api.cms.CmsLog; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.util.LangUtils; import org.eclipse.rap.rwt.RWT; @@ -20,7 +20,6 @@ import org.eclipse.rap.rwt.application.ExceptionHandler; import org.eclipse.rap.rwt.client.WebClient; import org.eclipse.swt.widgets.Display; import org.osgi.framework.BundleContext; -import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; import org.osgi.service.event.EventAdmin; @@ -30,7 +29,7 @@ public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, Cm private BundleContext bundleContext; private CmsApp cmsApp; - private String cmsAppId; +// private String cmsAppId; private EventAdmin eventAdmin; private ServiceRegistration rwtAppReg; @@ -124,13 +123,13 @@ public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, Cm public void setCmsApp(CmsApp cmsApp, Map properties) { this.cmsApp = cmsApp; - this.cmsAppId = properties.get(Constants.SERVICE_PID); +// this.cmsAppId = properties.get(Constants.SERVICE_PID); this.cmsApp.addCmsAppListener(this); } public void unsetCmsApp(CmsApp cmsApp, Map properties) { - String cmsAppId = properties.get(Constants.SERVICE_PID); - if (!cmsAppId.equals(this.cmsAppId)) + String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY); + if (!contextName.equals(this.contextName)) return; if (this.cmsApp != null) { this.cmsApp.removeCmsAppListener(this);