JCR as an ACR backend
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 13 May 2022 05:53:43 +0000 (07:53 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 13 May 2022 05:53:43 +0000 (07:53 +0200)
39 files changed:
eclipse/org.argeo.cms.swt/.project
eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml.xml [new file with mode: 0644]
eclipse/org.argeo.cms.swt/bnd.bnd
eclipse/org.argeo.cms.swt/build.properties
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java [new file with mode: 0644]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/AcrContentTreeView.java [new file with mode: 0644]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java [new file with mode: 0644]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java [new file with mode: 0644]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/gcr/GcrContentTreeView.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/gcr/SwtUiProvider.java [deleted file]
jcr/org.argeo.cms.jcr/OSGI-INF/jcrContentProvider.xml [new file with mode: 0644]
jcr/org.argeo.cms.jcr/bnd.bnd
jcr/org.argeo.cms.jcr/build.properties
jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java
jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java
jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrSessionAdapter.java [new file with mode: 0644]
org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java
org.argeo.api.acr/src/org/argeo/api/acr/ContentNameSupplier.java
org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java
org.argeo.api.acr/src/org/argeo/api/acr/ContentUtils.java
org.argeo.api.acr/src/org/argeo/api/acr/CrName.java
org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java [new file with mode: 0644]
org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java
org.argeo.api.acr/src/org/argeo/api/acr/spi/ProvidedSession.java
org.argeo.cms/OSGI-INF/acrContentRepository.xml [new file with mode: 0644]
org.argeo.cms/bnd.bnd
org.argeo.cms/build.properties
org.argeo.cms/src/org/argeo/cms/AbstractCmsApp.java
org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java
org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java
org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java
org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java
org.argeo.cms/src/org/argeo/cms/acr/xml/DomContentProvider.java
org.argeo.cms/src/org/argeo/cms/acr/xml/ElementIterator.java
org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsDeploymentImpl.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java [new file with mode: 0644]
rap/org.argeo.cms.ui.rap/build.properties
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java

index 082112e6d0e4325f3907f049d8afd7b241a2d06e..8ac021b59a3366666cd8e2ab739922b051b8c565 100644 (file)
                        <arguments>
                        </arguments>
                </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
        </buildSpec>
        <natures>
                <nature>org.eclipse.pde.PluginNature</nature>
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 (file)
index 0000000..4f2a405
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="CMS User App">
+   <implementation class="org.argeo.cms.swt.app.CmsUserApp"/>
+   <property name="argeo.cms.app.contextName" type="String" value="cms/user"/>
+   <service>
+      <provide interface="org.argeo.api.cms.CmsApp"/>
+   </service>
+   <reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
+   <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
+</scr:component>
index 29f820a4c15dddef1cd26bed757955abc9ab9920..a162ea4ae14d58a13e2cc798db3c600281599609 100644 (file)
@@ -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
index 0e0438744c3be63d9d042cf97e4afb15608d5d86..606619784ef76d82cef30d008d53bd721e920d95 100644 (file)
@@ -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 (file)
index 0000000..75fa197
--- /dev/null
@@ -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 (file)
index 0000000..c6d7380
--- /dev/null
@@ -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 (file)
index 0000000..46166b8
--- /dev/null
@@ -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<Composite, Content, Control> {
+
+}
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 (file)
index 0000000..8bb3cc7
--- /dev/null
@@ -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<String> getUiNames() {
+               Set<String> 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 (file)
index 4039f2b..0000000
+++ /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 (file)
index 8109c40..0000000
+++ /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<Composite, Content, Control> {
-
-}
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 (file)
index 0000000..eb15b4c
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="JCR Content Provider">
+   <implementation class="org.argeo.cms.jcr.acr.JcrContentProvider"/>
+   <reference bind="setJcrRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
+   <property name="acr.mount.path" type="String" value="/sys"/>
+   <service>
+      <provide interface="org.argeo.api.acr.spi.ContentProvider"/>
+   </service>
+</scr:component>
index 065f0d15d924e11b19079e1d63b96dc8eb0fe78a..b53745a6a4847564febc422b0d5ddff0127ab22e 100644 (file)
@@ -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
index 2b99dd7ba2b140ae683db2e8f968ac96bd1448c7..859c241febdf2baf040bbdfb0ae62ca2f932bd11 100644 (file)
@@ -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
index 04c5d2d8c3b2f0d32960c52d05e64bc572e70b77..94fe7cf58ef9f8b2ffd6c3a53f70af265f4a7107 100644 (file)
@@ -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<QName> keys() {
-               return new Iterable<QName>() {
-
-                       @Override
-                       public Iterator<QName> 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<QName> 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<QName>() {
+//
+//                     @Override
+//                     public Iterator<QName> 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<Object> 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<Content> {
                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);
                        }
index ef8e375d012731972b4597114aae57d4f5d2dbab..fc4a61bf9e61a72c316f5b5f5f8bf50a291932fb 100644 (file)
@@ -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<ProvidedSession, JcrSessionAdapter> 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 (file)
index 0000000..10b243b
--- /dev/null
@@ -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<Thread, Map<String, Session>> 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<String, Session> 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<String, Session> 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<Session>) () -> {
+                               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;
+       }
+
+}
index 341a3e29710a7d482c2ac46bde2bf0181bdb1965..1fee92e397dc70e6fdfb8477b1d41a25d9a6a8c0 100644 (file)
@@ -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;
index e3c721feff1e5e498f0f7c273c148c989e0703e0..f7900afd309114bf5a4ef13d4564e6d71ae5baca 100644 (file)
@@ -45,7 +45,7 @@ public interface ContentNameSupplier extends Supplier<ContentName>, 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<ContentName>, 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<ContentName>, NamespaceCon
 
        @Override
        default Iterator<String> getPrefixes(String namespaceURI) {
-               Iterator<String> it = getStandardPrefixes(namespaceURI);
+               Iterator<String> it = NamespaceUtils.getStandardPrefixes(namespaceURI);
                if (it != null)
                        return it;
                if (namespaceURI.equals(getNamespaceURI()))
@@ -73,34 +73,4 @@ public interface ContentNameSupplier extends Supplier<ContentName>, 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<String> 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;
-       }
-
 }
