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 */ 017package org.apache.logging.log4j.core.appender.rolling.action; 018 019import java.io.BufferedOutputStream; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileOutputStream; 023import java.io.IOException; 024import java.io.OutputStream; 025import java.util.Objects; 026import java.util.zip.Deflater; 027import java.util.zip.GZIPOutputStream; 028 029/** 030 * Compresses a file using GZ compression. 031 */ 032public final class GzCompressAction extends AbstractAction { 033 034 private static final int BUF_SIZE = 8192; 035 036 /** 037 * Source file. 038 */ 039 private final File source; 040 041 /** 042 * Destination file. 043 */ 044 private final File destination; 045 046 /** 047 * If true, attempt to delete file on completion. 048 */ 049 private final boolean deleteSource; 050 051 /** 052 * GZIP compression level to use. 053 * 054 * @see Deflater#setLevel(int) 055 */ 056 private final int compressionLevel; 057 058 /** 059 * Create new instance of GzCompressAction. 060 * 061 * @param source file to compress, may not be null. 062 * @param destination compressed file, may not be null. 063 * @param deleteSource if true, attempt to delete file on completion. Failure to delete 064 * does not cause an exception to be thrown or affect return value. 065 * @param compressionLevel 066 * Gzip deflater compression level. 067 */ 068 public GzCompressAction( 069 final File source, final File destination, final boolean deleteSource, final int compressionLevel) { 070 Objects.requireNonNull(source, "source"); 071 Objects.requireNonNull(destination, "destination"); 072 073 this.source = source; 074 this.destination = destination; 075 this.deleteSource = deleteSource; 076 this.compressionLevel = compressionLevel; 077 } 078 079 /** 080 * Prefer the constructor with compression level. 081 * 082 * @deprecated Prefer {@link GzCompressAction#GzCompressAction(File, File, boolean, int)}. 083 */ 084 @Deprecated 085 public GzCompressAction(final File source, final File destination, final boolean deleteSource) { 086 this(source, destination, deleteSource, Deflater.DEFAULT_COMPRESSION); 087 } 088 089 /** 090 * Compress. 091 * 092 * @return true if successfully compressed. 093 * @throws IOException on IO exception. 094 */ 095 @Override 096 public boolean execute() throws IOException { 097 return execute(source, destination, deleteSource, compressionLevel); 098 } 099 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}