View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.rolling.helper;
15  
16  import java.io.File;
17  
18  import ch.qos.logback.core.CoreConstants;
19  import ch.qos.logback.core.rolling.RollingFileAppender;
20  import ch.qos.logback.core.rolling.RolloverFailure;
21  import ch.qos.logback.core.spi.ContextAwareBase;
22  import ch.qos.logback.core.util.EnvUtil;
23  import ch.qos.logback.core.util.FileUtil;
24  
25  /**
26   * Utility class to help solving problems encountered while renaming files.
27   *
28   * @author Ceki Gulcu
29   */
30  public class RenameUtil extends ContextAwareBase {
31  
32      static String RENAMING_ERROR_URL = CoreConstants.CODES_URL + "#renamingError";
33  
34      /**
35       * A relatively robust file renaming method which in case of failure due to src
36       * and target being on different volumes, falls back onto renaming by copying.
37       *
38       * @param src
39       * @param target
40       * @throws RolloverFailure
41       */
42      public void rename(String src, String target) throws RolloverFailure {
43          if (src.equals(target)) {
44              addWarn("Source and target files are the same [" + src + "]. Skipping.");
45              return;
46          }
47          File srcFile = new File(src);
48  
49          if (srcFile.exists()) {
50              File targetFile = new File(target);
51              createMissingTargetDirsIfNecessary(targetFile);
52  
53              addInfo("Renaming file [" + srcFile + "] to [" + targetFile + "]");
54  
55              boolean result = srcFile.renameTo(targetFile);
56  
57              if (!result) {
58                  addWarn("Failed to rename file [" + srcFile + "] as [" + targetFile + "].");
59                  Boolean areOnDifferentVolumes = areOnDifferentVolumes(srcFile, targetFile);
60                  if (Boolean.TRUE.equals(areOnDifferentVolumes)) {
61                      addWarn("Detected different file systems for source [" + src + "] and target [" + target
62                              + "]. Attempting rename by copying.");
63                      renameByCopying(src, target);
64                      return;
65                  } else {
66                      addWarn("Please consider leaving the [file] option of " + RollingFileAppender.class.getSimpleName()
67                              + " empty.");
68                      addWarn("See also " + RENAMING_ERROR_URL);
69                  }
70              }
71          } else {
72              throw new RolloverFailure("File [" + src + "] does not exist.");
73          }
74      }
75  
76      /**
77       * Attempts to determine whether both files are on different volumes. Returns
78       * true if we could determine that the files are on different volumes. Returns
79       * false otherwise or if an error occurred while doing the check.
80       *
81       * @param srcFile
82       * @param targetFile
83       * @return true if on different volumes, false otherwise or if an error occurred
84       */
85      Boolean areOnDifferentVolumes(File srcFile, File targetFile) throws RolloverFailure {
86          if (!EnvUtil.isJDK7OrHigher())
87              return false;
88  
89          // target file is not certain to exist but its parent has to exist given the
90          // call hierarchy of this method
91          File parentOfTarget = targetFile.getAbsoluteFile().getParentFile();
92  
93          if (parentOfTarget == null) {
94              addWarn("Parent of target file [" + targetFile + "] is null");
95              return null;
96          }
97          if (!parentOfTarget.exists()) {
98              addWarn("Parent of target file [" + targetFile + "] does not exist");
99              return null;
100         }
101 
102         try {
103             boolean onSameFileStore = FileStoreUtil.areOnSameFileStore(srcFile, parentOfTarget);
104             return !onSameFileStore;
105         } catch (RolloverFailure rf) {
106             addWarn("Error while checking file store equality", rf);
107             return null;
108         }
109     }
110 
111     public void renameByCopying(String src, String target) throws RolloverFailure {
112 
113         FileUtil fileUtil = new FileUtil(getContext());
114         fileUtil.copy(src, target);
115 
116         File srcFile = new File(src);
117         if (!srcFile.delete()) {
118             addWarn("Could not delete " + src);
119         }
120 
121     }
122 
123     void createMissingTargetDirsIfNecessary(File toFile) throws RolloverFailure {
124         boolean result = FileUtil.createMissingParentDirectories(toFile);
125         if (!result) {
126             throw new RolloverFailure("Failed to create parent directories for [" + toFile.getAbsolutePath() + "]");
127         }
128     }
129 
130     @Override
131     public String toString() {
132         return "c.q.l.co.rolling.helper.RenameUtil";
133     }
134 }