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.BufferedOutputStream;
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.util.Objects;
26  import java.util.zip.Deflater;
27  import java.util.zip.GZIPOutputStream;
28  
29  /**
30   * Compresses a file using GZ compression.
31   */
32  public final class GzCompressAction extends AbstractAction {
33  
34      private static final int BUF_SIZE = 8192;
35  
36      /**
37       * Source file.
38       */
39      private final File source;
40  
41      /**
42       * Destination file.
43       */
44      private final File destination;
45  
46      /**
47       * If true, attempt to delete file on completion.
48       */
49      private final boolean deleteSource;
50  
51      /**
52       * GZIP compression level to use.
53       *
54       * @see Deflater#setLevel(int)
55       */
56      private final int compressionLevel;
57  
58      /**
59       * Create new instance of GzCompressAction.
60       *
61       * @param source       file to compress, may not be null.
62       * @param destination  compressed file, may not be null.
63       * @param deleteSource if true, attempt to delete file on completion.  Failure to delete
64       *                     does not cause an exception to be thrown or affect return value.
65       * @param compressionLevel
66       *                     Gzip deflater compression level.
67       */
68      public GzCompressAction(
69              final File source, final File destination, final boolean deleteSource, final int compressionLevel) {
70          Objects.requireNonNull(source, "source");
71          Objects.requireNonNull(destination, "destination");
72  
73          this.source = source;
74          this.destination = destination;
75          this.deleteSource = deleteSource;
76          this.compressionLevel = compressionLevel;
77      }
78  
79      /**
80       * Prefer the constructor with compression level.
81       *
82       * @deprecated Prefer {@link GzCompressAction#GzCompressAction(File, File, boolean, int)}.
83       */
84      @Deprecated
85      public GzCompressAction(final File source, final File destination, final boolean deleteSource) {
86          this(source, destination, deleteSource, Deflater.DEFAULT_COMPRESSION);
87      }
88  
89      /**
90       * Compress.
91       *
92       * @return true if successfully compressed.
93       * @throws IOException on IO exception.
94       */
95      @Override
96      public boolean execute() throws IOException {
97          return execute(source, destination, deleteSource, compressionLevel);
98      }
99  
100     /**
101      * Compress a file.
102      *
103      * @param source       file to compress, may not be null.
104      * @param destination  compressed file, may not be null.
105      * @param deleteSource if true, attempt to delete file on completion.  Failure to delete
106      *                     does not cause an exception to be thrown or affect return value.
107      * @return true if source file compressed.
108      * @throws IOException on IO exception.
109      * @deprecated In favor of {@link #execute(File, File, boolean, int)}.
110      */
111     @Deprecated
112     public static boolean execute(final File source, final File destination, final boolean deleteSource)
113             throws IOException {
114         return execute(source, destination, deleteSource, Deflater.DEFAULT_COMPRESSION);
115     }
116 
117     /**
118      * Compress a file.
119      *
120      * @param source       file to compress, may not be null.
121      * @param destination  compressed file, may not be null.
122      * @param deleteSource if true, attempt to delete file on completion.  Failure to delete
123      *                     does not cause an exception to be thrown or affect return value.
124      * @param compressionLevel
125      *                     Gzip deflater compression level.
126      * @return true if source file compressed.
127      * @throws IOException on IO exception.
128      */
129     public static boolean execute(
130             final File source,
131             final File destination,
132             final boolean deleteSource,
133             final int compressionLevel) throws IOException {
134         if (source.exists()) {
135             try (final FileInputStream fis = new FileInputStream(source);
136                  final OutputStream fos = new FileOutputStream(destination);
137                  final OutputStream gzipOut = new ConfigurableLevelGZIPOutputStream(
138                          fos, BUF_SIZE, compressionLevel);
139                  // Reduce native invocations by buffering data into GZIPOutputStream
140                  final OutputStream os = new BufferedOutputStream(gzipOut, BUF_SIZE)) {
141                 final byte[] inbuf = new byte[BUF_SIZE];
142                 int n;
143 
144                 while ((n = fis.read(inbuf)) != -1) {
145                     os.write(inbuf, 0, n);
146                 }
147             }
148 
149             if (deleteSource && !source.delete()) {
150                 LOGGER.warn("Unable to delete {}.", source);
151             }
152 
153             return true;
154         }
155 
156         return false;
157     }
158 
159     private static final class ConfigurableLevelGZIPOutputStream extends GZIPOutputStream {
160 
161         ConfigurableLevelGZIPOutputStream(OutputStream out, int bufSize, int level) throws IOException {
162             super(out, bufSize);
163             def.setLevel(level);
164         }
165     }
166 
167     /**
168      * Capture exception.
169      *
170      * @param ex exception.
171      */
172     @Override
173     protected void reportException(final Exception ex) {
174         LOGGER.warn("Exception during compression of '" + source.toString() + "'.", ex);
175     }
176 
177     @Override
178     public String toString() {
179         return GzCompressAction.class.getSimpleName() + '[' + source + " to " + destination
180                 + ", deleteSource=" + deleteSource + ']';
181     }
182 
183     public File getSource() {
184         return source;
185     }
186 
187     public File getDestination() {
188         return destination;
189     }
190 
191     public boolean isDeleteSource() {
192         return deleteSource;
193     }
194 }