/* * Copyright 2017-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.jvm.java; import com.facebook.buck.zip.CustomZipEntry; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.stream.Stream; import java.util.zip.ZipEntry; import javax.annotation.Nullable; /** Provides all entries of a given zip or jar file, so they can be added to another jar. */ class ZipFileJarEntryContainer implements JarEntryContainer { private final String owner; private final Path jarFilePath; @Nullable private JarFile jar; public ZipFileJarEntryContainer(Path jarFilePath) { this.jarFilePath = jarFilePath; this.owner = jarFilePath.toString(); } @Nullable @Override public Manifest getManifest() throws IOException { return getJarFile().getManifest(); } @Override public Stream<JarEntrySupplier> stream() throws IOException { return getJarFile() .stream() .map( entry -> new JarEntrySupplier( makeCustomEntry(entry), owner, () -> getJarFile().getInputStream(entry))); } @Override public void close() throws IOException { getJarFile().close(); } private JarFile getJarFile() throws IOException { if (jar == null) { try { File jarFile = jarFilePath.toFile(); jar = new JarFile(jarFile); } catch (IOException e) { throw new IOException("Failed to process ZipFile " + owner, e); } } return jar; } private static CustomZipEntry makeCustomEntry(ZipEntry entry) { CustomZipEntry wrappedEntry = new CustomZipEntry(entry); // For deflated entries, the act of re-"putting" this entry means we're re-compressing // the data that we've just uncompressed. Due to various environmental issues (e.g. a // newer version of zlib, changed compression settings), we may end up with a different // compressed size. This causes an issue in java's `java.util.zip.ZipOutputStream` // implementation, as it only updates the compressed size field if one of `crc`, // `compressedSize`, or `size` is -1. When we copy the entry as-is, none of these are // -1, and we may end up with an incorrect compressed size, in which case, we'll get an // exception. So, for deflated entries, reset the compressed size to -1 (as the // ZipEntry(String) would). // See https://github.com/spearce/buck/commit/8338c1c3d4a546f577eed0c9941d9f1c2ba0a1b7. if (wrappedEntry.getMethod() == ZipEntry.DEFLATED) { wrappedEntry.setCompressedSize(-1); } return wrappedEntry; } }