001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.rolling.helper; 015 016import java.io.File; 017 018import ch.qos.logback.core.CoreConstants; 019import ch.qos.logback.core.rolling.RollingFileAppender; 020import ch.qos.logback.core.rolling.RolloverFailure; 021import ch.qos.logback.core.spi.ContextAwareBase; 022import ch.qos.logback.core.util.EnvUtil; 023import ch.qos.logback.core.util.FileUtil; 024 025/** 026 * Utility class to help solving problems encountered while renaming files. 027 * 028 * @author Ceki Gulcu 029 */ 030public class RenameUtil extends ContextAwareBase { 031 032 static String RENAMING_ERROR_URL = CoreConstants.CODES_URL + "#renamingError"; 033 034 /** 035 * A relatively robust file renaming method which in case of failure due to src 036 * and target being on different volumes, falls back onto renaming by copying. 037 * 038 * @param src 039 * @param target 040 * @throws RolloverFailure 041 */ 042 public void rename(String src, String target) throws RolloverFailure { 043 if (src.equals(target)) { 044 addWarn("Source and target files are the same [" + src + "]. Skipping."); 045 return; 046 } 047 File srcFile = new File(src); 048 049 if (srcFile.exists()) { 050 File targetFile = new File(target); 051 createMissingTargetDirsIfNecessary(targetFile); 052 053 addInfo("Renaming file [" + srcFile + "] to [" + targetFile + "]"); 054 055 boolean result = srcFile.renameTo(targetFile); 056 057 if (!result) { 058 addWarn("Failed to rename file [" + srcFile + "] as [" + targetFile + "]."); 059 Boolean areOnDifferentVolumes = areOnDifferentVolumes(srcFile, targetFile); 060 if (Boolean.TRUE.equals(areOnDifferentVolumes)) { 061 addWarn("Detected different file systems for source [" + src + "] and target [" + target 062 + "]. Attempting rename by copying."); 063 renameByCopying(src, target); 064 return; 065 } else { 066 addWarn("Please consider leaving the [file] option of " + RollingFileAppender.class.getSimpleName() 067 + " empty."); 068 addWarn("See also " + RENAMING_ERROR_URL); 069 } 070 } 071 } else { 072 throw new RolloverFailure("File [" + src + "] does not exist."); 073 } 074 } 075 076 /** 077 * Attempts to determine whether both files are on different volumes. Returns 078 * true if we could determine that the files are on different volumes. Returns 079 * false otherwise or if an error occurred while doing the check. 080 * 081 * @param srcFile 082 * @param targetFile 083 * @return true if on different volumes, false otherwise or if an error occurred 084 */ 085 Boolean areOnDifferentVolumes(File srcFile, File targetFile) throws RolloverFailure { 086 if (!EnvUtil.isJDK7OrHigher()) 087 return false; 088 089 // target file is not certain to exist but its parent has to exist given the 090 // call hierarchy of this method 091 File parentOfTarget = targetFile.getAbsoluteFile().getParentFile(); 092 093 if (parentOfTarget == null) { 094 addWarn("Parent of target file [" + targetFile + "] is null"); 095 return null; 096 } 097 if (!parentOfTarget.exists()) { 098 addWarn("Parent of target file [" + targetFile + "] does not exist"); 099 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}