View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.core.appender.rolling.action;
    18
    19  import java.io.File;
    20  import java.io.FileInputStream;
    21  import java.io.FileOutputStream;
    22  import java.io.IOException;
    23  import java.nio.channels.FileChannel;
    24
    25  /**
    26   * File rename action.
    27   */
    28  public class FileRenameAction extends AbstractAction {
    29
    30      /**
    31       * Source.
    32       */
    33      private final File source;
    34
    35      /**
    36       * Destination.
    37       */
    38      private final File destination;
    39
    40      /**
    41       * If true, rename empty files, otherwise delete empty files.
    42       */
    43      private final boolean renameEmptyFiles;
    44
    45      /**
    46       * Creates an FileRenameAction.
    47       *
    48       * @param src              current file name.
    49       * @param dst              new file name.
    50       * @param renameEmptyFiles if true, rename file even if empty, otherwise delete empty files.
    51       */
    52      public FileRenameAction(final File src, final File dst, final boolean renameEmptyFiles) {
    53          source = src;
    54          destination = dst;
    55          this.renameEmptyFiles = renameEmptyFiles;
    56      }
    57
    58      /**
    59       * Rename file.
    60       *
    61       * @return true if successfully renamed.
    62       */
    63      @Override
    64      public boolean execute() {
    65          return execute(source, destination, renameEmptyFiles);
    66      }
    67
    68      /**
    69       * Rename file.
    70       *
    71       * @param source           current file name.
    72       * @param destination      new file name.
    73       * @param renameEmptyFiles if true, rename file even if empty, otherwise delete empty files.
    74       * @return true if successfully renamed.
    75       */
    76      public static boolean execute(final File source, final File destination, final boolean renameEmptyFiles) {
    77          if (renameEmptyFiles || source.length() > 0) {
    78              final File parent = destination.getParentFile();
    79              if (parent != null && !parent.exists()) {
    80                  // LOG4J2-679: ignore mkdirs() result: in multithreaded scenarios,
    81                  // if one thread succeeds the other thread returns false
    82                  // even though directories have been created. Check if dir exists instead.
    83                  parent.mkdirs();
    84                  if (!parent.exists()) {
    85                      LOGGER.error("Unable to create directory {}", parent.getAbsolutePath());
    86                      return false;
    87                  }
    88              }
    89              try {
    90                  if (!source.renameTo(destination)) {
    91                      try {
    92                          copyFile(source, destination);
    93                          return source.delete();
    94                      } catch (final IOException iex) {
    95                          LOGGER.error("Unable to rename file {} to {} - {}", source.getAbsolutePath(),
    96                              destination.getAbsolutePath(), iex.getMessage());
    97                      }
    98                  }
    99                  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 }