--- /dev/null
+package org.argeo.jcr.fs;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.Arrays;
+import java.util.Map;
+
+import javax.jcr.Property;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jackrabbit.fs.JackrabbitMemoryFsProvider;
+
+import junit.framework.TestCase;
+
+public class JcrFileSystemTest extends TestCase {
+ private final static Log log = LogFactory.getLog(JcrFileSystemTest.class);
+
+ public void testSimple() throws Exception {
+ FileSystemProvider fsProvider = new JackrabbitMemoryFsProvider();
+
+ // Simple file
+ Path testPath = fsProvider.getPath(new URI("jcr+memory:/test.txt"));
+ Files.createFile(testPath);
+ BasicFileAttributes bfa = Files.readAttributes(testPath, BasicFileAttributes.class);
+ FileTime ft = bfa.creationTime();
+ assertNotNull(ft);
+ assertTrue(bfa.isRegularFile());
+ log.debug("Created " + testPath + " (" + ft + ")");
+ Files.delete(testPath);
+ log.debug("Deleted " + testPath);
+ String txt = "TEST\nTEST2\n";
+ byte[] arr = txt.getBytes();
+ Files.write(testPath, arr);
+ log.debug("Wrote " + testPath);
+ byte[] read = Files.readAllBytes(testPath);
+ assertTrue(Arrays.equals(arr, read));
+ assertEquals(txt, new String(read));
+ log.debug("Read " + testPath);
+ Path rootPath = fsProvider.getPath(new URI("jcr+memory:/"));
+ log.debug("Got root " + rootPath);
+ Path testDir = rootPath.resolve("testDir");
+ log.debug("Resolved " + testDir);
+ // Copy
+ Files.createDirectory(testDir);
+ log.debug("Created directory " + testDir);
+ Path subsubdir = Files.createDirectories(testDir.resolve("subdir/subsubdir"));
+ log.debug("Created sub directories " + subsubdir);
+ Path copiedFile = testDir.resolve("copiedFile.txt");
+ log.debug("Resolved " + copiedFile);
+ try (OutputStream out = Files.newOutputStream(copiedFile); InputStream in = Files.newInputStream(testPath)) {
+ IOUtils.copy(in, out);
+ }
+ log.debug("Copied " + testPath + " to " + copiedFile);
+ Files.delete(testPath);
+ log.debug("Deleted " + testPath);
+ byte[] copiedRead = Files.readAllBytes(copiedFile);
+ assertTrue(Arrays.equals(copiedRead, read));
+ log.debug("Read " + copiedFile);
+ // Browse directories
+ DirectoryStream<Path> files = Files.newDirectoryStream(testDir);
+ int fileCount = 0;
+ Path listedFile = null;
+ for (Path file : files) {
+ fileCount++;
+ if (!Files.isDirectory(file))
+ listedFile = file;
+ }
+ assertEquals(2, fileCount);
+ assertEquals(copiedFile, listedFile);
+ assertEquals(copiedFile.toString(), listedFile.toString());
+ log.debug("Listed " + testDir);
+ // Generic attributes
+ Map<String, Object> attrs = Files.readAttributes(copiedFile, "*");
+ assertEquals(5, attrs.size());
+ log.debug("Read attributes of " + copiedFile + ": " + attrs.keySet());
+ // Direct node access
+ NodeFileAttributes nfa = Files.readAttributes(copiedFile, NodeFileAttributes.class);
+ nfa.getNode().addMixin(NodeType.MIX_LANGUAGE);
+ nfa.getNode().getSession().save();
+ log.debug("Add mix:language");
+ Files.setAttribute(copiedFile, Property.JCR_LANGUAGE, "fr");
+ log.debug("Set language");
+ attrs = Files.readAttributes(copiedFile, "*");
+ assertEquals(6, attrs.size());
+ log.debug("Read attributes of " + copiedFile + ": " + attrs.keySet());
+ }
+}
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
+@Deprecated
public class JcrResourceAdapterTest extends AbstractJackrabbitTestCase {
private static SimpleDateFormat sdf = new SimpleDateFormat(
"yyyyMMdd:hhmmss.SSS");
--- /dev/null
+<?xml version="1.0"?>\r
+<!--\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+-->\r
+\r
+<!DOCTYPE Repository\r
+ PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 2.0//EN"\r
+ "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">\r
+\r
+<!-- Example Repository Configuration File\r
+ Used by\r
+ - org.apache.jackrabbit.core.config.RepositoryConfigTest.java\r
+ -\r
+-->\r
+<Repository>\r
+ <!--\r
+ virtual file system where the repository stores global state\r
+ (e.g. registered namespaces, custom node types, etc.)\r
+ -->\r
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">\r
+ <param name="path" value="${rep.home}/repository"/>\r
+ </FileSystem>\r
+\r
+ <!--\r
+ data store configuration\r
+ -->\r
+ <DataStore class="org.apache.jackrabbit.core.data.FileDataStore"/>\r
+\r
+ <!--\r
+ security configuration\r
+ -->\r
+ <Security appName="Jackrabbit">\r
+ <!--\r
+ security manager:\r
+ class: FQN of class implementing the JackrabbitSecurityManager interface\r
+ -->\r
+ <SecurityManager class="org.apache.jackrabbit.core.DefaultSecurityManager" workspaceName="security">\r
+ <!--\r
+ workspace access:\r
+ class: FQN of class implementing the WorkspaceAccessManager interface\r
+ -->\r
+ <!-- <WorkspaceAccessManager class="..."/> -->\r
+ <!-- <param name="config" value="${rep.home}/security.xml"/> -->\r
+ </SecurityManager>\r
+\r
+ <!--\r
+ access manager:\r
+ class: FQN of class implementing the AccessManager interface\r
+ -->\r
+ <AccessManager class="org.apache.jackrabbit.core.security.DefaultAccessManager">\r
+ <!-- <param name="config" value="${rep.home}/access.xml"/> -->\r
+ </AccessManager>\r
+\r
+ <LoginModule class="org.apache.jackrabbit.core.security.authentication.DefaultLoginModule">\r
+ <!-- \r
+ anonymous user name ('anonymous' is the default value)\r
+ -->\r
+ <param name="anonymousId" value="anonymous"/>\r
+ <!--\r
+ administrator user id (default value if param is missing is 'admin')\r
+ -->\r
+ <param name="adminId" value="admin"/>\r
+ </LoginModule>\r
+ </Security>\r
+\r
+ <!--\r
+ location of workspaces root directory and name of default workspace\r
+ -->\r
+ <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>\r
+ <!--\r
+ workspace configuration template:\r
+ used to create the initial workspace if there's no workspace yet\r
+ -->\r
+ <Workspace name="${wsp.name}">\r
+ <!--\r
+ virtual file system of the workspace:\r
+ class: FQN of class implementing the FileSystem interface\r
+ -->\r
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">\r
+ <param name="path" value="${wsp.home}"/>\r
+ </FileSystem>\r
+ <!--\r
+ persistence manager of the workspace:\r
+ class: FQN of class implementing the PersistenceManager interface\r
+ -->\r
+ <PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">\r
+ <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>\r
+ <param name="schemaObjectPrefix" value="${wsp.name}_"/>\r
+ </PersistenceManager>\r
+ <!--\r
+ Search index and the file system it uses.\r
+ class: FQN of class implementing the QueryHandler interface\r
+ -->\r
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">\r
+ <param name="path" value="${wsp.home}/index"/>\r
+ <param name="supportHighlighting" value="true"/>\r
+ </SearchIndex>\r
+ </Workspace>\r
+\r
+ <!--\r
+ Configures the versioning\r
+ -->\r
+ <Versioning rootPath="${rep.home}/version">\r
+ <!--\r
+ Configures the filesystem to use for versioning for the respective\r
+ persistence manager\r
+ -->\r
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">\r
+ <param name="path" value="${rep.home}/version" />\r
+ </FileSystem>\r
+\r
+ <!--\r
+ Configures the persistence manager to be used for persisting version state.\r
+ Please note that the current versioning implementation is based on\r
+ a 'normal' persistence manager, but this could change in future\r
+ implementations.\r
+ -->\r
+ <PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">\r
+ <param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>\r
+ <param name="schemaObjectPrefix" value="version_"/>\r
+ </PersistenceManager>\r
+ </Versioning>\r
+\r
+ <!--\r
+ Search index for content that is shared repository wide\r
+ (/jcr:system tree, contains mainly versions)\r
+ -->\r
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">\r
+ <param name="path" value="${rep.home}/repository/index"/>\r
+ <param name="supportHighlighting" value="true"/>\r
+ </SearchIndex>\r
+\r
+ <!--\r
+ Run with a cluster journal\r
+ -->\r
+ <Cluster id="node1">\r
+ <Journal class="org.apache.jackrabbit.core.journal.MemoryJournal"/>\r
+ </Cluster>\r
+</Repository>\r
--- /dev/null
+cafebabe-cafe-babe-cafe-babecafebabe
\ No newline at end of file
--- /dev/null
+#Fri Oct 28 20:14:30 CEST 2016
+http\://www.jcp.org/jcr/1.0=1570322
+internal=16762557
+http\://www.jcp.org/jcr/sv/1.0=16463688
+http\://www.jcp.org/jcr/mix/1.0=14361695
+http\://www.jcp.org/jcr/nt/1.0=5688619
+.empty.key=0
+http\://www.w3.org/XML/1998/namespace=6829023
--- /dev/null
+#Fri Oct 28 20:14:30 CEST 2016
+jcr=http\://www.jcp.org/jcr/1.0
+sv=http\://www.jcp.org/jcr/sv/1.0
+xml=http\://www.w3.org/XML/1998/namespace
+nt=http\://www.jcp.org/jcr/nt/1.0
+mix=http\://www.jcp.org/jcr/mix/1.0
+rep=internal
+.empty.key=
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?><Workspace name="default">
+ <!--
+ virtual file system of the workspace:
+ class: FQN of class implementing the FileSystem interface
+ -->
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+ <param name="path" value="${wsp.home}"/>
+ </FileSystem>
+ <!--
+ persistence manager of the workspace:
+ class: FQN of class implementing the PersistenceManager interface
+ -->
+ <PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">
+ <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
+ <param name="schemaObjectPrefix" value="${wsp.name}_"/>
+ </PersistenceManager>
+ <!--
+ Search index and the file system it uses.
+ class: FQN of class implementing the QueryHandler interface
+ -->
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${wsp.home}/index"/>
+ <param name="supportHighlighting" value="true"/>
+ </SearchIndex>
+ </Workspace>
--- /dev/null
+package org.argeo.jackrabbit.fs;
+
+import org.argeo.jcr.fs.JcrFileSystemProvider;
+
+public abstract class AbstractJackrabbitFsProvider extends JcrFileSystemProvider {
+
+}
--- /dev/null
+package org.argeo.jackrabbit.fs;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.argeo.jcr.fs.JcrFileSystem;
+import org.argeo.jcr.fs.JcrFsException;
+
+public class JackrabbitMemoryFsProvider extends AbstractJackrabbitFsProvider {
+ private RepositoryImpl repository;
+ private JcrFileSystem fileSystem;
+
+ @Override
+ public String getScheme() {
+ return "jcr+memory";
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
+ try {
+ Path tempDir = Files.createTempDirectory("fs-memory");
+ URL confUrl = getClass().getResource("fs-memory.xml");
+ RepositoryConfig repositoryConfig = RepositoryConfig.create(confUrl.toURI(), tempDir.toString());
+ repository = RepositoryImpl.create(repositoryConfig);
+ String username = System.getProperty("user.name");
+ Session session = repository.login(new SimpleCredentials(username, username.toCharArray()));
+ fileSystem = new JcrFileSystem(this, session);
+ return fileSystem;
+ } catch (Exception e) {
+ throw new JcrFsException("Cannot login to repository", e);
+ }
+ }
+
+ @Override
+ public FileSystem getFileSystem(URI uri) {
+ return fileSystem;
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ String path = uri.getPath();
+ if(fileSystem==null)
+ try {
+ newFileSystem(uri, new HashMap<String, Object>());
+ } catch (IOException e) {
+ throw new JcrFsException("Could not autocreate file system",e);
+ }
+ return fileSystem.getPath(path);
+ }
+
+}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE Repository PUBLIC "Jackrabbit 2.6" "http://jackrabbit.apache.org/dtd/repository-2.6.dtd">
+<Repository>
+ <!-- File system and datastore -->
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+
+ <!-- Workspace templates -->
+ <Workspaces rootPath="${rep.home}/workspaces"
+ defaultWorkspace="main" configRootPath="/workspaces" />
+ <Workspace name="${wsp.name}">
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+ <PersistenceManager
+ class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+ </PersistenceManager>
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${wsp.home}/index" />
+ <param name="directoryManagerClass"
+ value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+ </SearchIndex>
+ </Workspace>
+
+ <!-- Versioning -->
+ <Versioning rootPath="${rep.home}/version">
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+ <PersistenceManager
+ class="org.apache.jackrabbit.core.persistence.bundle.BundleFsPersistenceManager">
+ </PersistenceManager>
+ </Versioning>
+
+ <!-- Indexing -->
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${rep.home}/index" />
+ <param name="directoryManagerClass"
+ value="org.apache.jackrabbit.core.query.lucene.directory.RAMDirectoryManager" />
+ <FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem" />
+ </SearchIndex>
+
+ <!-- Security -->
+ <Security appName="Jackrabbit">
+ <LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule" />
+ <!-- <SecurityManager class="org.argeo.security.jackrabbit.ArgeoSecurityManager" -->
+ <!-- workspaceName="security" /> -->
+ <!-- <AccessManager class="org.argeo.security.jackrabbit.ArgeoAccessManager"
+ /> -->
+ </Security>
+</Repository>
\ No newline at end of file
* Bridge Spring resources and JCR folder / files semantics (nt:folder /
* nt:file), supporting versioning as well.
*/
+@Deprecated
public class JcrResourceAdapter {
private final static Log log = LogFactory.getLog(JcrResourceAdapter.class);
--- /dev/null
+package org.argeo.jcr.fs;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.jcr.JcrUtils;
+
+public class BinaryChannel implements SeekableByteChannel {
+ private final Node file;
+ private Binary binary;
+ private boolean open = true;
+
+ private long position = 0;
+
+ // private ByteBuffer toWrite;
+ private FileChannel fc = null;
+
+ public BinaryChannel(Node file) throws RepositoryException, IOException {
+ this.file = file;
+ // int capacity = 1024 * 1024;
+ // this.toWrite = ByteBuffer.allocate(capacity);
+ if (file.isNodeType(NodeType.NT_FILE)) {
+ if (file.hasNode(Property.JCR_CONTENT)) {
+ Node data = file.getNode(Property.JCR_CONTENT);
+ this.binary = data.getProperty(Property.JCR_DATA).getBinary();
+ } else {
+ Node data = file.addNode(Property.JCR_CONTENT, NodeType.NT_RESOURCE);
+ try (InputStream in = new ByteArrayInputStream(new byte[0])) {
+ this.binary = data.getSession().getValueFactory().createBinary(in);
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported file node " + file + " (" + file.getPrimaryNodeType() + ")");
+ }
+ }
+
+ @Override
+ public synchronized boolean isOpen() {
+ return open;
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ if (isModified()) {
+ Binary newBinary=null;
+ try {
+ Session session = file.getSession();
+ // byte[] arr = new byte[(int) position];
+ // toWrite.flip();
+ // toWrite.get(arr);
+ fc.position(0);
+ InputStream in = Channels.newInputStream(fc);
+ newBinary = session.getValueFactory().createBinary(in);
+ file.getNode(Property.JCR_CONTENT).setProperty(Property.JCR_DATA, newBinary);
+ session.save();
+ open = false;
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot close " + file, e);
+ }finally{
+ JcrUtils.closeQuietly(newBinary);
+ IOUtils.closeQuietly(fc);
+ }
+ } else {
+ clearReadState();
+ open = false;
+ }
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ if (isModified()) {
+ return fc.read(dst);
+ } else {
+
+ try {
+ int read;
+// int capacity = dst.capacity();
+ byte[] arr = dst.array();
+ read = binary.read(arr, position);
+ //dst.put(arr, 0, read);
+
+ // try {
+ // byte[] arr = dst.array();
+ // read = binary.read(arr, position);
+ // } catch (UnsupportedOperationException e) {
+ // int capacity = dst.capacity();
+ // byte[] arr = new byte[capacity];
+ // read = binary.read(arr, position);
+ // dst.put(arr);
+ // }
+ if (read != -1)
+ position = position + read;
+ return read;
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot read into buffer", e);
+ }
+ }
+ }
+
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ int written = getFileChannel().write(src);
+ return written;
+ // int byteCount = src.remaining();
+ // if (toWrite.remaining() < byteCount)
+ // throw new JcrFsException("Write buffer is full");
+ // toWrite.put(src);
+ // if (position < binarySize)
+ // position = binarySize + byteCount;
+ // else
+ // position = position + byteCount;
+ // return byteCount;
+ }
+
+ @Override
+ public long position() throws IOException {
+ if (isModified())
+ return getFileChannel().position();
+ else
+ return position;
+ }
+
+ @Override
+ public SeekableByteChannel position(long newPosition) throws IOException {
+ if (isModified()) {
+ getFileChannel().position(position);
+ } else {
+ this.position = newPosition;
+ }
+ return this;
+ }
+
+ @Override
+ public long size() throws IOException {
+ if (isModified()) {
+ return getFileChannel().size();
+ } else {
+ try {
+ return binary.getSize();
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot get size", e);
+ }
+ }
+ }
+
+ @Override
+ public SeekableByteChannel truncate(long size) throws IOException {
+ getFileChannel().truncate(size);
+ // if (size != size())
+ // throw new UnsupportedOperationException("Cannot truncate JCR
+ // binary");
+ return this;
+ }
+
+ private FileChannel getFileChannel() throws IOException {
+ try {
+ if (fc == null) {
+ Path tempPath = Files.createTempFile(getClass().getSimpleName(), null);
+ fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.SPARSE);
+ ReadableByteChannel readChannel = Channels.newChannel(binary.getStream());
+ fc.transferFrom(readChannel, 0, binary.getSize());
+ clearReadState();
+ }
+ return fc;
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot get temp file channel", e);
+ }
+ }
+
+ private boolean isModified() {
+ return fc != null;
+ }
+
+ private void clearReadState(){
+ position = -1;
+ JcrUtils.closeQuietly(binary);
+ binary=null;
+ }
+}
--- /dev/null
+package org.argeo.jcr.fs;
+
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.jcr.JcrUtils;
+
+public class JcrBasicfileAttributes implements NodeFileAttributes {
+ private final Node node;
+
+ private FileTime EPOCH = FileTime.fromMillis(0);
+
+ public JcrBasicfileAttributes(Node node) {
+ this.node = node;
+ }
+
+ @Override
+ public FileTime lastModifiedTime() {
+ try {
+ if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
+ Instant instant = node.getProperty(Property.JCR_LAST_MODIFIED).getDate().toInstant();
+ return FileTime.from(instant);
+ }
+ return EPOCH;
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot get last modified time", e);
+ }
+ }
+
+ @Override
+ public FileTime lastAccessTime() {
+ return lastModifiedTime();
+ }
+
+ @Override
+ public FileTime creationTime() {
+ try {
+ if (node.isNodeType(NodeType.MIX_CREATED)) {
+ Instant instant = node.getProperty(Property.JCR_CREATED).getDate().toInstant();
+ return FileTime.from(instant);
+ }
+ return EPOCH;
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot get creation time", e);
+ }
+ }
+
+ @Override
+ public boolean isRegularFile() {
+ try {
+ return node.isNodeType(NodeType.NT_FILE);
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot check if regular file", e);
+ }
+ }
+
+ @Override
+ public boolean isDirectory() {
+ try {
+ return node.isNodeType(NodeType.NT_FOLDER);
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot check if directory", e);
+ }
+ }
+
+ @Override
+ public boolean isSymbolicLink() {
+ try {
+ return node.isNodeType(NodeType.NT_LINKED_FILE);
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot check if linked file", e);
+ }
+ }
+
+ @Override
+ public boolean isOther() {
+ return !(isDirectory() || isRegularFile() || isSymbolicLink());
+ }
+
+ @Override
+ public long size() {
+ if (isRegularFile()) {
+ Binary binary = null;
+ try {
+ binary = node.getNode(Property.JCR_DATA).getProperty(Property.JCR_CONTENT).getBinary();
+ return binary.getSize();
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot check size", e);
+ } finally {
+ JcrUtils.closeQuietly(binary);
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public Object fileKey() {
+ return null;
+ }
+
+ @Override
+ public Node getNode() {
+ return node;
+ }
+
+}
import java.nio.file.WatchService;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
+import java.util.HashSet;
import java.util.Set;
+import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.argeo.jcr.JcrUtils;
@Override
public Iterable<Path> getRootDirectories() {
- return null;
+ try {
+ Set<Path> single = new HashSet<>();
+ single.add(new JcrPath(this, session.getRootNode()));
+ return single;
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot get root path", e);
+ }
}
@Override
public Iterable<FileStore> getFileStores() {
- // TODO Auto-generated method stub
- return null;
+ throw new UnsupportedOperationException();
}
@Override
public Set<String> supportedFileAttributeViews() {
- // TODO Auto-generated method stub
- return null;
+ try {
+ String[] prefixes = session.getNamespacePrefixes();
+ Set<String> res = new HashSet<>();
+ for (String prefix : prefixes)
+ res.add(prefix);
+ res.add("basic");
+ return res;
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot get supported file attributes views", e);
+ }
}
@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 JcrPath(this, sb.toString());
}
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern) {
- // TODO Auto-generated method stub
- return null;
+ throw new UnsupportedOperationException();
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
- // TODO Auto-generated method stub
- return null;
+ throw new UnsupportedOperationException();
}
@Override
public WatchService newWatchService() throws IOException {
- // TODO Auto-generated method stub
- return null;
+ throw new UnsupportedOperationException();
}
public Session getSession() {
package org.argeo.jcr.fs;
import java.io.IOException;
-import java.net.URI;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessMode;
import java.nio.file.CopyOption;
+import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.FileStore;
-import java.nio.file.FileSystem;
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.Calendar;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-public class JcrFileSystemProvider extends FileSystemProvider {
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
- @Override
- public String getScheme() {
- return "jcr";
- }
+import org.apache.commons.io.FileExistsException;
+import org.argeo.jcr.JcrUtils;
+public abstract class JcrFileSystemProvider extends FileSystemProvider {
@Override
- public FileSystem newFileSystem(URI uri, Map<String, ?> env)
+ public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
throws IOException {
- // TODO Auto-generated method stub
- return null;
- }
+ try {
+ Node node = toNode(path);
+ if (node == null) {
+ Node parentNode = toNode(path.getParent());
+ if (parentNode == null)
+ throw new JcrFsException("No parent directory for " + path);
+ if (!(parentNode.getPath().equals("/") || parentNode.isNodeType(NodeType.NT_FOLDER)))
+ throw new JcrFsException("Parent of " + path + " is not a directory");
- @Override
- public FileSystem getFileSystem(URI uri) {
- // TODO Auto-generated method stub
- return null;
+ node = parentNode.addNode(path.getFileName().toString(), NodeType.NT_FILE);
+ node.addMixin(NodeType.MIX_CREATED);
+ node.addMixin(NodeType.MIX_LAST_MODIFIED);
+ }
+ if (!node.isNodeType(NodeType.NT_FILE))
+ throw new UnsupportedOperationException(node + " must be a file");
+ return new BinaryChannel(node);
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot read file", e);
+ }
}
@Override
- public Path getPath(URI uri) {
- // TODO Auto-generated method stub
- return null;
+ public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
+ try {
+ Node base = toNode(dir);
+ return new NodeDirectoryStream((JcrFileSystem) dir.getFileSystem(), base.getNodes(), filter);
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot list directory", e);
+ }
}
@Override
- public SeekableByteChannel newByteChannel(Path path,
- Set<? extends OpenOption> options, FileAttribute<?>... attrs)
- throws IOException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public DirectoryStream<Path> newDirectoryStream(Path dir,
- Filter<? super Path> filter) throws IOException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public void createDirectory(Path dir, FileAttribute<?>... attrs)
- throws IOException {
- // TODO Auto-generated method stub
+ public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
+ try {
+ Node node = toNode(dir);
+ if (node == null) {
+ Node parent = toNode(dir.getParent());
+ if (parent == null)
+ throw new IOException("Parent of " + dir + " does not exist");
+ if (!(parent.getPath().equals("/") || parent.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)))
+ throw new JcrFsException(dir + " parent is not a directory");
+ node = parent.addNode(dir.getFileName().toString(), NodeType.NT_FOLDER);
+ node.addMixin(NodeType.MIX_CREATED);
+ node.addMixin(NodeType.MIX_LAST_MODIFIED);
+ node.getSession().save();
+ } else {
+ if (!node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER))
+ throw new FileExistsException(dir + " exists and is not a directory");
+ }
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot create directory " + dir, e);
+ }
}
@Override
public void delete(Path path) throws IOException {
- // TODO Auto-generated method stub
+ try {
+ Node node = toNode(path);
+ if (node == null)
+ throw new NoSuchFileException(path + " does not exist");
+ Session session = node.getSession();
+ if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FILE))
+ node.remove();
+ else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) {
+ if (node.hasNodes())// TODO check only files
+ throw new DirectoryNotEmptyException(path.toString());
+ node.remove();
+ }
+ session.save();
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot delete " + path, e);
+ }
}
@Override
- public void copy(Path source, Path target, CopyOption... options)
- throws IOException {
- // TODO Auto-generated method stub
-
+ public void copy(Path source, Path target, CopyOption... options) throws IOException {
+ try {
+ Node sourceNode = toNode(source);
+ Node targetNode = toNode(target);
+ JcrUtils.copy(sourceNode, targetNode);
+ sourceNode.getSession().save();
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot copy from " + source + " to " + target, e);
+ }
}
@Override
- public void move(Path source, Path target, CopyOption... options)
- throws IOException {
- // TODO Auto-generated method stub
-
+ public void move(Path source, Path target, CopyOption... options) throws IOException {
+ try {
+ Node sourceNode = toNode(source);
+ Session session = sourceNode.getSession();
+ session.move(sourceNode.getPath(), target.toString());
+ session.save();
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot move from " + source + " to " + target, e);
+ }
}
@Override
public boolean isSameFile(Path path, Path path2) throws IOException {
- // TODO Auto-generated method stub
- return false;
+ if (path.getFileSystem() != path2.getFileSystem())
+ return false;
+ boolean equ = path.equals(path2);
+ if (equ)
+ return true;
+ else {
+ try {
+ Node node = toNode(path);
+ Node node2 = toNode(path2);
+ return node.isSame(node2);
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot check whether " + path + " and " + path2 + " are same", e);
+ }
+ }
+
}
@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;
+ Session session = ((JcrFileSystem) path.getFileSystem()).getSession();
+ return new WorkSpaceFileStore(session.getWorkspace());
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
- // TODO Auto-generated method stub
-
+ try {
+ Session session = ((JcrFileSystem) path.getFileSystem()).getSession();
+ if (!session.itemExists(path.toString()))
+ throw new NoSuchFileException(path + " does not exist");
+ // TODO check access via JCR api
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot delete " + path, e);
+ }
}
@Override
- public <V extends FileAttributeView> V getFileAttributeView(Path path,
- Class<V> type, LinkOption... options) {
- // TODO Auto-generated method stub
- return null;
+ public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
+ throw new UnsupportedOperationException();
}
+ @SuppressWarnings("unchecked")
@Override
- public <A extends BasicFileAttributes> A readAttributes(Path path,
- Class<A> type, LinkOption... options) throws IOException {
- // TODO Auto-generated method stub
- return null;
+ public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options)
+ throws IOException {
+ try {
+ // TODO check if assignable
+ Node node = toNode(path);
+ return (A) new JcrBasicfileAttributes(node);
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot read basic attributes of " + path, e);
+ }
}
@Override
- public Map<String, Object> readAttributes(Path path, String attributes,
- LinkOption... options) throws IOException {
- // TODO Auto-generated method stub
- return null;
+ public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
+ try {
+ Node node = toNode(path);
+ String pattern = attributes.replace(',', '|');
+ Map<String, Object> res = new HashMap<String, Object>();
+ PropertyIterator it = node.getProperties(pattern);
+ props: while (it.hasNext()) {
+ Property prop = it.nextProperty();
+ PropertyDefinition pd = prop.getDefinition();
+ if (pd.isMultiple())
+ continue props;
+ int requiredType = pd.getRequiredType();
+ switch (requiredType) {
+ case PropertyType.LONG:
+ res.put(prop.getName(), prop.getLong());
+ break;
+ case PropertyType.DOUBLE:
+ res.put(prop.getName(), prop.getDouble());
+ break;
+ case PropertyType.BOOLEAN:
+ res.put(prop.getName(), prop.getBoolean());
+ break;
+ case PropertyType.DATE:
+ res.put(prop.getName(), prop.getDate());
+ break;
+ case PropertyType.BINARY:
+ byte[] arr = JcrUtils.getBinaryAsBytes(prop);
+ res.put(prop.getName(), arr);
+ break;
+ default:
+ res.put(prop.getName(), prop.getString());
+ }
+ }
+ return res;
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot read attributes of " + path, e);
+ }
}
@Override
- public void setAttribute(Path path, String attribute, Object value,
- LinkOption... options) throws IOException {
- // TODO Auto-generated method stub
+ public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
+ try {
+ Node node = toNode(path);
+ if (value instanceof byte[]) {
+ JcrUtils.setBinaryAsBytes(node, attribute, (byte[]) value);
+ } else if (value instanceof Calendar) {
+ node.setProperty(attribute, (Calendar) value);
+ } else {
+ node.setProperty(attribute, value.toString());
+ }
+ node.getSession().save();
+ } catch (RepositoryException e) {
+ throw new JcrFsException("Cannot set attribute " + attribute + " on " + path, e);
+ }
+ }
+ protected Node toNode(Path path) throws RepositoryException {
+ return ((JcrPath) path).getNode();
}
}
import java.io.File;
import java.io.IOException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.WatchEvent.Modifier;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
+import java.util.Arrays;
import java.util.Iterator;
+import java.util.NoSuchElementException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
+import javax.jcr.Session;
public class JcrPath implements Path {
- private JcrFileSystem filesSystem;
- private String path;
+ private final static String delimStr = "/";
+ private final static char delimChar = '/';
+
+ private final JcrFileSystem fs;
+ private final String[] path;// null means root
+ private final boolean absolute;
+
+ // optim
+ private final int hashCode;
+
+ public JcrPath(JcrFileSystem filesSystem, String path) {
+ // this(filesSystem, path.equals("/") ? null : path.split("/"), path ==
+ // null ? true : path.startsWith("/"));
+ this.fs = filesSystem;
+ if (path == null)
+ throw new JcrFsException("Path cannot be null");
+ if (path.equals(delimStr)) {// root
+ this.path = null;
+ this.absolute = true;
+ this.hashCode = 0;
+ return;
+ }
+ this.absolute = path.charAt(0) == delimChar ? true : false;
+ String trimmedPath = path.substring(absolute ? 1 : 0,
+ path.charAt(path.length() - 1) == delimChar ? path.length() - 1 : path.length());
+ this.path = trimmedPath.split(delimStr);
+ this.hashCode = this.path[this.path.length - 1].hashCode();
+ }
- private Node node;
+ public JcrPath(JcrFileSystem filesSystem, Node node) throws RepositoryException {
+ this(filesSystem, node.getPath());
+ }
- public JcrPath(JcrFileSystem filesSystem, Node node) {
- super();
- this.filesSystem = filesSystem;
- this.node = node;
+ /** Internal optimisation */
+ private JcrPath(JcrFileSystem filesSystem, String[] path, boolean absolute) {
+ this.fs = filesSystem;
+ this.path = path;
+ this.absolute = path == null ? true : absolute;
+ this.hashCode = path == null ? 0 : path[path.length - 1].hashCode();
}
@Override
public FileSystem getFileSystem() {
- return filesSystem;
+ return fs;
}
@Override
public boolean isAbsolute() {
- return path.startsWith("/");
+ return absolute;
}
@Override
public Path getRoot() {
try {
- return new JcrPath(filesSystem, node.getSession().getRootNode());
+ if (path == null)
+ return this;
+ return new JcrPath(fs, fs.getSession().getRootNode());
} catch (RepositoryException e) {
throw new JcrFsException("Cannot get root", e);
}
}
+ @Override
+ public String toString() {
+ if (path == null)
+ return "/";
+ StringBuilder sb = new StringBuilder();
+ if (isAbsolute())
+ sb.append('/');
+ for (int i = 0; i < path.length; i++) {
+ if (i != 0)
+ sb.append('/');
+ sb.append(path[i]);
+ }
+ return sb.toString();
+ }
+
@Override
public Path getFileName() {
- return null;
+ if (path == null)
+ return null;
+ return new JcrPath(fs, path[path.length - 1]);
}
@Override
public Path getParent() {
- // TODO Auto-generated method stub
- return null;
+ if (path == null)
+ return null;
+ if (path.length == 1)// root
+ return new JcrPath(fs, delimStr);
+ String[] parentPath = Arrays.copyOfRange(path, 0, path.length - 1);
+ return new JcrPath(fs, parentPath, absolute);
}
@Override
public int getNameCount() {
- // TODO Auto-generated method stub
- return 0;
+ if (path == null)
+ return 0;
+ return path.length;
}
@Override
public Path getName(int index) {
- // TODO Auto-generated method stub
- return null;
+ if (path == null)
+ return null;
+ return new JcrPath(fs, path[index]);
}
@Override
public Path subpath(int beginIndex, int endIndex) {
- // TODO Auto-generated method stub
- return null;
+ if (path == null)
+ return null;
+ String[] parentPath = Arrays.copyOfRange(path, beginIndex, endIndex);
+ return new JcrPath(fs, parentPath, false);
}
@Override
public boolean startsWith(Path other) {
- // TODO Auto-generated method stub
- return false;
+ return toString().startsWith(other.toString());
}
@Override
public boolean startsWith(String other) {
- // TODO Auto-generated method stub
- return false;
+ return toString().startsWith(other);
}
@Override
public boolean endsWith(Path other) {
- // TODO Auto-generated method stub
- return false;
+ return toString().endsWith(other.toString());
}
@Override
public boolean endsWith(String other) {
- // TODO Auto-generated method stub
- return false;
+ return toString().endsWith(other);
}
@Override
public Path normalize() {
- // TODO Auto-generated method stub
- return null;
+ // always normalized
+ return this;
}
@Override
public Path resolve(Path other) {
- // TODO Auto-generated method stub
- return null;
+ JcrPath otherPath = (JcrPath) other;
+ if (otherPath.isAbsolute())
+ return other;
+ String[] newPath;
+ if (path == null) {
+ newPath = new String[otherPath.path.length];
+ System.arraycopy(otherPath.path, 0, newPath, 0, otherPath.path.length);
+ } else {
+ newPath = new String[path.length + otherPath.path.length];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ System.arraycopy(otherPath.path, 0, newPath, path.length, otherPath.path.length);
+ }
+ return new JcrPath(fs, newPath, absolute);
}
@Override
- public Path resolve(String other) {
- // TODO Auto-generated method stub
- return null;
+ public final Path resolve(String other) {
+ return resolve(getFileSystem().getPath(other));
}
@Override
- public Path resolveSibling(Path other) {
- // TODO Auto-generated method stub
- return null;
+ public final Path resolveSibling(Path other) {
+ if (other == null)
+ throw new NullPointerException();
+ Path parent = getParent();
+ return (parent == null) ? other : parent.resolve(other);
}
@Override
- public Path resolveSibling(String other) {
- // TODO Auto-generated method stub
- return null;
+ public final Path resolveSibling(String other) {
+ return resolveSibling(getFileSystem().getPath(other));
+ }
+
+ @Override
+ public final Iterator<Path> iterator() {
+ return new Iterator<Path>() {
+ private int i = 0;
+
+ @Override
+ public boolean hasNext() {
+ return (i < getNameCount());
+ }
+
+ @Override
+ public Path next() {
+ if (i < getNameCount()) {
+ Path result = getName(i);
+ i++;
+ return result;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
}
@Override
public Path relativize(Path other) {
- // TODO Auto-generated method stub
- return null;
+ if (equals(other))
+ return new JcrPath(fs, "");
+ if (other.startsWith(this)) {
+ String p1 = toString();
+ String p2 = other.toString();
+ return new JcrPath(fs, p2.substring(p1.length(), p2.length()));
+ }
+ throw new UnsupportedOperationException();
}
@Override
public URI toUri() {
- // TODO Auto-generated method stub
- return null;
+ try {
+ return new URI("jcr", toString(), null);
+ } catch (URISyntaxException e) {
+ throw new JcrFsException("Cannot create URI for " + toString(), e);
+ }
}
@Override
public Path toAbsolutePath() {
- // TODO Auto-generated method stub
- return null;
+ if (isAbsolute())
+ return this;
+ return new JcrPath(fs, path, true);
}
@Override
public Path toRealPath(LinkOption... options) throws IOException {
- // TODO Auto-generated method stub
- return null;
+ return this;
}
@Override
}
@Override
- public WatchKey register(WatchService watcher, Kind<?>[] events,
- Modifier... modifiers) throws IOException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public WatchKey register(WatchService watcher, Kind<?>... events)
- throws IOException {
+ public WatchKey register(WatchService watcher, Kind<?>[] events, Modifier... modifiers) throws IOException {
// TODO Auto-generated method stub
return null;
}
@Override
- public Iterator<Path> iterator() {
+ public WatchKey register(WatchService watcher, Kind<?>... events) throws IOException {
// TODO Auto-generated method stub
return null;
}
@Override
public int compareTo(Path other) {
- // TODO Auto-generated method stub
- return 0;
+ return toString().compareTo(other.toString());
}
- public Node getNode() {
+ public Node getNode() throws RepositoryException {
if (!isAbsolute())// TODO default dir
throw new JcrFsException("Cannot get node from relative path");
- try {
- if (node == null)
- node = filesSystem.getSession().getNode(path);
- return node;
- } catch (RepositoryException e) {
- throw new JcrFsException("Cannot get node", e);
+ String pathStr = toString();
+ Session session = fs.getSession();
+ // TODO synchronize on the session ?
+ if (!session.itemExists(pathStr))
+ return null;
+ return session.getNode(pathStr);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof JcrPath))
+ return false;
+ JcrPath other = (JcrPath) obj;
+ if (path.length != other.path.length)
+ return false;
+ for (int i = 0; i < path.length; i++) {
+ if (!path[i].equals(other.path[i]))
+ return false;
}
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return new JcrPath(fs, toString());
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ Arrays.fill(path, null);
}
}
--- /dev/null
+package org.argeo.jcr.fs;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.util.Iterator;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+
+public class NodeDirectoryStream implements DirectoryStream<Path> {
+ private final JcrFileSystem fs;
+ private final NodeIterator nodeIterator;
+ private final Filter<? super Path> filter;
+
+ public NodeDirectoryStream(JcrFileSystem fs, NodeIterator nodeIterator, Filter<? super Path> filter) {
+ this.fs = fs;
+ this.nodeIterator = nodeIterator;
+ this.filter = filter;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ return new Iterator<Path>() {
+ private JcrPath next = null;
+
+ @Override
+ public synchronized boolean hasNext() {
+ if (next != null)
+ return true;
+ nodes: while (nodeIterator.hasNext()) {
+ try {
+ Node node = nodeIterator.nextNode();
+ next = new JcrPath(fs, node);
+ if (filter != null) {
+ if (filter.accept(next))
+ break nodes;
+ } else
+ break nodes;
+ } catch (Exception e) {
+ throw new JcrFsException("Could not get next path", e);
+ }
+ }
+ return next != null;
+ }
+
+ @Override
+ public synchronized Path next() {
+ if (!hasNext())// make sure has next has been called
+ return null;
+ JcrPath res = next;
+ next = null;
+ return res;
+ }
+
+ };
+ }
+
+}
--- /dev/null
+package org.argeo.jcr.fs;
+
+import java.nio.file.attribute.BasicFileAttributes;
+
+import javax.jcr.Node;
+
+public interface NodeFileAttributes extends BasicFileAttributes {
+ public Node getNode();
+}
--- /dev/null
+package org.argeo.jcr.fs;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.Path;
+import java.util.Map;
+
+import javax.jcr.Session;
+
+/** An FS provider based on a single JCR session (experimental). */
+public class SessionFsProvider extends JcrFileSystemProvider {
+ private Session session;
+ private JcrFileSystem fileSystem;
+
+ public SessionFsProvider(Session session) {
+ this.session = session;
+ }
+
+ @Override
+ public String getScheme() {
+ return "jcr+session";
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
+ if (fileSystem != null && fileSystem.isOpen())
+ throw new FileSystemAlreadyExistsException();
+ fileSystem = new JcrFileSystem(this, session) {
+ boolean open;
+
+ @Override
+ public void close() throws IOException {
+ // prevent the session logout
+ open = false;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open;
+ }
+
+ };
+ return fileSystem;
+ }
+
+ @Override
+ public FileSystem getFileSystem(URI uri) {
+ return fileSystem;
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ return new JcrPath(fileSystem, uri.getPath());
+ }
+
+}
--- /dev/null
+package org.argeo.jcr.fs;
+
+import java.io.IOException;
+import java.nio.file.FileStore;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileStoreAttributeView;
+
+import javax.jcr.Workspace;
+
+public class WorkSpaceFileStore extends FileStore {
+ private Workspace workspace;
+
+ public WorkSpaceFileStore(Workspace workspace) {
+ this.workspace = workspace;
+ }
+
+ @Override
+ public String name() {
+ return workspace.getName();
+ }
+
+ @Override
+ public String type() {
+ return "workspace";
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
+ public long getTotalSpace() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getUsableSpace() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getUnallocatedSpace() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(String name) {
+ return false;
+ }
+
+ @Override
+ public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
+ return null;
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ return workspace.getSession().getRepository().getDescriptor(attribute);
+ }
+
+}