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 */
019 package org.apache.commons.compress.archivers;
020
021 import java.io.IOException;
022 import java.io.InputStream;
023 import java.io.OutputStream;
024
025 import org.apache.commons.compress.archivers.ar.ArArchiveInputStream;
026 import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream;
027 import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
028 import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream;
029 import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
030 import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream;
031 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
032 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
033 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
034 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
035
036 /**
037 * <p>Factory to create Archive[In|Out]putStreams from names or the first bytes of
038 * the InputStream. In order add other implementations you should extend
039 * ArchiveStreamFactory and override the appropriate methods (and call their
040 * implementation from super of course).</p>
041 *
042 * Compressing a ZIP-File:
043 *
044 * <pre>
045 * final OutputStream out = new FileOutputStream(output);
046 * ArchiveOutputStream os = new ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP, out);
047 *
048 * os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml"));
049 * IOUtils.copy(new FileInputStream(file1), os);
050 * os.closeArchiveEntry();
051 *
052 * os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml"));
053 * IOUtils.copy(new FileInputStream(file2), os);
054 * os.closeArchiveEntry();
055 * os.close();
056 * </pre>
057 *
058 * Decompressing a ZIP-File:
059 *
060 * <pre>
061 * final InputStream is = new FileInputStream(input);
062 * ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.ZIP, is);
063 * ZipArchiveEntry entry = (ZipArchiveEntry)in.getNextEntry();
064 * OutputStream out = new FileOutputStream(new File(dir, entry.getName()));
065 * IOUtils.copy(in, out);
066 * out.close();
067 * in.close();
068 * </pre>
069 *
070 * @Immutable
071 */
072 public class ArchiveStreamFactory {
073
074 /**
075 * Constant used to identify the AR archive format.
076 * @since Commons Compress 1.1
077 */
078 public static final String AR = "ar";
079 /**
080 * Constant used to identify the CPIO archive format.
081 * @since Commons Compress 1.1
082 */
083 public static final String CPIO = "cpio";
084 /**
085 * Constant used to identify the JAR archive format.
086 * @since Commons Compress 1.1
087 */
088 public static final String JAR = "jar";
089 /**
090 * Constant used to identify the TAR archive format.
091 * @since Commons Compress 1.1
092 */
093 public static final String TAR = "tar";
094 /**
095 * Constant used to identify the ZIP archive format.
096 * @since Commons Compress 1.1
097 */
098 public static final String ZIP = "zip";
099
100 /**
101 * Create an archive input stream from an archiver name and an input stream.
102 *
103 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio"
104 * @param in the input stream
105 * @return the archive input stream
106 * @throws ArchiveException if the archiver name is not known
107 * @throws IllegalArgumentException if the archiver name or stream is null
108 */
109 public ArchiveInputStream createArchiveInputStream(
110 final String archiverName, final InputStream in)
111 throws ArchiveException {
112
113 if (archiverName == null) {
114 throw new IllegalArgumentException("Archivername must not be null.");
115 }
116
117 if (in == null) {
118 throw new IllegalArgumentException("InputStream must not be null.");
119 }
120
121 if (AR.equalsIgnoreCase(archiverName)) {
122 return new ArArchiveInputStream(in);
123 }
124 if (ZIP.equalsIgnoreCase(archiverName)) {
125 return new ZipArchiveInputStream(in);
126 }
127 if (TAR.equalsIgnoreCase(archiverName)) {
128 return new TarArchiveInputStream(in);
129 }
130 if (JAR.equalsIgnoreCase(archiverName)) {
131 return new JarArchiveInputStream(in);
132 }
133 if (CPIO.equalsIgnoreCase(archiverName)) {
134 return new CpioArchiveInputStream(in);
135 }
136
137 throw new ArchiveException("Archiver: " + archiverName + " not found.");
138 }
139
140 /**
141 * Create an archive output stream from an archiver name and an input stream.
142 *
143 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio"
144 * @param out the output stream
145 * @return the archive output stream
146 * @throws ArchiveException if the archiver name is not known
147 * @throws IllegalArgumentException if the archiver name or stream is null
148 */
149 public ArchiveOutputStream createArchiveOutputStream(
150 final String archiverName, final OutputStream out)
151 throws ArchiveException {
152 if (archiverName == null) {
153 throw new IllegalArgumentException("Archivername must not be null.");
154 }
155 if (out == null) {
156 throw new IllegalArgumentException("OutputStream must not be null.");
157 }
158
159 if (AR.equalsIgnoreCase(archiverName)) {
160 return new ArArchiveOutputStream(out);
161 }
162 if (ZIP.equalsIgnoreCase(archiverName)) {
163 return new ZipArchiveOutputStream(out);
164 }
165 if (TAR.equalsIgnoreCase(archiverName)) {
166 return new TarArchiveOutputStream(out);
167 }
168 if (JAR.equalsIgnoreCase(archiverName)) {
169 return new JarArchiveOutputStream(out);
170 }
171 if (CPIO.equalsIgnoreCase(archiverName)) {
172 return new CpioArchiveOutputStream(out);
173 }
174 throw new ArchiveException("Archiver: " + archiverName + " not found.");
175 }
176
177 /**
178 * Create an archive input stream from an input stream, autodetecting
179 * the archive type from the first few bytes of the stream. The InputStream
180 * must support marks, like BufferedInputStream.
181 *
182 * @param in the input stream
183 * @return the archive input stream
184 * @throws ArchiveException if the archiver name is not known
185 * @throws IllegalArgumentException if the stream is null or does not support mark
186 */
187 public ArchiveInputStream createArchiveInputStream(final InputStream in)
188 throws ArchiveException {
189 if (in == null) {
190 throw new IllegalArgumentException("Stream must not be null.");
191 }
192
193 if (!in.markSupported()) {
194 throw new IllegalArgumentException("Mark is not supported.");
195 }
196
197 final byte[] signature = new byte[12];
198 in.mark(signature.length);
199 try {
200 int signatureLength = in.read(signature);
201 in.reset();
202 if (ZipArchiveInputStream.matches(signature, signatureLength)) {
203 return new ZipArchiveInputStream(in);
204 } else if (JarArchiveInputStream.matches(signature, signatureLength)) {
205 return new JarArchiveInputStream(in);
206 } else if (ArArchiveInputStream.matches(signature, signatureLength)) {
207 return new ArArchiveInputStream(in);
208 } else if (CpioArchiveInputStream.matches(signature, signatureLength)) {
209 return new CpioArchiveInputStream(in);
210 }
211 // Tar needs a bigger buffer to check the signature; read the first block
212 final byte[] tarheader = new byte[512];
213 in.mark(tarheader.length);
214 signatureLength = in.read(tarheader);
215 in.reset();
216 if (TarArchiveInputStream.matches(tarheader, signatureLength)) {
217 return new TarArchiveInputStream(in);
218 }
219 } catch (IOException e) {
220 throw new ArchiveException("Could not use reset and mark operations.", e);
221 }
222
223 throw new ArchiveException("No Archiver found for the stream signature");
224 }
225 }