<arguments>
</arguments>
</buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ds.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
--- /dev/null
+<?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>
*
Bundle-ActivationPolicy: lazy
-
\ No newline at end of file
+
+Service-Component: \
+OSGI-INF/cmsUserApp.xml.xml
+
\ No newline at end of file
-source.. = src/
output.. = bin/
bin.includes = META-INF/,\
- .
-
\ No newline at end of file
+ .,\
+ OSGI-INF/cmsUserApp.xml.xml
+source.. = src/
--- /dev/null
+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
--- /dev/null
+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();
+ }
+}
--- /dev/null
+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> {
+
+}
--- /dev/null
+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
+++ /dev/null
-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();
- }
-}
+++ /dev/null
-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> {
-
-}
--- /dev/null
+<?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>
OSGI-INF/jcrServletContext.xml,\
OSGI-INF/dataServletContext.xml,\
OSGI-INF/filesServletContext.xml,\
-OSGI-INF/filesServlet.xml
+OSGI-INF/filesServlet.xml,\
+OSGI-INF/jcrContentProvider.xml
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
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;
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;
@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
@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() {
/** 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
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);
}
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);
}
}
@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);
+ }
}
/*
--- /dev/null
+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;
+ }
+
+}
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;
@Override
default String getNamespaceURI(String prefix) {
- String namespaceURI = getStandardNamespaceURI(prefix);
+ String namespaceURI = NamespaceUtils.getStandardNamespaceURI(prefix);
if (namespaceURI != null)
return namespaceURI;
if (prefix.equals(getDefaultPrefix()))
@Override
default String getPrefix(String namespaceURI) {
- String prefix = getStandardPrefix(namespaceURI);
+ String prefix = NamespaceUtils.getStandardPrefix(namespaceURI);
if (prefix != null)
return prefix;
if (namespaceURI.equals(getNamespaceURI()))
@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()))
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;
- }
-
}
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();
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();
- }
-
}
}
- 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() {
}
+
}
* ATTRIBUTES
*/
UUID, // the UUID of a content
+ MOUNT,
/*
* ATTRIBUTES FROM FILE SEMANTICS
--- /dev/null
+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();
+ }
+
+}
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);
+// }
+
}
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();
+// }
}
--- /dev/null
+<?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>
OSGI-INF/nodeUserAdmin.xml,\
OSGI-INF/cmsUserManager.xml,\
OSGI-INF/deployConfig.xml,\
+OSGI-INF/acrContentRepository.xml,\
OSGI-INF/cmsDeployment.xml,\
OSGI-INF/cmsContext.xml,\
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/
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) {
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;
}
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() {
@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) {
// 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
*/
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);
*/
@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);
+// }
}
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;
// 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) {
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);
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);
}
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);
}
if (POSIX_KEYS.containsKey(key))
return POSIX_KEYS.get(key);
else
- return USER_ + session.toPrefixedName(key);
+ return USER_ + NamespaceUtils.toPrefixedName(provider, key);
}
/*
@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
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);
}
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);
+ }
+
}
value.toString());
return previous;
}
-
-
@Override
public boolean hasText() {
@Override
public Iterator<Content> iterator() {
NodeList nodeList = element.getChildNodes();
- return new ElementIterator(session, provider, nodeList);
+ return new ElementIterator(this, session, provider, nodeList);
}
@Override
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);
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;
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;
return Collections.unmodifiableList(res).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;
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;
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;
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());
/** 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) {
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);
}
}
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();
}
}
--- /dev/null
+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;
+ }
+
+}
.,\
OSGI-INF/cmsWebAppFactory.xml
source.. = src/
+additional.bundles = org.argeo.ext.slf4j,\
+ org.slf4j.api
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;
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;
private BundleContext bundleContext;
private CmsApp cmsApp;
- private String cmsAppId;
+// private String cmsAppId;
private EventAdmin eventAdmin;
private ServiceRegistration<ApplicationConfiguration> rwtAppReg;
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);