PackingUtils.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.pack200;

  18. import java.io.BufferedInputStream;
  19. import java.io.ByteArrayOutputStream;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.OutputStream;
  23. import java.util.ArrayList;
  24. import java.util.Enumeration;
  25. import java.util.Iterator;
  26. import java.util.List;
  27. import java.util.jar.JarEntry;
  28. import java.util.jar.JarFile;
  29. import java.util.jar.JarInputStream;
  30. import java.util.jar.JarOutputStream;
  31. import java.util.jar.Manifest;
  32. import java.util.logging.FileHandler;
  33. import java.util.logging.Level;
  34. import java.util.logging.LogManager;
  35. import java.util.logging.LogRecord;
  36. import java.util.logging.Logger;
  37. import java.util.logging.SimpleFormatter;

  38. import org.apache.commons.compress.harmony.pack200.Archive.PackingFile;

  39. public class PackingUtils {

  40.     private static final class PackingLogger extends Logger {

  41.         private boolean verbose;

  42.         protected PackingLogger(final String name, final String resourceBundleName) {
  43.             super(name, resourceBundleName);
  44.         }

  45.         @Override
  46.         public void log(final LogRecord logRecord) {
  47.             if (verbose) {
  48.                 super.log(logRecord);
  49.             }
  50.         }

  51.         private void setVerbose(final boolean isVerbose) {
  52.             verbose = isVerbose;
  53.         }
  54.     }

  55.     private static PackingLogger packingLogger;
  56.     private static FileHandler fileHandler;

  57.     static {
  58.         packingLogger = new PackingLogger("org.harmony.apache.pack200", null);
  59.         LogManager.getLogManager().addLogger(packingLogger);
  60.     }

  61.     public static void config(final PackingOptions options) throws IOException {
  62.         final String logFileName = options != null ? options.getLogFile() : null;
  63.         if (fileHandler != null) {
  64.             fileHandler.close();
  65.         }
  66.         if (logFileName != null) {
  67.             fileHandler = new FileHandler(logFileName, false);
  68.             fileHandler.setFormatter(new SimpleFormatter());
  69.             packingLogger.addHandler(fileHandler);
  70.             packingLogger.setUseParentHandlers(false);
  71.         }
  72.         if (options != null) {
  73.             packingLogger.setVerbose(options.isVerbose());
  74.         }
  75.     }

  76.     /**
  77.      * When effort is 0, the packer copys through the original jar file without compression
  78.      *
  79.      * @param jarFile      the input jar file
  80.      * @param outputStream the jar output stream
  81.      * @throws IOException If an I/O error occurs.
  82.      */
  83.     public static void copyThroughJar(final JarFile jarFile, final OutputStream outputStream) throws IOException {
  84.         try (JarOutputStream jarOutputStream = new JarOutputStream(outputStream)) {
  85.             jarOutputStream.setComment("PACK200");
  86.             final byte[] bytes = new byte[16384];
  87.             final Enumeration<JarEntry> entries = jarFile.entries();
  88.             while (entries.hasMoreElements()) {
  89.                 final JarEntry jarEntry = entries.nextElement();
  90.                 jarOutputStream.putNextEntry(jarEntry);
  91.                 try (InputStream inputStream = jarFile.getInputStream(jarEntry)) {
  92.                     int bytesRead;
  93.                     while ((bytesRead = inputStream.read(bytes)) != -1) {
  94.                         jarOutputStream.write(bytes, 0, bytesRead);
  95.                     }
  96.                     jarOutputStream.closeEntry();
  97.                     log("Packed " + jarEntry.getName());
  98.                 }
  99.             }
  100.             jarFile.close();
  101.         }
  102.     }

  103.     /**
  104.      * When effort is 0, the packer copies through the original jar input stream without compression
  105.      *
  106.      * @param jarInputStream the jar input stream
  107.      * @param outputStream   the jar output stream
  108.      * @throws IOException If an I/O error occurs.
  109.      */
  110.     public static void copyThroughJar(final JarInputStream jarInputStream, final OutputStream outputStream) throws IOException {
  111.         final Manifest manifest = jarInputStream.getManifest();
  112.         try (JarOutputStream jarOutputStream = new JarOutputStream(outputStream, manifest)) {
  113.             jarOutputStream.setComment("PACK200");
  114.             log("Packed " + JarFile.MANIFEST_NAME);

  115.             final byte[] bytes = new byte[16384];
  116.             JarEntry jarEntry;
  117.             int bytesRead;
  118.             while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
  119.                 jarOutputStream.putNextEntry(jarEntry);
  120.                 while ((bytesRead = jarInputStream.read(bytes)) != -1) {
  121.                     jarOutputStream.write(bytes, 0, bytesRead);
  122.                 }
  123.                 log("Packed " + jarEntry.getName());
  124.             }
  125.             jarInputStream.close();
  126.         }
  127.     }

  128.     public static List<PackingFile> getPackingFileListFromJar(final JarFile jarFile, final boolean keepFileOrder) throws IOException {
  129.         final List<PackingFile> packingFileList = new ArrayList<>();
  130.         final Enumeration<JarEntry> jarEntries = jarFile.entries();
  131.         while (jarEntries.hasMoreElements()) {
  132.             final JarEntry jarEntry = jarEntries.nextElement();
  133.             try (InputStream inputStream = jarFile.getInputStream(jarEntry)) {
  134.                 final byte[] bytes = readJarEntry(jarEntry, new BufferedInputStream(inputStream));
  135.                 packingFileList.add(new PackingFile(bytes, jarEntry));
  136.             }
  137.         }

  138.         // check whether it need reorder packing file list
  139.         if (!keepFileOrder) {
  140.             reorderPackingFiles(packingFileList);
  141.         }
  142.         return packingFileList;
  143.     }

  144.     public static List<PackingFile> getPackingFileListFromJar(final JarInputStream jarInputStream, final boolean keepFileOrder) throws IOException {
  145.         final List<PackingFile> packingFileList = new ArrayList<>();

  146.         // add manifest file
  147.         final Manifest manifest = jarInputStream.getManifest();
  148.         if (manifest != null) {
  149.             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
  150.             manifest.write(baos);
  151.             packingFileList.add(new PackingFile(JarFile.MANIFEST_NAME, baos.toByteArray(), 0));
  152.         }

  153.         // add rest of entries in the jar
  154.         JarEntry jarEntry;
  155.         byte[] bytes;
  156.         while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
  157.             bytes = readJarEntry(jarEntry, new BufferedInputStream(jarInputStream));
  158.             packingFileList.add(new PackingFile(bytes, jarEntry));
  159.         }

  160.         // check whether it need reorder packing file list
  161.         if (!keepFileOrder) {
  162.             reorderPackingFiles(packingFileList);
  163.         }
  164.         return packingFileList;
  165.     }

  166.     public static void log(final String message) {
  167.         packingLogger.log(Level.INFO, message);
  168.     }

  169.     private static byte[] readJarEntry(final JarEntry jarEntry, final InputStream inputStream) throws IOException {
  170.         long size = jarEntry.getSize();
  171.         if (size > Integer.MAX_VALUE) {
  172.             // TODO: Should probably allow this
  173.             throw new IllegalArgumentException("Large Class!");
  174.         }
  175.         if (size < 0) {
  176.             size = 0;
  177.         }
  178.         final byte[] bytes = new byte[(int) size];
  179.         if (inputStream.read(bytes) != size) {
  180.             throw new IllegalArgumentException("Error reading from stream");
  181.         }
  182.         return bytes;
  183.     }

  184.     private static void reorderPackingFiles(final List<PackingFile> packingFileList) {
  185.         final Iterator<PackingFile> iterator = packingFileList.iterator();
  186.         while (iterator.hasNext()) {
  187.             final PackingFile packingFile = iterator.next();
  188.             if (packingFile.isDirectory()) {
  189.                 // remove directory entries
  190.                 iterator.remove();
  191.             }
  192.         }

  193.         // Sort files by name, "META-INF/MANIFEST.MF" should be put in the 1st
  194.         // position
  195.         packingFileList.sort((arg0, arg1) -> {
  196.             final String fileName0 = arg0.getName();
  197.             final String fileName1 = arg1.getName();
  198.             if (fileName0.equals(fileName1)) {
  199.                 return 0;
  200.             }
  201.             if (JarFile.MANIFEST_NAME.equals(fileName0)) {
  202.                 return -1;
  203.             }
  204.             if (JarFile.MANIFEST_NAME.equals(fileName1)) {
  205.                 return 1;
  206.             }
  207.             return fileName0.compareTo(fileName1);
  208.         });
  209.     }

  210. }