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
.Repository
;
29 import javax
.jcr
.RepositoryException
;
30 import javax
.jcr
.Session
;
31 import javax
.jcr
.nodetype
.NodeType
;
32 import javax
.jcr
.nodetype
.PropertyDefinition
;
34 import org
.argeo
.jcr
.JcrUtils
;
36 /** Operations on a {@link JcrFileSystem}. */
37 public abstract class JcrFileSystemProvider
extends FileSystemProvider
{
40 public SeekableByteChannel
newByteChannel(Path path
, Set
<?
extends OpenOption
> options
, FileAttribute
<?
>... attrs
)
42 Node node
= toNode(path
);
45 Node parent
= toNode(path
.getParent());
47 throw new IOException("No parent directory for " + path
);
48 if (parent
.getPrimaryNodeType().isNodeType(NodeType
.NT_FILE
)
49 || parent
.getPrimaryNodeType().isNodeType(NodeType
.NT_LINKED_FILE
))
50 throw new IOException(path
+ " parent is a file");
52 String fileName
= path
.getFileName().toString();
53 fileName
= Text
.escapeIllegalJcrChars(fileName
);
54 node
= parent
.addNode(fileName
, NodeType
.NT_FILE
);
55 node
.addMixin(NodeType
.MIX_CREATED
);
56 // node.addMixin(NodeType.MIX_LAST_MODIFIED);
58 if (!node
.isNodeType(NodeType
.NT_FILE
))
59 throw new UnsupportedOperationException(node
+ " must be a file");
60 return new BinaryChannel(node
, path
);
61 } catch (RepositoryException e
) {
63 throw new IOException("Cannot read file", e
);
68 public DirectoryStream
<Path
> newDirectoryStream(Path dir
, Filter
<?
super Path
> filter
) throws IOException
{
70 Node base
= toNode(dir
);
72 throw new IOException(dir
+ " is not a JCR node");
73 JcrFileSystem fileSystem
= (JcrFileSystem
) dir
.getFileSystem();
74 return new NodeDirectoryStream(fileSystem
, base
.getNodes(), fileSystem
.listDirectMounts(dir
), filter
);
75 } catch (RepositoryException e
) {
76 throw new IOException("Cannot list directory", e
);
81 public void createDirectory(Path dir
, FileAttribute
<?
>... attrs
) throws IOException
{
82 Node node
= toNode(dir
);
85 Node parent
= toNode(dir
.getParent());
87 throw new IOException("Parent of " + dir
+ " does not exist");
88 Session session
= parent
.getSession();
89 synchronized (session
) {
90 if (parent
.getPrimaryNodeType().isNodeType(NodeType
.NT_FILE
)
91 || parent
.getPrimaryNodeType().isNodeType(NodeType
.NT_LINKED_FILE
))
92 throw new IOException(dir
+ " parent is a file");
93 String fileName
= dir
.getFileName().toString();
94 fileName
= Text
.escapeIllegalJcrChars(fileName
);
95 node
= parent
.addNode(fileName
, NodeType
.NT_FOLDER
);
96 node
.addMixin(NodeType
.MIX_CREATED
);
97 node
.addMixin(NodeType
.MIX_LAST_MODIFIED
);
101 // if (!node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER))
102 // throw new FileExistsException(dir + " exists and is not a directory");
104 } catch (RepositoryException e
) {
105 discardChanges(node
);
106 throw new IOException("Cannot create directory " + dir
, e
);
111 public void delete(Path path
) throws IOException
{
112 Node node
= toNode(path
);
115 throw new NoSuchFileException(path
+ " does not exist");
116 Session session
= node
.getSession();
117 synchronized (session
) {
118 session
.refresh(false);
119 if (node
.getPrimaryNodeType().isNodeType(NodeType
.NT_FILE
))
121 else if (node
.getPrimaryNodeType().isNodeType(NodeType
.NT_FOLDER
)) {
122 if (node
.hasNodes())// TODO check only files
123 throw new DirectoryNotEmptyException(path
.toString());
128 } catch (RepositoryException e
) {
129 discardChanges(node
);
130 throw new IOException("Cannot delete " + path
, e
);
136 public void copy(Path source
, Path target
, CopyOption
... options
) throws IOException
{
137 Node sourceNode
= toNode(source
);
138 Node targetNode
= toNode(target
);
140 Session targetSession
= targetNode
.getSession();
141 synchronized (targetSession
) {
142 JcrUtils
.copy(sourceNode
, targetNode
);
145 } catch (RepositoryException e
) {
146 discardChanges(sourceNode
);
147 discardChanges(targetNode
);
148 throw new IOException("Cannot copy from " + source
+ " to " + target
, e
);
153 public void move(Path source
, Path target
, CopyOption
... options
) throws IOException
{
154 JcrFileSystem sourceFileSystem
= (JcrFileSystem
) source
.getFileSystem();
155 WorkspaceFileStore sourceStore
= sourceFileSystem
.getFileStore(source
.toString());
156 WorkspaceFileStore targetStore
= sourceFileSystem
.getFileStore(target
.toString());
158 if (sourceStore
.equals(targetStore
)) {
159 sourceStore
.getWorkspace().move(sourceStore
.toJcrPath(source
.toString()),
160 targetStore
.toJcrPath(target
.toString()));
163 throw new UnsupportedOperationException("Can only move paths within the same workspace.");
165 } catch (RepositoryException e
) {
166 throw new IOException("Cannot move from " + source
+ " to " + target
, e
);
169 // Node sourceNode = toNode(source);
171 // Session session = sourceNode.getSession();
172 // synchronized (session) {
173 // session.move(sourceNode.getPath(), target.toString());
176 // } catch (RepositoryException e) {
177 // discardChanges(sourceNode);
178 // throw new IOException("Cannot move from " + source + " to " + target, e);
183 public boolean isSameFile(Path path
, Path path2
) throws IOException
{
184 if (path
.getFileSystem() != path2
.getFileSystem())
186 boolean equ
= path
.equals(path2
);
191 Node node
= toNode(path
);
192 Node node2
= toNode(path2
);
193 return node
.isSame(node2
);
194 } catch (RepositoryException e
) {
195 throw new IOException("Cannot check whether " + path
+ " and " + path2
+ " are same", e
);
202 public boolean isHidden(Path path
) throws IOException
{
203 return path
.getFileName().toString().charAt(0) == '.';
207 public FileStore
getFileStore(Path path
) throws IOException
{
208 JcrFileSystem fileSystem
= (JcrFileSystem
) path
.getFileSystem();
209 return fileSystem
.getFileStore(path
.toString());
213 public void checkAccess(Path path
, AccessMode
... modes
) throws IOException
{
214 Node node
= toNode(path
);
216 throw new NoSuchFileException(path
+ " does not exist");
217 // TODO check access via JCR api
221 public <V
extends FileAttributeView
> V
getFileAttributeView(Path path
, Class
<V
> type
, LinkOption
... options
) {
222 throw new UnsupportedOperationException();
225 @SuppressWarnings("unchecked")
227 public <A
extends BasicFileAttributes
> A
readAttributes(Path path
, Class
<A
> type
, LinkOption
... options
)
229 // TODO check if assignable
230 Node node
= toNode(path
);
232 throw new IOException("JCR node not found for " + path
);
234 return (A
) new JcrBasicfileAttributes(node
);
238 public Map
<String
, Object
> readAttributes(Path path
, String attributes
, LinkOption
... options
) throws IOException
{
240 Node node
= toNode(path
);
241 String pattern
= attributes
.replace(',', '|');
242 Map
<String
, Object
> res
= new HashMap
<String
, Object
>();
243 PropertyIterator it
= node
.getProperties(pattern
);
244 props
: while (it
.hasNext()) {
245 Property prop
= it
.nextProperty();
246 PropertyDefinition pd
= prop
.getDefinition();
249 int requiredType
= pd
.getRequiredType();
250 switch (requiredType
) {
251 case PropertyType
.LONG
:
252 res
.put(prop
.getName(), prop
.getLong());
254 case PropertyType
.DOUBLE
:
255 res
.put(prop
.getName(), prop
.getDouble());
257 case PropertyType
.BOOLEAN
:
258 res
.put(prop
.getName(), prop
.getBoolean());
260 case PropertyType
.DATE
:
261 res
.put(prop
.getName(), prop
.getDate());
263 case PropertyType
.BINARY
:
264 byte[] arr
= JcrUtils
.getBinaryAsBytes(prop
);
265 res
.put(prop
.getName(), arr
);
268 res
.put(prop
.getName(), prop
.getString());
272 } catch (RepositoryException e
) {
273 throw new IOException("Cannot read attributes of " + path
, e
);
278 public void setAttribute(Path path
, String attribute
, Object value
, LinkOption
... options
) throws IOException
{
279 Node node
= toNode(path
);
281 Session session
= node
.getSession();
282 synchronized (session
) {
283 if (value
instanceof byte[]) {
284 JcrUtils
.setBinaryAsBytes(node
, attribute
, (byte[]) value
);
285 } else if (value
instanceof Calendar
) {
286 node
.setProperty(attribute
, (Calendar
) value
);
288 node
.setProperty(attribute
, value
.toString());
292 } catch (RepositoryException e
) {
293 discardChanges(node
);
294 throw new IOException("Cannot set attribute " + attribute
+ " on " + path
, e
);
298 protected Node
toNode(Path path
) {
300 return ((JcrPath
) path
).getNode();
301 } catch (RepositoryException e
) {
302 throw new JcrFsException("Cannot convert path " + path
+ " to JCR Node", e
);
306 /** Discard changes in the underlying session */
307 protected void discardChanges(Node node
) {
312 node
.getSession().refresh(false);
313 } catch (RepositoryException e
) {
315 // TODO log out session?
316 // TODO use Commons logging?
320 /** Make sure save is robust. */
321 protected void save(Session session
) throws RepositoryException
{
322 session
.refresh(true);
328 * To be overriden in order to support the ~ path, with an implementation
329 * specific concept of user home.
331 * @return null by default
333 public Node
getUserHome(Repository session
) {