index 215bb9e22f8a70d577673b3601003068364e3273..e3a09f60367ea6cd5f4cf27fddc106f3a3e12523 100644 (file)
@@ -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();
-       }
-
 }
index 2036c86229ee3c2749fae93ba1c968f23c1b52ca..5d1f599204860fa20ba1d47ce13286e64d84be26 100644 (file)
@@ -66,12 +66,25 @@ public class ContentUtils {
 
        }
 
-       public static <T> boolean isString(T t) {
-               return t instanceof String;
+//     public static <T> 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() {
 
        }
+
 }
index 099be9fdad98a54378ba39d871fb588bddde09fd..7e089e8b22c736d25760df0422014b237e5af6dd 100644 (file)
@@ -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 (file)
index 0000000..a1b4062
--- /dev/null
@@ -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<String> 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<String, String> 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<String, String> 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<String> getPrefixes(Function<String, Set<String>> mapping, String namespaceURI) {
+               Iterator<String> 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<String> prefixes = mapping.apply(namespaceURI);
+               assert prefixes != null;
+               return prefixes.iterator();
+       }
+
+}
index d83cf49c95e27e8b828924a1f71547c235f16f83..9d2215f6579436e14f4d5dc3d9814d51419a5184 100644 (file)
@@ -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<String> 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);
+//     }
+
 }
index f90d6747565b81aeadee1d74075e7d86c2cd30c9..60f64def8dd77ad128e2b7931c6624f64b2ae161 100644 (file)
@@ -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<ProvidedSession> onClose();
+
        /*
         * NAMESPACE CONTEXT
         */
-       /** @return the bound namespace or null if not found */
-       String findNamespace(String prefix);
-
-       // TODO find the default prefix?
-       Set<String> findPrefixes(String namespaceURI);
-
-       /** To be overridden for optimisation, as it will be called a lot */
-       default String findPrefix(String namespaceURI) {
-               Set<String> 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<String> prefixes = getPrefixes(namespaceURI);
+               return prefixes.hasNext() ? prefixes.next() : null;
        }
 
-       @Override
-       default Iterator<String> getPrefixes(String namespaceURI) {
-               Iterator<String> 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<String> 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<String> findPrefixes(String namespaceURI);
+//
+//     /** To be overridden for optimisation, as it will be called a lot */
+//     default String findPrefix(String namespaceURI) {
+//             Set<String> 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<String> getPrefixes(String namespaceURI) {
+//             Iterator<String> 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<String> 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 (file)
index 0000000..281005d
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" immediate="true" name="ACR Content Repository">
+   <implementation class="org.argeo.cms.internal.runtime.DeployedContentRepository"/>
+   <reference bind="addContentProvider" cardinality="0..n" interface="org.argeo.api.acr.spi.ContentProvider" name="ContentProvider" policy="dynamic" unbind="removeContentProvider"/>
+   <service>
+      <provide interface="org.argeo.api.acr.ContentRepository"/>
+      <provide interface="org.argeo.api.acr.spi.ProvidedRepository"/>
+   </service>
+   <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
+</scr:component>
index e75adcdc3b49afd28a98c73422944d81afce5736..da33540591598dca23d6f3a16f055c7ef9bb4200 100644 (file)
@@ -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,\
 
index db86d95a50099a1a879c9b09b154cee6fd72227b..d1480bf5dd14078736cd4bd96ba1ac76fabfc520 100644 (file)
@@ -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/
index a7049a4f49a23ea2fcd27dc508418fa73845c5c1..7bf2c96d5a8b4a97fa05e5f2aaa3a5419a19d097 100644 (file)
@@ -16,7 +16,10 @@ public abstract class AbstractCmsApp implements CmsApp {
 
        private List<CmsAppListener> 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;
                        }
index 9cd8bd22dd6e8eef8670a45fada48cf5a8037f5f..6d17ea87dd4d6fc463e01cfcee3f75ca0c061549 100644 (file)
@@ -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<String, ContentProvider> partitions = new TreeMap<>();
 
        // TODO synchronize ?
        private NavigableMap<String, String> prefixes = new TreeMap<>();
 
+       private Schema schema;
+
+       private CmsContentSession systemSession;
+
+       private Map<CmsSession, CmsContentSession> 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<ProvidedSession> closed = new CompletableFuture<>();
+
                public CmsContentSession(Subject subject, Locale locale) {
                        this.subject = subject;
                        this.locale = locale;
                }
 
+               public void close() {
+                       closed.complete(this);
+               }
+
+               @Override
+               public CompletionStage<ProvidedSession> onClose() {
+                       return closed.minimalCompletionStage();
+               }
+
                @Override
                public Content get(String path) {
                        Map.Entry<String, ContentProvider> 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<String> findPrefixes(String namespaceURI) {
-                       Set<String> res = prefixes.entrySet().stream().filter(e -> e.getValue().equals(namespaceURI))
-                                       .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet());
-
-                       return res;
+               public Iterator<String> 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<String> findPrefixes(String namespaceURI) {
+//                     Set<String> 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);
+//             }
 
        }
 
index bfcd0118d76e6348fff11911cf71821dec183425..b8bbddb5016d13c2b4136e8e525c9c6858958409 100644 (file)
@@ -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<QName, String> BASIC_KEYS;
        private static final Map<QName, String> 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
index 99ed3a8ca61689c11d89f139aad9f2ad8984e5ff..65bdd343ef72b213289e982008f917940877c8c2 100644 (file)
@@ -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<String, String> 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<String> getPrefixes(String namespaceURI) {
+               return NamespaceUtils.getPrefixes((ns) -> prefixes.entrySet().stream().filter(e -> e.getValue().equals(ns))
+                               .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()), namespaceURI);
+       }
+
 }
