]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java
[maven-release-plugin] prepare for next development iteration
[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 public abstract class JcrFileSystemProvider extends FileSystemProvider {
36
37 @Override
38 public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
39 throws IOException {
40 Node node = toNode(path);
41 try {
42 if (node == null) {
43 Node parent = toNode(path.getParent());
44 if (parent == null)
45 throw new IOException("No parent directory for " + path);
46 if (parent.getPrimaryNodeType().isNodeType(NodeType.NT_FILE)
47 || parent.getPrimaryNodeType().isNodeType(NodeType.NT_LINKED_FILE))
48 throw new IOException(path + " parent is a file");
49
50 String fileName = path.getFileName().toString();
51 fileName = Text.escapeIllegalJcrChars(fileName);
52 node = parent.addNode(fileName, NodeType.NT_FILE);
53 node.addMixin(NodeType.MIX_CREATED);
54 node.addMixin(NodeType.MIX_LAST_MODIFIED);
55 }
56 if (!node.isNodeType(NodeType.NT_FILE))
57 throw new UnsupportedOperationException(node + " must be a file");
58 return new BinaryChannel(node, path);
59 } catch (RepositoryException e) {
60 discardChanges(node);
61 throw new IOException("Cannot read file", e);
62 }
63 }
64
65 @Override
66 public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
67 try {
68 Node base = toNode(dir);
69 if (base == null)
70 throw new IOException(dir + " is not a JCR node");
71 return new NodeDirectoryStream((JcrFileSystem) dir.getFileSystem(), base.getNodes(), filter);
72 } catch (RepositoryException e) {
73 throw new IOException("Cannot list directory", e);
74 }
75 }
76
77 @Override
78 public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
79 Node node = toNode(dir);
80 try {
81 if (node == null) {
82 Node parent = toNode(dir.getParent());
83 if (parent == null)
84 throw new IOException("Parent of " + dir + " does not exist");
85 if (parent.getPrimaryNodeType().isNodeType(NodeType.NT_FILE)
86 || parent.getPrimaryNodeType().isNodeType(NodeType.NT_LINKED_FILE))
87 throw new IOException(dir + " parent is a file");
88 String fileName = dir.getFileName().toString();
89 fileName = Text.escapeIllegalJcrChars(fileName);
90 node = parent.addNode(fileName, NodeType.NT_FOLDER);
91 node.addMixin(NodeType.MIX_CREATED);
92 node.addMixin(NodeType.MIX_LAST_MODIFIED);
93 node.getSession().save();
94 } else {
95 // if (!node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER))
96 // throw new FileExistsException(dir + " exists and is not a directory");
97 }
98 } catch (RepositoryException e) {
99 discardChanges(node);
100 throw new IOException("Cannot create directory " + dir, e);
101 }
102 }
103
104 @Override
105 public void delete(Path path) throws IOException {
106 Node node = toNode(path);
107 try {
108 if (node == null)
109 throw new NoSuchFileException(path + " does not exist");
110 Session session = node.getSession();
111 if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FILE))
112 node.remove();
113 else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) {
114 if (node.hasNodes())// TODO check only files
115 throw new DirectoryNotEmptyException(path.toString());
116 node.remove();
117 }
118 session.save();
119 } catch (RepositoryException e) {
120 discardChanges(node);
121 throw new IOException("Cannot delete " + path, e);
122 }
123
124 }
125
126 @Override
127 public void copy(Path source, Path target, CopyOption... options) throws IOException {
128 Node sourceNode = toNode(source);
129 Node targetNode = toNode(target);
130 try {
131 JcrUtils.copy(sourceNode, targetNode);
132 sourceNode.getSession().save();
133 } catch (RepositoryException e) {
134 discardChanges(sourceNode);
135 discardChanges(targetNode);
136 throw new IOException("Cannot copy from " + source + " to " + target, e);
137 }
138 }
139
140 @Override
141 public void move(Path source, Path target, CopyOption... options) throws IOException {
142 Node sourceNode = toNode(source);
143 try {
144 Session session = sourceNode.getSession();
145 session.move(sourceNode.getPath(), target.toString());
146 session.save();
147 } catch (RepositoryException e) {
148 discardChanges(sourceNode);
149 throw new IOException("Cannot move from " + source + " to " + target, e);
150 }
151 }
152
153 @Override
154 public boolean isSameFile(Path path, Path path2) throws IOException {
155 if (path.getFileSystem() != path2.getFileSystem())
156 return false;
157 boolean equ = path.equals(path2);
158 if (equ)
159 return true;
160 else {
161 try {
162 Node node = toNode(path);
163 Node node2 = toNode(path2);
164 return node.isSame(node2);
165 } catch (RepositoryException e) {
166 throw new IOException("Cannot check whether " + path + " and " + path2 + " are same", e);
167 }
168 }
169
170 }
171
172 @Override
173 public boolean isHidden(Path path) throws IOException {
174 return path.getFileName().toString().charAt(0) == '.';
175 }
176
177 @Override
178 public FileStore getFileStore(Path path) throws IOException {
179 Session session = ((JcrFileSystem) path.getFileSystem()).getSession();
180 return new WorkSpaceFileStore(session.getWorkspace());
181 }
182
183 @Override
184 public void checkAccess(Path path, AccessMode... modes) throws IOException {
185 try {
186 Session session = ((JcrFileSystem) path.getFileSystem()).getSession();
187 if (!session.itemExists(path.toString()))
188 throw new NoSuchFileException(path + " does not exist");
189 // TODO check access via JCR api
190 } catch (RepositoryException e) {
191 throw new IOException("Cannot delete " + path, e);
192 }
193 }
194
195 @Override
196 public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
197 throw new UnsupportedOperationException();
198 }
199
200 @SuppressWarnings("unchecked")
201 @Override
202 public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options)
203 throws IOException {
204 // TODO check if assignable
205 Node node = toNode(path);
206 if (node == null) {
207 throw new IOException("JCR node not found for " + path);
208 }
209 return (A) new JcrBasicfileAttributes(node);
210 }
211
212 @Override
213 public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
214 try {
215 Node node = toNode(path);
216 String pattern = attributes.replace(',', '|');
217 Map<String, Object> res = new HashMap<String, Object>();
218 PropertyIterator it = node.getProperties(pattern);
219 props: while (it.hasNext()) {
220 Property prop = it.nextProperty();
221 PropertyDefinition pd = prop.getDefinition();
222 if (pd.isMultiple())
223 continue props;
224 int requiredType = pd.getRequiredType();
225 switch (requiredType) {
226 case PropertyType.LONG:
227 res.put(prop.getName(), prop.getLong());
228 break;
229 case PropertyType.DOUBLE:
230 res.put(prop.getName(), prop.getDouble());
231 break;
232 case PropertyType.BOOLEAN:
233 res.put(prop.getName(), prop.getBoolean());
234 break;
235 case PropertyType.DATE:
236 res.put(prop.getName(), prop.getDate());
237 break;
238 case PropertyType.BINARY:
239 byte[] arr = JcrUtils.getBinaryAsBytes(prop);
240 res.put(prop.getName(), arr);
241 break;
242 default:
243 res.put(prop.getName(), prop.getString());
244 }
245 }
246 return res;
247 } catch (RepositoryException e) {
248 throw new IOException("Cannot read attributes of " + path, e);
249 }
250 }
251
252 @Override
253 public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
254 Node node = toNode(path);
255 try {
256 if (value instanceof byte[]) {
257 JcrUtils.setBinaryAsBytes(node, attribute, (byte[]) value);
258 } else if (value instanceof Calendar) {
259 node.setProperty(attribute, (Calendar) value);
260 } else {
261 node.setProperty(attribute, value.toString());
262 }
263 node.getSession().save();
264 } catch (RepositoryException e) {
265 discardChanges(node);
266 throw new IOException("Cannot set attribute " + attribute + " on " + path, e);
267 }
268 }
269
270 protected Node toNode(Path path) {
271 try {
272 return ((JcrPath) path).getNode();
273 } catch (RepositoryException e) {
274 throw new JcrFsException("Cannot convert path " + path + " to JCR Node", e);
275 }
276 }
277
278 /** Discard changes in the underlying session */
279 protected void discardChanges(Node node) {
280 if (node == null)
281 return;
282 try {
283 // discard changes
284 node.getSession().refresh(false);
285 } catch (RepositoryException e) {
286 e.printStackTrace();
287 // TODO log out session?
288 // TODO use Commons logging?
289 }
290 }
291
292 /**
293 * To be overriden in order to support the ~ path, with an implementation
294 * specific concept of user home.
295 *
296 * @return null by default
297 */
298 public Node getUserHome(Session session) {
299 return null;
300 }
301 }