001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.rolling.helper;
015
016import java.util.concurrent.ExecutorService;
017import java.util.concurrent.Future;
018
019import ch.qos.logback.core.rolling.RolloverFailure;
020import ch.qos.logback.core.spi.ContextAwareBase;
021import ch.qos.logback.core.util.DynamicClassLoadingException;
022import ch.qos.logback.core.util.IncompatibleClassException;
023
024import static ch.qos.logback.core.util.OptionHelper.instantiateByClassName;
025
026/**
027 * The <code>Compression</code> class implements ZIP and GZ file
028 * compression/decompression methods.
029 *
030 * @author Ceki G&uuml;lc&uuml;
031 */
032public class Compressor extends ContextAwareBase {
033
034    public static final String COULD_NOT_OBTAIN_COMPRESSION_STRATEGY_MESSAGE = "Could not obtain compression strategy";
035    public static final String XZ_COMPRESSION_STRATEGY_CLASS_NAME = "ch.qos.logback.core.rolling.helper.XZCompressionStrategy";
036
037    final CompressionMode compressionMode;
038
039    static final int BUFFER_SIZE = 8192;
040
041    public Compressor(CompressionMode compressionMode) {
042        this.compressionMode = compressionMode;
043    }
044
045    /**
046     * @param originalFileName
047     * @param compressedFileName
048     * @param innerEntryName       The name of the file within the zip file. Use for
049     *                             ZIP compression.
050     */
051    public void compress(String originalFileName, String compressedFileName, String innerEntryName) {
052        CompressionStrategy compressionStrategy = makeCompressionStrategy(compressionMode);
053        if (compressionStrategy == null) {
054            addWarn(COULD_NOT_OBTAIN_COMPRESSION_STRATEGY_MESSAGE);
055            return;
056        }
057        compressionStrategy.setContext(getContext());
058        compressionStrategy.compress(originalFileName, compressedFileName, innerEntryName);
059    }
060
061    CompressionStrategy makeCompressionStrategy(CompressionMode compressionMode) {
062        switch (compressionMode) {
063        case GZ:
064            return new GZCompressionStrategy();
065        case ZIP:
066            return new ZipCompressionStrategy();
067        case XZ:
068            return dynamicInstantiation(XZ_COMPRESSION_STRATEGY_CLASS_NAME);
069        case NONE:
070            throw new UnsupportedOperationException("compress method called in NONE compression mode");
071        default:
072            return null;
073        }
074    }
075
076    private CompressionStrategy dynamicInstantiation(String className) {
077        try {
078            return (CompressionStrategy) instantiateByClassName(className, CompressionStrategy.class, getContext());
079        } catch (IncompatibleClassException | DynamicClassLoadingException e) {
080            addError("Could not instantiate " + className, e);
081            return null;
082        }
083    }
084
085    static public String computeFileNameStrWithoutCompSuffix(String fileNamePatternStr, CompressionMode compressionMode) {
086        int len = fileNamePatternStr.length();
087        switch (compressionMode) {
088        case GZ:
089            if (fileNamePatternStr.endsWith(".gz"))
090                return fileNamePatternStr.substring(0, len - 3);
091            else
092                return fileNamePatternStr;
093        case ZIP:
094            if (fileNamePatternStr.endsWith(".zip"))
095                return fileNamePatternStr.substring(0, len - 4);
096            else
097                return fileNamePatternStr;
098        case XZ:
099            if (fileNamePatternStr.endsWith(".xz"))
100                return fileNamePatternStr.substring(0, len - 3);
101            else
102                return fileNamePatternStr;
103        case NONE:
104            return fileNamePatternStr;
105        }
106        throw new IllegalStateException("Execution should not reach this point");
107    }
108
109    @Override
110    public String toString() {
111        return this.getClass().getName();
112    }
113
114    public Future<?> asyncCompress(String nameOfFile2Compress, String nameOfCompressedFile, String innerEntryName) throws RolloverFailure {
115        CompressionRunnable runnable = new CompressionRunnable(nameOfFile2Compress, nameOfCompressedFile, innerEntryName);
116        ExecutorService executorService = context.getExecutorService();
117        Future<?> future = executorService.submit(runnable);
118        return future;
119    }
120
121    class CompressionRunnable implements Runnable {
122        final String nameOfFile2Compress;
123        final String nameOfCompressedFile;
124        final String innerEntryName;
125
126        public CompressionRunnable(String nameOfFile2Compress, String nameOfCompressedFile, String innerEntryName) {
127            this.nameOfFile2Compress = nameOfFile2Compress;
128            this.nameOfCompressedFile = nameOfCompressedFile;
129            this.innerEntryName = innerEntryName;
130        }
131
132        public void run() {
133
134            Compressor.this.compress(nameOfFile2Compress, nameOfCompressedFile, innerEntryName);
135        }
136    }
137
138}