1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.waarp.common.file.filesystembased;
21
22 import org.waarp.common.command.exception.CommandAbstractException;
23 import org.waarp.common.exception.FileEndOfTransferException;
24 import org.waarp.common.exception.FileTransferException;
25 import org.waarp.common.file.AbstractDir;
26 import org.waarp.common.file.AbstractFile;
27 import org.waarp.common.file.DataBlock;
28 import org.waarp.common.file.DirInterface;
29 import org.waarp.common.file.FileUtils;
30 import org.waarp.common.file.SessionInterface;
31 import org.waarp.common.logging.SysErrLogger;
32 import org.waarp.common.logging.WaarpLogger;
33 import org.waarp.common.logging.WaarpLoggerFactory;
34
35 import java.io.File;
36 import java.io.FileInputStream;
37 import java.io.FileNotFoundException;
38 import java.io.FileOutputStream;
39 import java.io.IOException;
40 import java.io.RandomAccessFile;
41
42
43
44
45 public abstract class FilesystemBasedFileImpl extends AbstractFile {
46 private static final String ERROR_DURING_GET = "Error during get:";
47
48 private static final String INTERNAL_ERROR_FILE_IS_NOT_READY =
49 "Internal error, file is not ready";
50
51 private static final String NO_FILE_IS_READY = "No file is ready";
52
53
54
55
56 private static final WaarpLogger logger =
57 WaarpLoggerFactory.getLogger(FilesystemBasedFileImpl.class);
58
59
60
61
62 protected final SessionInterface session;
63
64
65
66
67
68
69 private final FilesystemBasedDirImpl dir;
70
71
72
73
74 private final FilesystemBasedAuthImpl auth;
75
76
77
78
79 protected String currentFile;
80
81
82
83 protected File currentRealFile = null;
84
85
86
87
88 protected boolean isAppend;
89
90
91
92
93 private long position;
94
95
96
97
98 private FileOutputStream fileOutputStream;
99
100
101
102 private FileInputStream fileInputStream;
103
104 private byte[] reusableBytes;
105
106
107
108
109
110
111
112
113
114 protected FilesystemBasedFileImpl(final SessionInterface session,
115 final FilesystemBasedDirImpl dir,
116 final String path, final boolean append)
117 throws CommandAbstractException {
118 this.session = session;
119 auth = (FilesystemBasedAuthImpl) session.getAuth();
120 this.dir = dir;
121 currentFile = path;
122 isAppend = append;
123 currentRealFile = getFileFromPath(path);
124 if (append) {
125 try {
126 setPosition(currentRealFile.length());
127 } catch (final IOException e) {
128
129 return;
130 }
131 } else {
132 try {
133 setPosition(0);
134 } catch (final IOException ignored) {
135
136 }
137 }
138 isReady = true;
139 }
140
141
142
143
144
145
146
147
148 protected FilesystemBasedFileImpl(final SessionInterface session,
149 final FilesystemBasedDirImpl dir,
150 final String path) {
151 this.session = session;
152 auth = (FilesystemBasedAuthImpl) session.getAuth();
153 this.dir = dir;
154 currentFile = path;
155 currentRealFile = null;
156 isReady = true;
157 isAppend = false;
158 position = 0;
159 }
160
161 @Override
162 public final void clear() throws CommandAbstractException {
163 super.clear();
164 currentFile = null;
165 currentRealFile = null;
166 isAppend = false;
167 }
168
169 @Override
170 public SessionInterface getSession() {
171 return session;
172 }
173
174 @Override
175 public final DirInterface getDir() {
176 return dir;
177 }
178
179
180
181
182
183
184
185
186
187
188 protected final File getFileFromPath(final String path)
189 throws CommandAbstractException {
190 final String newdir = getDir().validatePath(path);
191 if (dir.isAbsolute(newdir)) {
192 return new File(newdir);
193 }
194 final String truedir = auth.getAbsolutePath(newdir);
195 final File file = new File(truedir);
196 logger.debug("Final File: {} CanRead: {}", truedir, file.canRead());
197 return file;
198 }
199
200
201
202
203
204
205
206
207 protected final String getRelativePath(final File file) {
208 return auth.getRelativePath(
209 AbstractDir.normalizePath(file.getAbsolutePath()));
210 }
211
212
213
214
215
216
217
218
219 public static boolean isDirectory(final File file) {
220 for (int i = 0; i < 3; i++) {
221 if (file.isDirectory()) {
222 return true;
223 }
224 try {
225 Thread.sleep(10);
226 } catch (final InterruptedException ignored) {
227 SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
228 }
229 }
230 return false;
231 }
232
233 @Override
234 public boolean isDirectory() throws CommandAbstractException {
235 checkIdentify();
236 if (currentRealFile == null) {
237 currentRealFile = getFileFromPath(currentFile);
238 }
239 return isDirectory(currentRealFile);
240 }
241
242
243
244
245
246
247
248
249 public static boolean isFile(final File file) {
250 for (int i = 0; i < 3; i++) {
251 if (file.isFile()) {
252 return true;
253 }
254 try {
255 Thread.sleep(10);
256 } catch (final InterruptedException ignored) {
257 SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
258 }
259 }
260 return false;
261 }
262
263 @Override
264 public boolean isFile() throws CommandAbstractException {
265 checkIdentify();
266 if (currentRealFile == null) {
267 currentRealFile = getFileFromPath(currentFile);
268 }
269 return isFile(currentRealFile);
270 }
271
272 @Override
273 public final String getFile() throws CommandAbstractException {
274 checkIdentify();
275 return currentFile;
276 }
277
278 @Override
279 public synchronized boolean closeFile() throws CommandAbstractException {
280 if (fileInputStream != null) {
281 FileUtils.close(fileInputStream);
282 fileInputStream = null;
283 }
284 if (reusableBytes != null) {
285 reusableBytes = null;
286 }
287 if (fileOutputStream != null) {
288 FileUtils.close(fileOutputStream);
289 fileOutputStream = null;
290 }
291 position = 0;
292 isReady = false;
293
294 return true;
295 }
296
297 @Override
298 public final synchronized boolean abortFile()
299 throws CommandAbstractException {
300 if (isInWriting() &&
301 ((FilesystemBasedFileParameterImpl) getSession().getFileParameter()).deleteOnAbort) {
302 delete();
303 }
304 closeFile();
305 return true;
306 }
307
308 @Override
309 public long length() throws CommandAbstractException {
310 checkIdentify();
311 if (!isReady) {
312 return -1;
313 }
314 if (!exists()) {
315 return -1;
316 }
317 if (currentRealFile == null) {
318 currentRealFile = getFileFromPath(currentFile);
319 }
320 return currentRealFile.length();
321 }
322
323 @Override
324 public final synchronized boolean isInReading() {
325 if (!isReady) {
326 return false;
327 }
328 return fileInputStream != null;
329 }
330
331 @Override
332 public final synchronized boolean isInWriting() {
333 if (!isReady) {
334 return false;
335 }
336 return fileOutputStream != null;
337 }
338
339
340
341
342
343
344
345
346 public static boolean canRead(final File file) {
347 for (int i = 0; i < 3; i++) {
348 if (file.canRead()) {
349 return true;
350 }
351 try {
352 Thread.sleep(10);
353 } catch (final InterruptedException ignored) {
354 SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
355 }
356 }
357 return false;
358 }
359
360 @Override
361 public boolean canRead() throws CommandAbstractException {
362 checkIdentify();
363 if (!isReady) {
364 return false;
365 }
366 if (currentRealFile == null) {
367 currentRealFile = getFileFromPath(currentFile);
368 }
369 return canRead(currentRealFile);
370 }
371
372 @Override
373 public boolean canWrite() throws CommandAbstractException {
374 checkIdentify();
375 if (!isReady) {
376 return false;
377 }
378 if (currentRealFile == null) {
379 currentRealFile = getFileFromPath(currentFile);
380 }
381 if (currentRealFile.exists()) {
382 return currentRealFile.canWrite();
383 }
384 return currentRealFile.getParentFile().canWrite();
385 }
386
387
388
389
390
391
392
393
394 public static boolean exists(final File file) {
395 for (int i = 0; i < 3; i++) {
396 if (file.exists()) {
397 return true;
398 }
399 try {
400 Thread.sleep(10);
401 } catch (final InterruptedException ignored) {
402 SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
403 }
404 }
405 return false;
406 }
407
408 @Override
409 public boolean exists() throws CommandAbstractException {
410 checkIdentify();
411 if (!isReady) {
412 return false;
413 }
414 if (currentRealFile == null) {
415 currentRealFile = getFileFromPath(currentFile);
416 }
417 return exists(currentRealFile);
418 }
419
420 @Override
421 public boolean delete() throws CommandAbstractException {
422 checkIdentify();
423 if (!isReady) {
424 return false;
425 }
426 if (!exists()) {
427 return true;
428 }
429 closeFile();
430 if (currentRealFile == null) {
431 currentRealFile = getFileFromPath(currentFile);
432 }
433 return currentRealFile.delete();
434 }
435
436 @Override
437 public boolean renameTo(final String path) throws CommandAbstractException {
438 checkIdentify();
439 if (!isReady) {
440 logger.warn("File not ready: {}", this);
441 return false;
442 }
443 if (currentRealFile == null) {
444 currentRealFile = getFileFromPath(currentFile);
445 }
446 if (canRead(currentRealFile)) {
447 final File newFile = getFileFromPath(path);
448 if (newFile.exists()) {
449 logger.warn("Target file already exists: " + newFile.getAbsolutePath());
450 return false;
451 }
452 if (newFile.getAbsolutePath().equals(currentRealFile.getAbsolutePath())) {
453
454 isReady = true;
455 return true;
456 }
457 if (newFile.getParentFile().canWrite()) {
458 if (!currentRealFile.renameTo(newFile)) {
459 FileUtils.copy(currentRealFile, newFile, true, false);
460 }
461 currentFile = getRelativePath(newFile);
462 currentRealFile = newFile;
463 isReady = true;
464 logger.debug("File renamed to: {} and real position: {}", this,
465 newFile);
466 return true;
467 } else {
468 logger.warn("Cannot write file: {} from {}", newFile, currentFile);
469 return false;
470 }
471 }
472 logger.warn("Cannot read file: {}", currentFile);
473 return false;
474 }
475
476 @Override
477 public final synchronized DataBlock readDataBlock()
478 throws FileTransferException, FileEndOfTransferException {
479 if (isReady) {
480 return getByteBlock(getSession().getBlockSize());
481 }
482 throw new FileTransferException(NO_FILE_IS_READY);
483 }
484
485 @Override
486 public final synchronized DataBlock readDataBlock(final byte[] bufferGiven)
487 throws FileTransferException, FileEndOfTransferException {
488 if (isReady) {
489 return getByteBlock(bufferGiven);
490 }
491 throw new FileTransferException(NO_FILE_IS_READY);
492 }
493
494 @Override
495 public final synchronized void writeDataBlock(final DataBlock dataBlock)
496 throws FileTransferException {
497 if (isReady) {
498 if (dataBlock.isEOF()) {
499 writeBlockEnd(dataBlock.getByteBlock(), dataBlock.getOffset(),
500 dataBlock.getByteCount());
501 return;
502 }
503 writeBlock(dataBlock.getByteBlock(), dataBlock.getOffset(),
504 dataBlock.getByteCount());
505 return;
506 }
507 throw new FileTransferException(
508 "No file is ready while trying to write: " + dataBlock);
509 }
510
511
512
513
514
515
516
517
518 public final synchronized long getPosition() {
519 return position;
520 }
521
522
523
524
525
526
527
528
529 @Override
530 public final synchronized void setPosition(final long position)
531 throws IOException {
532 if (this.position != position) {
533 this.position = position;
534 if (fileInputStream != null) {
535 FileUtils.close(fileInputStream);
536 fileInputStream = getFileInputStream();
537 }
538 if (fileOutputStream != null) {
539 FileUtils.close(fileOutputStream);
540 fileOutputStream = getFileOutputStream(true);
541 if (fileOutputStream == null) {
542 throw new IOException("File cannot changed of Position");
543 }
544 }
545 }
546 }
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561 private synchronized void writeBlock(final byte[] buffer, final int offset,
562 final int length)
563 throws FileTransferException {
564 if (length > 0 && !isReady) {
565 throw new FileTransferException(NO_FILE_IS_READY);
566 }
567
568 if (buffer == null || length == 0) {
569 return;
570 }
571 if (fileOutputStream == null) {
572 fileOutputStream = getFileOutputStream(position > 0);
573 }
574 if (fileOutputStream == null) {
575 throw new FileTransferException(INTERNAL_ERROR_FILE_IS_NOT_READY);
576 }
577 try {
578 fileOutputStream.write(buffer, offset, length);
579 } catch (final IOException e2) {
580 logger.error("Error during write: {}", e2.getMessage());
581 try {
582 closeFile();
583 } catch (final CommandAbstractException ignored) {
584
585 }
586
587
588 throw new FileTransferException(INTERNAL_ERROR_FILE_IS_NOT_READY);
589 }
590 position += length;
591 }
592
593
594
595
596
597
598
599
600
601
602
603 private synchronized void writeBlockEnd(final byte[] buffer, final int offset,
604 final int length)
605 throws FileTransferException {
606 writeBlock(buffer, offset, length);
607 try {
608 closeFile();
609 } catch (final CommandAbstractException e) {
610 throw new FileTransferException("Close in error", e);
611 }
612 }
613
614 private void checkByteBufSize(final int size) {
615 if (reusableBytes == null || reusableBytes.length != size) {
616 reusableBytes = new byte[size];
617 }
618 }
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636 private synchronized DataBlock getByteBlock(final int sizeblock)
637 throws FileTransferException, FileEndOfTransferException {
638 if (!isReady) {
639 throw new FileTransferException(NO_FILE_IS_READY);
640 }
641 if (fileInputStream == null) {
642 checkByteBufSize(sizeblock);
643 }
644 return getByteBlock(reusableBytes);
645 }
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663 private synchronized DataBlock getByteBlock(final byte[] bufferGiven)
664 throws FileTransferException, FileEndOfTransferException {
665 if (!isReady) {
666 throw new FileTransferException(NO_FILE_IS_READY);
667 }
668 final int sizeblock = bufferGiven.length;
669 if (fileInputStream == null) {
670 fileInputStream = getFileInputStream();
671 if (fileInputStream == null) {
672 throw new FileTransferException(INTERNAL_ERROR_FILE_IS_NOT_READY);
673 }
674 reusableBytes = bufferGiven;
675 }
676 int sizeout = 0;
677 while (sizeout < sizeblock) {
678 try {
679 final int sizeread =
680 fileInputStream.read(reusableBytes, sizeout, sizeblock - sizeout);
681 if (sizeread <= 0) {
682 break;
683 }
684 sizeout += sizeread;
685 } catch (final IOException e) {
686 logger.error(ERROR_DURING_GET + " {}", e.getMessage());
687 try {
688 closeFile();
689 } catch (final CommandAbstractException ignored) {
690
691 }
692 throw new FileTransferException(INTERNAL_ERROR_FILE_IS_NOT_READY);
693 }
694 }
695 if (sizeout <= 0) {
696 try {
697 closeFile();
698 } catch (final CommandAbstractException ignored) {
699
700 }
701 isReady = false;
702 throw new FileEndOfTransferException("End of file");
703 }
704 position += sizeout;
705 final DataBlock dataBlock = new DataBlock();
706 dataBlock.setBlock(reusableBytes, sizeout);
707 if (sizeout < sizeblock) {
708 dataBlock.setEOF(true);
709 try {
710 closeFile();
711 } catch (final CommandAbstractException ignored) {
712
713 }
714 isReady = false;
715 }
716 return dataBlock;
717 }
718
719 protected FileInputStream getFileInputStream() {
720 if (!isReady) {
721 return null;
722 }
723 try {
724 if (currentRealFile == null) {
725 currentRealFile = getFileFromPath(currentFile);
726 }
727 } catch (final CommandAbstractException e1) {
728 return null;
729 }
730 @SuppressWarnings("resource")
731 FileInputStream fileInputStreamTemp = null;
732 try {
733 fileInputStreamTemp = new FileInputStream(currentRealFile);
734 if (position != 0) {
735 final long read = fileInputStreamTemp.skip(position);
736 if (read != position) {
737 logger.warn("Cannot ensure position: {} while is {}", position, read);
738 }
739 }
740 } catch (final FileNotFoundException e) {
741 FileUtils.close(fileInputStreamTemp);
742 logger.error("File not found in getFileInputStream: {}", e.getMessage());
743 return null;
744 } catch (final IOException e) {
745 FileUtils.close(fileInputStreamTemp);
746 logger.error("Change position in getFileInputStream: {}", e.getMessage());
747 return null;
748 }
749 return fileInputStreamTemp;
750 }
751
752
753
754
755
756
757
758 protected RandomAccessFile getRandomFile() {
759 if (!isReady) {
760 return null;
761 }
762 try {
763 if (currentRealFile == null) {
764 currentRealFile = getFileFromPath(currentFile);
765 }
766 } catch (final CommandAbstractException e1) {
767 return null;
768 }
769 final RandomAccessFile raf;
770 try {
771 raf = new RandomAccessFile(currentRealFile, "rw");
772 raf.seek(position);
773 } catch (final FileNotFoundException e) {
774 logger.error("File not found in getRandomFile: {}", e.getMessage());
775 return null;
776 } catch (final IOException e) {
777 logger.error("Change position in getRandomFile: {}", e.getMessage());
778 return null;
779 }
780 return raf;
781 }
782
783
784
785
786
787
788
789
790
791
792 protected FileOutputStream getFileOutputStream(final boolean append) {
793 if (!isReady) {
794 return null;
795 }
796 try {
797 if (currentRealFile == null) {
798 currentRealFile = getFileFromPath(currentFile);
799 }
800 } catch (final CommandAbstractException e1) {
801 return null;
802 }
803 if (position > 0) {
804 if (currentRealFile.length() < position) {
805 logger.error(
806 "Cannot Change position in getFileOutputStream: file is smaller than required position");
807 return null;
808 }
809 final RandomAccessFile raf = getRandomFile();
810 try {
811 raf.setLength(position);
812 FileUtils.close(raf);
813 } catch (final IOException e) {
814 logger.error("Change position in getFileOutputStream: {}",
815 e.getMessage());
816 return null;
817 }
818 if (logger.isDebugEnabled()) {
819 logger.debug("New size: {}:{}", currentRealFile.length(), position);
820 }
821 }
822 final FileOutputStream fos;
823 try {
824 fos = new FileOutputStream(currentRealFile, append);
825 } catch (final FileNotFoundException e) {
826 logger.error("File not found in getRandomFile: {}", e.getMessage());
827 return null;
828 }
829 return fos;
830 }
831 }