]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.jcr/src/org/argeo/jcr/fs/JcrFileSystemProvider.java
Improve localization framework.
[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.Repository;
29 import javax.jcr.RepositoryException;
30 import javax.jcr.Session;
31 import javax.jcr.nodetype.NodeType;
32 import javax.jcr.nodetype.PropertyDefinition;
33
34 import org.argeo.jcr.JcrUtils;
35
36 /** Operations on a {@link JcrFileSystem}. */
37 public abstract class JcrFileSystemProvider extends FileSystemProvider {
38
39 @Override
40 public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
41 throws IOException {
42 Node node = toNode(path);
43 try {
44 if (node == null) {
45 Node parent = toNode(path.getParent());
46 if (parent == null)
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");
51
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);
57 }
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) {
62 discardChanges(node);
63 throw new IOException("Cannot read file", e);
64 }
65 }
66
67 @Override
68 public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
69 try {
70 Node base = toNode(dir);
71 if (base == null)
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);
77 }
78 }
79
80 @Override
81 public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
82 Node node = toNode(dir);
83 try {
84 if (node == null) {
85 Node parent = toNode(dir.getParent());
86 if (parent == null)
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);
98 save(session);
99 }
100 } else {
101 // if (!node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER))
102 // throw new FileExistsException(dir + " exists and is not a directory");
103 }
104 } catch (RepositoryException e) {
105 discardChanges(node);
106 throw new IOException("Cannot create directory " + dir, e);
107 }
108 }
109
110 @Override
111 public void delete(Path path) throws IOException {
112 Node node = toNode(path);
113 try {
114 if (node == null)
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))
120 node.remove();
121 else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) {
122 if (node.hasNodes())// TODO check only files
123 throw new DirectoryNotEmptyException(path.toString());
124 node.remove();
125 }
126 save(session);
127 }
128 } catch (RepositoryException e) {
129 discardChanges(node);
130 throw new IOException("Cannot delete " + path, e);
131 }
132
133 }
134
135 @Override
136 public void copy(Path source, Path target, CopyOption... options) throws IOException {
137 Node sourceNode = toNode(source);
138 Node targetNode = toNode(target);
139 try {
140 Session targetSession = targetNode.getSession();
141 synchronized (targetSession) {
142 JcrUtils.copy(sourceNode, targetNode);
143 save(targetSession);
144 }
145 } catch (RepositoryException e) {
146 discardChanges(sourceNode);
147 discardChanges(targetNode);
148 throw new IOException("Cannot copy from " + source + " to " + target, e);
149 }
150 }
151
152 @Override
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());
157 try {
158 if (sourceStore.equals(targetStore)) {
159 sourceStore.getWorkspace().move(sourceStore.toJcrPath(source.toString()),
160 targetStore.toJcrPath(target.toString()));
161 } else {
162 // TODO implement it
163 throw new UnsupportedOperationException("Can only move paths within the same workspace.");
164 }
165 } catch (RepositoryException e) {
166 throw new IOException("Cannot move from " + source + " to " + target, e);
167 }
168
169 // Node sourceNode = toNode(source);
170 // try {
171 // Session session = sourceNode.getSession();
172 // synchronized (session) {
173 // session.move(sourceNode.getPath(), target.toString());
174 // save(session);
175 // }
176 // } catch (RepositoryException e) {
177 // discardChanges(sourceNode);
178 // throw new IOException("Cannot move from " + source + " to " + target, e);
179 // }
180 }
181
182 @Override
183 public boolean isSameFile(Path path, Path path2) throws IOException {
184 if (path.getFileSystem() != path2.getFileSystem())
185 return false;
186 boolean equ = path.equals(path2);
187 if (equ)
188 return true;
189 else {
190 try {
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);
196 }
197 }
198
199 }
200
201 @Override
202 public boolean isHidden(Path path) throws IOException {
203 return path.getFileName().toString().charAt(0) == '.';
204 }
205
206 @Override
207 public FileStore getFileStore(Path path) throws IOException {
208 JcrFileSystem fileSystem = (JcrFileSystem) path.getFileSystem();
209 return fileSystem.getFileStore(path.toString());
210 }
211
212 @Override
213 public void checkAccess(Path path, AccessMode... modes) throws IOException {
214 Node node = toNode(path);
215 if (node == null)
216 throw new NoSuchFileException(path + " does not exist");
217 // TODO check access via JCR api
218 }
219
220 @Override
221 public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
222 throw new UnsupportedOperationException();
223 }
224
225 @SuppressWarnings("unchecked")
226 @Override
227 public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options)
228 throws IOException {
229 // TODO check if assignable
230 Node node = toNode(path);
231 if (node == null) {
232 throw new IOException("JCR node not found for " + path);
233 }
234 return (A) new JcrBasicfileAttributes(node);
235 }
236
237 @Override
238 public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
239 try {
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();
247 if (pd.isMultiple())
248 continue props;
249 int requiredType = pd.getRequiredType();
250 switch (requiredType) {
251 case PropertyType.LONG:
252 res.put(prop.getName(), prop.getLong());
253 break;
254 case PropertyType.DOUBLE:
255 res.put(prop.getName(), prop.getDouble());
256 break;
257 case PropertyType.BOOLEAN:
258 res.put(prop.getName(), prop.getBoolean());
259 break;
260 case PropertyType.DATE:
261 res.put(prop.getName(), prop.getDate());
262 break;
263 case PropertyType.BINARY:
264 byte[] arr = JcrUtils.getBinaryAsBytes(prop);
265 res.put(prop.getName(), arr);
266 break;
267 default:
268 res.put(prop.getName(), prop.getString());
269 }
270 }
271 return res;
272 } catch (RepositoryException e) {
273 throw new IOException("Cannot read attributes of " + path, e);
274 }
275 }
276
277 @Override
278 public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
279 Node node = toNode(path);
280 try {
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);
287 } else {
288 node.setProperty(attribute, value.toString());
289 }
290 save(session);
291 }
292 } catch (RepositoryException e) {
293 discardChanges(node);
294 throw new IOException("Cannot set attribute " + attribute + " on " + path, e);
295 }
296 }
297
298 protected Node toNode(Path path) {
299 try {
300 return ((JcrPath) path).getNode();
301 } catch (RepositoryException e) {
302 throw new JcrFsException("Cannot convert path " + path + " to JCR Node", e);
303 }
304 }
305
306 /** Discard changes in the underlying session */
307 protected void discardChanges(Node node) {
308 if (node == null)
309 return;
310 try {
311 // discard changes
312 node.getSession().refresh(false);
313 } catch (RepositoryException e) {
314 e.printStackTrace();
315 // TODO log out session?
316 // TODO use Commons logging?
317 }
318 }
319
320 /** Make sure save is robust. */
321 protected void save(Session session) throws RepositoryException {
322 session.refresh(true);
323 session.save();
324 session.notifyAll();
325 }
326
327 /**
328 * To be overriden in order to support the ~ path, with an implementation
329 * specific concept of user home.
330 *
331 * @return null by default
332 */
333 public Node getUserHome(Repository session) {
334 return null;
335 }
336
337 }