View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.datamodel.niofs.utils;
20  
21  import java.io.IOException;
22  import java.nio.file.FileAlreadyExistsException;
23  import java.nio.file.FileSystemLoopException;
24  import java.nio.file.FileVisitResult;
25  import java.nio.file.FileVisitor;
26  import java.nio.file.Files;
27  import java.nio.file.NoSuchFileException;
28  import java.nio.file.Path;
29  import java.nio.file.StandardCopyOption;
30  import java.nio.file.attribute.BasicFileAttributes;
31  import java.nio.file.attribute.FileTime;
32  
33  import org.apache.logging.log4j.LogManager;
34  import org.apache.logging.log4j.Logger;
35  import org.mycore.common.MCRSessionMgr;
36  import org.mycore.common.MCRTransactionHelper;
37  
38  /**
39   * Simple {@link FileVisitor} that recursive copies a directory
40   * @author Thomas Scheffler (yagee)
41   *
42   */
43  public class MCRTreeCopier implements FileVisitor<Path> {
44      private static final Logger LOGGER = LogManager.getLogger(MCRTreeCopier.class);
45  
46      private final Path source;
47  
48      private final Path target;
49  
50      private final boolean renameExisting;
51  
52      private final boolean restartTransaction;
53  
54      public MCRTreeCopier(Path source, Path target) throws NoSuchFileException {
55          this(source, target, false, false);
56      }
57  
58      public MCRTreeCopier(Path source, Path target, boolean renameOnExisting) throws NoSuchFileException {
59          this(source, target, renameOnExisting, false);
60      }
61  
62      public MCRTreeCopier(Path source, Path target, boolean renameOnExisting, boolean restartTransaction)
63          throws NoSuchFileException {
64          this.renameExisting = renameOnExisting;
65          if (Files.notExists(target)) {
66              throw new NoSuchFileException(target.toString(), null, "Target directory does not exist.");
67          }
68          this.source = source;
69          this.target = target;
70          this.restartTransaction = restartTransaction;
71      }
72  
73      @Override
74      public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
75          Path newdir = target.resolve(toTargetFS(source.relativize(dir)));
76          try {
77              Files.copy(dir, newdir, StandardCopyOption.COPY_ATTRIBUTES);
78          } catch (FileAlreadyExistsException x) {
79              // (okay if directory already exists).
80              // ignore
81          } catch (IOException x) {
82              LOGGER.error("Unable to create: {}", newdir, x);
83              return FileVisitResult.SKIP_SUBTREE;
84          }
85          return FileVisitResult.CONTINUE;
86      }
87  
88      private void copyFile(Path source, Path target) {
89          try {
90              if (renameExisting && Files.exists(target)) {
91                  int nameTry = 1;
92                  String fileName = target.getFileName().toString();
93                  int numberPosition = fileName.lastIndexOf(".") == -1 ? fileName.length() : fileName.lastIndexOf(".");
94                  String prefixString = fileName.substring(0, numberPosition);
95                  String suffixString = fileName.substring(numberPosition);
96                  String newName = null;
97                  Path parent = target.getParent();
98                  do {
99                      newName = prefixString + nameTry++ + suffixString;
100                     target = parent.resolve(newName);
101                 } while (Files.exists(target));
102             }
103             if (restartTransaction && MCRSessionMgr.hasCurrentSession()) {
104                 MCRTransactionHelper.commitTransaction();
105                 MCRTransactionHelper.beginTransaction();
106             }
107             Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
108         } catch (IOException x) {
109             LOGGER.error("Unable to copy: {}", source, x);
110         }
111     }
112 
113     private Path toTargetFS(Path source) {
114         if (target.getFileSystem().equals(source.getFileSystem())) {
115             return source;
116         }
117         String[] nameParts = new String[source.getNameCount() - 1];
118         for (int i = 0; i < nameParts.length; i++) {
119             nameParts[i] = source.getName(i + 1).toString();
120         }
121         return target.getFileSystem().getPath(source.getName(0).toString(), nameParts);
122     }
123 
124     @Override
125     public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
126         // fix up modification time of directory when done
127         if (exc == null) {
128             Path newdir = target.resolve(toTargetFS(source.relativize(dir)));
129             try {
130                 FileTime time = Files.getLastModifiedTime(dir);
131                 Files.setLastModifiedTime(newdir, time);
132             } catch (IOException x) {
133                 LOGGER.error("Unable to copy all attributes to: {}", newdir, x);
134             }
135         }
136         return FileVisitResult.CONTINUE;
137     }
138 
139     @Override
140     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
141         copyFile(file, target.resolve(toTargetFS(source.relativize(file))));
142         return FileVisitResult.CONTINUE;
143     }
144 
145     @Override
146     public FileVisitResult visitFileFailed(Path file, IOException exc) {
147         if (exc instanceof FileSystemLoopException) {
148             LOGGER.error("cycle detected: {}", file);
149         } else {
150             LOGGER.error("Unable to copy: {}", file, exc);
151         }
152         return FileVisitResult.CONTINUE;
153     }
154 }