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("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("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 * Create an archive input stream from an archiver name and an input stream.
076 *
077 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio"
078 * @param in the input stream
079 * @return the archive input stream
080 * @throws ArchiveException if the archiver name is not known
081 * @throws IllegalArgumentException if the archiver name or stream is null
082 */
083 public ArchiveInputStream createArchiveInputStream(
084 final String archiverName, final InputStream in)
085 throws ArchiveException {
086 if (archiverName == null || in == null) {
087 throw new IllegalArgumentException("Archivername must not be null.");
088 }
089
090 if ("ar".equalsIgnoreCase(archiverName)) {
091 return new ArArchiveInputStream(in);
092 } else if ("zip".equalsIgnoreCase(archiverName)) {
093 return new ZipArchiveInputStream(in);
094 } else if ("tar".equalsIgnoreCase(archiverName)) {
095 return new TarArchiveInputStream(in);
096 } else if ("jar".equalsIgnoreCase(archiverName)) {
097 return new JarArchiveInputStream(in);
098 } else if ("cpio".equalsIgnoreCase(archiverName)) {
099 return new CpioArchiveInputStream(in);
100 }
101 throw new ArchiveException("Archiver: " + archiverName + " not found.");
102 }
103
104 /**
105 * Create an archive output stream from an archiver name and an input stream.
106 *
107 * @param archiverName the archive name, i.e. "ar", "zip", "tar", "jar" or "cpio"
108 * @param out the output stream
109 * @return the archive output stream
110 * @throws ArchiveException if the archiver name is not known
111 * @throws IllegalArgumentException if the archiver name or stream is null
112 */
113 public ArchiveOutputStream createArchiveOutputStream(
114 final String archiverName, final OutputStream out)
115 throws ArchiveException {
116 if (archiverName == null || out == null) {
117 throw new IllegalArgumentException(
118 "Archivername and stream must not be null.");
119 }
120
121 if ("ar".equalsIgnoreCase(archiverName)) {
122 return new ArArchiveOutputStream(out);
123 } else if ("zip".equalsIgnoreCase(archiverName)) {
124 return new ZipArchiveOutputStream(out);
125 } else if ("tar".equalsIgnoreCase(archiverName)) {
126 return new TarArchiveOutputStream(out);
127 } else if ("jar".equalsIgnoreCase(archiverName)) {
128 return new JarArchiveOutputStream(out);
129 } else if ("cpio".equalsIgnoreCase(archiverName)) {
130 return new CpioArchiveOutputStream(out);
131 }
132 throw new ArchiveException("Archiver: " + archiverName + " not found.");
133 }
134
135 /**
136 * Create an archive input stream from an input stream, autodetecting
137 * the archive type from the first few bytes of the stream. The InputStream
138 * must support marks, like BufferedInputStream.
139 *
140 * @param in the input stream
141 * @return the archive input stream
142 * @throws ArchiveException if the archiver name is not known
143 * @throws IllegalArgumentException if the stream is null or does not support mark
144 */
145 public ArchiveInputStream createArchiveInputStream(final InputStream in)
146 throws ArchiveException {
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 = in.read(signature);
159 in.reset();
160 if (ZipArchiveInputStream.matches(signature, signatureLength)) {
161 return new ZipArchiveInputStream(in);
162 } else if (JarArchiveInputStream.matches(signature, signatureLength)) {
163 return new JarArchiveInputStream(in);
164 } else if (ArArchiveInputStream.matches(signature, signatureLength)) {
165 return new ArArchiveInputStream(in);
166 } else if (CpioArchiveInputStream.matches(signature, signatureLength)) {
167 return new CpioArchiveInputStream(in);
168 }
169 // Tar needs a bigger buffer to check the signature; read the first block
170 final byte[] tarheader = new byte[512];
171 in.mark(tarheader.length);
172 signatureLength = in.read(tarheader);
173 in.reset();
174 if (TarArchiveInputStream.matches(tarheader, signatureLength)) {
175 return new TarArchiveInputStream(in);
176 }
177 } catch (IOException e) {
178 throw new ArchiveException("Could not use reset and mark operations.", e);
179 }
180
181 throw new ArchiveException("No Archiver found for the stream signature");
182 }
183 }