From ed428a55d9e898a1b132c7c5578eb8b9213ba318 Mon Sep 17 00:00:00 2001 From: tallison Date: Wed, 11 Mar 2026 15:19:38 -0400 Subject: [PATCH 1/6] COMPRESS-721 -- avoid reflection when possible. --- .../compress/harmony/unpack200/Archive.java | 3 ++- .../unpack200/Pack200UnpackerAdapter.java | 25 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/commons/compress/harmony/unpack200/Archive.java b/src/main/java/org/apache/commons/compress/harmony/unpack200/Archive.java index 024a9bfc8ea..4ebc30fde1d 100644 --- a/src/main/java/org/apache/commons/compress/harmony/unpack200/Archive.java +++ b/src/main/java/org/apache/commons/compress/harmony/unpack200/Archive.java @@ -81,7 +81,8 @@ public Archive(final InputStream inputStream, final JarOutputStream outputStream this.inputStream = Pack200UnpackerAdapter.newBoundedInputStream(inputStream); this.outputStream = outputStream; if (inputStream instanceof FileInputStream) { - inputPath = Paths.get(Pack200UnpackerAdapter.readPathString((FileInputStream) inputStream)); + final String pathString = Pack200UnpackerAdapter.readPathString((FileInputStream) inputStream); + inputPath = pathString != null ? Paths.get(pathString) : null; } else { inputPath = null; } diff --git a/src/main/java/org/apache/commons/compress/harmony/unpack200/Pack200UnpackerAdapter.java b/src/main/java/org/apache/commons/compress/harmony/unpack200/Pack200UnpackerAdapter.java index ebed34cb947..55fa70308ac 100644 --- a/src/main/java/org/apache/commons/compress/harmony/unpack200/Pack200UnpackerAdapter.java +++ b/src/main/java/org/apache/commons/compress/harmony/unpack200/Pack200UnpackerAdapter.java @@ -59,8 +59,26 @@ static BoundedInputStream newBoundedInputStream(final File file) throws IOExcept return newBoundedInputStream(file.toPath()); } + @SuppressWarnings("resource") // Caller closes. private static BoundedInputStream newBoundedInputStream(final FileInputStream fileInputStream) throws IOException { - return newBoundedInputStream(readPathString(fileInputStream)); + final String pathString = readPathString(fileInputStream); + if (pathString != null) { + return newBoundedInputStream(pathString); + } + // Reflection failed (e.g., Java 17+ strong encapsulation), fall back to channel size. + try { + final long size = fileInputStream.getChannel().size(); + // @formatter:off + return BoundedInputStream.builder() + .setInputStream(new BufferedInputStream(fileInputStream)) + .setMaxCount(size) + .setPropagateClose(false) + .get(); + // @formatter:on + } catch (final IOException e) { + // No limit + return BoundedInputStream.builder().setInputStream(new BufferedInputStream(fileInputStream)).get(); + } } @SuppressWarnings("resource") // Caller closes. @@ -74,7 +92,10 @@ static BoundedInputStream newBoundedInputStream(final InputStream inputStream) t return newBoundedInputStream(BoundedInputStream.builder().setInputStream(inputStream).get()); } if (inputStream instanceof FilterInputStream) { - return newBoundedInputStream(unwrap((FilterInputStream) inputStream)); + final InputStream unwrapped = unwrap((FilterInputStream) inputStream); + if (unwrapped != null) { + return newBoundedInputStream(unwrapped); + } } if (inputStream instanceof FileInputStream) { return newBoundedInputStream((FileInputStream) inputStream); From bda8b1f118429d6ca51e86dced829c00f7934ae1 Mon Sep 17 00:00:00 2001 From: tallison Date: Wed, 11 Mar 2026 15:36:38 -0400 Subject: [PATCH 2/6] COMPRESS-721 -- avoid reflection when possible -- add unit tests --- .../unpack200/Pack200UnpackerAdapter.java | 7 +++++ .../harmony/unpack200/ArchiveTest.java | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/main/java/org/apache/commons/compress/harmony/unpack200/Pack200UnpackerAdapter.java b/src/main/java/org/apache/commons/compress/harmony/unpack200/Pack200UnpackerAdapter.java index 55fa70308ac..4893f0f047e 100644 --- a/src/main/java/org/apache/commons/compress/harmony/unpack200/Pack200UnpackerAdapter.java +++ b/src/main/java/org/apache/commons/compress/harmony/unpack200/Pack200UnpackerAdapter.java @@ -161,6 +161,13 @@ private static T readField(final Object object, final String fieldName) { return (T) FieldUtils.readField(object, fieldName, true); } catch (final IllegalAccessException e) { return null; + } catch (final RuntimeException e) { + // InaccessibleObjectException (Java 9+) extends RuntimeException, not IllegalAccessException. + // Thrown when Java 17+ strong encapsulation blocks setAccessible on JDK internal fields. + if ("java.lang.reflect.InaccessibleObjectException".equals(e.getClass().getName())) { + return null; + } + throw e; } } diff --git a/src/test/java/org/apache/commons/compress/harmony/unpack200/ArchiveTest.java b/src/test/java/org/apache/commons/compress/harmony/unpack200/ArchiveTest.java index 0802196d76c..6b821545bd1 100644 --- a/src/test/java/org/apache/commons/compress/harmony/unpack200/ArchiveTest.java +++ b/src/test/java/org/apache/commons/compress/harmony/unpack200/ArchiveTest.java @@ -60,6 +60,36 @@ void testAlternativeConstructor() throws Exception { new Archive(inputFile, outputFile).unpack(); } + /** + * Tests that unpacking works when a FileInputStream is passed directly. + * This exercises the reflection-based path extraction code in Pack200UnpackerAdapter, + * which fails on Java 17+ due to strong encapsulation (InaccessibleObjectException). + */ + @Test + void testWithFileInputStream() throws Exception { + final File inputFile = new File(Archive.class.getResource("/pack200/sql.pack.gz").toURI()); + final File outputFile = createTempFile("sql", ".jar"); + try (FileInputStream in = new FileInputStream(inputFile); + JarOutputStream out = new JarOutputStream(new FileOutputStream(outputFile))) { + new Archive(in, out).unpack(); + } + } + + /** + * Tests that unpacking works when a BufferedInputStream wrapping a FileInputStream is passed. + * This exercises the FilterInputStream unwrap reflection code in Pack200UnpackerAdapter, + * which fails on Java 17+ due to strong encapsulation (InaccessibleObjectException). + */ + @Test + void testWithBufferedFileInputStream() throws Exception { + final File inputFile = new File(Archive.class.getResource("/pack200/sql.pack.gz").toURI()); + final File outputFile = createTempFile("sql", ".jar"); + try (InputStream in = new BufferedInputStream(new FileInputStream(inputFile)); + JarOutputStream out = new JarOutputStream(new FileOutputStream(outputFile))) { + new Archive(in, out).unpack(); + } + } + @Test void testDeflateHint() throws Exception { File file = createTempFile("sql", ".jar"); From e42139363ef271cec06b016b910359a2fa0422a5 Mon Sep 17 00:00:00 2001 From: tallison Date: Thu, 12 Mar 2026 08:53:33 -0400 Subject: [PATCH 3/6] COMPRESS-721 -- avoid reflection when possible --- pom.xml | 19 ------------ .../harmony/unpack200/ArchiveTest.java | 30 ------------------- 2 files changed, 49 deletions(-) diff --git a/pom.xml b/pom.xml index 8ed7db1da49..626baf56e8f 100644 --- a/pom.xml +++ b/pom.xml @@ -584,25 +584,6 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj. - - java17 - - [17,) - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${argLine} --add-opens java.base/java.io=ALL-UNNAMED - - - - - - diff --git a/src/test/java/org/apache/commons/compress/harmony/unpack200/ArchiveTest.java b/src/test/java/org/apache/commons/compress/harmony/unpack200/ArchiveTest.java index 6b821545bd1..0802196d76c 100644 --- a/src/test/java/org/apache/commons/compress/harmony/unpack200/ArchiveTest.java +++ b/src/test/java/org/apache/commons/compress/harmony/unpack200/ArchiveTest.java @@ -60,36 +60,6 @@ void testAlternativeConstructor() throws Exception { new Archive(inputFile, outputFile).unpack(); } - /** - * Tests that unpacking works when a FileInputStream is passed directly. - * This exercises the reflection-based path extraction code in Pack200UnpackerAdapter, - * which fails on Java 17+ due to strong encapsulation (InaccessibleObjectException). - */ - @Test - void testWithFileInputStream() throws Exception { - final File inputFile = new File(Archive.class.getResource("/pack200/sql.pack.gz").toURI()); - final File outputFile = createTempFile("sql", ".jar"); - try (FileInputStream in = new FileInputStream(inputFile); - JarOutputStream out = new JarOutputStream(new FileOutputStream(outputFile))) { - new Archive(in, out).unpack(); - } - } - - /** - * Tests that unpacking works when a BufferedInputStream wrapping a FileInputStream is passed. - * This exercises the FilterInputStream unwrap reflection code in Pack200UnpackerAdapter, - * which fails on Java 17+ due to strong encapsulation (InaccessibleObjectException). - */ - @Test - void testWithBufferedFileInputStream() throws Exception { - final File inputFile = new File(Archive.class.getResource("/pack200/sql.pack.gz").toURI()); - final File outputFile = createTempFile("sql", ".jar"); - try (InputStream in = new BufferedInputStream(new FileInputStream(inputFile)); - JarOutputStream out = new JarOutputStream(new FileOutputStream(outputFile))) { - new Archive(in, out).unpack(); - } - } - @Test void testDeflateHint() throws Exception { File file = createTempFile("sql", ".jar"); From 68df8d1c4bd3f7135898dd7c9e0aea7033a4fc90 Mon Sep 17 00:00:00 2001 From: tallison Date: Thu, 12 Mar 2026 09:04:04 -0400 Subject: [PATCH 4/6] COMPRESS-721 -- return java 17 profile --- pom.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pom.xml b/pom.xml index 626baf56e8f..8ed7db1da49 100644 --- a/pom.xml +++ b/pom.xml @@ -584,6 +584,25 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj. + + java17 + + [17,) + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${argLine} --add-opens java.base/java.io=ALL-UNNAMED + + + + + + From e53770450cd47eb8908dce40c7e6057b4ce1172d Mon Sep 17 00:00:00 2001 From: tallison Date: Thu, 12 Mar 2026 09:20:16 -0400 Subject: [PATCH 5/6] COMPRESS-721 -- split executions to show that unpack200 fails without --add-opens --- pom.xml | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 8ed7db1da49..fbdfc50f159 100644 --- a/pom.xml +++ b/pom.xml @@ -594,11 +594,33 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj. org.apache.maven.plugins maven-surefire-plugin - - - ${argLine} --add-opens java.base/java.io=ALL-UNNAMED - - + + + + default-test + + + ${argLine} --add-opens java.base/java.io=ALL-UNNAMED + + + **/harmony/unpack200/** + + + + + pack200-tests + + test + + + + **/harmony/unpack200/** + + + + From 6ff3dcfbe0833c4b66068a54c1abf35ee2d35634 Mon Sep 17 00:00:00 2001 From: tallison Date: Thu, 12 Mar 2026 09:44:46 -0400 Subject: [PATCH 6/6] COMPRESS-721 -- special handling for sevenz...test --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index fbdfc50f159..53f493b5c29 100644 --- a/pom.xml +++ b/pom.xml @@ -606,6 +606,8 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj. **/harmony/unpack200/** + + **/SevenZReadSubStreamsInfoTest.java @@ -615,6 +617,7 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj. test + ${argLine} **/harmony/unpack200/**