]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java
Remove node data model, home areas based on workspaces instead.
[lgpl/argeo-commons.git] / org.argeo.jcr / src / org / argeo / jcr / fs / JcrFileSystemProvider.java
1 package org.argeo.jcr.fs;
2
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;
21 import java.util.Map;
22 import java.util.Set;
23
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;
32
33 import org.argeo.jcr.JcrUtils;
34
35 /** Operations on a {@link JcrFileSystem}. */
36 public abstract class JcrFileSystemProvider extends FileSystemProvider {
37
38 @Override
39 public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
40 throws IOException {
41 Node node = toNode(path);
42 try {
43 if (node == null) {
44 Node parent = toNode(path.getParent());
45 if (parent == null)
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");
50
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);
56 }
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) {
61 discardChanges(node);
62 throw new IOException("Cannot read file", e);
63 }
64 }
65
66 @Override
67 public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
68 try {
69 Node base = toNode(dir);
70 if (base == null)
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);
75 }
76 }
77
78 @Override
79 public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
80 Node node = toNode(dir);
81 try {
82 if (node == null) {
83 Node parent = toNode(dir.getParent());
84 if (parent == null)
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);
96 save(session);
97 }
98 } else {
99 // if (!node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER))
100 // throw new FileExistsException(dir + " exists and is not a directory");
101 }
102 } catch (RepositoryException e) {
103 discardChanges(node);
104 throw new IOException("Cannot create directory " + dir, e);
105 }
106 }
107
108 @Override
109 public void delete(Path path) throws IOException {
110 Node node = toNode(path);
111 try {
112 if (node == null)
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))
118 node.remove();
119 else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) {
120 if (node.hasNodes())// TODO check only files
121 throw new DirectoryNotEmptyException(path.toString());
122 node.remove();
123 }
124 save(session);
125 }
126 } catch (RepositoryException e) {
127 discardChanges(node);
128 throw new IOException("Cannot delete " + path, e);
129 }
130
131 }
132
133 @Override
134 public void copy(Path source, Path target, CopyOption... options) throws IOException {
135 Node sourceNode = toNode(source);
136 Node targetNode = toNode(target);
137 try {
138 Session targetSession = targetNode.getSession();
139 synchronized (targetSession) {
140 JcrUtils.copy(sourceNode, targetNode);
141 save(targetSession);
142 }
143 } catch (RepositoryException e) {
144 discardChanges(sourceNode);
145 discardChanges(targetNode);
146 throw new IOException("Cannot copy from " + source + " to " + target, e);
147 }
148 }
149
150 @Override
151 public void move(Path source, Path target, CopyOption... options) throws IOException {
152 Node sourceNode = toNode(source);
153 try {
154 Session session = sourceNode.getSession();
155 synchronized (session) {
156 session.move(sourceNode.getPath(), target.toString());
157 save(session);
158 }
159 } catch (RepositoryException e) {
160 discardChanges(sourceNode);
161 throw new IOException("Cannot move from " + source + " to " + target, e);
162 }
163 }
164
165 @Override
166 public boolean isSameFile(Path path, Path path2) throws IOException {
167 if (path.getFileSystem() != path2.getFileSystem())
168 return false;
169 boolean equ = path.equals(path2);
170 if (equ)
171 return true;
172 else {
173 try {
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);
179 }
180 }
181
182 }
183
184 @Override
185 public boolean isHidden(Path path) throws IOException {
186 return path.getFileName().toString().charAt(0) == '.';
187 }
188
189 @Override
190 public FileStore getFileStore(Path path) throws IOException {
191 Session session = ((JcrFileSystem) path.getFileSystem()).getSession();
192 return new WorkSpaceFileStore(session.getWorkspace());
193 }
194
195 @Override
196 public void checkAccess(Path path, AccessMode... modes) throws IOException {
197 try {
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);
204 }
205 }
206
207 @Override
208 public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
209 throw new UnsupportedOperationException();
210 }
211
212 @SuppressWarnings("unchecked")
213 @Override
214 public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options)
215 throws IOException {
216 // TODO check if assignable
217 Node node = toNode(path);
218 if (node == null) {
219 throw new IOException("JCR node not found for " + path);
220 }
221 return (A) new JcrBasicfileAttributes(node);
222 }
223
224 @Override
225 public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
226 try {
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();
234 if (pd.isMultiple())
235 continue props;
236 int requiredType = pd.getRequiredType();
237 switch (requiredType) {
238 case PropertyType.LONG:
239 res.put(prop.getName(), prop.getLong());
240 break;
241 case PropertyType.DOUBLE:
242 res.put(prop.getName(), prop.getDouble());
243 break;
244 case PropertyType.BOOLEAN:
245 res.put(prop.getName(), prop.getBoolean());
246 break;
247 case PropertyType.DATE:
248 res.put(prop.getName(), prop.getDate());
249 break;
250 case PropertyType.BINARY:
251 byte[] arr = JcrUtils.getBinaryAsBytes(prop);
252 res.put(prop.getName(), arr);
253 break;
254 default:
255 res.put(prop.getName(), prop.getString());
256 }
257 }
258 return res;
259 } catch (RepositoryException e) {
260 throw new IOException("Cannot read attributes of " + path, e);
261 }
262 }
263
264 @Override
265 public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
266 Node node = toNode(path);
267 try {
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);
274 } else {
275 node.setProperty(attribute, value.toString());
276 }
277 save(session);
278 }
279 } catch (RepositoryException e) {
280 discardChanges(node);
281 throw new IOException("Cannot set attribute " + attribute + " on " + path, e);
282 }
283 }
284
285 protected Node toNode(Path path) {
286 try {
287 return ((JcrPath) path).getNode();
288 } catch (RepositoryException e) {
289 throw new JcrFsException("Cannot convert path " + path + " to JCR Node", e);
290 }
291 }
292
293 /** Discard changes in the underlying session */
294 protected void discardChanges(Node node) {
295 if (node == null)
296 return;
297 try {
298 // discard changes
299 node.getSession().refresh(false);
300 } catch (RepositoryException e) {
301 e.printStackTrace();
302 // TODO log out session?
303 // TODO use Commons logging?
304 }
305 }
306
307 /** Make sure save is robust. */
308 protected void save(Session session) throws RepositoryException {
309 session.refresh(true);
310 session.save();
311 session.notifyAll();
312 }
313
314 /**
315 * To be overriden in order to support the ~ path, with an implementation
316 * specific concept of user home.
317 *
318 * @return null by default
319 */
320 public Node getUserHome(Session session) {
321 return null;
322 }
323 }