Pack200UnpackerAdapter.java

  1. /*
  2.  *  Licensed to the Apache Software Foundation (ASF) under one or more
  3.  *  contributor license agreements.  See the NOTICE file distributed with
  4.  *  this work for additional information regarding copyright ownership.
  5.  *  The ASF licenses this file to You under the Apache License, Version 2.0
  6.  *  (the "License"); you may not use this file except in compliance with
  7.  *  the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.commons.compress.harmony.unpack200;

  18. import java.io.BufferedInputStream;
  19. import java.io.File;
  20. import java.io.FileInputStream;
  21. import java.io.FilterInputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.net.URISyntaxException;
  25. import java.net.URL;
  26. import java.nio.file.Files;
  27. import java.nio.file.Path;
  28. import java.nio.file.Paths;
  29. import java.util.jar.JarOutputStream;

  30. import org.apache.commons.compress.harmony.pack200.Pack200Adapter;
  31. import org.apache.commons.compress.harmony.pack200.Pack200Exception;
  32. import org.apache.commons.compress.java.util.jar.Pack200.Unpacker;
  33. import org.apache.commons.io.input.BoundedInputStream;
  34. import org.apache.commons.io.input.CloseShieldInputStream;
  35. import org.apache.commons.lang3.reflect.FieldUtils;

  36. /**
  37.  * This class provides the binding between the standard Pack200 interface and the internal interface for (un)packing.
  38.  */
  39. public class Pack200UnpackerAdapter extends Pack200Adapter implements Unpacker {

  40.     /**
  41.      * Creates a new BoundedInputStream bound by the size of the given file.
  42.      * <p>
  43.      * The new BoundedInputStream wraps a new {@link BufferedInputStream}.
  44.      * </p>
  45.      *
  46.      * @param file The file.
  47.      * @return a new BoundedInputStream
  48.      * @throws IOException if an I/O error occurs
  49.      */
  50.     static BoundedInputStream newBoundedInputStream(final File file) throws IOException {
  51.         return newBoundedInputStream(file.toPath());
  52.     }

  53.     private static BoundedInputStream newBoundedInputStream(final FileInputStream fileInputStream) throws IOException {
  54.         return newBoundedInputStream(readPathString(fileInputStream));
  55.     }

  56.     @SuppressWarnings("resource") // Caller closes.
  57.     static BoundedInputStream newBoundedInputStream(final InputStream inputStream) throws IOException {
  58.         if (inputStream instanceof BoundedInputStream) {
  59.             // Already bound.
  60.             return (BoundedInputStream) inputStream;
  61.         }
  62.         if (inputStream instanceof CloseShieldInputStream) {
  63.             // Don't unwrap to keep close shield.
  64.             return newBoundedInputStream(BoundedInputStream.builder().setInputStream(inputStream).get());
  65.         }
  66.         if (inputStream instanceof FilterInputStream) {
  67.             return newBoundedInputStream(unwrap((FilterInputStream) inputStream));
  68.         }
  69.         if (inputStream instanceof FileInputStream) {
  70.             return newBoundedInputStream((FileInputStream) inputStream);
  71.         }
  72.         // No limit
  73.         return newBoundedInputStream(BoundedInputStream.builder().setInputStream(inputStream).get());
  74.     }

  75.     /**
  76.      * Creates a new BoundedInputStream bound by the size of the given path.
  77.      * <p>
  78.      * The new BoundedInputStream wraps a new {@link BufferedInputStream}.
  79.      * </p>
  80.      *
  81.      * @param path The path.
  82.      * @return a new BoundedInputStream
  83.      * @throws IOException if an I/O error occurs
  84.      */
  85.     @SuppressWarnings("resource") // Caller closes.
  86.     static BoundedInputStream newBoundedInputStream(final Path path) throws IOException {
  87.         // @formatter:off
  88.         return BoundedInputStream.builder()
  89.                 .setInputStream(new BufferedInputStream(Files.newInputStream(path)))
  90.                 .setMaxCount(Files.size(path))
  91.                 .setPropagateClose(false)
  92.                 .get();
  93.         // @formatter:on
  94.     }

  95.     /**
  96.      * Creates a new BoundedInputStream bound by the size of the given file.
  97.      * <p>
  98.      * The new BoundedInputStream wraps a new {@link BufferedInputStream}.
  99.      * </p>
  100.      *
  101.      * @param first the path string or initial part of the path string.
  102.      * @param more  additional strings to be joined to form the path string.
  103.      * @return a new BoundedInputStream
  104.      * @throws IOException if an I/O error occurs
  105.      */
  106.     static BoundedInputStream newBoundedInputStream(final String first, final String... more) throws IOException {
  107.         return newBoundedInputStream(Paths.get(first, more));
  108.     }

  109.     /**
  110.      * Creates a new BoundedInputStream bound by the size of the given URL to a file.
  111.      * <p>
  112.      * The new BoundedInputStream wraps a new {@link BufferedInputStream}.
  113.      * </p>
  114.      *
  115.      * @param url The URL.
  116.      * @return a new BoundedInputStream
  117.      * @throws IOException        if an I/O error occurs
  118.      * @throws URISyntaxException
  119.      */
  120.     static BoundedInputStream newBoundedInputStream(final URL url) throws IOException, URISyntaxException {
  121.         return newBoundedInputStream(Paths.get(url.toURI()));
  122.     }

  123.     @SuppressWarnings("unchecked")
  124.     private static <T> T readField(final Object object, final String fieldName) {
  125.         try {
  126.             return (T) FieldUtils.readField(object, fieldName, true);
  127.         } catch (final IllegalAccessException e) {
  128.             return null;
  129.         }
  130.     }

  131.     static String readPathString(final FileInputStream fis) {
  132.         return readField(fis, "path");
  133.     }

  134.     /**
  135.      * Unwraps the given FilterInputStream to return its wrapped InputStream.
  136.      *
  137.      * @param filterInputStream The FilterInputStream to unwrap.
  138.      * @return The wrapped InputStream
  139.      */
  140.     static InputStream unwrap(final FilterInputStream filterInputStream) {
  141.         return readField(filterInputStream, "in");
  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.     @Override
  154.     public void unpack(final File file, final JarOutputStream out) throws IOException {
  155.         if (file == null || out == null) {
  156.             throw new IllegalArgumentException("Must specify both input and output streams");
  157.         }
  158.         final long size = file.length();
  159.         final int bufferSize = size > 0 && size < DEFAULT_BUFFER_SIZE ? (int) size : DEFAULT_BUFFER_SIZE;
  160.         try (InputStream in = new BufferedInputStream(Files.newInputStream(file.toPath()), bufferSize)) {
  161.             unpack(in, out);
  162.         }
  163.     }

  164.     @Override
  165.     public void unpack(final InputStream in, final JarOutputStream out) throws IOException {
  166.         if (in == null || out == null) {
  167.             throw new IllegalArgumentException("Must specify both input and output streams");
  168.         }
  169.         completed(0);
  170.         try {
  171.             new Archive(in, out).unpack();
  172.         } catch (final Pack200Exception e) {
  173.             throw new IOException("Failed to unpack Jar:" + e);
  174.         }
  175.         completed(1);
  176.     }
  177. }