index 626f582e515cc40d7e3d9d1f897fbd8812584063..5cb4583624c59fc41dd17956ab5ec355013a0cdf 100644 (file)
@@ -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<Content> 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);
index c5fde8d7c521482c26b5a81932d223744026e9c4..efca3effdd7737e9dc13c464a74d41e640d3d472 100644 (file)
@@ -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();
        }
 
+       
 }
index 3b07081e4a7f3477e92a6137709eb95ad8f90bb4..1206fa86a10f3759b4cdee662b963eb1d6846b77 100644 (file)
@@ -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<Content> {
+       private DomContent parent;
        private final ProvidedSession session;
        private final DomContentProvider provider;
        private final NodeList nodeList;
@@ -18,7 +20,8 @@ class ElementIterator implements Iterator<Content> {
        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<Content> {
        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;
index 85a4824646bec37124c8cefc79627240165a435a..86a748325ec0e0a553ab3e46d2be403a59fe5c73 100644 (file)
@@ -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());
index 4ffa03a63fffe2ff37d4f18db8494b849275c97a..44a2866f8df50613cbeede05262e66c660272428 100644 (file)
@@ -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<NodeState> 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<HttpService, HttpService>(bc, HttpService.class, null) {
-//
-//                     @Override
-//                     public HttpService addingService(ServiceReference<HttpService> 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<UserAdmin, UserAdmin>(bc, UserAdmin.class, null) {
-//                     @Override
-//                     public UserAdmin addingService(ServiceReference<UserAdmin> reference) {
-//                             UserAdmin userAdmin = super.addingService(reference);
-//                             addStandardSystemRoles(userAdmin);
-//                             userAdminAvailable = true;
-//                             checkReadiness();
-//                             return userAdmin;
-//                     }
-//             };
-//             // userAdminSt.open();
-//             KernelUtils.asyncOpen(userAdminSt);
-
-//             ServiceTracker<?, ?> confAdminSt = new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>(bc,
-//                             ConfigurationAdmin.class, null) {
-//                     @Override
-//                     public ConfigurationAdmin addingService(ServiceReference<ConfigurationAdmin> 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<String, Object> 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 (file)
index 0000000..6392fda
--- /dev/null
@@ -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<String, Object> properties) {
+               String base = LangUtils.get(properties, ACR_MOUNT_PATH);
+               addProvider(base, provider);
+       }
+
+       public void removeContentProvider(ContentProvider provider, Map<String, Object> properties) {
+       }
+
+       public void setCmsState(CmsState cmsState) {
+               this.cmsState = cmsState;
+       }
+
+}
index 7d543b56b0a15320fc9acfd6950c175f8155c320..2416e522ad29993305287f61de61ef147c207a43 100644 (file)
@@ -3,3 +3,5 @@ bin.includes = META-INF/,\
                .,\
                OSGI-INF/cmsWebAppFactory.xml
 source.. = src/
+additional.bundles = org.argeo.ext.slf4j,\
+                     org.slf4j.api
index 4008b49eb58d5f776b4081d599dab1c294e39f16..68fd803b9e7fc75590e133086982fda5d4a58892 100644 (file)
@@ -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<ApplicationConfiguration> rwtAppReg;
@@ -124,13 +123,13 @@ public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, Cm
 
        public void setCmsApp(CmsApp cmsApp, Map<String, String> 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<String, String> 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);