1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.datamodel.niofs.ifs2;
20
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.nio.channels.FileChannel;
24 import java.nio.channels.SeekableByteChannel;
25 import java.nio.file.ClosedDirectoryStreamException;
26 import java.nio.file.DirectoryStream;
27 import java.nio.file.FileAlreadyExistsException;
28 import java.nio.file.Files;
29 import java.nio.file.LinkOption;
30 import java.nio.file.NoSuchFileException;
31 import java.nio.file.NotDirectoryException;
32 import java.nio.file.OpenOption;
33 import java.nio.file.Path;
34 import java.nio.file.StandardCopyOption;
35 import java.nio.file.StandardOpenOption;
36 import java.nio.file.attribute.BasicFileAttributeView;
37 import java.nio.file.attribute.BasicFileAttributes;
38 import java.nio.file.attribute.FileAttribute;
39 import java.nio.file.attribute.FileAttributeView;
40 import java.nio.file.attribute.FileTime;
41 import java.util.Iterator;
42 import java.util.Set;
43
44 import org.apache.logging.log4j.LogManager;
45 import org.apache.logging.log4j.Logger;
46 import org.mycore.common.function.MCRThrowFunction;
47 import org.mycore.datamodel.ifs2.MCRDirectory;
48 import org.mycore.datamodel.ifs2.MCRFile;
49 import org.mycore.datamodel.ifs2.MCRFileCollection;
50 import org.mycore.datamodel.ifs2.MCRStoredNode;
51 import org.mycore.datamodel.niofs.MCRAbstractFileSystem;
52 import org.mycore.datamodel.niofs.MCRFileAttributes;
53 import org.mycore.datamodel.niofs.MCRMD5AttributeView;
54 import org.mycore.datamodel.niofs.MCRPath;
55
56
57
58
59
60
61
62 class MCRDirectoryStreamHelper {
63 static Logger LOGGER = LogManager.getLogger();
64
65 static DirectoryStream<Path> getInstance(MCRDirectory dir, MCRPath path) throws IOException {
66 DirectoryStream.Filter<Path> filter = (dir instanceof MCRFileCollection) ? MCRFileCollectionFilter.FILTER
67 : AcceptAllFilter.FILTER;
68 LOGGER.debug("Dir {}, class {}, filter {}", path, dir.getClass(), filter.getClass());
69 DirectoryStream<Path> baseDirectoryStream = Files.newDirectoryStream(dir.getLocalPath(), filter);
70 LOGGER.debug("baseStream {}", baseDirectoryStream.getClass());
71 if (baseDirectoryStream instanceof java.nio.file.SecureDirectoryStream) {
72 LOGGER.debug("Returning SecureDirectoryStream");
73 return new SecureDirectoryStream(dir, path,
74 (java.nio.file.SecureDirectoryStream<Path>) baseDirectoryStream);
75 }
76 return new SimpleDirectoryStream(path, baseDirectoryStream);
77 }
78
79 private static class AcceptAllFilter
80 implements DirectoryStream.Filter<Path> {
81 static final MCRDirectoryStreamHelper.AcceptAllFilter FILTER = new AcceptAllFilter();
82
83 @Override
84 public boolean accept(Path entry) {
85 return true;
86 }
87 }
88
89 private static class MCRFileCollectionFilter
90 implements DirectoryStream.Filter<Path> {
91 static final MCRDirectoryStreamHelper.MCRFileCollectionFilter FILTER = new MCRFileCollectionFilter();
92
93 @Override
94 public boolean accept(Path entry) {
95 return !MCRFileCollection.DATA_FILE.equals(entry.getFileName().toString());
96 }
97 }
98
99 private static class SimpleDirectoryStream<T extends DirectoryStream<Path>> implements DirectoryStream<Path> {
100 protected final MCRPath dirPath;
101
102 protected final T baseStream;
103
104 boolean isClosed;
105
106 SimpleDirectoryStream(MCRPath dirPath, T baseStream) {
107 this.dirPath = dirPath;
108 this.baseStream = baseStream;
109 }
110
111 @Override
112 public Iterator<Path> iterator() {
113 return new SimpleDirectoryIterator(dirPath, baseStream);
114 }
115
116 @Override
117 public void close() throws IOException {
118 baseStream.close();
119 isClosed = true;
120 }
121
122 protected boolean isClosed() {
123 return isClosed;
124 }
125 }
126
127 private static class SecureDirectoryStream extends SimpleDirectoryStream<java.nio.file.SecureDirectoryStream<Path>>
128 implements java.nio.file.SecureDirectoryStream<Path> {
129
130 private final MCRDirectory dir;
131
132 SecureDirectoryStream(MCRDirectory dir, MCRPath dirPath,
133 java.nio.file.SecureDirectoryStream<Path> baseStream) {
134 super(dirPath, baseStream);
135 this.dir = dir;
136 }
137
138 @Override
139 public java.nio.file.SecureDirectoryStream<Path> newDirectoryStream(Path path, LinkOption... options)
140 throws IOException {
141 checkClosed();
142 if (path.isAbsolute()) {
143 return (SecureDirectoryStream) Files.newDirectoryStream(path);
144 }
145 MCRStoredNode nodeByPath = resolve(path);
146 if (!nodeByPath.isDirectory()) {
147 throw new NotDirectoryException(nodeByPath.getPath());
148 }
149 MCRDirectory newDir = (MCRDirectory) nodeByPath;
150 return (java.nio.file.SecureDirectoryStream<Path>) MCRDirectoryStreamHelper.getInstance(newDir,
151 getCurrentSecurePath(newDir));
152 }
153
154 private MCRStoredNode resolve(Path path) throws IOException {
155 checkRelativePath(path);
156 return (MCRStoredNode) dir.getNodeByPath(path.toString());
157 }
158
159
160
161
162
163
164 private MCRPath getCurrentSecurePath(MCRStoredNode node) {
165 return MCRAbstractFileSystem.getPath(MCRFileSystemUtils.getOwnerID(node), node.getPath(),
166 MCRFileSystemProvider.getMCRIFSFileSystem());
167 }
168
169 @Override
170 public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options,
171 FileAttribute<?>... attrs) throws IOException {
172 checkClosed();
173 if (path.isAbsolute()) {
174 return Files.newByteChannel(path, options);
175 }
176 MCRPath mcrPath = checkRelativePath(path);
177 Path resolved = getCurrentSecurePath(dir).resolve(mcrPath);
178 return Files.newByteChannel(resolved, options);
179 }
180
181 @Override
182 public void deleteFile(Path path) throws IOException {
183 checkClosed();
184 if (path.isAbsolute()) {
185 Files.delete(path);
186 }
187 final MCRStoredNode storedNode = resolve(path);
188 final MCRPath mcrPath = getCurrentSecurePath(storedNode);
189 storedNode.delete();
190 MCRPathEventHelper.fireFileDeleteEvent(mcrPath);
191 }
192
193 @Override
194 public void deleteDirectory(Path path) throws IOException {
195 checkClosed();
196 if (path.isAbsolute()) {
197 Files.delete(path);
198 }
199 resolve(path).delete();
200 }
201
202 @Override
203 public void move(Path srcpath, java.nio.file.SecureDirectoryStream<Path> targetdir, Path targetpath)
204 throws IOException {
205 checkClosed();
206 MCRPath src = checkFileSystem(srcpath);
207 MCRFile srcFile = srcpath.isAbsolute() ? MCRFileSystemUtils.getMCRFile(src, false, false, true)
208 : (MCRFile) resolve(srcpath);
209 if (srcFile == null) {
210 throw new NoSuchFileException(this.dirPath.toString(), srcpath.toString(), null);
211 }
212 if (!targetpath.isAbsolute() && targetdir instanceof SecureDirectoryStream) {
213 LOGGER.debug("Move Case #1");
214 SecureDirectoryStream that = (SecureDirectoryStream) targetdir;
215 MCRFile file = getMCRFile(that, targetpath);
216 Files.delete(file.getLocalPath());
217 if (!srcpath.isAbsolute()) {
218 LOGGER.debug("Move Case #1.1");
219 baseStream.move(toLocalPath(src), that.baseStream, toLocalPath(targetpath));
220 } else {
221 LOGGER.debug("Move Case #1.2");
222 baseStream.move(srcFile.getLocalPath(), that.baseStream, toLocalPath(targetpath));
223 }
224 file.setMD5(srcFile.getMD5());
225 final MCRPath targetAbsolutePath = that.getCurrentSecurePath(file);
226 final BasicFileAttributes attrs = that.getFileAttributeView(targetpath, BasicFileAttributeView.class)
227 .readAttributes();
228 MCRPathEventHelper.fireFileCreateEvent(targetAbsolutePath, attrs);
229 } else {
230 LOGGER.debug("Move Case #2");
231 if (targetpath.isAbsolute()) {
232 LOGGER.debug("Move Case #2.1");
233 Files.move(srcFile.getLocalPath(), targetpath, StandardCopyOption.COPY_ATTRIBUTES);
234 } else {
235 LOGGER.debug("Move Case #2.2");
236 try (FileInputStream fis = new FileInputStream(srcFile.getLocalPath().toFile());
237 FileChannel inChannel = fis.getChannel();
238 SeekableByteChannel targetChannel = targetdir.newByteChannel(targetpath,
239 Set.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))) {
240 long bytesTransferred = 0;
241 while (bytesTransferred < inChannel.size()) {
242 bytesTransferred += inChannel.transferTo(bytesTransferred, inChannel.size(), targetChannel);
243 }
244 }
245 }
246 }
247 srcFile.delete();
248 MCRPathEventHelper.fireFileDeleteEvent(this.dirPath.resolve(src));
249 }
250
251 private static MCRFile getMCRFile(SecureDirectoryStream ds, Path relativePath) throws IOException {
252 MCRStoredNode storedNode = ds.resolve(relativePath);
253 if (storedNode != null) {
254 throw new FileAlreadyExistsException(ds.dirPath.resolve(relativePath).toString());
255 }
256
257 MCRStoredNode parent = ds.dir;
258 if (relativePath.getNameCount() > 1) {
259 parent = (MCRStoredNode) parent.getNodeByPath(relativePath.getParent().toString());
260 if (parent == null) {
261 throw new NoSuchFileException(ds.dirPath.resolve(relativePath.getParent()).toString());
262 }
263 if (!(parent instanceof MCRDirectory)) {
264 throw new NotDirectoryException(ds.dirPath.resolve(relativePath.getParent()).toString());
265 }
266 }
267 return ((MCRDirectory) parent).createFile(relativePath.getFileName().toString());
268 }
269
270 @Override
271 public <V extends FileAttributeView> V getFileAttributeView(Class<V> type) {
272 V fileAttributeView = baseStream.getFileAttributeView(type);
273 if (fileAttributeView != null) {
274 return fileAttributeView;
275 }
276 if (type == MCRMD5AttributeView.class) {
277 BasicFileAttributeView baseView = baseStream.getFileAttributeView(BasicFileAttributeView.class);
278 return (V) new MD5FileAttributeViewImpl(baseView, (v) -> dir);
279 }
280 return null;
281 }
282
283 @Override
284 public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
285 Path localRelativePath = toLocalPath(path);
286 if (type == MCRMD5AttributeView.class) {
287 BasicFileAttributeView baseView = baseStream.getFileAttributeView(localRelativePath,
288 BasicFileAttributeView.class, options);
289 return (V) new MD5FileAttributeViewImpl(baseView, (v) -> resolve(path));
290 }
291 return baseStream.getFileAttributeView(localRelativePath, type, options);
292 }
293
294 private Path toLocalPath(Path path) {
295 return MCRFileSystemUtils.toNativePath(dir.getLocalPath().getFileSystem(), path);
296 }
297
298 void checkClosed() {
299 if (isClosed) {
300 throw new ClosedDirectoryStreamException();
301 }
302 }
303
304 MCRPath checkRelativePath(Path path) {
305 if (path.isAbsolute()) {
306 throw new IllegalArgumentException(path + " is absolute.");
307 }
308 return checkFileSystem(path);
309 }
310
311 private MCRPath checkFileSystem(Path path) {
312 if (!(path.getFileSystem() instanceof MCRIFSFileSystem)) {
313 throw new IllegalArgumentException(path + " is not from " + MCRIFSFileSystem.class.getSimpleName());
314 }
315 return MCRPath.toMCRPath(path);
316 }
317 }
318
319 private static class SimpleDirectoryIterator implements Iterator<Path> {
320 private final Iterator<Path> baseIterator;
321
322 private final MCRPath dir;
323
324 SimpleDirectoryIterator(MCRPath dir, DirectoryStream<Path> baseStream) {
325 this.baseIterator = baseStream.iterator();
326 this.dir = dir;
327 }
328
329 @Override
330 public boolean hasNext() {
331 return baseIterator.hasNext();
332 }
333
334 @Override
335 public Path next() {
336 Path basePath = baseIterator.next();
337 return dir.resolve(basePath.getFileName().toString());
338 }
339 }
340
341 private static class MD5FileAttributeViewImpl implements
342 MCRMD5AttributeView {
343
344 private final BasicFileAttributeView baseAttrView;
345
346 private final MCRThrowFunction<Void, MCRStoredNode, IOException> nodeSupplier;
347
348 MD5FileAttributeViewImpl(BasicFileAttributeView baseAttrView,
349 MCRThrowFunction<Void, MCRStoredNode, IOException> nodeSupplier) {
350 this.baseAttrView = baseAttrView;
351 this.nodeSupplier = nodeSupplier;
352 }
353
354 @Override
355 public String name() {
356 return "md5";
357 }
358
359 @Override
360 public BasicFileAttributes readAttributes() throws IOException {
361 return null;
362 }
363
364 @Override
365 public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime)
366 throws IOException {
367 baseAttrView.setTimes(lastModifiedTime, lastAccessTime, createTime);
368 }
369
370 @Override
371 public MCRFileAttributes readAllAttributes() throws IOException {
372 MCRStoredNode node = nodeSupplier.apply(null);
373 if (node instanceof MCRFile) {
374 return MCRFileAttributes.fromAttributes(baseAttrView.readAttributes(),
375 ((MCRFile) node).getMD5());
376 }
377 return MCRFileAttributes.fromAttributes(baseAttrView.readAttributes(), null);
378 }
379 }
380 }