1 package org
.argeo
.jcr
.fs
;
3 import java
.io
.IOException
;
4 import java
.nio
.channels
.SeekableByteChannel
;
5 import java
.nio
.file
.AccessMode
;
6 import java
.nio
.file
.CopyOption
;
7 import java
.nio
.file
.DirectoryNotEmptyException
;
8 import java
.nio
.file
.DirectoryStream
;
9 import java
.nio
.file
.DirectoryStream
.Filter
;
10 import java
.nio
.file
.FileStore
;
11 import java
.nio
.file
.LinkOption
;
12 import java
.nio
.file
.NoSuchFileException
;
13 import java
.nio
.file
.OpenOption
;
14 import java
.nio
.file
.Path
;
15 import java
.nio
.file
.attribute
.BasicFileAttributes
;
16 import java
.nio
.file
.attribute
.FileAttribute
;
17 import java
.nio
.file
.attribute
.FileAttributeView
;
18 import java
.nio
.file
.spi
.FileSystemProvider
;
19 import java
.util
.Calendar
;
20 import java
.util
.HashMap
;
24 import javax
.jcr
.Node
;
25 import javax
.jcr
.Property
;
26 import javax
.jcr
.PropertyIterator
;
27 import javax
.jcr
.PropertyType
;
28 import javax
.jcr
.RepositoryException
;
29 import javax
.jcr
.Session
;
30 import javax
.jcr
.nodetype
.NodeType
;
31 import javax
.jcr
.nodetype
.PropertyDefinition
;
33 import org
.argeo
.jcr
.JcrUtils
;
35 /** Operations on a {@link JcrFileSystem}. */
36 public abstract class JcrFileSystemProvider
extends FileSystemProvider
{
39 public SeekableByteChannel
newByteChannel(Path path
, Set
<?
extends OpenOption
> options
, FileAttribute
<?
>... attrs
)
41 Node node
= toNode(path
);
44 Node parent
= toNode(path
.getParent());
46 throw new IOException("No parent directory for " + path
);
47 if (parent
.getPrimaryNodeType().isNodeType(NodeType
.NT_FILE
)
48 || parent
.getPrimaryNodeType().isNodeType(NodeType
.NT_LINKED_FILE
))
49 throw new IOException(path
+ " parent is a file");
51 String fileName
= path
.getFileName().toString();
52 fileName
= Text
.escapeIllegalJcrChars(fileName
);
53 node
= parent
.addNode(fileName
, NodeType
.NT_FILE
);
54 node
.addMixin(NodeType
.MIX_CREATED
);
55 // node.addMixin(NodeType.MIX_LAST_MODIFIED);
57 if (!node
.isNodeType(NodeType
.NT_FILE
))
58 throw new UnsupportedOperationException(node
+ " must be a file");
59 return new BinaryChannel(node
, path
);
60 } catch (RepositoryException e
) {
62 throw new IOException("Cannot read file", e
);
67 public DirectoryStream
<Path
> newDirectoryStream(Path dir
, Filter
<?
super Path
> filter
) throws IOException
{
69 Node base
= toNode(dir
);
71 throw new IOException(dir
+ " is not a JCR node");
72 return new NodeDirectoryStream((JcrFileSystem
) dir
.getFileSystem(), base
.getNodes(), filter
);
73 } catch (RepositoryException e
) {
74 throw new IOException("Cannot list directory", e
);
79 public void createDirectory(Path dir
, FileAttribute
<?
>... attrs
) throws IOException
{
80 Node node
= toNode(dir
);
83 Node parent
= toNode(dir
.getParent());
85 throw new IOException("Parent of " + dir
+ " does not exist");
86 Session session
= parent
.getSession();
87 synchronized (session
) {
88 if (parent
.getPrimaryNodeType().isNodeType(NodeType
.NT_FILE
)
89 || parent
.getPrimaryNodeType().isNodeType(NodeType
.NT_LINKED_FILE
))
90 throw new IOException(dir
+ " parent is a file");
91 String fileName
= dir
.getFileName().toString();
92 fileName
= Text
.escapeIllegalJcrChars(fileName
);
93 node
= parent
.addNode(fileName
, NodeType
.NT_FOLDER
);
94 node
.addMixin(NodeType
.MIX_CREATED
);
95 node
.addMixin(NodeType
.MIX_LAST_MODIFIED
);
99 // if (!node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER))
100 // throw new FileExistsException(dir + " exists and is not a directory");
102 } catch (RepositoryException e
) {
103 discardChanges(node
);
104 throw new IOException("Cannot create directory " + dir
, e
);
109 public void delete(Path path
) throws IOException
{
110 Node node
= toNode(path
);
113 throw new NoSuchFileException(path
+ " does not exist");
114 Session session
= node
.getSession();
115 synchronized (session
) {
116 session
.refresh(false);
117 if (node
.getPrimaryNodeType().isNodeType(NodeType
.NT_FILE
))
119 else if (node
.getPrimaryNodeType().isNodeType(NodeType
.NT_FOLDER
)) {
120 if (node
.hasNodes())// TODO check only files
121 throw new DirectoryNotEmptyException(path
.toString());
126 } catch (RepositoryException e
) {
127 discardChanges(node
);
128 throw new IOException("Cannot delete " + path
, e
);
134 public void copy(Path source
, Path target
, CopyOption
... options
) throws IOException
{
135 Node sourceNode
= toNode(source
);
136 Node targetNode
= toNode(target
);
138 Session targetSession
= targetNode
.getSession();
139 synchronized (targetSession
) {
140 JcrUtils
.copy(sourceNode
, targetNode
);
143 } catch (RepositoryException e
) {
144 discardChanges(sourceNode
);
145 discardChanges(targetNode
);
146 throw new IOException("Cannot copy from " + source
+ " to " + target
, e
);
151 public void move(Path source
, Path target
, CopyOption
... options
) throws IOException
{
152 Node sourceNode
= toNode(source
);
154 Session session
= sourceNode
.getSession();
155 synchronized (session
) {
156 session
.move(sourceNode
.getPath(), target
.toString());
159 } catch (RepositoryException e
) {
160 discardChanges(sourceNode
);
161 throw new IOException("Cannot move from " + source
+ " to " + target
, e
);
166 public boolean isSameFile(Path path
, Path path2
) throws IOException
{
167 if (path
.getFileSystem() != path2
.getFileSystem())
169 boolean equ
= path
.equals(path2
);
174 Node node
= toNode(path
);
175 Node node2
= toNode(path2
);
176 return node
.isSame(node2
);
177 } catch (RepositoryException e
) {
178 throw new IOException("Cannot check whether " + path
+ " and " + path2
+ " are same", e
);
185 public boolean isHidden(Path path
) throws IOException
{
186 return path
.getFileName().toString().charAt(0) == '.';
190 public FileStore
getFileStore(Path path
) throws IOException
{
191 JcrFileSystem fileSystem
= (JcrFileSystem
) path
.getFileSystem();
192 return fileSystem
.getFileStore(path
.toString());
196 public void checkAccess(Path path
, AccessMode
... modes
) throws IOException
{
198 Session session
= ((JcrFileSystem
) path
.getFileSystem()).getSession();
199 if (!session
.itemExists(path
.toString()))
200 throw new NoSuchFileException(path
+ " does not exist");
201 // TODO check access via JCR api
202 } catch (RepositoryException e
) {
203 throw new IOException("Cannot delete " + path
, e
);
208 public <V
extends FileAttributeView
> V
getFileAttributeView(Path path
, Class
<V
> type
, LinkOption
... options
) {
209 throw new UnsupportedOperationException();
212 @SuppressWarnings("unchecked")
214 public <A
extends BasicFileAttributes
> A
readAttributes(Path path
, Class
<A
> type
, LinkOption
... options
)
216 // TODO check if assignable
217 Node node
= toNode(path
);
219 throw new IOException("JCR node not found for " + path
);
221 return (A
) new JcrBasicfileAttributes(node
);
225 public Map
<String
, Object
> readAttributes(Path path
, String attributes
, LinkOption
... options
) throws IOException
{
227 Node node
= toNode(path
);
228 String pattern
= attributes
.replace(',', '|');
229 Map
<String
, Object
> res
= new HashMap
<String
, Object
>();
230 PropertyIterator it
= node
.getProperties(pattern
);
231 props
: while (it
.hasNext()) {
232 Property prop
= it
.nextProperty();
233 PropertyDefinition pd
= prop
.getDefinition();
236 int requiredType
= pd
.getRequiredType();
237 switch (requiredType
) {
238 case PropertyType
.LONG
:
239 res
.put(prop
.getName(), prop
.getLong());
241 case PropertyType
.DOUBLE
:
242 res
.put(prop
.getName(), prop
.getDouble());
244 case PropertyType
.BOOLEAN
:
245 res
.put(prop
.getName(), prop
.getBoolean());
247 case PropertyType
.DATE
:
248 res
.put(prop
.getName(), prop
.getDate());
250 case PropertyType
.BINARY
:
251 byte[] arr
= JcrUtils
.getBinaryAsBytes(prop
);
252 res
.put(prop
.getName(), arr
);
255 res
.put(prop
.getName(), prop
.getString());
259 } catch (RepositoryException e
) {
260 throw new IOException("Cannot read attributes of " + path
, e
);
265 public void setAttribute(Path path
, String attribute
, Object value
, LinkOption
... options
) throws IOException
{
266 Node node
= toNode(path
);
268 Session session
= node
.getSession();
269 synchronized (session
) {
270 if (value
instanceof byte[]) {
271 JcrUtils
.setBinaryAsBytes(node
, attribute
, (byte[]) value
);
272 } else if (value
instanceof Calendar
) {
273 node
.setProperty(attribute
, (Calendar
) value
);
275 node
.setProperty(attribute
, value
.toString());
279 } catch (RepositoryException e
) {
280 discardChanges(node
);
281 throw new IOException("Cannot set attribute " + attribute
+ " on " + path
, e
);
285 protected Node
toNode(Path path
) {
287 return ((JcrPath
) path
).getNode();
288 } catch (RepositoryException e
) {
289 throw new JcrFsException("Cannot convert path " + path
+ " to JCR Node", e
);
293 /** Discard changes in the underlying session */
294 protected void discardChanges(Node node
) {
299 node
.getSession().refresh(false);
300 } catch (RepositoryException e
) {
302 // TODO log out session?
303 // TODO use Commons logging?
307 /** Make sure save is robust. */
308 protected void save(Session session
) throws RepositoryException
{
309 session
.refresh(true);
315 * To be overriden in order to support the ~ path, with an implementation
316 * specific concept of user home.
318 * @return null by default
320 public Node
getUserHome(Session session
) {