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  import java.nio.file.Files;
25  import java.nio.file.Path;
26  
27  /**
28   * File rename action.
29   */
30  public class FileRenameAction extends AbstractAction {
31  
32      /**
33       * Source.
34       */
35      private final File source;
36  
37      /**
38       * Destination.
39       */
40      private final File destination;
41  
42      /**
43       * If true, rename empty files, otherwise delete empty files.
44       */
45      private final boolean renameEmptyFiles;
46  
47      /**
48       * Creates an FileRenameAction.
49       *
50       * @param src current file name.
51       * @param dst new file name.
52       * @param renameEmptyFiles if true, rename file even if empty, otherwise delete empty files.
53       */
54      public FileRenameAction(final File src, final File dst, final boolean renameEmptyFiles) {
55          source = src;
56          destination = dst;
57          this.renameEmptyFiles = renameEmptyFiles;
58      }
59  
60      /**
61       * Rename file.
62       *
63       * @return true if successfully renamed.
64       */
65      @Override
66      public boolean execute() {
67          return execute(source, destination, renameEmptyFiles);
68      }
69  
70      /**
71       * Gets the destination.
72       *
73       * @return the destination.
74       */
75      public File getDestination() {
76          return this.destination;
77      }
78  
79      /**
80       * Gets the source.
81       *
82       * @return the source.
83       */
84      public File getSource() {
85          return this.source;
86      }
87  
88      /**
89       * Whether to rename empty files. If true, rename empty files, otherwise delete empty files.
90       *
91       * @return Whether to rename empty files.
92       */
93      public boolean isRenameEmptyFiles() {
94          return renameEmptyFiles;
95      }
96  
97      /**
98       * Rename file.
99       *
100      * @param source current file name.
101      * @param destination new file name.
102      * @param renameEmptyFiles if true, rename file even if empty, otherwise delete empty files.
103      * @return true if successfully renamed.
104      */
105     public static boolean execute(final File source, final File destination, final boolean renameEmptyFiles) {
106         if (renameEmptyFiles || source.length() > 0) {
107             final File parent = destination.getParentFile();
108             if (parent != null && !parent.exists()) {
109                 // LOG4J2-679: ignore mkdirs() result: in multithreaded scenarios,
110                 // if one thread succeeds the other thread returns false
111                 // even though directories have been created. Check if dir exists instead.
112                 parent.mkdirs();
113                 if (!parent.exists()) {
114                     LOGGER.error("Unable to create directory {}", parent.getAbsolutePath());
115                     return false;
116                 }
117             }
118             final Path sourcePath = source.toPath();
119             try {
120                 Files.move(sourcePath, destination.toPath());
121                 return true;
122             } catch (final Exception ex) {
123                 LOGGER.error("Unable to rename {} to {} due to {} - {}", source.toString(), destination.toString(),
124                         ex.getClass().getSimpleName(), ex.getMessage());
125                 try {
126 
127                     Files.copy(sourcePath, destination.toPath());
128                     Files.delete(sourcePath);
129                     return true;
130                 } catch (final Exception iex) {
131                     LOGGER.error("Unable to rename file {} to {} due to {} - {}", source.getAbsolutePath(),
132                             destination.getAbsolutePath(), iex.getClass().getSimpleName(), iex.getMessage());
133                 }
134             }
135         } else {
136             try {
137                 Files.delete(source.toPath());
138             } catch (final Exception ex) {
139                 LOGGER.error("Unable to delete empty file " + source.getAbsolutePath());
140             }
141         }
142 
143         return false;
144     }
145 
146     @Override
147     public String toString() {
148         return FileRenameAction.class.getSimpleName() + '[' + source + " to " + destination //
149                 + ", renameEmptyFiles=" + renameEmptyFiles + ']';
150     }
151 
152 }