1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.waarp.openr66.context.filesystem;
21
22 import io.netty.channel.ChannelFuture;
23 import org.waarp.common.command.exception.CommandAbstractException;
24 import org.waarp.common.digest.FilesystemBasedDigest;
25 import org.waarp.common.exception.FileEndOfTransferException;
26 import org.waarp.common.exception.FileTransferException;
27 import org.waarp.common.file.AbstractDir;
28 import org.waarp.common.file.DataBlock;
29 import org.waarp.common.file.FileUtils;
30 import org.waarp.common.file.filesystembased.FilesystemBasedFileImpl;
31 import org.waarp.common.logging.SysErrLogger;
32 import org.waarp.common.logging.WaarpLogger;
33 import org.waarp.common.logging.WaarpLoggerFactory;
34 import org.waarp.common.utility.WaarpNettyUtil;
35 import org.waarp.openr66.context.ErrorCode;
36 import org.waarp.openr66.context.R66Result;
37 import org.waarp.openr66.context.R66Session;
38 import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
39 import org.waarp.openr66.database.data.DbTaskRunner;
40 import org.waarp.openr66.protocol.configuration.Configuration;
41 import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
42 import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
43 import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
44 import org.waarp.openr66.protocol.localhandler.RetrieveRunner;
45 import org.waarp.openr66.protocol.utils.ChannelUtils;
46
47 import java.io.File;
48 import java.io.FileInputStream;
49 import java.io.FileNotFoundException;
50 import java.io.FileOutputStream;
51 import java.io.IOException;
52 import java.io.RandomAccessFile;
53 import java.security.NoSuchAlgorithmException;
54 import java.util.concurrent.atomic.AtomicBoolean;
55
56
57
58
59 public class R66File extends FilesystemBasedFileImpl {
60
61
62
63 private static final WaarpLogger logger =
64 WaarpLoggerFactory.getLogger(R66File.class);
65
66
67
68
69 private boolean isExternal;
70
71
72
73
74
75
76
77
78
79 public R66File(final R66Session session, final R66Dir dir, final String path,
80 final boolean append) throws CommandAbstractException {
81 super(session, dir, path, append);
82 }
83
84
85
86
87
88
89
90
91 public R66File(final R66Session session, final R66Dir dir,
92 final String path) {
93 super(session, dir, path);
94 isExternal = true;
95 }
96
97
98
99
100
101
102
103
104
105 public final synchronized void retrieveBlocking(final AtomicBoolean running)
106 throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException {
107 boolean retrieveDone = false;
108 String errorMesg = "";
109 FilesystemBasedDigest digestGlobal = null;
110 logger.debug("File to retrieve: {}", this);
111 long toRead = 0;
112 try {
113 final long length = length();
114 toRead = (length - getPosition());
115 if (!isReady) {
116 logger.error(
117 "File is not ready to be retrieved: Filename {} isReady {}",
118 getBasename(), isReady);
119 errorMesg = "File is not ready to be retrieved: " + "Filename " +
120 getBasename() + " isReady " + isReady;
121 } else if (toRead < 0) {
122 logger.error(
123 "File is not ready to be read: Filename {} initialLength {} position {}",
124 getBasename(), length, getPosition());
125 errorMesg =
126 "File is not ready to be read: " + "Filename " + getBasename() +
127 " initialLength " + length + " position " + getPosition();
128 }
129 } catch (final CommandAbstractException e) {
130 logger.warn(e.getMessage());
131 }
132 final LocalChannelReference localChannelReference =
133 getSession().getLocalChannelReference();
134 final FilesystemBasedDigest digestBlock =
135 ((R66Session) session).getDigestBlock();
136 DataBlock block = null;
137 final byte[] buffer =
138 ((R66Session) session).getReusableBuffer(session.getBlockSize());
139 try {
140 if (!isReady) {
141 return;
142 }
143 try {
144 block = readDataBlock(buffer);
145 } catch (final FileEndOfTransferException e) {
146 if (toRead > 0) {
147
148 retrieveDone = false;
149 errorMesg = "File is ready to be retrieved and needs to read but " +
150 "EndOfTransfer: " + "Filename " + getBasename() +
151 " isReady " + isReady + " " + "toRead " + toRead +
152 " position " + getPosition();
153 return;
154 }
155
156 retrieveDone = true;
157 return;
158 }
159 if (block == null) {
160 if (toRead > 0) {
161
162 retrieveDone = false;
163 errorMesg = "File is ready to be retrieved and needs to read but " +
164 "no Read: " + "Filename " + getBasename() + " isReady " +
165 isReady + " " + "toRead " + toRead + " position " +
166 getPosition();
167 return;
168 }
169
170 retrieveDone = true;
171 return;
172 }
173 if (Configuration.configuration.isGlobalDigest()) {
174 try {
175 digestGlobal = new FilesystemBasedDigest(
176 Configuration.configuration.getDigest());
177 } catch (final NoSuchAlgorithmException e2) {
178
179 }
180 }
181 ChannelFuture future1 = null;
182 ChannelFuture future2;
183 if (running.get() && !Thread.interrupted()) {
184 if (Configuration.configuration.isGlobalDigest()) {
185 future1 =
186 RetrieveRunner.writeWhenPossible(block, localChannelReference,
187 digestGlobal, digestBlock);
188 } else {
189 future1 =
190 RetrieveRunner.writeWhenPossible(block, localChannelReference,
191 null, digestBlock);
192 }
193 }
194
195 while (block != null && !block.isEOF() && running.get() &&
196 !Thread.interrupted()) {
197 WaarpNettyUtil.awaitOrInterrupted(future1);
198 if (!future1.isSuccess()) {
199 errorMesg = "Message not written: " + future1.cause() != null?
200 future1.cause().getMessage() : " no cause";
201 return;
202 }
203 if (!running.get() && Thread.interrupted()) {
204 errorMesg = "Running stopped";
205 return;
206 }
207 try {
208 block = readDataBlock(buffer);
209 } catch (final FileEndOfTransferException e) {
210
211 WaarpNettyUtil.awaitOrInterrupted(future1);
212 if (future1.isSuccess()) {
213 retrieveDone = true;
214 } else {
215 errorMesg =
216 "Message not written in loop: " + future1.cause() != null?
217 future1.cause().getMessage() : " no cause";
218 }
219 return;
220 }
221 if (Configuration.configuration.isGlobalDigest()) {
222 future2 =
223 RetrieveRunner.writeWhenPossible(block, localChannelReference,
224 digestGlobal, digestBlock);
225 } else {
226 future2 =
227 RetrieveRunner.writeWhenPossible(block, localChannelReference,
228 null, digestBlock);
229 }
230 future1 = future2;
231 }
232 if (!running.get() && Thread.interrupted()) {
233
234 errorMesg = "Running stopped step 2";
235 return;
236 }
237
238 if (future1 != null) {
239 WaarpNettyUtil.awaitOrInterrupted(future1);
240 if (!future1.isSuccess()) {
241 errorMesg = "Message not written at end: " + future1.cause() != null?
242 future1.cause().getMessage() : " no cause";
243 return;
244 }
245 }
246 retrieveDone = true;
247 } catch (final FileTransferException e) {
248
249 getSession().setFinalizeTransfer(false, new R66Result(
250 new OpenR66ProtocolSystemException(e), getSession(), false,
251 ErrorCode.TransferError, getSession().getRunner()));
252 } catch (final OpenR66ProtocolPacketException e) {
253
254 getSession().setFinalizeTransfer(false,
255 new R66Result(e, getSession(), false,
256 ErrorCode.Internal,
257 getSession().getRunner()));
258 } finally {
259 if (block != null) {
260 block.clear();
261 }
262 try {
263 closeFile();
264 } catch (final CommandAbstractException ignore) {
265 SysErrLogger.FAKE_LOGGER.ignoreLog(ignore);
266 }
267 if (retrieveDone) {
268 String hash = null;
269 if (digestGlobal != null) {
270 hash = FilesystemBasedDigest.getHex(digestGlobal.Final());
271 }
272 try {
273 if (hash == null) {
274 ChannelUtils.writeEndTransfer(localChannelReference);
275 } else {
276 ChannelUtils.writeEndTransfer(localChannelReference, hash);
277 }
278 } catch (final OpenR66ProtocolPacketException e) {
279
280 getSession().setFinalizeTransfer(false,
281 new R66Result(e, getSession(), false,
282 ErrorCode.Internal,
283 getSession().getRunner()));
284 }
285 } else {
286
287 logger.error("Cannot send file: " + errorMesg);
288 getSession().setFinalizeTransfer(false, new R66Result(
289 new OpenR66ProtocolSystemException(
290 "Transfer in error: " + errorMesg), getSession(), false,
291 ErrorCode.TransferError, getSession().getRunner()));
292 }
293 }
294 }
295
296
297
298
299
300
301 public final File getTrueFile() {
302 if (isExternal) {
303 if (currentRealFile == null) {
304 currentRealFile = new File(currentFile);
305 }
306 return currentRealFile;
307 }
308 try {
309 if (currentRealFile == null) {
310 currentRealFile = getFileFromPath(getFile());
311 }
312 return currentRealFile;
313 } catch (final CommandAbstractException e) {
314 logger.warn("Exception while getting file: " + this + " : {}",
315 e.getMessage());
316 return null;
317 }
318 }
319
320
321
322
323 public final String getBasename() {
324 getTrueFile();
325 return currentRealFile.getName();
326 }
327
328
329
330
331
332
333 public static String getBasename(final String path) {
334 int pos = path.lastIndexOf('/');
335 final int pos2 = path.lastIndexOf('\\');
336 if (pos2 > pos) {
337 pos = pos2;
338 }
339 if (pos > 0) {
340 return path.substring(pos + 1);
341 }
342 return path;
343 }
344
345 @Override
346 public final R66Session getSession() {
347 return (R66Session) session;
348 }
349
350 @Override
351 public final boolean canRead() throws CommandAbstractException {
352 if (isExternal) {
353 if (currentRealFile == null) {
354 currentRealFile = new File(currentFile);
355 }
356 logger.debug("Final File: {} CanRead: {}", currentRealFile,
357 currentRealFile.canRead());
358 return canRead(currentRealFile);
359 }
360 return super.canRead();
361 }
362
363 @Override
364 public final boolean canWrite() throws CommandAbstractException {
365 if (isExternal) {
366 if (currentRealFile == null) {
367 currentRealFile = new File(currentFile);
368 }
369 if (currentRealFile.exists()) {
370 return currentRealFile.canWrite();
371 }
372 return currentRealFile.getParentFile().canWrite();
373 }
374 return super.canWrite();
375 }
376
377 @Override
378 public final boolean delete() throws CommandAbstractException {
379 if (isExternal) {
380 if (currentRealFile == null) {
381 currentRealFile = new File(currentFile);
382 }
383 checkIdentify();
384 if (!isReady) {
385 return false;
386 }
387 if (!currentRealFile.exists()) {
388 return true;
389 }
390 closeFile();
391 return currentRealFile.delete();
392 }
393 return super.delete();
394 }
395
396 @Override
397 public final boolean exists() throws CommandAbstractException {
398 if (isExternal) {
399 if (currentRealFile == null) {
400 currentRealFile = new File(currentFile);
401 }
402 return exists(currentRealFile);
403 }
404 return super.exists();
405 }
406
407 @Override
408 protected final FileInputStream getFileInputStream() {
409 if (!isExternal) {
410 return super.getFileInputStream();
411 }
412 if (!isReady) {
413 return null;
414 }
415 final File trueFile = getTrueFile();
416 @SuppressWarnings("resource")
417 FileInputStream fileInputStream = null;
418 try {
419 fileInputStream = new FileInputStream(trueFile);
420 final long pos = getPosition();
421 if (pos > 0) {
422 final long read = fileInputStream.skip(pos);
423 if (read != pos) {
424 logger.warn("Cannot ensure position: {} while is {}", pos, read);
425 }
426 }
427 } catch (final FileNotFoundException e) {
428 logger.error("FileInterface not found in getFileInputStream: {}",
429 e.getMessage());
430 return null;
431 } catch (final IOException e) {
432 logger.error("Change position in getFileInputStream: {}", e.getMessage());
433 return null;
434 }
435 return fileInputStream;
436 }
437
438 @Override
439 protected final RandomAccessFile getRandomFile() {
440 if (!isExternal) {
441 return super.getRandomFile();
442 }
443 if (!isReady) {
444 return null;
445 }
446 final File trueFile = getTrueFile();
447 final RandomAccessFile raf;
448 try {
449 raf = new RandomAccessFile(trueFile, "rw");
450 raf.seek(getPosition());
451 } catch (final FileNotFoundException e) {
452 logger.error("File not found in getRandomFile: {}", e.getMessage());
453 return null;
454 } catch (final IOException e) {
455 logger.error("Change position in getRandomFile: {}", e.getMessage());
456 return null;
457 }
458 return raf;
459 }
460
461
462
463
464
465
466
467
468
469
470 @Override
471 protected final FileOutputStream getFileOutputStream(final boolean append) {
472 if (!isExternal) {
473 return super.getFileOutputStream(append);
474 }
475 if (!isReady) {
476 return null;
477 }
478 final File trueFile = getTrueFile();
479 if (trueFile == null) {
480 logger.error("File is unknown so cannot get OutputStream");
481 return null;
482 }
483 if (getPosition() > 0) {
484 if (trueFile.length() < getPosition()) {
485 logger.error(
486 "Cannot Change position in getFileOutputStream: file is smaller than required position");
487 return null;
488 }
489 final RandomAccessFile raf = getRandomFile();
490 if (raf == null) {
491 logger.error("File is unknown so cannot get OutputStream");
492 return null;
493 }
494 try {
495 raf.setLength(getPosition());
496 org.waarp.common.file.FileUtils.close(raf);
497 } catch (final IOException e) {
498 logger.error("Change position in getFileOutputStream: {}",
499 e.getMessage());
500 return null;
501 }
502 }
503 final FileOutputStream fos;
504 try {
505 fos = new FileOutputStream(trueFile, append);
506 } catch (final FileNotFoundException e) {
507 logger.error("File not found in getRandomFile: {}", e.getMessage());
508 return null;
509 }
510 return fos;
511 }
512
513 @Override
514 public final boolean isDirectory() throws CommandAbstractException {
515 if (isExternal) {
516 if (currentRealFile == null) {
517 currentRealFile = new File(currentFile);
518 }
519 return isDirectory(currentRealFile);
520 }
521 return super.isDirectory();
522 }
523
524 @Override
525 public final boolean isFile() throws CommandAbstractException {
526 if (isExternal) {
527 if (currentRealFile == null) {
528 currentRealFile = new File(currentFile);
529 }
530 return isFile(currentRealFile);
531 }
532 return super.isFile();
533 }
534
535 @Override
536 public final long length() throws CommandAbstractException {
537 if (isExternal) {
538 if (currentRealFile == null) {
539 currentRealFile = new File(currentFile);
540 }
541 if (canRead(currentRealFile)) {
542 return currentRealFile.length();
543 } else {
544 return -1;
545 }
546 }
547 return super.length();
548 }
549
550 protected final String getFullInDir() {
551 final DbTaskRunner runner = getSession().getRunner();
552 if (runner != null) {
553 final R66Dir dir = new R66Dir(getSession());
554 try {
555 dir.changeDirectory(runner.getRule().getRecvPath());
556 return dir.getFullPath();
557 } catch (final CommandAbstractException ignored) {
558
559 }
560 }
561 return null;
562 }
563
564 @Override
565 public final boolean renameTo(final String path)
566 throws CommandAbstractException {
567 if (!isExternal) {
568 return super.renameTo(path);
569 }
570 checkIdentify();
571 if (!isReady) {
572 logger.warn("File not ready: {}", this);
573 return false;
574 }
575 final File file = getTrueFile();
576 if (file != null && canRead(file)) {
577 File newFile = getFileFromPath(path);
578 File parentFile = newFile.getParentFile();
579 if (parentFile == null) {
580 final String dir = getFullInDir();
581 if (dir != null) {
582 newFile = new File(dir, newFile.getPath());
583 parentFile = newFile.getParentFile();
584 }
585 }
586 if (newFile.exists()) {
587 logger.warn("Target file already exists: " + newFile.getAbsolutePath());
588 return false;
589 }
590 if (newFile.getAbsolutePath().equals(file.getAbsolutePath())) {
591
592 isReady = true;
593 return true;
594 }
595 if (parentFile != null && parentFile.canWrite()) {
596 if (renameTo(file, newFile)) {
597 return false;
598 }
599 currentFile = getRelativePath(newFile);
600 currentRealFile = newFile;
601 isExternal = false;
602 isReady = true;
603 logger.debug("File renamed to: {} and real position: {}", this,
604 newFile);
605 return true;
606 }
607 }
608 logger.warn("Cannot read file: {}", file);
609 return false;
610 }
611
612 private boolean renameTo(final File file, final File newFile)
613 throws CommandAbstractException {
614 if (!file.renameTo(newFile)) {
615 FileUtils.copy(file, newFile, true, false);
616 return true;
617 }
618 return false;
619 }
620
621
622
623
624
625
626
627
628
629
630
631
632 public final boolean renameTo(final String path, final boolean external)
633 throws CommandAbstractException {
634 if (!external) {
635 return renameTo(path);
636 }
637 checkIdentify();
638 if (!isReady) {
639 return false;
640 }
641 final File file = getTrueFile();
642 if (file != null && canRead(file)) {
643 File newFile = new File(path);
644 File parentFile = newFile.getParentFile();
645 if (parentFile == null) {
646 final String dir = getFullInDir();
647 if (dir != null) {
648 newFile = new File(dir, newFile.getPath());
649 parentFile = newFile.getParentFile();
650 }
651 }
652 if (newFile.exists()) {
653 logger.warn("Target file already exists: " + newFile.getAbsolutePath());
654 return false;
655 }
656 if (newFile.getAbsolutePath().equals(file.getAbsolutePath())) {
657
658 isReady = true;
659 return true;
660 }
661 if (parentFile != null && parentFile.canWrite()) {
662 if (renameTo(file, newFile)) {
663 return false;
664 }
665 currentFile = AbstractDir.normalizePath(newFile.getAbsolutePath());
666 currentRealFile = newFile;
667 isExternal = true;
668 isReady = true;
669 return true;
670 }
671 logger.error("Cannot write to parent directory: {}", newFile.getParent());
672 }
673 logger.error("Cannot read file: {}", file);
674 return false;
675 }
676
677
678
679
680
681
682
683
684
685
686 public final void replaceFilename(final String filename,
687 final boolean isExternal)
688 throws CommandAbstractException {
689 closeFile();
690 currentFile = filename;
691 currentRealFile = null;
692 this.isExternal = isExternal;
693 isReady = true;
694 }
695
696 @Override
697 public final synchronized boolean closeFile()
698 throws CommandAbstractException {
699 final boolean status = super.closeFile();
700
701 isReady = true;
702 return status;
703 }
704
705
706
707
708 public final boolean isExternal() {
709 return isExternal;
710 }
711
712 @Override
713 public String toString() {
714 return "File: " + currentFile + " Ready " + isReady + " isExternal " +
715 isExternal + ' ' + getPosition();
716 }
717 }