001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.compress.harmony.unpack200; 018 019import java.io.BufferedInputStream; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FilterInputStream; 023import java.io.IOException; 024import java.io.InputStream; 025import java.net.URISyntaxException; 026import java.net.URL; 027import java.nio.file.Files; 028import java.nio.file.Path; 029import java.nio.file.Paths; 030import java.util.jar.JarOutputStream; 031 032import org.apache.commons.compress.harmony.pack200.Pack200Adapter; 033import org.apache.commons.compress.harmony.pack200.Pack200Exception; 034import org.apache.commons.compress.java.util.jar.Pack200.Unpacker; 035import org.apache.commons.io.input.BoundedInputStream; 036import org.apache.commons.lang3.reflect.FieldUtils; 037 038/** 039 * This class provides the binding between the standard Pack200 interface and the internal interface for (un)packing. 040 */ 041public class Pack200UnpackerAdapter extends Pack200Adapter implements Unpacker { 042 043 /** 044 * Creates a new BoundedInputStream bound by the size of the given file. 045 * <p> 046 * The new BoundedInputStream wraps a new {@link BufferedInputStream}. 047 * </p> 048 * 049 * @param file The file. 050 * @return a new BoundedInputStream 051 * @throws IOException if an I/O error occurs 052 */ 053 static BoundedInputStream newBoundedInputStream(final File file) throws IOException { 054 return newBoundedInputStream(file.toPath()); 055 } 056 057 private static BoundedInputStream newBoundedInputStream(final FileInputStream fileInputStream) throws IOException { 058 return newBoundedInputStream(readPath(fileInputStream)); 059 } 060 061 static BoundedInputStream newBoundedInputStream(final InputStream inputStream) throws IOException { 062 if (inputStream instanceof BoundedInputStream) { 063 return (BoundedInputStream) inputStream; 064 } 065 if (inputStream instanceof FilterInputStream) { 066 return newBoundedInputStream(unwrap((FilterInputStream) inputStream)); 067 } 068 if (inputStream instanceof FileInputStream) { 069 return newBoundedInputStream((FileInputStream) inputStream); 070 } 071 // No limit 072 return new BoundedInputStream(inputStream); 073 } 074 075 /** 076 * Creates a new BoundedInputStream bound by the size of the given path. 077 * <p> 078 * The new BoundedInputStream wraps a new {@link BufferedInputStream}. 079 * </p> 080 * 081 * @param path The path. 082 * @return a new BoundedInputStream 083 * @throws IOException if an I/O error occurs 084 */ 085 @SuppressWarnings("resource") 086 static BoundedInputStream newBoundedInputStream(final Path path) throws IOException { 087 return new BoundedInputStream(new BufferedInputStream(Files.newInputStream(path)), Files.size(path)); 088 } 089 090 /** 091 * Creates a new BoundedInputStream bound by the size of the given file. 092 * <p> 093 * The new BoundedInputStream wraps a new {@link BufferedInputStream}. 094 * </p> 095 * 096 * @param first the path string or initial part of the path string. 097 * @param more additional strings to be joined to form the path string. 098 * @return a new BoundedInputStream 099 * @throws IOException if an I/O error occurs 100 */ 101 static BoundedInputStream newBoundedInputStream(final String first, final String... more) throws IOException { 102 return newBoundedInputStream(Paths.get(first, more)); 103 } 104 105 /** 106 * Creates a new BoundedInputStream bound by the size of the given URL to a file. 107 * <p> 108 * The new BoundedInputStream wraps a new {@link BufferedInputStream}. 109 * </p> 110 * 111 * @param path The URL. 112 * @return a new BoundedInputStream 113 * @throws IOException if an I/O error occurs 114 * @throws URISyntaxException 115 */ 116 static BoundedInputStream newBoundedInputStream(final URL url) throws IOException, URISyntaxException { 117 return newBoundedInputStream(Paths.get(url.toURI())); 118 } 119 120 @SuppressWarnings("unchecked") 121 private static <T> T readField(final Object object, final String fieldName) { 122 try { 123 return (T) FieldUtils.readField(object, fieldName, true); 124 } catch (final IllegalAccessException e) { 125 return null; 126 } 127 } 128 129 static String readPath(final FileInputStream fis) { 130 return readField(fis, "path"); 131 } 132 133 /** 134 * Unwraps the given FilterInputStream to return its wrapped InputStream. 135 * 136 * @param filterInputStream The FilterInputStream to unwrap. 137 * @return The wrapped InputStream 138 */ 139 static InputStream unwrap(final FilterInputStream filterInputStream) { 140 return readField(filterInputStream, "in"); 141 } 142 143 /** 144 * Unwraps the given InputStream if it is an FilterInputStream to return its wrapped InputStream. 145 * 146 * @param filterInputStream The FilterInputStream to unwrap. 147 * @return The wrapped InputStream 148 */ 149 @SuppressWarnings("resource") 150 static InputStream unwrap(final InputStream inputStream) { 151 return inputStream instanceof FilterInputStream ? unwrap((FilterInputStream) inputStream) : inputStream; 152 } 153 154 @Override 155 public void unpack(final File file, final JarOutputStream out) throws IOException { 156 if (file == null || out == null) { 157 throw new IllegalArgumentException("Must specify both input and output streams"); 158 } 159 final long size = file.length(); 160 final int bufferSize = size > 0 && size < DEFAULT_BUFFER_SIZE ? (int) size : DEFAULT_BUFFER_SIZE; 161 try (InputStream in = new BufferedInputStream(Files.newInputStream(file.toPath()), bufferSize)) { 162 unpack(in, out); 163 } 164 } 165 166 @Override 167 public void unpack(final InputStream in, final JarOutputStream out) throws IOException { 168 if (in == null || out == null) { 169 throw new IllegalArgumentException("Must specify both input and output streams"); 170 } 171 completed(0); 172 try { 173 new Archive(in, out).unpack(); 174 } catch (final Pack200Exception e) { 175 throw new IOException("Failed to unpack Jar:" + e); 176 } 177 completed(1); 178 } 179}