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 020 package org.apache.commons.compress.compressors.pack200; 021 022 import java.io.File; 023 import java.io.FilterInputStream; 024 import java.io.IOException; 025 import java.io.InputStream; 026 import java.util.Map; 027 import java.util.jar.JarOutputStream; 028 import java.util.jar.Pack200; 029 030 import org.apache.commons.compress.compressors.CompressorInputStream; 031 032 /** 033 * An input stream that decompresses from the Pack200 format to be read 034 * as any other stream. 035 * 036 * <p>The {@link CompressorInputStream#getCount getCount} and {@link 037 * CompressorInputStream#getBytesRead getBytesRead} methods always 038 * return 0.</p> 039 * 040 * @NotThreadSafe 041 * @since 1.3 042 */ 043 public class Pack200CompressorInputStream extends CompressorInputStream { 044 private final InputStream originalInput; 045 private final StreamBridge streamBridge; 046 047 /** 048 * Decompresses the given stream, caching the decompressed data in 049 * memory. 050 * 051 * <p>When reading from a file the File-arg constructor may 052 * provide better performance.</p> 053 */ 054 public Pack200CompressorInputStream(final InputStream in) 055 throws IOException { 056 this(in, Pack200Strategy.IN_MEMORY); 057 } 058 059 /** 060 * Decompresses the given stream using the given strategy to cache 061 * the results. 062 * 063 * <p>When reading from a file the File-arg constructor may 064 * provide better performance.</p> 065 */ 066 public Pack200CompressorInputStream(final InputStream in, 067 final Pack200Strategy mode) 068 throws IOException { 069 this(in, null, mode, null); 070 } 071 072 /** 073 * Decompresses the given stream, caching the decompressed data in 074 * memory and using the given properties. 075 * 076 * <p>When reading from a file the File-arg constructor may 077 * provide better performance.</p> 078 */ 079 public Pack200CompressorInputStream(final InputStream in, 080 final Map<String, String> props) 081 throws IOException { 082 this(in, Pack200Strategy.IN_MEMORY, props); 083 } 084 085 /** 086 * Decompresses the given stream using the given strategy to cache 087 * the results and the given properties. 088 * 089 * <p>When reading from a file the File-arg constructor may 090 * provide better performance.</p> 091 */ 092 public Pack200CompressorInputStream(final InputStream in, 093 final Pack200Strategy mode, 094 final Map<String, String> props) 095 throws IOException { 096 this(in, null, mode, props); 097 } 098 099 /** 100 * Decompresses the given file, caching the decompressed data in 101 * memory. 102 */ 103 public Pack200CompressorInputStream(final File f) throws IOException { 104 this(f, Pack200Strategy.IN_MEMORY); 105 } 106 107 /** 108 * Decompresses the given file using the given strategy to cache 109 * the results. 110 */ 111 public Pack200CompressorInputStream(final File f, final Pack200Strategy mode) 112 throws IOException { 113 this(null, f, mode, null); 114 } 115 116 /** 117 * Decompresses the given file, caching the decompressed data in 118 * memory and using the given properties. 119 */ 120 public Pack200CompressorInputStream(final File f, 121 final Map<String, String> props) 122 throws IOException { 123 this(f, Pack200Strategy.IN_MEMORY, props); 124 } 125 126 /** 127 * Decompresses the given file using the given strategy to cache 128 * the results and the given properties. 129 */ 130 public Pack200CompressorInputStream(final File f, final Pack200Strategy mode, 131 final Map<String, String> props) 132 throws IOException { 133 this(null, f, mode, props); 134 } 135 136 private Pack200CompressorInputStream(final InputStream in, final File f, 137 final Pack200Strategy mode, 138 final Map<String, String> props) 139 throws IOException { 140 originalInput = in; 141 streamBridge = mode.newStreamBridge(); 142 JarOutputStream jarOut = new JarOutputStream(streamBridge); 143 Pack200.Unpacker u = Pack200.newUnpacker(); 144 if (props != null) { 145 u.properties().putAll(props); 146 } 147 if (f == null) { 148 u.unpack(new FilterInputStream(in) { 149 @Override 150 public void close() { 151 // unpack would close this stream but we 152 // want to give the user code more control 153 } 154 }, 155 jarOut); 156 } else { 157 u.unpack(f, jarOut); 158 } 159 jarOut.close(); 160 } 161 162 /** {@inheritDoc} */ 163 @Override 164 public int read() throws IOException { 165 return streamBridge.getInput().read(); 166 } 167 168 /** {@inheritDoc} */ 169 @Override 170 public int read(byte[] b) throws IOException { 171 return streamBridge.getInput().read(b); 172 } 173 174 /** {@inheritDoc} */ 175 @Override 176 public int read(byte[] b, int off, int count) throws IOException { 177 return streamBridge.getInput().read(b, off, count); 178 } 179 180 /** {@inheritDoc} */ 181 @Override 182 public int available() throws IOException { 183 return streamBridge.getInput().available(); 184 } 185 186 /** {@inheritDoc} */ 187 @Override 188 public boolean markSupported() { 189 try { 190 return streamBridge.getInput().markSupported(); 191 } catch (IOException ex) { 192 return false; 193 } 194 } 195 196 /** {@inheritDoc} */ 197 @Override 198 public void mark(int limit) { 199 try { 200 streamBridge.getInput().mark(limit); 201 } catch (IOException ex) { 202 throw new RuntimeException(ex); 203 } 204 } 205 206 /** {@inheritDoc} */ 207 @Override 208 public void reset() throws IOException { 209 streamBridge.getInput().reset(); 210 } 211 212 /** {@inheritDoc} */ 213 @Override 214 public long skip(long count) throws IOException { 215 return streamBridge.getInput().skip(count); 216 } 217 218 @Override 219 public void close() throws IOException { 220 try { 221 streamBridge.stop(); 222 } finally { 223 if (originalInput != null) { 224 originalInput.close(); 225 } 226 } 227 } 228 229 private static final byte[] CAFE_DOOD = new byte[] { 230 (byte) 0xCA, (byte) 0xFE, (byte) 0xD0, (byte) 0x0D 231 }; 232 private static final int SIG_LENGTH = CAFE_DOOD.length; 233 234 /** 235 * Checks if the signature matches what is expected for a pack200 236 * file (0xCAFED00D). 237 * 238 * @param signature 239 * the bytes to check 240 * @param length 241 * the number of bytes to check 242 * @return true, if this stream is a pack200 compressed stream, 243 * false otherwise 244 */ 245 public static boolean matches(byte[] signature, int length) { 246 if (length < SIG_LENGTH) { 247 return false; 248 } 249 250 for (int i = 0; i < SIG_LENGTH; i++) { 251 if (signature[i] != CAFE_DOOD[i]) { 252 return false; 253 } 254 } 255 256 return true; 257 } 258 }