001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.compressors; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024 025import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; 026import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; 027import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 028import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; 029import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream; 030import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; 031import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; 032import org.apache.commons.compress.compressors.xz.XZUtils; 033import org.apache.commons.compress.compressors.pack200.Pack200CompressorInputStream; 034import org.apache.commons.compress.compressors.pack200.Pack200CompressorOutputStream; 035import org.apache.commons.compress.compressors.snappy.FramedSnappyCompressorInputStream; 036import org.apache.commons.compress.compressors.snappy.SnappyCompressorInputStream; 037import org.apache.commons.compress.compressors.z.ZCompressorInputStream; 038import org.apache.commons.compress.utils.IOUtils; 039 040/** 041 * <p>Factory to create Compressor[In|Out]putStreams from names. To add other 042 * implementations you should extend CompressorStreamFactory and override the 043 * appropriate methods (and call their implementation from super of course).</p> 044 * 045 * Example (Compressing a file): 046 * 047 * <pre> 048 * final OutputStream out = new FileOutputStream(output); 049 * CompressorOutputStream cos = 050 * new CompressorStreamFactory().createCompressorOutputStream(CompressorStreamFactory.BZIP2, out); 051 * IOUtils.copy(new FileInputStream(input), cos); 052 * cos.close(); 053 * </pre> 054 * 055 * Example (Decompressing a file): 056 * <pre> 057 * final InputStream is = new FileInputStream(input); 058 * CompressorInputStream in = 059 * new CompressorStreamFactory().createCompressorInputStream(CompressorStreamFactory.BZIP2, is); 060 * IOUtils.copy(in, new FileOutputStream(output)); 061 * in.close(); 062 * </pre> 063 * 064 * @Immutable 065 */ 066public class CompressorStreamFactory { 067 068 /** 069 * Constant used to identify the BZIP2 compression algorithm. 070 * @since 1.1 071 */ 072 public static final String BZIP2 = "bzip2"; 073 074 /** 075 * Constant used to identify the GZIP compression algorithm. 076 * @since 1.1 077 */ 078 public static final String GZIP = "gz"; 079 /** 080 * Constant used to identify the PACK200 compression algorithm. 081 * @since 1.3 082 */ 083 public static final String PACK200 = "pack200"; 084 085 /** 086 * Constant used to identify the XZ compression method. 087 * @since 1.4 088 */ 089 public static final String XZ = "xz"; 090 091 /** 092 * Constant used to identify the LZMA compression method. 093 * @since 1.6 094 */ 095 public static final String LZMA = "lzma"; 096 097 /** 098 * Constant used to identify the "framed" Snappy compression method. 099 * @since 1.7 100 */ 101 public static final String SNAPPY_FRAMED = "snappy-framed"; 102 103 /** 104 * Constant used to identify the "raw" Snappy compression method. 105 * @since 1.7 106 */ 107 public static final String SNAPPY_RAW = "snappy-raw"; 108 109 /** 110 * Constant used to identify the traditional Unix compress method. 111 * @since 1.7 112 */ 113 public static final String Z = "z"; 114 115 private boolean decompressConcatenated = false; 116 117 /** 118 * Whether to decompress the full input or only the first stream 119 * in formats supporting multiple concatenated input streams. 120 * 121 * <p>This setting applies to the gzip, bzip2 and xz formats only.</p> 122 * 123 * @param decompressConcatenated 124 * if true, decompress until the end of the 125 * input; if false, stop after the first 126 * stream and leave the input position to point 127 * to the next byte after the stream 128 * @since 1.5 129 */ 130 public void setDecompressConcatenated(boolean decompressConcatenated) { 131 this.decompressConcatenated = decompressConcatenated; 132 } 133 134 /** 135 * Create an compressor input stream from an input stream, autodetecting 136 * the compressor type from the first few bytes of the stream. The InputStream 137 * must support marks, like BufferedInputStream. 138 * 139 * @param in the input stream 140 * @return the compressor input stream 141 * @throws CompressorException if the compressor name is not known 142 * @throws IllegalArgumentException if the stream is null or does not support mark 143 * @since 1.1 144 */ 145 public CompressorInputStream createCompressorInputStream(final InputStream in) 146 throws CompressorException { 147 if (in == null) { 148 throw new IllegalArgumentException("Stream must not be null."); 149 } 150 151 if (!in.markSupported()) { 152 throw new IllegalArgumentException("Mark is not supported."); 153 } 154 155 final byte[] signature = new byte[12]; 156 in.mark(signature.length); 157 try { 158 int signatureLength = IOUtils.readFully(in, signature); 159 in.reset(); 160 161 if (BZip2CompressorInputStream.matches(signature, signatureLength)) { 162 return new BZip2CompressorInputStream(in, decompressConcatenated); 163 } 164 165 if (GzipCompressorInputStream.matches(signature, signatureLength)) { 166 return new GzipCompressorInputStream(in, decompressConcatenated); 167 } 168 169 if (XZUtils.isXZCompressionAvailable() && 170 XZCompressorInputStream.matches(signature, signatureLength)) { 171 return new XZCompressorInputStream(in, decompressConcatenated); 172 } 173 174 if (Pack200CompressorInputStream.matches(signature, signatureLength)) { 175 return new Pack200CompressorInputStream(in); 176 } 177 178 if (FramedSnappyCompressorInputStream.matches(signature, signatureLength)) { 179 return new FramedSnappyCompressorInputStream(in); 180 } 181 182 if (ZCompressorInputStream.matches(signature, signatureLength)) { 183 return new ZCompressorInputStream(in); 184 } 185 186 } catch (IOException e) { 187 throw new CompressorException("Failed to detect Compressor from InputStream.", e); 188 } 189 190 throw new CompressorException("No Compressor found for the stream signature."); 191 } 192 193 /** 194 * Create a compressor input stream from a compressor name and an input stream. 195 * 196 * @param name of the compressor, i.e. "gz", "bzip2", "xz", 197 * "lzma", "snappy-raw", "snappy-framed", "pack200", "z" 198 * @param in the input stream 199 * @return compressor input stream 200 * @throws CompressorException if the compressor name is not known 201 * @throws IllegalArgumentException if the name or input stream is null 202 */ 203 public CompressorInputStream createCompressorInputStream(final String name, 204 final InputStream in) throws CompressorException { 205 if (name == null || in == null) { 206 throw new IllegalArgumentException( 207 "Compressor name and stream must not be null."); 208 } 209 210 try { 211 212 if (GZIP.equalsIgnoreCase(name)) { 213 return new GzipCompressorInputStream(in, decompressConcatenated); 214 } 215 216 if (BZIP2.equalsIgnoreCase(name)) { 217 return new BZip2CompressorInputStream(in, decompressConcatenated); 218 } 219 220 if (XZ.equalsIgnoreCase(name)) { 221 return new XZCompressorInputStream(in, decompressConcatenated); 222 } 223 224 if (LZMA.equalsIgnoreCase(name)) { 225 return new LZMACompressorInputStream(in); 226 } 227 228 if (PACK200.equalsIgnoreCase(name)) { 229 return new Pack200CompressorInputStream(in); 230 } 231 232 if (SNAPPY_RAW.equalsIgnoreCase(name)) { 233 return new SnappyCompressorInputStream(in); 234 } 235 236 if (SNAPPY_FRAMED.equalsIgnoreCase(name)) { 237 return new FramedSnappyCompressorInputStream(in); 238 } 239 240 if (Z.equalsIgnoreCase(name)) { 241 return new ZCompressorInputStream(in); 242 } 243 244 } catch (IOException e) { 245 throw new CompressorException( 246 "Could not create CompressorInputStream.", e); 247 } 248 throw new CompressorException("Compressor: " + name + " not found."); 249 } 250 251 /** 252 * Create an compressor output stream from an compressor name and an input stream. 253 * 254 * @param name the compressor name, i.e. "gz", "bzip2", "xz", or "pack200" 255 * @param out the output stream 256 * @return the compressor output stream 257 * @throws CompressorException if the archiver name is not known 258 * @throws IllegalArgumentException if the archiver name or stream is null 259 */ 260 public CompressorOutputStream createCompressorOutputStream( 261 final String name, final OutputStream out) 262 throws CompressorException { 263 if (name == null || out == null) { 264 throw new IllegalArgumentException( 265 "Compressor name and stream must not be null."); 266 } 267 268 try { 269 270 if (GZIP.equalsIgnoreCase(name)) { 271 return new GzipCompressorOutputStream(out); 272 } 273 274 if (BZIP2.equalsIgnoreCase(name)) { 275 return new BZip2CompressorOutputStream(out); 276 } 277 278 if (XZ.equalsIgnoreCase(name)) { 279 return new XZCompressorOutputStream(out); 280 } 281 282 if (PACK200.equalsIgnoreCase(name)) { 283 return new Pack200CompressorOutputStream(out); 284 } 285 286 } catch (IOException e) { 287 throw new CompressorException( 288 "Could not create CompressorOutputStream", e); 289 } 290 throw new CompressorException("Compressor: " + name + " not found."); 291 } 292}