* CONTENT OPERATIONS
*/
/** Adds a new empty {@link Content} to this {@link Content}. */
- Content add(QName name, QName... classes);
+ Content add(QName name, QName... contentClass);
+
+ default Content add(QName name, QNamed... contentClass) {
+ return add(name, toQNames(contentClass));
+ }
/**
* Adds a new {@link Content} to this {@link Content}, setting the provided
/** AND */
default boolean isContentClass(QNamed... contentClass) {
- List<QName> lst = new ArrayList<>();
- for (QNamed qNamed : contentClass)
- lst.add(qNamed.qName());
- return isContentClass(lst.toArray(new QName[lst.size()]));
+ return isContentClass(toQNames(contentClass));
}
/** OR */
/** OR */
default boolean hasContentClass(QNamed... contentClass) {
- List<QName> lst = new ArrayList<>();
- for (QNamed qNamed : contentClass)
- lst.add(qNamed.qName());
- return hasContentClass(lst.toArray(new QName[lst.size()]));
+ return hasContentClass(toQNames(contentClass));
+ }
+
+ static QName[] toQNames(QNamed... names) {
+ QName[] res = new QName[names.length];
+ for (int i = 0; i < names.length; i++)
+ res[i] = names[i].qName();
+ return res;
}
/*
--- /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="org.argeo.cms.cmsFileSystemProvider">
+ <implementation class="org.argeo.cms.file.provider.CmsFileSystemProvider"/>
+ <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.spi.ProvidedRepository" name="ProvidedRepository" policy="static"/>
+ <service>
+ <provide interface="java.nio.file.spi.FileSystemProvider"/>
+ </service>
+</scr:component>
OSGI-INF/cmsAcrHttpHandler.xml,\
OSGI-INF/cmsDeployment.xml,\
OSGI-INF/cmsContext.xml,\
+OSGI-INF/cmsFileSystemProvider.xml,\
.,\
bin/,\
OSGI-INF/,\
- OSGI-INF/cmsEventBus.xml
+ OSGI-INF/cmsEventBus.xml,\
+ OSGI-INF/cmsFileSystemProvider.xml
source.. = src/
output.. = bin/
import java.nio.file.attribute.FileStoreAttributeView;
import org.argeo.api.acr.fs.AbstractFsStore;
+import org.argeo.api.acr.spi.ContentProvider;
public class CmsFileStore extends AbstractFsStore {
+ private final ContentProvider contentProvider;
+
+ public CmsFileStore(ContentProvider contentProvider) {
+ this.contentProvider = contentProvider;
+ }
@Override
public String name() {
- // TODO Auto-generated method stub
- return null;
+ // TODO return an URI
+ String name = contentProvider.getMountPath();
+ return name;
}
@Override
public String type() {
- // TODO Auto-generated method stub
- return null;
+ String type = contentProvider.getClass().getName();
+ return type;
}
@Override
public boolean isReadOnly() {
- // TODO Auto-generated method stub
return false;
}
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
- // TODO Auto-generated method stub
+ if (ContentAttributeView.class.isAssignableFrom(type))
+ return true;
return false;
}
@Override
public boolean supportsFileAttributeView(String name) {
- // TODO Auto-generated method stub
+ if (ContentAttributeView.NAME.equals(name))
+ return true;
return false;
}
import java.nio.file.WatchService;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
+import java.util.Collections;
import java.util.Set;
import org.argeo.api.acr.fs.AbstractFsSystem;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.acr.spi.ProvidedRepository;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.CmsSession;
+import org.argeo.cms.acr.ContentUtils;
public class CmsFileSystem extends AbstractFsSystem<CmsFileStore> {
+ private final CmsFileSystemProvider provider;
+// private final ProvidedRepository contentRepository;
+ private final CmsSession cmsSession;
+ private final ProvidedSession contentSession;
+
+ private final CmsPath rootPath;
+ private final CmsFileStore baseFileStore;
+
+ public CmsFileSystem(CmsFileSystemProvider provider, ProvidedRepository contentRepository, CmsSession cmsSession) {
+ this.provider = provider;
+// this.contentRepository = contentRepository;
+ this.cmsSession = cmsSession;
+ this.contentSession = (ProvidedSession) ContentUtils.openSession(contentRepository, cmsSession);
+
+ rootPath = new CmsPath(this, ProvidedContent.ROOT_PATH);
+ baseFileStore = new CmsFileStore(rootPath.getContent().getProvider());
+ }
@Override
public CmsFileStore getBaseFileStore() {
- // TODO Auto-generated method stub
- return null;
+ return baseFileStore;
}
@Override
public CmsFileStore getFileStore(String path) {
- // TODO Auto-generated method stub
- return null;
+ ProvidedContent c = (ProvidedContent) contentSession.get(path);
+ return new CmsFileStore(c.getProvider());
}
@Override
public FileSystemProvider provider() {
- // TODO Auto-generated method stub
- return null;
+ return provider;
}
@Override
public void close() throws IOException {
- // TODO Auto-generated method stub
-
+ // TODO close content session?
+ provider.close(this);
}
@Override
public boolean isOpen() {
- // TODO Auto-generated method stub
- return false;
+ // TODO check provider
+ return true;
}
@Override
public boolean isReadOnly() {
- // TODO Auto-generated method stub
return false;
}
@Override
public String getSeparator() {
- // TODO Auto-generated method stub
- return null;
+ return CmsPath.SEPARATOR;
}
@Override
public Iterable<Path> getRootDirectories() {
- // TODO Auto-generated method stub
- return null;
+ return Collections.singleton(rootPath);
}
@Override
public Iterable<FileStore> getFileStores() {
- // TODO Auto-generated method stub
- return null;
+ // TODO return all mount points
+ return Collections.singleton(baseFileStore);
}
@Override
public Set<String> supportedFileAttributeViews() {
- // TODO Auto-generated method stub
- return null;
+ return Collections.singleton(ContentAttributeView.NAME);
}
@Override
public Path getPath(String first, String... more) {
- // TODO Auto-generated method stub
- return null;
+ StringBuilder sb = new StringBuilder(first);
+ // TODO Make it more robust
+ for (String part : more)
+ sb.append('/').append(part);
+ return new CmsPath(this, sb.toString());
}
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern) {
- // TODO Auto-generated method stub
return null;
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
- // TODO Auto-generated method stub
return null;
}
@Override
public WatchService newWatchService() throws IOException {
- // TODO Auto-generated method stub
return null;
}
+ /*
+ * ACR
+ */
+
+ ProvidedContent getContent(String acrPath) {
+ return (ProvidedContent) contentSession.get(acrPath);
+ }
+
+ ProvidedSession getContentSession() {
+ return contentSession;
+ }
+
+ /*
+ * CMS
+ */
+
+ CmsSession getCmsSession() {
+ return cmsSession;
+ }
+
}
package org.argeo.cms.file.provider;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessMode;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryStream.Filter;
+import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.spi.FileSystemProvider;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.DName;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.spi.ProvidedRepository;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.CmsSession;
+import org.argeo.cms.CurrentUser;
+
public class CmsFileSystemProvider extends FileSystemProvider {
+ private Map<CmsSession, CmsFileSystem> fileSystems = Collections.synchronizedMap(new HashMap<>());
+ private ProvidedRepository contentRepository;
+
+
+ public void start() {
+
+ }
+
+ public void stop() {
+
+ }
+
@Override
public String getScheme() {
return "cms";
@Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
- // TODO Auto-generated method stub
- return null;
+ CmsSession cmsSession = CurrentUser.getCmsSession();
+ if (cmsSession.isAnonymous()) {
+ // TODO deal with anonymous
+ return null;
+ }
+ if (fileSystems.containsKey(cmsSession))
+ throw new FileSystemAlreadyExistsException("CMS file system already exists for user " + cmsSession);
+
+ String host = uri.getHost();
+ if (host != null && !host.trim().equals("")) {
+// URI repoUri = new URI("http", uri.getUserInfo(), uri.getHost(), uri.getPort(), "/jcr/node", null, null);
+ // FIXME deal with remote
+ CmsFileSystem fileSystem = null;
+ fileSystems.put(cmsSession, fileSystem);
+ return fileSystem;
+ } else {
+ // FIXME send exception if it exists already
+ CmsFileSystem fileSystem = new CmsFileSystem(this, contentRepository, cmsSession);
+ fileSystems.put(cmsSession, fileSystem);
+ cmsSession.addOnCloseCallback((s) -> {
+ fileSystems.remove(s);
+ });
+ return fileSystem;
+ }
}
@Override
public FileSystem getFileSystem(URI uri) {
- // TODO Auto-generated method stub
- return null;
+ return currentUserFileSystem();
}
@Override
public Path getPath(URI uri) {
- // TODO Auto-generated method stub
- return null;
+ CmsFileSystem fileSystem = currentUserFileSystem();
+ String path = uri.getPath();
+ if (fileSystem == null)
+ try {
+ fileSystem = (CmsFileSystem) newFileSystem(uri, new HashMap<String, Object>());
+ } catch (IOException e) {
+ throw new UncheckedIOException("Could not autocreate file system for " + uri, e);
+ }
+ return fileSystem.getPath(path);
}
@Override
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
- // TODO Auto-generated method stub
- return null;
+ CmsPath cmsPath = (CmsPath) dir;
+ return new ContentDirectoryStream(cmsPath, filter);
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
- // TODO Auto-generated method stub
+ CmsPath cmsPath = (CmsPath) dir;
+ ProvidedSession contentSession = cmsPath.getContentSession();
+ if (contentSession.exists(dir.toString()))
+ throw new FileAlreadyExistsException(dir.toString());
+ CmsPath parent = (CmsPath) cmsPath.getParent();
+ if (!contentSession.exists(parent.toString()))
+ throw new NoSuchFileException(parent.toString());
+ // TODO use a proper naming context
+ QName fileName = NamespaceUtils.parsePrefixedName(dir.getFileName().toString());
+ parent.getContent().add(fileName, DName.collection);
}
@Override
public void delete(Path path) throws IOException {
- // TODO Auto-generated method stub
-
+ CmsPath cmsPath = (CmsPath) path;
+ ProvidedSession contentSession = cmsPath.getContentSession();
+ if (!contentSession.exists(cmsPath.toString()))
+ throw new NoSuchFileException(cmsPath.toString());
+ contentSession.edit((s) -> {
+ cmsPath.getContent().remove();
+ });
}
@Override
@Override
public boolean isSameFile(Path path, Path path2) throws IOException {
- // TODO Auto-generated method stub
- return false;
+ // TODO make it smarter
+ return path.toString().equals(path2.toString());
}
@Override
public boolean isHidden(Path path) throws IOException {
- // TODO Auto-generated method stub
return false;
}
@Override
public FileStore getFileStore(Path path) throws IOException {
- // TODO Auto-generated method stub
- return null;
+ CmsFileSystem fileSystem = (CmsFileSystem) path.getFileSystem();
+ return fileSystem.getFileStore(path.toString());
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
- // TODO Auto-generated method stub
-
}
@Override
}
+ /*
+ * UTILITIES
+ */
+
+ CmsFileSystem currentUserFileSystem() {
+ CmsSession cmsSession = CurrentUser.getCmsSession();
+ return fileSystems.get(cmsSession);
+ }
+
+ void close(CmsFileSystem fileSystem) {
+ CmsSession cmsSession = fileSystem.getCmsSession();
+ CmsFileSystem ref = fileSystems.remove(cmsSession);
+ assert ref == fileSystem;
+ }
+
+ /*
+ * DEPENDENCY INJECTION
+ */
+ public void setContentRepository(ProvidedRepository contentRepository) {
+ this.contentRepository = contentRepository;
+ }
+
}
package org.argeo.cms.file.provider;
+import org.argeo.api.acr.Content;
import org.argeo.api.acr.fs.AbstractFsPath;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.acr.spi.ProvidedSession;
public class CmsPath extends AbstractFsPath<CmsFileSystem, CmsFileStore> {
+ final static String SEPARATOR = "/";
+
+ // lazy loaded
+ private ProvidedContent content;
+
+ ProvidedContent getContent() {
+ if (content == null) {
+ content = getFileSystem().getContent(toString());
+ }
+ return content;
+ }
+
+ CmsPath(CmsFileSystem fileSystem, Content content) {
+ this(fileSystem, content.getPath());
+ this.content = (ProvidedContent) content;
+ }
public CmsPath(CmsFileSystem filesSystem, CmsFileStore fileStore, String[] segments, boolean absolute) {
super(filesSystem, fileStore, segments, absolute);
return new CmsPath(getFileSystem(), getFileStore(), segments, absolute);
}
+ ProvidedSession getContentSession() {
+ return getFileSystem().getContentSession();
+ }
+
}
--- /dev/null
+package org.argeo.cms.file.provider;
+
+import java.nio.file.attribute.FileAttributeView;
+
+import org.argeo.api.acr.Content;
+
+public class ContentAttributeView implements FileAttributeView {
+ final static String NAME = "content";
+
+ private final Content content;
+
+ public ContentAttributeView(Content content) {
+ this.content = content;
+ }
+
+ @Override
+ public String name() {
+ return NAME;
+ }
+
+ public Content getContent() {
+ return content;
+ }
+}
--- /dev/null
+package org.argeo.cms.file.provider;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.util.Iterator;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.DName;
+
+public class ContentDirectoryStream implements DirectoryStream<Path> {
+ private final CmsPath dir;
+ private final Filter<? super Path> filter;
+
+ private FilesAndCollectionsIterator iterator;
+
+ public ContentDirectoryStream(CmsPath dir, Filter<? super Path> filter) {
+ this.dir = dir;
+ this.filter = filter;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ if (iterator == null)
+ iterator = new FilesAndCollectionsIterator();
+ return iterator;
+ }
+
+ static boolean isFile(Content c) {
+ return !c.get(DName.getcontenttype, String.class).isEmpty();
+ }
+
+ static boolean isDirectory(Content c) {
+ return c.isContentClass(DName.collection);
+ }
+
+ class FilesAndCollectionsIterator implements Iterator<Path> {
+ private Content next;
+ private final Iterator<Content> it;
+
+ public FilesAndCollectionsIterator() {
+ Content content = dir.getContent();
+ if (!content.isContentClass(DName.collection))
+ throw new IllegalStateException("Content " + content + " is not a collection");
+ it = content.iterator();
+ findNext();
+ }
+
+ private void findNext() {
+ next = null;
+ while (it.hasNext() && next != null) {
+ Content n = it.next();
+ if (isFile(n) || isDirectory(n)) {
+ if (filter != null) {
+ try {
+ if (filter.accept(new CmsPath(dir.getFileSystem(), n)))
+ next = n;
+ } catch (IOException e) {
+ throw new UncheckedIOException("Cannot filter " + dir, e);
+ }
+ } else {
+ next = n;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public Path next() {
+ if (next == null)
+ throw new IllegalStateException("Iterator doesn't have more elements");
+ CmsPath p = new CmsPath(dir.getFileSystem(), next);
+ findNext();
+ return p;
+ }
+
+ }
+}
}
public void createHttpContext(String contextPath, HttpHandler httpHandler, CmsAuthenticator authenticator) {
+ if (!httpExpected) {
+ if (log.isTraceEnabled())
+ log.warn("Ignore HTTP context " + contextPath + " as we don't provide an HTTP server");
+ return;
+ }
HttpContext httpContext = httpServer.join().createContext(contextPath);
// we want to set the authenticator BEFORE the handler actually becomes active
httpContext.setAuthenticator(authenticator);
-argeo.osgi.start.2.node=\
-org.eclipse.equinox.metatype,\
-org.eclipse.equinox.cm,\
-org.eclipse.equinox.ds,\
+argeo.osgi.start.2=\
+org.apache.felix.scr,\
org.argeo.init
-argeo.osgi.start.3.node=\
+argeo.osgi.start.3=\
org.argeo.cms,\
-org.argeo.cms.jcr,\
-org.argeo.cms.ui.rcp
+org.argeo.cms.swt.rcp,\
+org.argeo.cms.ee,\
+org.argeo.cms.lib.dbus,\
+argeo.osgi.start.5=\
+org.argeo.app.profile.acr.fs,\
-# Local
-argeo.node.repo.type=h2
-org.osgi.service.http.port=7070
-#org.osgi.service.http.port.secure=7073
-argeo.node.useradmin.uris=os:///
+
+#argeo.node.useradmin.uris=os:///
+argeo.directory=ipa:///
#argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@localhost:10389/dc=example,dc=com
# DON'T CHANGE BELOW
org.eclipse.equinox.http.jetty.autostart=false
-org.osgi.framework.bootdelegation=com.sun.jndi.ldap,\
+org.osgi.framework.bootdelegation=\
+sun.security.internal.spec,\
+sun.security.provider,\
+com.sun.jndi.ldap,\
com.sun.jndi.ldap.sasl,\
com.sun.security.jgss,\
com.sun.jndi.dns,\
</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"/>
+ <reference bind="setCmsFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="CmsFileSystemProvider" policy="static"/>
</scr:component>
package org.argeo.cms.swt.app;
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
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.ux.CmsUi;
import org.argeo.api.cms.ux.CmsView;
import org.argeo.cms.AbstractCmsApp;
import org.argeo.cms.swt.CmsSwtUi;
import org.argeo.cms.swt.CmsSwtUtils;
import org.argeo.cms.swt.auth.CmsLogin;
+import org.argeo.eclipse.ui.fs.SimpleFsBrowser;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
public class CmsUserApp extends AbstractCmsApp {
private ContentRepository contentRepository;
+ private FileSystemProvider cmsFileSystemProvider;
+
@Override
public Set<String> getUiNames() {
Set<String> uiNames = new HashSet<>();
AcrContentTreeView view = new AcrContentTreeView(cmsUi, 0, rootContent);
view.setLayoutData(CmsSwtUtils.fillAll());
+ } else if ("app".equals(uiName)) {
+ Path rootPath = cmsFileSystemProvider.getPath(URI.create("cms:///"));
+ SimpleFsBrowser view = new SimpleFsBrowser(parent, 0);
+ view.setInput(rootPath);
+ view.setLayoutData(CmsSwtUtils.fillAll());
+
}
return cmsUi;
}
this.contentRepository = contentRepository;
}
+ public void setCmsFileSystemProvider(FileSystemProvider cmsFileSystemProvider) {
+ this.cmsFileSystemProvider = cmsFileSystemProvider;
+ }
+
}
\ No newline at end of file