You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Jeroen Peschier (Jira)" <ji...@apache.org> on 2026/10/20 17:34:00 UTC
[jira] [Updated] (IO-895) ExceptionInInitializerError when using Commons IO on the module path
[ https://issues.apache.org/jira/browse/IO-895?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Jeroen Peschier updated IO-895:
-------------------------------
Description:
When migrating a project to Java 25 with modularization, I ran into an issue where Commons IO 2.22.0 throws an Error when invoking BufferedFileChannelInputStream::close with Commons IO JAR on the module path while having "requires org.apache.commons.io;" in the module-info:
{noformat}
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.cleanDirectBuffer(BufferedFileChannelInputStream.java:230)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.clean(BufferedFileChannelInputStream.java:217)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.close(BufferedFileChannelInputStream.java:240)
... snip private code
Caused by: java.lang.IllegalStateException: Failed to initialize a Cleaner.
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:102)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.<clinit>(ByteBufferCleaner.java:79)
... 6 more
Caused by: java.lang.ClassNotFoundException: sun.misc.Cleaner
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:467)
at java.base/java.lang.Class.forName(Class.java:458)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:47)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:41)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:97)
... 7 more{noformat}
Code that reproduces the above stacktrace:
{code:java}
var input = new BufferedFileChannelInputStream.Builder()
.setPath("some-existing-file")
.get();
input.close();{code}
I noticed that the current HEAD ([f9de7a4|https://github.com/apache/commons-io/commit/f9de7a458b9ff70151994bbbacae3088047b8338]) has an extra setter "setClean" for the builder that aims to skip the cleanup:
{code:java}
var input = new BufferedFileChannelInputStream.Builder()
.setPath("some-existing-file")
.setClean(false)
.get();
input.close();{code}
However running with a snapshot of ([f9de7a4|https://github.com/apache/commons-io/commit/f9de7a458b9ff70151994bbbacae3088047b8338]) and using "setClean(false)" I am seeing the same exception:
{noformat}
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.BufferedFileChannelInputStream.cleanBuffer(BufferedFileChannelInputStream.java:252)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.BufferedFileChannelInputStream.close(BufferedFileChannelInputStream.java:261)
... snip private code
Caused by: java.lang.IllegalStateException: Failed to initialize a direct ByteBuffer Cleaner.
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:116)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.<clinit>(ByteBufferCleaner.java:82)
... 3 more
Caused by: java.lang.ClassNotFoundException: sun.misc.Cleaner
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:467)
at java.base/java.lang.Class.forName(Class.java:458)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:50)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:44)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:111)
... 4 more{noformat}
So both 2.22.0 and 2.23.0-SNAPSHOT fails on construction of ByteBufferCleaner.Java8Cleaner when executed from the module path. Note that the stacktrace is misleading: it appears to fail on the Java8Cleaner (it does), but it actually also attempts to create a Java9Cleaner and when that fails it prints the stacktrace ofthe Java8Cleaner failure hiding that the creation of the Java9Cleaner also failed.
The Common IO unit tests seem to run on the classpath so they don't highlight this issue, although there are warnings when running with the classpath as well:
{noformat}
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called WARNING: sun.misc.Unsafe::invokeCleaner has been called by org.apache.commons.io.input.ByteBufferCleaner$Java9Cleaner (file:/REDACTED/.m2/repository/commons-io/commons-io/2.22.0/commons-io-2.22.0.jar) WARNING: Please consider reporting this to the maintainers of class org.apache.commons.io.input.ByteBufferCleaner$Java9Cleaner WARNING: sun.misc.Unsafe::invokeCleaner will be removed in a future release{noformat}
An obvious workaround is to put the Commons IO jar on the classpath. Beside negating the benefits of Java modules, this is not an (easy) option for application distributions that no longer have a classpath.
Ideas on how to fix this:
- Maybe ByteBufferCleaner needs a Java8 and Java9+ version in a multi-release JAR? That way the ExceptionInInitializerError could be prevented altogether.
- Since Java 9, the replacement for sun.misc.Cleaner is jdk.internal.ref.Cleaner. Still internal to the JDK though.
was:
When migrating a project to Java 25 with modularization, I ran into an issue where Commons IO 2.22.0 throws an Error when invoking BufferedFileChannelInputStream::close with Commons IO JAR on the module path while having "requires org.apache.commons.io;" in the module-info:
{noformat}
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.cleanDirectBuffer(BufferedFileChannelInputStream.java:230)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.clean(BufferedFileChannelInputStream.java:217)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.close(BufferedFileChannelInputStream.java:240)
... snip private code
Caused by: java.lang.IllegalStateException: Failed to initialize a Cleaner.
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:102)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.<clinit>(ByteBufferCleaner.java:79)
... 6 more
Caused by: java.lang.ClassNotFoundException: sun.misc.Cleaner
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:467)
at java.base/java.lang.Class.forName(Class.java:458)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:47)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:41)
at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:97)
... 7 more{noformat}
Code that reproduces the above stacktrace:
{code:java}
var input = new BufferedFileChannelInputStream.Builder()
.setPath("some-existing-file")
.get();
input.close();{code}
I noticed that the current HEAD ([f9de7a4|https://github.com/apache/commons-io/commit/f9de7a458b9ff70151994bbbacae3088047b8338]) has an extra setter "setClean" for the builder that aims to skip the cleanup:
{code:java}
var input = new BufferedFileChannelInputStream.Builder()
.setPath("some-existing-file")
.setClean(false)
.get();
input.close();{code}
However running with a snapshot of ([f9de7a4|https://github.com/apache/commons-io/commit/f9de7a458b9ff70151994bbbacae3088047b8338]) and using "setClean(false)" I am seeing the same exception:
{noformat}
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.BufferedFileChannelInputStream.cleanBuffer(BufferedFileChannelInputStream.java:252)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.BufferedFileChannelInputStream.close(BufferedFileChannelInputStream.java:261)
... snip private code
Caused by: java.lang.IllegalStateException: Failed to initialize a direct ByteBuffer Cleaner.
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:116)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.<clinit>(ByteBufferCleaner.java:82)
... 3 more
Caused by: java.lang.ClassNotFoundException: sun.misc.Cleaner
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:467)
at java.base/java.lang.Class.forName(Class.java:458)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:50)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:44)
at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:111)
... 4 more{noformat}
So both 2.22.0 and 2.23.0-SNAPSHOT fails on construction of ByteBufferCleaner.Java8Cleaner when executed from the module path. It never gets to a Java9+ compatible cleaner because ExceptionInInitializerError (not an Exception) never triggers the fall-through exception handling in ByteBufferCleaner::getCleaner.
The Common IO unit tests seem to run on the classpath so they don't highlight this issue, although there are warnings when running with the classpath as well:
{noformat}
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called WARNING: sun.misc.Unsafe::invokeCleaner has been called by org.apache.commons.io.input.ByteBufferCleaner$Java9Cleaner (file:/REDACTED/.m2/repository/commons-io/commons-io/2.22.0/commons-io-2.22.0.jar) WARNING: Please consider reporting this to the maintainers of class org.apache.commons.io.input.ByteBufferCleaner$Java9Cleaner WARNING: sun.misc.Unsafe::invokeCleaner will be removed in a future release{noformat}
An obvious workaround is to put the Commons IO jar on the classpath. Beside negating the benefits of Java modules, this is not an (easy) option for application distributions that no longer have a classpath.
Ideas on how to fix this:
- Maybe ByteBufferCleaner needs a Java8 and Java9+ version in a multi-release JAR? That way the ExceptionInInitializerError could be prevented altogether.
- Since Java 9, the replacement for sun.misc.Cleaner is jdk.internal.ref.Cleaner. Still internal to the JDK though.
> ExceptionInInitializerError when using Commons IO on the module path
> --------------------------------------------------------------------
>
> Key: IO-895
> URL: https://issues.apache.org/jira/browse/IO-895
> Project: Commons IO
> Issue Type: Bug
> Affects Versions: 2.22.0
> Environment: Java 25, Linux
> Reporter: Jeroen Peschier
> Priority: Blocker
>
> When migrating a project to Java 25 with modularization, I ran into an issue where Commons IO 2.22.0 throws an Error when invoking BufferedFileChannelInputStream::close with Commons IO JAR on the module path while having "requires org.apache.commons.io;" in the module-info:
> {noformat}
> Exception in thread "main" java.lang.ExceptionInInitializerError
> at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.cleanDirectBuffer(BufferedFileChannelInputStream.java:230)
> at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.clean(BufferedFileChannelInputStream.java:217)
> at org.apache.commons.io@2.22.0/org.apache.commons.io.input.BufferedFileChannelInputStream.close(BufferedFileChannelInputStream.java:240)
> ... snip private code
> Caused by: java.lang.IllegalStateException: Failed to initialize a Cleaner.
> at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:102)
> at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.<clinit>(ByteBufferCleaner.java:79)
> ... 6 more
> Caused by: java.lang.ClassNotFoundException: sun.misc.Cleaner
> at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
> at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
> at java.base/java.lang.Class.forName0(Native Method)
> at java.base/java.lang.Class.forName(Class.java:467)
> at java.base/java.lang.Class.forName(Class.java:458)
> at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:47)
> at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:41)
> at org.apache.commons.io@2.22.0/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:97)
> ... 7 more{noformat}
> Code that reproduces the above stacktrace:
> {code:java}
> var input = new BufferedFileChannelInputStream.Builder()
> .setPath("some-existing-file")
> .get();
> input.close();{code}
> I noticed that the current HEAD ([f9de7a4|https://github.com/apache/commons-io/commit/f9de7a458b9ff70151994bbbacae3088047b8338]) has an extra setter "setClean" for the builder that aims to skip the cleanup:
> {code:java}
> var input = new BufferedFileChannelInputStream.Builder()
> .setPath("some-existing-file")
> .setClean(false)
> .get();
> input.close();{code}
> However running with a snapshot of ([f9de7a4|https://github.com/apache/commons-io/commit/f9de7a458b9ff70151994bbbacae3088047b8338]) and using "setClean(false)" I am seeing the same exception:
> {noformat}
> Exception in thread "main" java.lang.ExceptionInInitializerError
> at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.BufferedFileChannelInputStream.cleanBuffer(BufferedFileChannelInputStream.java:252)
> at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.BufferedFileChannelInputStream.close(BufferedFileChannelInputStream.java:261)
> ... snip private code
> Caused by: java.lang.IllegalStateException: Failed to initialize a direct ByteBuffer Cleaner.
> at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:116)
> at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.<clinit>(ByteBufferCleaner.java:82)
> ... 3 more
> Caused by: java.lang.ClassNotFoundException: sun.misc.Cleaner
> at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
> at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
> at java.base/java.lang.Class.forName0(Native Method)
> at java.base/java.lang.Class.forName(Class.java:467)
> at java.base/java.lang.Class.forName(Class.java:458)
> at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:50)
> at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner.<init>(ByteBufferCleaner.java:44)
> at org.apache.commons.io@2.23.0-SNAPSHOT/org.apache.commons.io.input.ByteBufferCleaner.getCleaner(ByteBufferCleaner.java:111)
> ... 4 more{noformat}
> So both 2.22.0 and 2.23.0-SNAPSHOT fails on construction of ByteBufferCleaner.Java8Cleaner when executed from the module path. Note that the stacktrace is misleading: it appears to fail on the Java8Cleaner (it does), but it actually also attempts to create a Java9Cleaner and when that fails it prints the stacktrace ofthe Java8Cleaner failure hiding that the creation of the Java9Cleaner also failed.
> The Common IO unit tests seem to run on the classpath so they don't highlight this issue, although there are warnings when running with the classpath as well:
> {noformat}
> WARNING: A terminally deprecated method in sun.misc.Unsafe has been called WARNING: sun.misc.Unsafe::invokeCleaner has been called by org.apache.commons.io.input.ByteBufferCleaner$Java9Cleaner (file:/REDACTED/.m2/repository/commons-io/commons-io/2.22.0/commons-io-2.22.0.jar) WARNING: Please consider reporting this to the maintainers of class org.apache.commons.io.input.ByteBufferCleaner$Java9Cleaner WARNING: sun.misc.Unsafe::invokeCleaner will be removed in a future release{noformat}
> An obvious workaround is to put the Commons IO jar on the classpath. Beside negating the benefits of Java modules, this is not an (easy) option for application distributions that no longer have a classpath.
> Ideas on how to fix this:
> - Maybe ByteBufferCleaner needs a Java8 and Java9+ version in a multi-release JAR? That way the ExceptionInInitializerError could be prevented altogether.
> - Since Java 9, the replacement for sun.misc.Cleaner is jdk.internal.ref.Cleaner. Still internal to the JDK though.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)