001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.appender.rolling.action; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.PrintWriter; 022import java.nio.file.Files; 023import java.nio.file.Paths; 024import java.nio.file.StandardCopyOption; 025 026/** 027 * File rename action. 028 */ 029public class FileRenameAction extends AbstractAction { 030 031 /** 032 * Source. 033 */ 034 private final File source; 035 036 /** 037 * Destination. 038 */ 039 private final File destination; 040 041 /** 042 * If true, rename empty files, otherwise delete empty files. 043 */ 044 private final boolean renameEmptyFiles; 045 046 /** 047 * Creates an FileRenameAction. 048 * 049 * @param src current file name. 050 * @param dst new file name. 051 * @param renameEmptyFiles if true, rename file even if empty, otherwise delete empty files. 052 */ 053 public FileRenameAction(final File src, final File dst, final boolean renameEmptyFiles) { 054 source = src; 055 destination = dst; 056 this.renameEmptyFiles = renameEmptyFiles; 057 } 058 059 /** 060 * Rename file. 061 * 062 * @return true if successfully renamed. 063 */ 064 @Override 065 public boolean execute() { 066 return execute(source, destination, renameEmptyFiles); 067 } 068 069 /** 070 * Gets the destination. 071 * 072 * @return the destination. 073 */ 074 public File getDestination() { 075 return this.destination; 076 } 077 078 /** 079 * Gets the source. 080 * 081 * @return the source. 082 */ 083 public File getSource() { 084 return this.source; 085 } 086 087 /** 088 * Whether to rename empty files. If true, rename empty files, otherwise delete empty files. 089 * 090 * @return Whether to rename empty files. 091 */ 092 public boolean isRenameEmptyFiles() { 093 return renameEmptyFiles; 094 } 095 096 /** 097 * Rename file. 098 * 099 * @param source current file name. 100 * @param destination new file name. 101 * @param renameEmptyFiles if true, rename file even if empty, otherwise delete empty files. 102 * @return true if successfully renamed. 103 */ 104 public static boolean execute(final File source, final File destination, final boolean renameEmptyFiles) { 105 if (renameEmptyFiles || (source.length() > 0)) { 106 final File parent = destination.getParentFile(); 107 if ((parent != null) && !parent.exists()) { 108 // LOG4J2-679: ignore mkdirs() result: in multithreaded scenarios, 109 // if one thread succeeds the other thread returns false 110 // even though directories have been created. Check if dir exists instead. 111 parent.mkdirs(); 112 if (!parent.exists()) { 113 LOGGER.error("Unable to create directory {}", parent.getAbsolutePath()); 114 return false; 115 } 116 } 117 try { 118 try { 119 Files.move(Paths.get(source.getAbsolutePath()), Paths.get(destination.getAbsolutePath()), 120 StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); 121 LOGGER.trace("Renamed file {} to {} with Files.move", source.getAbsolutePath(), 122 destination.getAbsolutePath()); 123 return true; 124 } catch (final IOException exMove) { 125 LOGGER.error("Unable to move file {} to {}: {} {}", source.getAbsolutePath(), 126 destination.getAbsolutePath(), exMove.getClass().getName(), exMove.getMessage()); 127 boolean result = source.renameTo(destination); 128 if (!result) { 129 try { 130 Files.copy(Paths.get(source.getAbsolutePath()), Paths.get(destination.getAbsolutePath()), 131 StandardCopyOption.REPLACE_EXISTING); 132 try { 133 Files.delete(Paths.get(source.getAbsolutePath())); 134 result = true; 135 LOGGER.trace("Renamed file {} to {} using copy and delete", 136 source.getAbsolutePath(), destination.getAbsolutePath()); 137 } catch (final IOException exDelete) { 138 LOGGER.error("Unable to delete file {}: {} {}", source.getAbsolutePath(), 139 exDelete.getClass().getName(), exDelete.getMessage()); 140 try { 141 new PrintWriter(source.getAbsolutePath()).close(); 142 LOGGER.trace("Renamed file {} to {} with copy and truncation", 143 source.getAbsolutePath(), destination.getAbsolutePath()); 144 } catch (final IOException exOwerwrite) { 145 LOGGER.error("Unable to overwrite file {}: {} {}", 146 source.getAbsolutePath(), exOwerwrite.getClass().getName(), 147 exOwerwrite.getMessage()); 148 } 149 } 150 } catch (final IOException exCopy) { 151 LOGGER.error("Unable to copy file {} to {}: {} {}", source.getAbsolutePath(), 152 destination.getAbsolutePath(), exCopy.getClass().getName(), exCopy.getMessage()); 153 } 154 } else { 155 LOGGER.trace("Renamed file {} to {} with source.renameTo", 156 source.getAbsolutePath(), destination.getAbsolutePath()); 157 } 158 return result; 159 } 160 } catch (final RuntimeException ex) { 161 LOGGER.error("Unable to rename file {} to {}: {} {}", source.getAbsolutePath(), 162 destination.getAbsolutePath(), ex.getClass().getName(), ex.getMessage()); 163 } 164 } else { 165 try { 166 source.delete(); 167 } catch (final Exception exDelete) { 168 LOGGER.error("Unable to delete empty file {}: {} {}", source.getAbsolutePath(), 169 exDelete.getClass().getName(), exDelete.getMessage()); 170 } 171 } 172 173 return false; 174 } 175 176 @Override 177 public String toString() { 178 return FileRenameAction.class.getSimpleName() + '[' + source + " to " + destination 179 + ", renameEmptyFiles=" + renameEmptyFiles + ']'; 180 } 181 182}