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