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 */
017 package org.apache.logging.log4j.core.appender.rolling.action;
018
019 import java.io.File;
020 import java.io.FileInputStream;
021 import java.io.FileOutputStream;
022 import java.io.IOException;
023 import java.nio.channels.FileChannel;
024
025 /**
026 * File rename action.
027 */
028 public class FileRenameAction extends AbstractAction {
029
030 /**
031 * Source.
032 */
033 private final File source;
034
035 /**
036 * Destination.
037 */
038 private final File destination;
039
040 /**
041 * If true, rename empty files, otherwise delete empty files.
042 */
043 private final boolean renameEmptyFiles;
044
045 /**
046 * Creates an FileRenameAction.
047 *
048 * @param src current file name.
049 * @param dst new file name.
050 * @param renameEmptyFiles if true, rename file even if empty, otherwise delete empty files.
051 */
052 public FileRenameAction(final File src, final File dst, final boolean renameEmptyFiles) {
053 source = src;
054 destination = dst;
055 this.renameEmptyFiles = renameEmptyFiles;
056 }
057
058 /**
059 * Rename file.
060 *
061 * @return true if successfully renamed.
062 */
063 @Override
064 public boolean execute() {
065 return execute(source, destination, renameEmptyFiles);
066 }
067
068 /**
069 * Rename file.
070 *
071 * @param source current file name.
072 * @param destination new file name.
073 * @param renameEmptyFiles if true, rename file even if empty, otherwise delete empty files.
074 * @return true if successfully renamed.
075 */
076 public static boolean execute(final File source, final File destination, final boolean renameEmptyFiles) {
077 if (renameEmptyFiles || source.length() > 0) {
078 final File parent = destination.getParentFile();
079 if (parent != null && !parent.exists()) {
080 // LOG4J2-679: ignore mkdirs() result: in multithreaded scenarios,
081 // if one thread succeeds the other thread returns false
082 // even though directories have been created. Check if dir exists instead.
083 parent.mkdirs();
084 if (!parent.exists()) {
085 LOGGER.error("Unable to create directory {}", parent.getAbsolutePath());
086 return false;
087 }
088 }
089 try {
090 if (!source.renameTo(destination)) {
091 try {
092 copyFile(source, destination);
093 return source.delete();
094 } catch (final IOException iex) {
095 LOGGER.error("Unable to rename file {} to {} - {}", source.getAbsolutePath(),
096 destination.getAbsolutePath(), iex.getMessage());
097 }
098 }
099 return true;
100 } catch (final Exception ex) {
101 try {
102 copyFile(source, destination);
103 return source.delete();
104 } catch (final IOException iex) {
105 LOGGER.error("Unable to rename file {} to {} - {}", source.getAbsolutePath(),
106 destination.getAbsolutePath(), iex.getMessage());
107 }
108 }
109 } else {
110 try {
111 source.delete();
112 } catch (final Exception ex) {
113 LOGGER.error("Unable to delete empty file " + source.getAbsolutePath());
114 }
115 }
116
117 return false;
118 }
119
120 private static void copyFile(final File source, final File destination) throws IOException {
121 if (!destination.exists()) {
122 destination.createNewFile();
123 }
124
125 FileChannel srcChannel = null;
126 FileChannel destChannel = null;
127 FileInputStream srcStream = null;
128 FileOutputStream destStream = null;
129 try {
130 srcStream = new FileInputStream(source);
131 destStream = new FileOutputStream(destination);
132 srcChannel = srcStream.getChannel();
133 destChannel = destStream.getChannel();
134 destChannel.transferFrom(srcChannel, 0, srcChannel.size());
135 } finally {
136 if (srcChannel != null) {
137 srcChannel.close();
138 }
139 if (srcStream != null) {
140 srcStream.close();
141 }
142 if (destChannel != null) {
143 destChannel.close();
144 }
145 if (destStream != null) {
146 destStream.close();
147 }
148 }
149 }
150
151 @Override
152 public String toString() {
153 return FileRenameAction.class.getSimpleName() + '[' + source + " to " + destination //
154 + ", renameEmptyFiles=" + renameEmptyFiles + ']';
155 }
156 }