X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.jcr%2Fsrc%2Forg%2Fargeo%2Fjcr%2Ffs%2FJcrFileSystemProvider.java;h=7a38ba3701d45f071e1f654f1c4145dafc7c0c46;hb=b4c4c47379e740b494a4a759df07c7b09a7649fa;hp=8ea4cca2ea1fe13a8e923dab228b6389756b1087;hpb=e66b9893b0e511f8ab295e3cee42b7dc966f1597;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java index 8ea4cca2e..7a38ba370 100644 --- a/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java +++ b/org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java @@ -1,142 +1,322 @@ 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.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.PropertyDefinition; - @Override - public String getScheme() { - return "jcr"; - } - - @Override - public FileSystem newFileSystem(URI uri, Map env) - throws IOException { - // TODO Auto-generated method stub - return null; - } +import org.argeo.jcr.JcrUtils; - @Override - public FileSystem getFileSystem(URI uri) { - // TODO Auto-generated method stub - return null; - } +/** Operations on a {@link JcrFileSystem}. */ +public abstract class JcrFileSystemProvider extends FileSystemProvider { @Override - public Path getPath(URI uri) { - // TODO Auto-generated method stub - return null; - } - - @Override - public SeekableByteChannel newByteChannel(Path path, - Set options, FileAttribute... attrs) + public SeekableByteChannel newByteChannel(Path path, Set options, FileAttribute... attrs) throws IOException { - // TODO Auto-generated method stub - return null; + Node node = toNode(path); + try { + if (node == null) { + Node parent = toNode(path.getParent()); + if (parent == null) + throw new IOException("No parent directory for " + path); + if (parent.getPrimaryNodeType().isNodeType(NodeType.NT_FILE) + || parent.getPrimaryNodeType().isNodeType(NodeType.NT_LINKED_FILE)) + throw new IOException(path + " parent is a file"); + + String fileName = path.getFileName().toString(); + fileName = Text.escapeIllegalJcrChars(fileName); + node = parent.addNode(fileName, 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, path); + } catch (RepositoryException e) { + discardChanges(node); + throw new IOException("Cannot read file", e); + } } @Override - public DirectoryStream newDirectoryStream(Path dir, - Filter filter) throws IOException { - // TODO Auto-generated method stub - return null; + public DirectoryStream newDirectoryStream(Path dir, Filter filter) throws IOException { + try { + Node base = toNode(dir); + if (base == null) + throw new IOException(dir + " is not a JCR node"); + JcrFileSystem fileSystem = (JcrFileSystem) dir.getFileSystem(); + return new NodeDirectoryStream(fileSystem, base.getNodes(), fileSystem.listDirectMounts(dir), filter); + } catch (RepositoryException e) { + throw new IOException("Cannot list directory", e); + } } @Override - public void createDirectory(Path dir, FileAttribute... attrs) - throws IOException { - // TODO Auto-generated method stub - + public void createDirectory(Path dir, FileAttribute... attrs) throws IOException { + Node node = toNode(dir); + try { + if (node == null) { + Node parent = toNode(dir.getParent()); + if (parent == null) + throw new IOException("Parent of " + dir + " does not exist"); + Session session = parent.getSession(); + synchronized (session) { + if (parent.getPrimaryNodeType().isNodeType(NodeType.NT_FILE) + || parent.getPrimaryNodeType().isNodeType(NodeType.NT_LINKED_FILE)) + throw new IOException(dir + " parent is a file"); + String fileName = dir.getFileName().toString(); + fileName = Text.escapeIllegalJcrChars(fileName); + node = parent.addNode(fileName, NodeType.NT_FOLDER); + node.addMixin(NodeType.MIX_CREATED); + node.addMixin(NodeType.MIX_LAST_MODIFIED); + save(session); + } + } else { + // if (!node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) + // throw new FileExistsException(dir + " exists and is not a directory"); + } + } catch (RepositoryException e) { + discardChanges(node); + throw new IOException("Cannot create directory " + dir, e); + } } @Override public void delete(Path path) throws IOException { - // TODO Auto-generated method stub + Node node = toNode(path); + try { + if (node == null) + throw new NoSuchFileException(path + " does not exist"); + Session session = node.getSession(); + synchronized (session) { + session.refresh(false); + 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(); + } + save(session); + } + } catch (RepositoryException e) { + discardChanges(node); + throw new IOException("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 { + Node sourceNode = toNode(source); + Node targetNode = toNode(target); + try { + Session targetSession = targetNode.getSession(); + synchronized (targetSession) { + JcrUtils.copy(sourceNode, targetNode); + save(targetSession); + } + } catch (RepositoryException e) { + discardChanges(sourceNode); + discardChanges(targetNode); + throw new IOException("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 { + Node sourceNode = toNode(source); + try { + Session session = sourceNode.getSession(); + synchronized (session) { + session.move(sourceNode.getPath(), target.toString()); + save(session); + } + } catch (RepositoryException e) { + discardChanges(sourceNode); + throw new IOException("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 IOException("Cannot check whether " + path + " and " + path2 + " are same", e); + } + } + } @Override public boolean isHidden(Path path) throws IOException { - // TODO Auto-generated method stub - return false; + return path.getFileName().toString().charAt(0) == '.'; } @Override public FileStore getFileStore(Path path) throws IOException { - // TODO Auto-generated method stub - return null; + JcrFileSystem fileSystem = (JcrFileSystem) path.getFileSystem(); + return fileSystem.getFileStore(path.toString()); } @Override public void checkAccess(Path path, AccessMode... modes) throws IOException { - // TODO Auto-generated method stub - + Node node = toNode(path); + if (node == null) + throw new NoSuchFileException(path + " does not exist"); + // TODO check access via JCR api } @Override - public V getFileAttributeView(Path path, - Class type, LinkOption... options) { - // TODO Auto-generated method stub - return null; + public V getFileAttributeView(Path path, Class type, LinkOption... options) { + throw new UnsupportedOperationException(); } + @SuppressWarnings("unchecked") @Override - public A readAttributes(Path path, - Class type, LinkOption... options) throws IOException { - // TODO Auto-generated method stub - return null; + public A readAttributes(Path path, Class type, LinkOption... options) + throws IOException { + // TODO check if assignable + Node node = toNode(path); + if (node == null) { + throw new IOException("JCR node not found for " + path); + } + return (A) new JcrBasicfileAttributes(node); } @Override - public Map readAttributes(Path path, String attributes, - LinkOption... options) throws IOException { - // TODO Auto-generated method stub - return null; + public Map readAttributes(Path path, String attributes, LinkOption... options) throws IOException { + try { + Node node = toNode(path); + String pattern = attributes.replace(',', '|'); + Map res = new HashMap(); + 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 IOException("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 { + Node node = toNode(path); + try { + Session session = node.getSession(); + synchronized (session) { + 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()); + } + save(session); + } + } catch (RepositoryException e) { + discardChanges(node); + throw new IOException("Cannot set attribute " + attribute + " on " + path, e); + } + } + protected Node toNode(Path path) { + try { + return ((JcrPath) path).getNode(); + } catch (RepositoryException e) { + throw new JcrFsException("Cannot convert path " + path + " to JCR Node", e); + } + } + + /** Discard changes in the underlying session */ + protected void discardChanges(Node node) { + if (node == null) + return; + try { + // discard changes + node.getSession().refresh(false); + } catch (RepositoryException e) { + e.printStackTrace(); + // TODO log out session? + // TODO use Commons logging? + } + } + + /** Make sure save is robust. */ + protected void save(Session session) throws RepositoryException { + session.refresh(true); + session.save(); + session.notifyAll(); + } + + /** + * To be overriden in order to support the ~ path, with an implementation + * specific concept of user home. + * + * @return null by default + */ + public Node getUserHome(Repository session) { + return null; } }