| /* | |
| * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| import org.testng.annotations.AfterTest; | |
| import org.testng.annotations.BeforeTest; | |
| import org.testng.annotations.DataProvider; | |
| import org.testng.annotations.Test; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.FileInputStream; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.net.JarURLConnection; | |
| import java.net.URL; | |
| import java.nio.charset.StandardCharsets; | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.nio.file.Paths; | |
| import java.util.Arrays; | |
| import java.util.Formatter; | |
| import java.util.List; | |
| import java.util.jar.JarEntry; | |
| import java.util.jar.JarFile; | |
| import java.util.jar.JarInputStream; | |
| import java.util.jar.JarOutputStream; | |
| import java.util.stream.Collectors; | |
| import java.util.zip.ZipEntry; | |
| import java.util.zip.ZipFile; | |
| import java.util.zip.ZipInputStream; | |
| import java.util.zip.ZipOutputStream; | |
| import static org.testng.Assert.*; | |
| /** | |
| * @test | |
| * @bug 8276123 | |
| * @summary ZipFile::getEntry will not return a file entry when there is a | |
| * directory entry of the same name within a Zip File | |
| * @run testng/othervm ZipFileDuplicateEntryTest | |
| */ | |
| public class ZipFileDuplicateEntryTest { | |
| /** | |
| * Name to use for creating Zip entries | |
| */ | |
| private static final String ENTRY_NAME = "entry"; | |
| /** | |
| * Zip and Jar files to be created | |
| */ | |
| private static final Path ZIP_FILE = Paths.get("fileDirEntry.zip"); | |
| private static final Path ZIP_FILE2 = Paths.get("OnlyDirEntry.zip"); | |
| private static final Path DUPLICATE_FILE_ENTRY_FILE = Paths.get("DupFIleEntry.zip"); | |
| private static final Path TEST_JAR = Paths.get("fileDirEntry.jar"); | |
| /** | |
| * Directory entry added to the Zip File. | |
| */ | |
| private static final Entry DIR_ENTRY = | |
| Entry.of(ENTRY_NAME + "/", ZipEntry.DEFLATED, | |
| "I am a Directory"); | |
| /** | |
| * File entry added to the Zip File. | |
| */ | |
| private static final Entry FILE_ENTRY = | |
| Entry.of(ENTRY_NAME, ZipEntry.DEFLATED, "I am a File"); | |
| /** | |
| * Duplicate File entry added to the Zip file. This is the 2nd entry added | |
| * to the Zip file and is expected to be returned. | |
| */ | |
| private static final Entry DUPLICATE_FILE_ENTRY = | |
| Entry.of(ENTRY_NAME, ZipEntry.DEFLATED, "Yet another File"); | |
| /** | |
| * Entries expected to be returned via ZipFile::stream | |
| */ | |
| private static final List<String> EXPECTED_ENTRIES = | |
| Arrays.asList(FILE_ENTRY.name, DIR_ENTRY.name); | |
| /** | |
| * Max buffer size for readAllBytes method which can be used when | |
| * InputStream::readAllBytes is not available | |
| */ | |
| private static final int MAX_BUFFER_SIZE = 1024; | |
| /** | |
| * Flag to enable test output | |
| */ | |
| private static final boolean DEBUG = false; | |
| /** | |
| * Array representing a Jar File with the entries: | |
| * Name: entry, contents: "I am a File" | |
| * Name: entry, contents: "Yet another File" | |
| * See createByteArray() | |
| */ | |
| private static final byte[] DUPLICATE_ENTRY_JAR_BYTES = { | |
| (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x8, | |
| (byte) 0x8, (byte) 0x0, (byte) 0x60, (byte) 0x59, (byte) 0x55, (byte) 0x53, (byte) 0x8e, (byte) 0x39, | |
| (byte) 0x14, (byte) 0x49, (byte) 0xd, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xb, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x65, (byte) 0x6e, | |
| (byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x1, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0xb, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xd, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xf3, | |
| (byte) 0x54, (byte) 0x48, (byte) 0xcc, (byte) 0x55, (byte) 0x48, (byte) 0x54, (byte) 0x70, (byte) 0xcb, | |
| (byte) 0xcc, (byte) 0x49, (byte) 0x5, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, | |
| (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x60, (byte) 0x59, | |
| (byte) 0x55, (byte) 0x53, (byte) 0xe1, (byte) 0x4c, (byte) 0x29, (byte) 0xa4, (byte) 0x12, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5, (byte) 0x0, | |
| (byte) 0x14, (byte) 0x0, (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x1, | |
| (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x10, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x12, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x8b, (byte) 0x4c, (byte) 0x2d, (byte) 0x51, (byte) 0x48, | |
| (byte) 0xcc, (byte) 0xcb, (byte) 0x2f, (byte) 0xc9, (byte) 0x48, (byte) 0x2d, (byte) 0x52, (byte) 0x70, | |
| (byte) 0xcb, (byte) 0xcc, (byte) 0x49, (byte) 0x5, (byte) 0x0, (byte) 0x50, (byte) 0x4b, (byte) 0x1, | |
| (byte) 0x2, (byte) 0x14, (byte) 0x0, (byte) 0x14, (byte) 0x0, (byte) 0x0, (byte) 0x8, (byte) 0x8, | |
| (byte) 0x0, (byte) 0x60, (byte) 0x59, (byte) 0x55, (byte) 0x53, (byte) 0x8e, (byte) 0x39, (byte) 0x14, | |
| (byte) 0x49, (byte) 0xd, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xb, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x65, (byte) 0x6e, (byte) 0x74, (byte) 0x72, (byte) 0x79, | |
| (byte) 0x50, (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x14, (byte) 0x0, (byte) 0x14, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x8, (byte) 0x8, (byte) 0x0, (byte) 0x60, (byte) 0x59, (byte) 0x55, (byte) 0x53, | |
| (byte) 0xe1, (byte) 0x4c, (byte) 0x29, (byte) 0xa4, (byte) 0x12, (byte) 0x0, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x10, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x5, (byte) 0x0, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x44, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x65, (byte) 0x6e, | |
| (byte) 0x74, (byte) 0x72, (byte) 0x79, (byte) 0x50, (byte) 0x4b, (byte) 0x5, (byte) 0x6, (byte) 0x0, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2, (byte) 0x0, (byte) 0x2, (byte) 0x0, (byte) 0x66, | |
| (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x8d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, | |
| (byte) 0x0, | |
| }; | |
| /** | |
| * Create Zip files used by the tests. | |
| * | |
| * @throws IOException If an error occurs | |
| */ | |
| @BeforeTest | |
| public static void setup() throws IOException { | |
| /** | |
| * Zip contains two entries named "entry" and "entry/" | |
| */ | |
| Files.deleteIfExists(ZIP_FILE); | |
| try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(ZIP_FILE))) { | |
| zos.putNextEntry(new ZipEntry(FILE_ENTRY.name)); | |
| zos.write(FILE_ENTRY.bytes); | |
| zos.closeEntry(); | |
| zos.putNextEntry(new ZipEntry(DIR_ENTRY.name)); | |
| zos.write(DIR_ENTRY.bytes); | |
| zos.closeEntry(); | |
| } | |
| /** | |
| * Jar contains two entries named "entry" and "entry/" | |
| */ | |
| Files.deleteIfExists(TEST_JAR); | |
| try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(TEST_JAR))) { | |
| jos.putNextEntry(new JarEntry(FILE_ENTRY.name)); | |
| jos.write(FILE_ENTRY.bytes); | |
| jos.closeEntry(); | |
| jos.putNextEntry(new JarEntry(DIR_ENTRY.name)); | |
| jos.write(DIR_ENTRY.bytes); | |
| jos.closeEntry(); | |
| } | |
| /** | |
| * Zip contains the entry "entry/" | |
| */ | |
| Files.deleteIfExists(ZIP_FILE2); | |
| try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(ZIP_FILE2))) { | |
| zos.putNextEntry(new ZipEntry(DIR_ENTRY.name)); | |
| zos.write(DIR_ENTRY.bytes); | |
| zos.closeEntry(); | |
| } | |
| /** | |
| * Create a Jar that contains two entries named "entry" | |
| */ | |
| Files.deleteIfExists(DUPLICATE_FILE_ENTRY_FILE); | |
| Files.write(DUPLICATE_FILE_ENTRY_FILE, DUPLICATE_ENTRY_JAR_BYTES); | |
| } | |
| /** | |
| * Clean up after the test run | |
| * | |
| * @throws IOException If an error occurs | |
| */ | |
| @AfterTest | |
| public static void cleanup() throws IOException { | |
| Files.deleteIfExists(ZIP_FILE); | |
| Files.deleteIfExists(ZIP_FILE2); | |
| Files.deleteIfExists(DUPLICATE_FILE_ENTRY_FILE); | |
| Files.deleteIfExists(TEST_JAR); | |
| } | |
| /** | |
| * DataProvider used to specify the Zip entries to use | |
| * | |
| * @return The Entry to use within the test | |
| */ | |
| @DataProvider | |
| public Object[][] entries() { | |
| return new Object[][]{ | |
| {FILE_ENTRY}, | |
| {DIR_ENTRY} | |
| }; | |
| } | |
| /** | |
| * Test whether ZipFile::getEntry can find a directory entry within a Zip | |
| * file specifying "name" vs "name/" | |
| * | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test | |
| public void readDirWithoutSlash() throws IOException { | |
| System.out.printf("%n%n**** readDirWithoutSlash ***%n"); | |
| try (ZipFile zip = new ZipFile(ZIP_FILE2.toString())) { | |
| ZipEntry ze = zip.getEntry(ENTRY_NAME); | |
| if (DEBUG) { | |
| System.out.printf(" Entry:%s, found:%s%n", ENTRY_NAME, ze != null); | |
| } | |
| assertNotNull(ze); | |
| assertTrue(ze.isDirectory()); | |
| try (InputStream in = zip.getInputStream(ze)) { | |
| byte[] bytes = in.readAllBytes(); | |
| if (DEBUG) { | |
| System.out.printf("name: %s, isDirectory: %s, payload= %s%n", | |
| ze.getName(), ze.isDirectory(), new String(bytes)); | |
| } | |
| assertEquals(bytes, DIR_ENTRY.bytes, | |
| String.format("Expected payload: %s", | |
| new String(DIR_ENTRY.bytes))); | |
| } | |
| } | |
| } | |
| /** | |
| * Validate that ZipFile::getEntry will return the correct entry when a file | |
| * and directory have the same name | |
| * | |
| * @param entry The entry to search for | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test(dataProvider = "entries") | |
| public void testSameFileDirEntryName(Entry entry) throws IOException { | |
| System.out.printf("%n%n**** testSameFileDirEntryName ***%n"); | |
| try (ZipFile zip = new ZipFile(ZIP_FILE.toString())) { | |
| ZipEntry ze = zip.getEntry(entry.name); | |
| if (DEBUG) { | |
| System.out.printf(" Entry:%s, found:%s%n", entry.name, ze != null); | |
| } | |
| assertNotNull(ze); | |
| try (InputStream in = zip.getInputStream(ze)) { | |
| byte[] bytes = in.readAllBytes(); | |
| if (DEBUG) { | |
| System.out.printf("name: %s, isDirectory: %s, payload= %s%n", | |
| ze.getName(), ze.isDirectory(), new String(bytes)); | |
| } | |
| assertEquals(entry.bytes, bytes, | |
| String.format("Expected payload: %s", new String(entry.bytes))); | |
| } | |
| } | |
| } | |
| /** | |
| * Validate that ZipFile::getEntry will return the correct entry, which | |
| * is the second entry, when there are duplicate entries within the Zip file. | |
| * | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test | |
| public void DupFileEntryTest() throws IOException { | |
| System.out.printf("%n%n**** DupFileEntryTest ***%n"); | |
| try (ZipFile zip = | |
| new ZipFile(DUPLICATE_FILE_ENTRY_FILE.toString())) { | |
| ZipEntry ze = zip.getEntry(ENTRY_NAME); | |
| if (DEBUG) { | |
| System.out.printf(" Entry:%s, found:%s%n", ENTRY_NAME, ze != null); | |
| } | |
| assertNotNull(ze); | |
| try (InputStream in = zip.getInputStream(ze)) { | |
| byte[] bytes = in.readAllBytes(); | |
| if (DEBUG) { | |
| System.out.printf("name: %s, isDirectory: %s, payload= %s%n", | |
| ze.getName(), ze.isDirectory(), new String(bytes)); | |
| } | |
| assertEquals(bytes, DUPLICATE_FILE_ENTRY.bytes, | |
| String.format("Expected payload: %s", new String(DUPLICATE_FILE_ENTRY.bytes))); | |
| } | |
| } | |
| } | |
| /** | |
| * Verify that ZipInputStream can be used to read all Zip entries including | |
| * a file and directory entry with the same name | |
| * | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test | |
| public void ZipInputStreamTest() throws IOException { | |
| System.out.printf("%n%n**** ZipInputStreamTest ***%n"); | |
| try (ZipInputStream zis = new ZipInputStream( | |
| new FileInputStream(ZIP_FILE.toFile()))) { | |
| ZipEntry zipEntry = zis.getNextEntry(); | |
| assertNotNull(zipEntry); | |
| while (zipEntry != null) { | |
| Entry e; | |
| if (zipEntry.getName().equals(FILE_ENTRY.name)) { | |
| e = FILE_ENTRY; | |
| } else if (zipEntry.getName().equals(DIR_ENTRY.name)) { | |
| e = DIR_ENTRY; | |
| } else { | |
| throw new RuntimeException( | |
| String.format("Invalid Zip entry: %s", zipEntry.getName())); | |
| } | |
| assertEquals(zipEntry.getMethod(), e.method); | |
| assertEquals(zis.readAllBytes(), e.bytes, | |
| String.format("Expected payload: %s", new String(e.bytes))); | |
| zipEntry = zis.getNextEntry(); | |
| } | |
| } | |
| } | |
| /** | |
| * Verify that ZipFile::stream returns all Zip entries including | |
| * a file and directory entry with the same name | |
| * | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test | |
| public void ZipFileStreamTest() throws IOException { | |
| System.out.printf("%n%n**** ZipFileStreamTest ***%n"); | |
| try (ZipFile zf = new ZipFile(ZIP_FILE.toFile())) { | |
| List<? extends ZipEntry> entries = zf.stream().collect(Collectors.toList()); | |
| assertEquals(EXPECTED_ENTRIES.size(), entries.size()); | |
| for (ZipEntry e : entries) { | |
| assertTrue(EXPECTED_ENTRIES.contains(e.getName())); | |
| } | |
| } | |
| } | |
| /** | |
| * Verify that JarFile can be used to read all the entries including | |
| * a file and directory entry with the same name | |
| * | |
| * @param entry The entry to validate | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test(dataProvider = "entries") | |
| public static void JarFileInputStreamTest(Entry entry) throws IOException { | |
| System.out.printf("%n%n**** JarFileInputStreamTest ***%n"); | |
| try (JarFile jarFile = new JarFile(TEST_JAR.toFile())) { | |
| JarEntry je = jarFile.getJarEntry(entry.name); | |
| assertNotNull(je); | |
| if (DEBUG) { | |
| System.out.printf("Entry Name: %s, method: %s, Expected Method: %s%n", | |
| entry.name, je.getMethod(), entry.method); | |
| } | |
| assertEquals(entry.method, je.getMethod()); | |
| try (InputStream in = jarFile.getInputStream(je)) { | |
| byte[] bytes = in.readAllBytes(); | |
| if (DEBUG) { | |
| System.out.printf("bytes= %s, expected=%s%n", | |
| new String(bytes), new String(entry.bytes)); | |
| } | |
| assertEquals(bytes, entry.bytes, | |
| String.format("Expected payload: %s", new String(entry.bytes))); | |
| } | |
| } | |
| } | |
| /** | |
| * Verify that JarInputStream can be used to read all entries including | |
| * a file and directory entry with the same name | |
| * | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test | |
| public void JarInputStreamTest() throws IOException { | |
| System.out.printf("%n%n**** JarInputStreamTest ***%n"); | |
| try (JarInputStream jis = new JarInputStream( | |
| new FileInputStream(TEST_JAR.toFile()))) { | |
| JarEntry jarEntry = jis.getNextJarEntry(); | |
| assertNotNull(jarEntry); | |
| while (jarEntry != null) { | |
| Entry e; | |
| if (jarEntry.getName().equals(FILE_ENTRY.name)) { | |
| e = FILE_ENTRY; | |
| } else if (jarEntry.getName().equals(DIR_ENTRY.name)) { | |
| e = DIR_ENTRY; | |
| } else { | |
| throw new RuntimeException( | |
| String.format("Invalid Jar entry: %s", jarEntry.getName())); | |
| } | |
| assertEquals(jarEntry.getMethod(), e.method); | |
| assertEquals(jis.readAllBytes(), e.bytes, | |
| String.format("Expected payload: %s", new String(e.bytes))); | |
| jarEntry = jis.getNextJarEntry(); | |
| } | |
| } | |
| } | |
| /** | |
| * Verify that JarURLConnection can be used to access all the entries including | |
| * a file and directory entry with the same name within a jar file | |
| * | |
| * @param entry The entry to validate | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test(dataProvider = "entries") | |
| public void JarURLConnectionTest(Entry entry) throws Exception { | |
| System.out.printf("%n%n**** JarURLConnectionTest ***%n"); | |
| URL url = new URL("jar:" + TEST_JAR.toUri().toURL() + "!/" + entry.name); | |
| if (DEBUG) { | |
| System.out.printf("URL=%s%n", url); | |
| } | |
| JarURLConnection con = (JarURLConnection) url.openConnection(); | |
| con.connect(); | |
| JarEntry je = con.getJarEntry(); | |
| try (JarFile jarFile = con.getJarFile()) { | |
| assertNotNull(je); | |
| assertNotNull(jarFile); | |
| assertNull(con.getAttributes()); | |
| assertNull(con.getMainAttributes()); | |
| assertNull(con.getManifest()); | |
| assertEquals(je.getName(), entry.name); | |
| assertEquals(con.getEntryName(), entry.name); | |
| assertEquals(je.getMethod(), entry.method); | |
| assertEquals(con.getJarFileURL(), TEST_JAR.toUri().toURL()); | |
| if (DEBUG) { | |
| System.out.printf(" getEntryName: %s, getJarFileURL:%s%n", | |
| con.getEntryName(), con.getJarFileURL()); | |
| System.out.printf(" Jar Entry= %s, size= %s%n", je.getName(), je.getSize()); | |
| } | |
| try (InputStream is = jarFile.getInputStream(je)) { | |
| byte[] bytes = is.readAllBytes(); | |
| if (DEBUG) { | |
| System.out.printf(" Bytes read:%s%n", new String(bytes)); | |
| } | |
| assertEquals(bytes, entry.bytes, | |
| String.format("Expected payload: %s", new String(entry.bytes))); | |
| } | |
| } | |
| } | |
| /** | |
| * Verify that JarFile::stream returns all entries including | |
| * a file and directory entry with the same name | |
| * | |
| * @throws IOException If an error occurs | |
| */ | |
| @Test | |
| public void JarFileStreamTest() throws IOException { | |
| System.out.printf("%n%n**** JarFileStreamTest ***%n"); | |
| try (JarFile jf = new JarFile(TEST_JAR.toFile())) { | |
| List<? extends JarEntry> entries = jf.stream().collect(Collectors.toList()); | |
| assertEquals(EXPECTED_ENTRIES.size(), jf.size()); | |
| for (JarEntry e : entries) { | |
| assertTrue(EXPECTED_ENTRIES.contains(e.getName())); | |
| } | |
| } | |
| } | |
| /** | |
| * Method used to read the bytes from an InputStream. This method is | |
| * here so that the test could be backported to JDK 8 if needed as | |
| * InputStream::readAllBytes() does not exist | |
| * | |
| * @param is The InputStream to read from | |
| * @return The byte array representing bytes read from the InputStream | |
| * @throws IOException If an error occurs | |
| */ | |
| public static byte[] readAllBytes(InputStream is) throws IOException { | |
| byte[] data = new byte[MAX_BUFFER_SIZE]; | |
| ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | |
| int len; | |
| while ((len = is.read(data, 0, data.length)) != -1) { | |
| buffer.write(data, 0, len); | |
| } | |
| buffer.flush(); | |
| return buffer.toByteArray(); | |
| } | |
| /** | |
| * Method used to create a byte[] representing a Jar file with | |
| * duplicate file entries. This uses ZipArchiveOutputStream as ZipOutputStream | |
| * will fail with a "java.util.zip.ZipException: duplicate entry". | |
| */ | |
| // public static void createJarWithDuplicateFileEntries() throws IOException { | |
| // Files.deleteIfExists(DUPFILE_ENTRY_FILE); | |
| // try (ZipArchiveOutputStream zos = | |
| // new ZipArchiveOutputStream(DUPFILE_ENTRY_FILE.toFile())) { | |
| // zos.putArchiveEntry(new ZipArchiveEntry(FILE_ENTRY.name)); | |
| // zos.write(FILE_ENTRY.bytes); | |
| // zos.putArchiveEntry(new ZipArchiveEntry(FILE_ENTRY.name)); | |
| // zos.write("Yet another File".getBytes(StandardCharsets.UTF_8)); | |
| // zos.closeArchiveEntry(); | |
| // } catch (IOException e) { | |
| // e.printStackTrace(); | |
| // } | |
| // byte[] jarBytes = Files.readAllBytes(DUPFILE_ENTRY_FILE); | |
| // String result = createByteArray(jarBytes, "DUPLICATE_ENTRY_JAR_BYTES"); | |
| // System.out.println(result); | |
| // } | |
| /** | |
| * Utility method which takes a byte array and converts to byte array | |
| * declaration. For example: | |
| * <pre> | |
| * {@code | |
| * var fooJar = Files.readAllBytes(Path.of("foo.jar")); | |
| * var result = createByteArray(fooJar, "FOOBYTES"); | |
| * } | |
| * </pre> | |
| * | |
| * @param bytes A byte array used to create a byte array declaration | |
| * @param name Name to be used in the byte array declaration | |
| * @return The formatted byte array declaration | |
| */ | |
| public static String createByteArray(byte[] bytes, String name) { | |
| StringBuilder sb = new StringBuilder(bytes.length * 5); | |
| Formatter fmt = new Formatter(sb); | |
| fmt.format(" public static byte[] %s = {", name); | |
| final int linelen = 8; | |
| for (int i = 0; i < bytes.length; i++) { | |
| if (i % linelen == 0) { | |
| fmt.format("%n "); | |
| } | |
| fmt.format(" (byte) 0x%x,", bytes[i] & 0xff); | |
| } | |
| fmt.format("%n };%n"); | |
| return sb.toString(); | |
| } | |
| /** | |
| * Represents an entry in a Zip file. An entry encapsulates a name, a | |
| * compression method, and its contents/data. | |
| */ | |
| public static class Entry { | |
| public final String name; | |
| public final int method; | |
| public final byte[] bytes; | |
| public Entry(String name, int method, String contents) { | |
| this.name = name; | |
| this.method = method; | |
| this.bytes = contents.getBytes(StandardCharsets.UTF_8); | |
| } | |
| public static Entry of(String name, int method, String contents) { | |
| return new Entry(name, method, contents); | |
| } | |
| } | |
| } |