001/******************************************************************************* 002The MIT License (MIT) 003 004Copyright (c) 2024 KILLCODING.COM 005 006Permission is hereby granted, free of charge, to any person obtaining a copy 007of this software and associated documentation files (the "Software"), to deal 008in the Software without restriction, including without limitation the rights 009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010copies of the Software, and to permit persons to whom the Software is 011furnished to do so, subject to the following conditions: 012 013The above copyright notice and this permission notice shall be included in 014all copies or substantial portions of the Software. 015 016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 022THE SOFTWARE. 023*****************************************************************************/ 024package com.killcoding.file; 025 026import java.io.File; 027import java.nio.file.Paths; 028import java.nio.file.Files; 029import java.io.IOException; 030import java.nio.file.OpenOption; 031import java.nio.file.StandardOpenOption; 032import java.nio.channels.FileChannel; 033import java.nio.channels.FileLock; 034import java.nio.file.Path; 035import java.nio.ByteBuffer; 036import com.killcoding.log.Logger; 037import java.net.URI; 038import java.util.Comparator; 039import java.util.List; 040import java.util.ArrayList; 041import java.nio.channels.SeekableByteChannel; 042import java.io.ByteArrayOutputStream; 043import java.nio.file.attribute.BasicFileAttributes; 044import java.nio.file.attribute.FileTime; 045import java.time.Instant; 046import java.util.Arrays; 047import java.io.ByteArrayInputStream; 048import java.sql.Timestamp; 049import java.util.stream.Stream; 050import java.util.stream.Collectors; 051import java.util.HashSet; 052import java.util.Set; 053import java.nio.file.DirectoryStream; 054import java.nio.file.CopyOption; 055import java.nio.file.LinkOption; 056import com.killcoding.tool.CommonTools; 057import com.killcoding.file.RemoteFile; 058import com.killcoding.log.LoggerFactory; 059import java.util.concurrent.Future; 060import com.killcoding.tool.ConfigProperties; 061import com.killcoding.datasource.Clock; 062import java.util.concurrent.Callable; 063import com.killcoding.file.DiskFile; 064import java.nio.file.attribute.FileAttribute; 065import java.text.SimpleDateFormat; 066import com.killcoding.cache.CacheArray; 067import com.killcoding.cache.CacheArrayFilter; 068import java.util.Map; 069import java.util.concurrent.ExecutorService; 070import java.util.concurrent.Executors; 071import java.nio.file.NoSuchFileException; 072import java.util.concurrent.ConcurrentHashMap; 073import java.util.Iterator; 074import java.util.Date; 075import java.time.ZonedDateTime; 076import java.time.LocalDateTime; 077import java.time.ZoneId; 078import java.time.format.DateTimeFormatter; 079import java.util.Calendar; 080import java.util.TimeZone; 081import java.text.ParseException; 082import java.util.Collections; 083import java.util.HashMap; 084import java.nio.file.FileAlreadyExistsException; 085import java.util.LinkedList; 086 087public class DiskFile extends BaseFile { 088 089 public static final int RETRY_OPEN_LIMITED = 100; 090 091 protected static Integer MAX_POOL_SIZE = 100; 092 private static final Map<String, Long> QUEUE_PATH_MAPPING = new ConcurrentHashMap<String, Long>(); 093 094 private static ExecutorService splitPool = null; 095 096 protected boolean copyStructureOnly = false; 097 private FileChannel channel = null; 098 private FileLock lock = null; 099 private long modifyTimeMs = 0L; 100 101 public DiskFile(String path) { 102 super(path); 103 } 104 105 public DiskFile(File origin) { 106 super(origin); 107 } 108 109 public static synchronized void initPool(int poolSize) { 110 if (splitPool == null) { 111 MAX_POOL_SIZE = poolSize; 112 LoggerFactory.getLogger(DiskFile.class).mark("MAX_POOL_SIZE={}", MAX_POOL_SIZE); 113 splitPool = Executors.newFixedThreadPool(MAX_POOL_SIZE); 114 } 115 } 116 117 private static synchronized void initDefaultPool() { 118 if (splitPool == null) { 119 LoggerFactory.getLogger(DiskFile.class).mark("MAX_POOL_SIZE={}", MAX_POOL_SIZE); 120 splitPool = Executors.newFixedThreadPool(MAX_POOL_SIZE); 121 } 122 } 123 124 public void split(int partSize, FilePart filePart) throws IOException { 125 if (isLink()) { 126 throw new IOException(String.format("The disk file '%s' is a link.", origin.getAbsolutePath())); 127 } 128 if (isDir()) { 129 throw new IOException(String.format("The disk file '%s' is a folder.", origin.getAbsolutePath())); 130 } 131 if (!exists()) { 132 throw new IOException(String.format("The disk file '%s' does not exist.", origin.getAbsolutePath())); 133 } 134 initDefaultPool(); 135 splitPool.execute(new Runnable() { 136 @Override 137 public void run() { 138 SeekableByteChannel ch = null; 139 int partIndex = -1; 140 long fileSize = 0L; 141 try { 142 Path originPath = Paths.get(origin.toURI()); 143 Thread.currentThread().setName(String.format("DiskFile-split-%s", origin.getName())); 144 ch = Files.newByteChannel(originPath, StandardOpenOption.READ); 145 ByteBuffer bf = ByteBuffer.allocate(partSize); 146 while (ch.read(bf) != -1) { 147 bf.flip(); 148 partIndex += 1; 149 List<Byte> parts = new ArrayList<Byte>(); 150 while (bf.hasRemaining()) { 151 parts.add(bf.get()); 152 } 153 int partsSize = parts.size(); 154 fileSize += partsSize; 155 byte[] part = new byte[partsSize]; 156 for (int i = 0; i < partsSize; i++) { 157 part[i] = parts.get(i); 158 } 159 filePart.process(partIndex, part); 160 bf.clear(); 161 } 162 filePart.completed(partIndex, getModifyTimeForClock(), fileSize); 163 } catch (Exception e) { 164 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 165 } finally { 166 if (ch != null) { 167 try { 168 ch.close(); 169 } catch (IOException e) { 170 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 171 } 172 } 173 filePart.ended(partIndex, fileSize); 174 } 175 } 176 }); 177 } 178 179 @Override 180 public void copyTo(String toPath) throws IOException { 181 Path originPath = Paths.get(origin.toURI()); 182 Path destPath = Paths.get(toPath); 183 LoggerFactory.getLogger(DiskFile.class).debug("CopyTo: {} -> {}", originPath, destPath); 184 Files.copy(originPath, destPath); 185 } 186 187 @Override 188 public void moveTo(String toPath) throws IOException { 189 Path originPath = Paths.get(origin.toURI()); 190 Path destPath = Paths.get(toPath); 191 LoggerFactory.getLogger(DiskFile.class).debug("MoveTo: {} -> {}", originPath, destPath); 192 if (Files.exists(destPath)) { 193 Files.delete(destPath); 194 } 195 Files.move(originPath, destPath); 196 } 197 198 public RemoteFile writeToRemote(RemoteFile remoteFile) throws IOException { 199 return writeToRemote(remoteFile, new Runnable() { 200 @Override 201 public void run() { 202 203 } 204 }); 205 } 206 207 public RemoteFile writeToRemote(RemoteFile remoteFile, Runnable completedCallback) throws IOException { 208 if (remoteFile.exists(true)) { 209 throw new IOException(String.format("The remote file '%s' already exists.", remoteFile.getPath())); 210 } else { 211 remoteFile.copyFrom(copyStructureOnly, this); 212 if (isDir()) { 213 startFullAsync(0, this.getPath() + "/", remoteFile.getPath() + "/", completedCallback, null); 214 } 215 } 216 return remoteFile; 217 } 218 219 protected boolean isLogicModify() throws Exception { 220 boolean b = false; 221 String tmpPath = toModifyTmpFilePath(); 222 223 DiskFile df = null; 224 Path logicPath = Paths.get(tmpPath); 225 if (Files.exists(logicPath)) { 226 df = new DiskFile(tmpPath); 227 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 228 Timestamp dfModifyTime = df.getModifyTimeForClock(); 229 230 long diffMs = Calendar.getInstance().getTimeInMillis() - dfModifyTime.getTime(); 231 232 b = diffMs < LOGIC_TIMEOUT_MS; 233 } 234 return b; 235 } 236 237 protected DiskFile logicModify() throws Exception { 238 return logicModify(null); 239 } 240 241 protected DiskFile logicModify(String msg) throws Exception { 242 if (getParentFile().exists()) { 243 String tmpLogicPath = toModifyTmpFilePath(); 244 245 if (tmpLogicPath == null) 246 return null; 247 248 Path logicPath = Paths.get(tmpLogicPath); 249 DiskFile df = new DiskFile(tmpLogicPath); 250 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 251 252 if (msg == null) { 253 df.write(String.format("%s-%s", Thread.currentThread().getName(), Thread.currentThread().getId()), 254 false); 255 } else { 256 df.write(msg, false); 257 } 258 259 Files.setLastModifiedTime(logicPath, FileTime.fromMillis(Calendar.getInstance().getTimeInMillis())); 260 return df; 261 } 262 return null; 263 } 264 265 protected void removeLogicModify() { 266 try { 267 String tmpPath = toModifyTmpFilePath(); 268 if (tmpPath != null) { 269 DiskFile df = new DiskFile(tmpPath); 270 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 271 if (df.exists()) { 272 df.delete(); 273 } 274 } 275 } catch (Exception e) { 276 LoggerFactory.getLogger(DiskFile.class).warn(e.getMessage(), e); 277 } 278 } 279 280 protected boolean isLogicAccess() throws Exception { 281 boolean b = false; 282 String tmpPath = toAccessTmpFilePath(); 283 DiskFile df = null; 284 Path logicPath = Paths.get(tmpPath); 285 if (Files.exists(logicPath)) { 286 df = new DiskFile(tmpPath); 287 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 288 Timestamp dfModifyTime = df.getModifyTimeForClock(); 289 290 long diffMs = Calendar.getInstance().getTimeInMillis() - dfModifyTime.getTime(); 291 292 b = diffMs < LOGIC_ACCESS_TIMEOUT_MS; 293 } 294 return b; 295 } 296 297 protected DiskFile logicAccess() throws Exception { 298 return logicAccess(null); 299 } 300 301 protected DiskFile logicAccess(String msg) throws Exception { 302 if (getParentFile().exists()) { 303 String tmpLogicPath = toAccessTmpFilePath(); 304 Path logicPath = Paths.get(tmpLogicPath); 305 DiskFile df = new DiskFile(tmpLogicPath); 306 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 307 if (msg == null) { 308 df.write(String.format("%s-%s", Thread.currentThread().getName(), Thread.currentThread().getId()), 309 false); 310 } else { 311 df.write(msg, false); 312 } 313 314 Files.setLastModifiedTime(logicPath, FileTime.fromMillis(Calendar.getInstance().getTimeInMillis())); 315 return df; 316 } 317 return null; 318 } 319 320 protected void removeLogicAccess() { 321 try { 322 String tmpPath = toAccessTmpFilePath(); 323 if (tmpPath != null) { 324 DiskFile df = new DiskFile(tmpPath); 325 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 326 if (df.exists()) { 327 df.delete(); 328 } 329 } 330 } catch (Exception e) { 331 LoggerFactory.getLogger(DiskFile.class).warn(e.getMessage(), e); 332 } 333 } 334 335 protected boolean isLogicCheck() throws Exception { 336 boolean b = false; 337 String tmpPath = toCheckTmpFilePath(); 338 339 DiskFile df = null; 340 Path logicPath = Paths.get(tmpPath); 341 if (Files.exists(logicPath)) { 342 df = new DiskFile(tmpPath); 343 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 344 Timestamp dfModifyTime = df.getModifyTimeForClock(); 345 346 long diffMs = Calendar.getInstance().getTimeInMillis() - dfModifyTime.getTime(); 347 348 b = diffMs < LOGIC_CHECK_TIMEOUT_MS; 349 } 350 return b; 351 } 352 353 protected DiskFile logicCheck() throws Exception { 354 return logicCheck(null); 355 } 356 357 public DiskFile logicCheck(String msg) throws Exception { 358 if (getParentFile().exists()) { 359 String tmpLogicPath = toCheckTmpFilePath(); 360 if (tmpLogicPath == null) 361 return null; 362 363 Path logicPath = Paths.get(tmpLogicPath); 364 DiskFile df = new DiskFile(tmpLogicPath); 365 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 366 if (msg == null) { 367 df.write(String.format("%s-%s", Thread.currentThread().getName(), Thread.currentThread().getId()), 368 false); 369 } else { 370 df.write(msg, false); 371 } 372 373 Files.setLastModifiedTime(logicPath, FileTime.fromMillis(Calendar.getInstance().getTimeInMillis())); 374 return df; 375 } 376 return null; 377 } 378 379 protected void removeLogicCheck() { 380 try{ 381 String tmpPath = toCheckTmpFilePath(); 382 if (tmpPath != null) { 383 DiskFile df = new DiskFile(tmpPath); 384 DiskFile.copyAttrs(df, this.copyStructureOnly, this.syncRoot); 385 if (df.exists()) { 386 df.delete(); 387 DiskFile pdf = new DiskFile(this.getOrigin().getParent()); 388 DiskFile.copyAttrs(pdf, this.copyStructureOnly, this.syncRoot); 389 if(pdf.isLogicCheck()){ 390 pdf.removeLogicCheck(); 391 } 392 } 393 } 394 }catch(Exception e){ 395 LoggerFactory.getLogger(DiskFile.class).warn(e.getMessage(), e); 396 } 397 } 398 399 @Override 400 public boolean beforeDelete(boolean realDeleted) { 401 return true; 402 } 403 404 @Override 405 public void afterDelete(boolean realDeleted) { 406 407 } 408 409 @Override 410 public boolean beforeMkdirs() { 411 return true; 412 } 413 414 @Override 415 public void afterMkdirs() { 416 417 } 418 419 @Override 420 public boolean beforeCreateLink(String target) { 421 return true; 422 } 423 424 @Override 425 public void afterCreateLink(String target) { 426 427 } 428 429 @Override 430 public boolean beforeWrite() { 431 return true; 432 } 433 434 @Override 435 public void afterWrite() { 436 437 } 438 439 @Override 440 public boolean delete() throws IOException { 441 return deleteFrom(); 442 } 443 444 private boolean deleteFrom() throws IOException { 445 boolean allowed = beforeDelete(true); 446 if (allowed) { 447 LoggerFactory.getLogger(DiskFile.class).debug("Delete: {}", getPath()); 448 if (isFile()) { 449 Path originPath = Paths.get(origin.toURI()); 450 boolean exists = Files.exists(originPath); 451 if (exists) { 452 Files.deleteIfExists(Paths.get(origin.toURI())); 453 afterDelete(true); 454 return true; 455 } 456 } 457 if (isDir()) { 458 Path originPath = Paths.get(origin.toURI()); 459 boolean exists = Files.exists(originPath); 460 if (exists) { 461 Files.walk(originPath).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); 462 afterDelete(true); 463 return true; 464 } 465 } 466 if (isLink()) { 467 Path originPath = Paths.get(origin.toURI()); 468 boolean exists = Files.exists(originPath); 469 if (exists) { 470 Files.deleteIfExists(Paths.get(origin.toURI())); 471 afterDelete(true); 472 return true; 473 } 474 } 475 } 476 return false; 477 } 478 479 @Override 480 public boolean isFile() throws IOException { 481 return this.origin.isFile() && !isLink(); 482 } 483 484 @Override 485 public boolean isDirectory() throws IOException { 486 return this.origin.isDirectory() && !isLink(); 487 } 488 489 @Override 490 public boolean isLink() throws IOException { 491 return Files.isSymbolicLink(Paths.get(this.origin.toURI())); 492 } 493 494 @Override 495 public boolean exists() throws IOException { 496 return this.origin.exists(); 497 } 498 499 @Override 500 public boolean mkdirs() throws IOException { 501 boolean allowed = beforeMkdirs(); 502 if (allowed) { 503 LoggerFactory.getLogger(DiskFile.class).debug("Mkidrs: {}", getPath()); 504 boolean b = this.origin.mkdirs(); 505 if (b && modifyTimeMs > 0) { 506 setModifyTime(new Timestamp(modifyTimeMs)); 507 } 508 afterMkdirs(); 509 return b; 510 } 511 return false; 512 } 513 514 @Override 515 public boolean write(byte[] data, boolean append) throws IOException { 516 517 boolean allowed = beforeWrite(); 518 519 if (!allowed) 520 return false; 521 522 if (data == null) 523 data = new byte[] {}; 524 525 try { 526 527 OpenOption[] options = null; 528 if (append) { 529 options = new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.WRITE, 530 StandardOpenOption.APPEND }; 531 } else { 532 options = new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.WRITE, 533 StandardOpenOption.TRUNCATE_EXISTING }; 534 } 535 536 File parent = origin.getParentFile(); 537 if (!parent.exists()) { 538 parent.mkdirs(); 539 } 540 int retryCount = 0; 541 Path path = Paths.get(origin.toURI()); 542 while(retryCount < RETRY_OPEN_LIMITED){ 543 retryCount++; 544 try{ 545 channel = FileChannel.open(path, options); 546 lock = channel.lock(); 547 if (lock != null) { 548 ByteBuffer buf = ByteBuffer.wrap(data); 549 channel.write(buf); 550 if (modifyTimeMs > 0) { 551 setModifyTime(new Timestamp(modifyTimeMs)); 552 } 553 return true; 554 } 555 }catch(Exception e){ 556 LoggerFactory.getLogger(DiskFile.class).debug(e.getMessage(),e); 557 558 if(retryCount >= RETRY_OPEN_LIMITED) throw new IOException(e.getMessage(),e); 559 560 } 561 } 562 return false; 563 } catch (Exception e) { 564 throw e; 565 } finally { 566 close(); 567 } 568 } 569 570 public boolean manualOpen(boolean append) throws IOException, FileAlreadyExistsException { 571 572 boolean allowed = beforeWrite(); 573 574 if (!allowed) 575 return false; 576 577 OpenOption[] options = null; 578 if (append) { 579 options = new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.WRITE, 580 StandardOpenOption.APPEND }; 581 } else { 582 options = new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.WRITE, 583 StandardOpenOption.TRUNCATE_EXISTING }; 584 } 585 Path path = Paths.get(origin.toURI()); 586 File parent = origin.getParentFile(); 587 if (parent != null && !parent.exists() && !parent.mkdirs()) { 588 throw new IOException(String.format("Failed to create parent directory: %s", parent)); 589 } 590 if (!Files.exists(path)) { 591 try { 592 Files.createFile(path); 593 } catch (FileAlreadyExistsException e) { 594 throw e; 595 } 596 } 597 int retryCount = 0; 598 while(retryCount < RETRY_OPEN_LIMITED){ 599 retryCount++; 600 try{ 601 channel = FileChannel.open(path, options); 602 lock = channel.lock(); 603 return lock != null; 604 }catch(Exception e){ 605 LoggerFactory.getLogger(DiskFile.class).debug(e.getMessage(),e); 606 607 if(retryCount >= RETRY_OPEN_LIMITED) throw new IOException(e.getMessage(),e); 608 } 609 } 610 return false; 611 } 612 613 public void manualWrite(byte[] data) throws IOException { 614 if (data == null) 615 data = new byte[] {}; 616 617 if (lock != null) { 618 ByteBuffer buf = ByteBuffer.wrap(data); 619 channel.write(buf); 620 if (modifyTimeMs > 0) { 621 setModifyTime(new Timestamp(modifyTimeMs)); 622 } 623 } 624 } 625 626 public void manualWrite(String data) throws IOException { 627 manualWrite(data.getBytes(CHARSET)); 628 } 629 630 public void manualClose() throws IOException { 631 close(); 632 } 633 634 @Override 635 public boolean write(byte[] data) throws IOException { 636 return write(data, false); 637 } 638 639 @Override 640 public boolean write(String data, boolean append) throws IOException { 641 return write(data.getBytes(CHARSET), append); 642 } 643 644 public boolean write(String data, String charset, boolean append) throws IOException { 645 return write(data.getBytes(charset), append); 646 } 647 648 @Override 649 public boolean write(String data) throws IOException { 650 return write(data.getBytes(CHARSET)); 651 } 652 653 public boolean write(String data, String charset) throws IOException { 654 return write(data.getBytes(charset)); 655 } 656 657 @Override 658 public boolean createLink(String target) throws IOException { 659 boolean allowed = beforeCreateLink(target); 660 if (allowed) { 661 LoggerFactory.getLogger(DiskFile.class).debug("CreateLink: {}", getPath()); 662 try { 663 Path link = Paths.get(origin.toURI()); 664 Path targetPath = Paths.get(target); 665 Files.createSymbolicLink(link, targetPath); 666 if (modifyTimeMs > 0) { 667 setModifyTime(new Timestamp(modifyTimeMs)); 668 } 669 afterCreateLink(target); 670 return true; 671 } catch (IOException e) { 672 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 673 } 674 } 675 return false; 676 } 677 678 @Override 679 public String readLink() throws IOException { 680 Path link = Paths.get(origin.toURI()); 681 return Files.readSymbolicLink(link).toString(); 682 } 683 684 @Override 685 public byte[] readAllBytes() throws IOException { 686 return readAllBytesFrom(); 687 } 688 689 private byte[] readAllBytesFrom() throws IOException { 690 if (isLink()) { 691 throw new IOException(String.format("The disk file '%s' is a link.", origin.getAbsolutePath())); 692 } 693 if (isDir()) { 694 throw new IOException(String.format("The disk file '%s' is a folder.", origin.getAbsolutePath())); 695 } 696 if (!exists()) { 697 throw new IOException(String.format("The disk file '%s' does not exist.", origin.getAbsolutePath())); 698 } 699 return Files.readAllBytes(Paths.get(getPath())); 700 } 701 702 @Override 703 public String readAllString() throws IOException { 704 return readAllString(BaseFile.CHARSET); 705 } 706 707 public String readAllString(String charset) throws IOException { 708 ByteArrayOutputStream baos = null; 709 try { 710 byte[] bytes = readAllBytes(); 711 baos = new ByteArrayOutputStream(); 712 baos.write(bytes); 713 baos.flush(); 714 return baos.toString(charset); 715 } finally { 716 if (baos != null) { 717 try { 718 baos.close(); 719 } catch (IOException e) { 720 Logger.systemError(DiskFile.class, e.getMessage(), e); 721 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 722 } 723 } 724 } 725 } 726 727 @Override 728 public long size() throws IOException { 729 if (isFile()) { 730 Path path = Paths.get(origin.getAbsolutePath()); 731 return Files.size(path); 732 } 733 return 0L; 734 } 735 736 private void close() { 737 if (lock != null) { 738 try { 739 lock.release(); 740 } catch (IOException e) { 741 Logger.systemError(DiskFile.class, e.getMessage(), e); 742 } 743 try { 744 lock.close(); 745 } catch (IOException e) { 746 Logger.systemError(DiskFile.class, e.getMessage(), e); 747 } finally { 748 lock = null; 749 } 750 } 751 752 if (channel != null) { 753 try { 754 channel.close(); 755 } catch (IOException e) { 756 Logger.systemError(DiskFile.class, e.getMessage(), e); 757 } finally { 758 channel = null; 759 } 760 } 761 afterWrite(); 762 } 763 764 @Override 765 public boolean complete() throws IOException { 766 close(); 767 return true; 768 } 769 770 public void setModifyTimeMs(long modifyTimeMs) throws IOException { 771 this.modifyTimeMs = modifyTimeMs; 772 } 773 774 public void setModifyTimeMsFromClock(long modifyTimeMsFromClock) throws IOException { 775 String remoteTzId = new Clock().getCalendar().getTimeZone().getID(); 776 String localTzId = Calendar.getInstance().getTimeZone().getID(); 777 Timestamp fileLocalTimestamp = timeZoneConver(localTzId, remoteTzId, new Timestamp(modifyTimeMsFromClock)); 778 this.modifyTimeMs = fileLocalTimestamp.getTime(); 779 } 780 781 @Override 782 public void setModifyTime(Timestamp modifyTime) throws IOException { 783 if (exists()) { 784 Path originPath = Paths.get(origin.toURI()); 785 Files.setLastModifiedTime(originPath, FileTime.fromMillis(modifyTime.getTime())); 786 } 787 } 788 789 public void setModifyTimeFromClock(Timestamp modifyTimeFromClock) throws IOException { 790 String remoteTzId = new Clock().getCalendar().getTimeZone().getID(); 791 String localTzId = Calendar.getInstance().getTimeZone().getID(); 792 Timestamp fileLocalTimestamp = timeZoneConver(localTzId, remoteTzId, modifyTimeFromClock); 793 setModifyTime(fileLocalTimestamp); 794 } 795 796 @Override 797 public Timestamp getModifyTime() throws IOException { 798 Path originPath = Paths.get(origin.toURI()); 799 if (Files.exists(originPath)) { 800 BasicFileAttributes attr = Files.readAttributes(originPath, BasicFileAttributes.class); 801 FileTime fileTime = attr.lastModifiedTime(); 802 Timestamp fileLocalTimestamp = new Timestamp(fileTime.toMillis()); 803 return fileLocalTimestamp; 804 } 805 return null; 806 } 807 808 public Timestamp getModifyTimeForClock() throws IOException { 809 Path originPath = Paths.get(origin.toURI()); 810 if (Files.exists(originPath)) { 811 BasicFileAttributes attr = Files.readAttributes(originPath, BasicFileAttributes.class); 812 FileTime fileTime = attr.lastModifiedTime(); 813 Timestamp fileLocalTimestamp = new Timestamp(fileTime.toMillis()); 814 String remoteTzId = new Clock().getCalendar().getTimeZone().getID(); 815 String localTzId = Calendar.getInstance().getTimeZone().getID(); 816 Timestamp fileRemoteTimestamp = timeZoneConver(remoteTzId, localTzId, fileLocalTimestamp); 817 return fileRemoteTimestamp; 818 } 819 return null; 820 } 821 822 public void startFullAsync(long timerMs, String replaceTo) throws IOException { 823 startFullAsync(timerMs, getPath() + "/", replaceTo, null, null); 824 } 825 826 public void startFullAsync(long timerMs, String replaceTo, Runnable completedCallback) throws IOException { 827 startFullAsync(timerMs, getPath() + "/", replaceTo, completedCallback, null); 828 } 829 830 public void startFullAsync(long timerMs, String replaceTo, Runnable completedCallback, 831 Runnable checkDeleteCompletedCallback) throws IOException { 832 startFullAsync(timerMs, getPath() + "/", replaceTo, completedCallback, checkDeleteCompletedCallback); 833 } 834 835 private void startFullAsync(long timerMs, String replaceRootPath, String replaceTo, Runnable completedCallback, 836 Runnable checkDeleteCompletedCallback) throws IOException { 837 838 if (!RemoteFile.FIRST_LOADED) { 839 RemoteFile firstLoaded = new RemoteFile(replaceTo); 840 } 841 842 Runnable runnable = new Runnable() { 843 @Override 844 public void run() { 845 try { 846 startFullAsyncThread(timerMs, replaceRootPath, replaceTo, completedCallback); 847 } catch (Exception e) { 848 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 849 } 850 } 851 }; 852 Executors.newFixedThreadPool(1).execute(runnable); 853 854 runnable = new Runnable() { 855 @Override 856 public void run() { 857 try { 858 startFullSyncForDelete(-1, timerMs, replaceTo, replaceRootPath, checkDeleteCompletedCallback); 859 } catch (Exception e) { 860 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 861 } 862 } 863 }; 864 Executors.newFixedThreadPool(1).execute(runnable); 865 } 866 867 private synchronized void startFullSyncForDelete(final Integer filePartDataTable, final long timerMs, 868 final String replaceTo, final String replaceRootPath, final Runnable completedCallback) throws IOException { 869 DiskFile the = this; 870 RemoteFile rootDir = null; 871 if (!CommonTools.isBlank(replaceTo)) { 872 rootDir = new RemoteFile(replaceTo); 873 } 874 if (rootDir == null) { 875 throw new IOException("The root folder is null."); 876 } 877 RemoteFile _rootDir = rootDir; 878 Integer maxDataTable = rootDir.getMaxFilePartDataTable(); 879 880 Integer _filePartDataTable = filePartDataTable; 881 882 if (filePartDataTable > maxDataTable) 883 _filePartDataTable = -1; 884 885 final Integer finalFilePartDataTable = _filePartDataTable; 886 887 final CacheArray rows = new CacheArray(); 888 889 rows.filter(getScanDeleteFilter(rootDir, finalFilePartDataTable, timerMs, replaceTo, replaceRootPath, 890 completedCallback)); 891 892 Runnable runnable = new Runnable() { 893 @Override 894 public void run() { 895 try { 896 _rootDir.listAllForScanDelete(finalFilePartDataTable, rows); 897 } catch (Exception e) { 898 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 899 } 900 } 901 }; 902 Executors.newFixedThreadPool(1).execute(runnable); 903 } 904 905 private CacheArrayFilter getScanDeleteFilter(final RemoteFile rootDir, final Integer filePartDataTable, 906 final long timerMs, final String replaceTo, final String replaceRootPath, 907 final Runnable completedCallback) { 908 DiskFile the = this; 909 CacheArrayFilter filter = new CacheArrayFilter(CACHE_ARRAY_FILTER_TIMER) { 910 @Override 911 public void execute(Integer index, Object o) { 912 913 try { 914 Map<String, Object> item = (Map<String, Object>) o; 915 Object id = item.get("id"); 916 String parent = (String) item.get("file_parent_path"); 917 String fileName = (String) item.get("file_name"); 918 String pf = String.format("%s/%s", parent, fileName); 919 RemoteFile rf = new RemoteFile(pf); 920 Thread.currentThread() 921 .setName(String.format("DiskFile-startFullSyncForDelete-(DataTable=%s Index=%s)-%s", 922 filePartDataTable, index, fileName)); 923 924 if (memoryUsage() < RemoteFile.MAX_MEMORY_USAGE) { 925 String toDf = pf; 926 if (!CommonTools.isBlank(replaceRootPath) && !CommonTools.isBlank(replaceTo)) { 927 toDf = pf.replaceFirst(replaceTo + "[/]*", replaceRootPath + "/"); 928 } 929 930 DiskFile df = new DiskFile(toDf); 931 DiskFile.copyAttrs(df, the.copyStructureOnly, the.syncRoot); 932 boolean isDebug = isDebug(rf); 933 boolean isRoot = rootDir.getPath().equals(rf.getPath()); 934 boolean isSyncedHost = rf.isSyncedOnHostname(id); 935 boolean rfExists = rf.exists(); 936 937 boolean allowedForceDelete = rootDir.exists() && !isRoot && isSyncedHost && rfExists 938 && !df.exists() && !df.isLogicModify(); 939 940 if (allowedForceDelete) { 941 df.logicModify(); 942 if (isDebug) { 943 LoggerFactory.getLogger(DiskFile.class).mark("ForceRemoteDelete - {}", rf.getPath()); 944 } 945 rf.forceDeleteAll(true); 946 df.removeLogicModify(); 947 } 948 } else { 949 LoggerFactory.getLogger(DiskFile.class).warn("Over max memory usage 'MAX_MEMORY_USAGE={}'.", 950 RemoteFile.MAX_MEMORY_USAGE); 951 } 952 } catch (Exception e) { 953 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 954 } 955 } 956 957 @Override 958 public void completed(Integer size) { 959 if (isStopSync()) { 960 LoggerFactory.getLogger(DiskFile.class).mark("Stoped start full sync for delete."); 961 } 962 if (timerMs <= 0) { 963 LoggerFactory.getLogger(DiskFile.class).mark("Completed start full sync for delete."); 964 } 965 966 boolean isRootCompleted = the.getPath().equals(new DiskFile(replaceRootPath).getPath()); 967 968 if (isRootCompleted && completedCallback != null) { 969 completedCallback.run(); 970 } 971 972 if (!isStopSync() && timerMs > 0) { 973 while (true) { 974 975 if (isStopSync()) 976 break; 977 978 try { 979 Thread.sleep(timerMs); 980 } catch (InterruptedException e) { 981 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 982 } 983 boolean allowed = checkUsage(rootDir); 984 if (allowed) { 985 try { 986 if (!isStopSync()) { 987 startFullSyncForDelete(filePartDataTable + 1, timerMs, replaceTo, replaceRootPath, 988 completedCallback); 989 } 990 } catch (IOException e) { 991 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 992 } 993 break; 994 } 995 } 996 } 997 } 998 }; 999 return filter; 1000 } 1001 1002 private void startFullAsyncThread(final long timerMs, String replaceRootPath, String replaceTo, 1003 final Runnable completedCallback) { 1004 stopSync = false; 1005 this.setCopyStructureOnly(RemoteFile.COPY_STRUCTURE_ONLY); 1006 final DiskFile the = this; 1007 Thread.currentThread().setName(String.format("DiskFile-startFullAsyncThread-%s", the.getOrigin().getName())); 1008 replaceRootPath = replacePath(replaceRootPath); 1009 replaceTo = replacePath(replaceTo); 1010 DiskFile rootDisk = new DiskFile(replaceRootPath); 1011 boolean rootExists = false; 1012 try { 1013 rootExists = rootDisk.exists(); 1014 } catch (Exception e) { 1015 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 1016 } 1017 if (!rootExists) { 1018 Logger.systemError(DiskFile.class, "The root path '{}' does not exist.", rootDisk.getPath()); 1019 } 1020 boolean isRoot = the.getPath().equals(rootDisk.getPath()); 1021 LoggerFactory.getLogger(DiskFile.class).debug("IsRoot: {} - {}", isRoot, the.getPath()); 1022 LoggerFactory.getLogger(DiskFile.class).debug("CacheArrayUsage: {}", CacheArray.getUsage()); 1023 if (rootExists) { 1024 1025 DirectoryStream<Path> stream = null; 1026 try { 1027 if (syncRoot == null) { 1028 File replaceRootFile = new File(replaceRootPath); 1029 if (replaceRootFile.getParent() == null) { 1030 throw new IOException("Cannot sync the root path."); 1031 } 1032 String replaceRootParent = replacePath(replaceRootFile.getParent()); 1033 syncRoot = new File(String.format("%s/%s", replaceRootParent, replaceRootFile.getName())); 1034 } 1035 1036 if (!CommonTools.isBlank(replaceTo)) { 1037 RemoteFile rootDir = new RemoteFile(replaceTo); 1038 if (!rootDir.exists()) { 1039 rootDir.setModifyTime(this.getModifyTimeForClock()); 1040 rootDir.mkdirs(); 1041 } 1042 } 1043 Path originPath = Paths.get(origin.toURI()); 1044 CacheArray rows = new CacheArray(); 1045 CacheArrayFilter filter = getFullAsyncFilter(the, timerMs, replaceRootPath, replaceTo, 1046 completedCallback); 1047 rows.filter(filter); 1048 stream = Files.newDirectoryStream(originPath); 1049 for (Path p : stream) { 1050 if (p != null) { 1051 File f = p.toFile(); 1052 boolean isFile = f.isFile(); 1053 if (isFile) { 1054 boolean isWritingDf = f.getName().matches(TMP_WRITING_SWP); 1055 if (isWritingDf) { 1056 BasicFileAttributes attributes = Files.readAttributes(p, BasicFileAttributes.class); 1057 FileTime creationTime = attributes.creationTime(); 1058 boolean timeouted = (Calendar.getInstance().getTimeInMillis() 1059 - creationTime.toMillis()) > (RemoteFile.BATCH_SIZE * LOGIC_TIMEOUT_MS); 1060 if (timeouted) { 1061 try { 1062 boolean exists = Files.exists(p); 1063 if (exists) { 1064 Files.deleteIfExists(p); 1065 } 1066 } catch (Exception e) { 1067 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 1068 } 1069 } 1070 } 1071 } 1072 boolean allowedPath = the.checkSyncPathAvailable(p.toString()); 1073 if (!allowedPath) { 1074 LoggerFactory.getLogger(DiskFile.class).debug("Ignored: {}", p.toString()); 1075 } 1076 if (allowedPath) { 1077 rows.add(p); 1078 } 1079 } 1080 } 1081 rows.add(null); 1082 } catch (Exception e) { 1083 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 1084 removeLogicAccess(); 1085 if (!(e instanceof NoSuchFileException)) { 1086 if (!isStopSync() && timerMs > 0) { 1087 try { 1088 Thread.sleep(timerMs); 1089 } catch (InterruptedException ee) { 1090 LoggerFactory.getLogger(DiskFile.class).error(ee.getMessage(), ee); 1091 } 1092 if (!isStopSync()) { 1093 startFullAsyncThread(timerMs, replaceRootPath, replaceTo, completedCallback); 1094 } 1095 } 1096 } 1097 } finally { 1098 if (stream != null) { 1099 try { 1100 stream.close(); 1101 } catch (IOException e) { 1102 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 1103 } 1104 } 1105 } 1106 } 1107 } 1108 1109 private CacheArrayFilter getFullAsyncFilter(DiskFile the, long timerMs, String replaceRootPath, String replaceTo, 1110 Runnable completedCallback) { 1111 1112 return new CacheArrayFilter(CACHE_ARRAY_FILTER_TIMER) { 1113 RemoteFile rf = null; 1114 final CacheArrayFilter theFilter = this; 1115 1116 @Override 1117 public void completed(Integer size) { 1118 boolean isRootCompleted = the.getPath().equals(new DiskFile(replaceRootPath).getPath()); 1119 1120 if (isRootCompleted) { 1121 try { 1122 if (rf != null && the.isStopSync() && isDebug(rf)) { 1123 LoggerFactory.getLogger(DiskFile.class).mark("StopedRootScan - {}", the.getPath()); 1124 } 1125 if (!the.isStopSync() && timerMs > 0) { 1126 Thread.sleep(timerMs); 1127 if (!the.isStopSync()) { 1128 if (rf != null && isDebug(rf)) { 1129 LoggerFactory.getLogger(DiskFile.class).mark("RootScaning - {}", the.getPath()); 1130 } 1131 the.startFullAsyncThread(timerMs, replaceRootPath, replaceTo, completedCallback); 1132 } 1133 } 1134 if (completedCallback != null) { 1135 completedCallback.run(); 1136 } 1137 } catch (Exception e) { 1138 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 1139 } 1140 } 1141 } 1142 1143 @Override 1144 public void execute(Integer index, Object o) { 1145 DiskFile df = null; 1146 try { 1147 Path p = (Path) o; 1148 String pf = replacePath(p.toFile().getAbsolutePath()); 1149 Thread.currentThread().setName( 1150 String.format("DiskFile-getFullAsyncFilter-%s", p.toFile().getParentFile().getName())); 1151 String toRf = pf; 1152 if (!CommonTools.isBlank(replaceRootPath) && !CommonTools.isBlank(replaceTo)) { 1153 toRf = pf.replaceFirst(replaceRootPath + "[/]*", replaceTo + "/"); 1154 } 1155 1156 df = new DiskFile(pf); 1157 DiskFile.copyAttrs(df, the.copyStructureOnly, the.syncRoot); 1158 rf = new RemoteFile(toRf); 1159 1160 boolean isRoot = the.getPath().equals(new DiskFile(replaceRootPath).getPath()); 1161 1162 boolean allowedConn = checkUsage(rf); 1163 String fileName = p.toFile().getName(); 1164 boolean isDebug = isDebug(rf); 1165 1166 boolean timeouted = rf.isTimeout(); 1167 if (timeouted && RemoteFile.addQueuePathMapping(rf.getPath()) && allowedConn) { 1168 if (isDebug) { 1169 LoggerFactory.getLogger(DiskFile.class).mark("ForceDelete(Timeouted) - {}", rf.getPath()); 1170 } 1171 rf.forceDeleteFile(false); 1172 RemoteFile.removeQueuePathMapping(rf.getPath()); 1173 } 1174 Timestamp modifyTime = df.getModifyTimeForClock(); 1175 boolean allowedModifyTime = modifyTime != null; 1176 boolean allowed = DiskFile.checkQueuePathMapping() && !isStopSync() && allowedModifyTime 1177 && allowedConn; 1178 1179 if (isDebug) { 1180 debugLog("CheckUsage", rf, this); 1181 } 1182 1183 if (!isRoot && allowed) { 1184 allowed = !df.isLogicCheck(); 1185 } 1186 1187 if (allowed) { 1188 1189 if (!isRoot) 1190 df.logicCheck(); 1191 1192 rf.setModifyTime(modifyTime); 1193 long dfModifyTimeMs = modifyTime.getTime(); 1194 boolean blocked = false; 1195 boolean isRealDelete = rf.isLastOperateRealDelete(); 1196 1197 if (isRealDelete) { 1198 String clientHostname = CommonTools.getHostname(); 1199 String delHostname = rf.getLastSourceHostname(); 1200 boolean isOwnerDel = clientHostname.equals(delHostname); 1201 if (isOwnerDel) { 1202 Timestamp deletedTime = rf.getLastOperateTime(); 1203 if (deletedTime != null) { 1204 boolean waiting = new Clock().getTime() - deletedTime.getTime() <= LOGIC_TIMEOUT_MS; 1205 if (waiting) { 1206 blocked = true; 1207 } 1208 } 1209 } else { 1210 Timestamp deletedTime = rf.getLastOperateTime(); 1211 if (deletedTime != null) { 1212 boolean olded = modifyTime.getTime() < deletedTime.getTime(); 1213 if (olded) { 1214 blocked = true; 1215 } 1216 } 1217 } 1218 } 1219 1220 boolean haveParent = rf.getParentFile().exists(); 1221 if (!blocked && haveParent) { 1222 if (df.isLink()) { 1223 if (!df.isLogicAccess()) { 1224 df.logicAccess(); 1225 if (rf.exists()) { 1226 if (rf.isLink()) { 1227 long rfModifyTimeMs = rf.getModifyTime().getTime(); 1228 boolean changedTime = dfModifyTimeMs > rfModifyTimeMs; 1229 boolean changedValue = !rf.readLink().equals(df.readLink()); 1230 boolean changed = changedTime && changedValue; 1231 if (changed) { 1232 RemoteFile prf = rf.getParentFile(); 1233 if (prf != null && prf.exists()) { 1234 if (isDebug) { 1235 LoggerFactory.getLogger(DiskFile.class).mark("CreateLink - {}", 1236 rf.getPath()); 1237 } 1238 rf.forceDeleteLink(false); 1239 rf.createLink(df.readLink()); 1240 } 1241 } 1242 } else { 1243 RemoteFile prf = rf.getParentFile(); 1244 if (prf != null && prf.exists()) { 1245 if (isDebug) { 1246 LoggerFactory.getLogger(DiskFile.class).mark("CreateLink - {}", 1247 rf.getPath()); 1248 } 1249 rf.forceDeleteLink(false); 1250 rf.createLink(df.readLink()); 1251 } 1252 } 1253 } else { 1254 RemoteFile prf = rf.getParentFile(); 1255 if (prf != null && prf.exists()) { 1256 if (isDebug) { 1257 LoggerFactory.getLogger(DiskFile.class).mark("CreateLink - {}", 1258 rf.getPath()); 1259 } 1260 rf.createLink(df.readLink()); 1261 } 1262 } 1263 } 1264 } else if (df.isFile()) { 1265 if (!df.isLogicModify() && !df.isLogicAccess()) { 1266 if (rf.exists()) { 1267 if (rf.isFile()) { 1268 long rfModifyTimeMs = rf.getModifyTime().getTime(); 1269 if (dfModifyTimeMs > rfModifyTimeMs) { 1270 RemoteFile prf = rf.getParentFile(); 1271 if (prf != null && prf.exists()) { 1272 if (DiskFile.addQueuePathMapping(df.getPath())) { 1273 if (isDebug) { 1274 LoggerFactory.getLogger(DiskFile.class) 1275 .mark("RemoteQueuing - {}", df.getPath()); 1276 } 1277 df.logicAccess(); 1278 rf.forceDeleteFile(false); 1279 df.writeToRemote(rf); 1280 } else { 1281 df.removeLogicAccess(); 1282 df.removeLogicCheck(); 1283 } 1284 } 1285 } 1286 } else { 1287 rf.forceDeleteFile(false); 1288 } 1289 } else { 1290 RemoteFile prf = rf.getParentFile(); 1291 if (prf != null && prf.exists()) { 1292 if (DiskFile.addQueuePathMapping(df.getPath())) { 1293 if (isDebug) { 1294 LoggerFactory.getLogger(DiskFile.class).mark("RemoteQueuing - {}", 1295 df.getPath()); 1296 } 1297 df.logicAccess(); 1298 df.writeToRemote(rf); 1299 } else { 1300 df.removeLogicAccess(); 1301 df.removeLogicCheck(); 1302 } 1303 } 1304 } 1305 } 1306 } else if (df.isDir()) { 1307 if (isStopSync() && isDebug) { 1308 LoggerFactory.getLogger(DiskFile.class).mark("Stopping queue"); 1309 } 1310 if (!isStopSync()) { 1311 boolean allowedPoolSize = theFilter.getCacheArray().getUsingPoolSize() < theFilter 1312 .getCacheArray().getMaxPoolSize() && checkUsage(rf); 1313 if (isDebug && allowedPoolSize) { 1314 LoggerFactory.getLogger(DiskFile.class).debug("Scaning - {}", df.getPath()); 1315 } 1316 if (isDebug && !allowedPoolSize) { 1317 LoggerFactory.getLogger(DiskFile.class).debug("Skiped - {}", df.getPath()); 1318 } 1319 if (rf.exists()) { 1320 if (rf.isDir()) { 1321 if (isRoot || allowedPoolSize) { 1322 df.startFullAsyncThread(timerMs, replaceRootPath, replaceTo, 1323 completedCallback); 1324 } 1325 } else { 1326 if (rf.isDir()) 1327 rf.forceDeleteDir(false); 1328 1329 if (rf.isFile()) 1330 rf.forceDeleteFile(false); 1331 1332 if (rf.isLink()) 1333 rf.forceDeleteLink(false); 1334 1335 RemoteFile prf = rf.getParentFile(); 1336 if (isRoot || (prf != null && prf.exists())) { 1337 if (isDebug) { 1338 LoggerFactory.getLogger(DiskFile.class) 1339 .mark("ReCreateRemoteDir - {}", rf.getPath()); 1340 } 1341 rf.mkdirs(); 1342 if (isRoot || allowedPoolSize) { 1343 df.startFullAsyncThread(timerMs, replaceRootPath, replaceTo, 1344 completedCallback); 1345 } 1346 } 1347 } 1348 } else { 1349 RemoteFile prf = rf.getParentFile(); 1350 if (isRoot || (prf != null && prf.exists())) { 1351 if (isDebug) { 1352 LoggerFactory.getLogger(DiskFile.class).mark("CreateRemoteDir - {}", 1353 rf.getPath()); 1354 } 1355 rf.mkdirs(); 1356 if (isRoot || allowedPoolSize) { 1357 df.startFullAsyncThread(timerMs, replaceRootPath, replaceTo, 1358 completedCallback); 1359 } 1360 } 1361 } 1362 if (!isRoot && !allowedPoolSize) { 1363 df.removeLogicCheck(); 1364 } 1365 } 1366 } 1367 } 1368 } 1369 } catch (Exception e) { 1370 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 1371 if (df != null) { 1372 df.removeLogicAccess(); 1373 } 1374 } 1375 } 1376 1377 }; 1378 } 1379 1380 public void setCopyStructureOnly(boolean copyStructureOnly) { 1381 this.copyStructureOnly = copyStructureOnly; 1382 } 1383 1384 public boolean isCopyStructureOnly() { 1385 return this.copyStructureOnly; 1386 } 1387 1388 private boolean isViEditSwp(String fileName) { 1389 return fileName.matches(TMP_MATCHES_EDIT) || fileName.matches(TMP_MATCHES_VI_SWP); 1390 } 1391 1392 protected static DiskFile copyAttrs(DiskFile source, boolean _copyStructureOnly, File _syncRoot) { 1393 source.setCopyStructureOnly(_copyStructureOnly); 1394 source.syncRoot = _syncRoot; 1395 return source; 1396 } 1397 1398 public File getParentFile() { 1399 return new File(getParent()); 1400 } 1401 1402 protected boolean checkSyncPathAvailable(String path) { 1403 try { 1404 return checkSyncPathAvailable(path, RemoteFile.SHOW_HIDDEN, RemoteFile.SHOW_LINK, RemoteFile.MAX_FILE_SZIE, 1405 SYNC_PATH_ALLOWED, SYNC_PATH_IGNORED); 1406 } catch (Exception e) { 1407 LoggerFactory.getLogger(DiskFile.class).error(e.getMessage(), e); 1408 return false; 1409 } 1410 } 1411 1412 protected static synchronized boolean addQueuePathMapping(String path) { 1413 Long addTimeMs = null; 1414 boolean allowed = checkQueuePathMapping(); 1415 1416 if (!allowed) 1417 return false; 1418 1419 addTimeMs = QUEUE_PATH_MAPPING.get(path); 1420 1421 if (addTimeMs == null) { 1422 QUEUE_PATH_MAPPING.put(path, System.currentTimeMillis()); 1423 return true; 1424 } 1425 1426 return false; 1427 } 1428 1429 public static synchronized boolean checkQueuePathMapping() { 1430 Long addTimeMs = null; 1431 Map<String, Long> copyQueueMap = getQueuePathMapping(); 1432 List<String> keyList = new ArrayList<String>(copyQueueMap.keySet()); 1433 for (String key : keyList) { 1434 addTimeMs = copyQueueMap.get(key); 1435 if (addTimeMs != null) { 1436 boolean timeout = (System.currentTimeMillis() - addTimeMs) >= LOGIC_TIMEOUT_MS; 1437 1438 if (timeout) { 1439 QUEUE_PATH_MAPPING.remove(key); 1440 } 1441 } 1442 } 1443 1444 if (QUEUE_PATH_MAPPING.keySet().size() >= RemoteFile.MAX_QUEUE_SIZE) 1445 return false; 1446 1447 return true; 1448 } 1449 1450 protected static synchronized void removeQueuePathMapping(String path) { 1451 QUEUE_PATH_MAPPING.remove(path); 1452 } 1453 1454 public static synchronized Map<String, Long> getQueuePathMapping() { 1455 return Collections.synchronizedMap(QUEUE_PATH_MAPPING); 1456 } 1457 1458 private static void debugLog(String point, RemoteFile rf, CacheArrayFilter filter) { 1459 debugLog(DiskFile.class, point, rf, filter); 1460 } 1461 1462}