Got a project which is built to still support Java 6. The code below is inside a jar file built with Compiler compliance level 1.6
That jar file should be called from java apps built for java 6 or newer. It runs fine in Java 8 as well.
Now with Java9, I get problems with nio.DirectByteBuffer, and I tried to solve it this way, using reflection:
@SuppressWarnings("unchecked")
static void cleanDirectBuffer(sun.nio.ch.DirectBuffer buffer) {
if (JAVA_VERSION < 1.9) {
sun.misc.Cleaner cleaner = buffer.cleaner();
if (cleaner != null) cleaner.clean();
} else {
// For java9 do it the reflection way
@SuppressWarnings("rawtypes")
Class B = buffer.getClass();
// will be a java.nio.DirectBuffer, which is unknown if compiled in 1.6 compliance mode
try {
java.lang.reflect.Method CleanerMethod = B.getMethod("cleaner");
CleanerMethod.setAccessible(true); // fails here !
Object cleaner = CleanerMethod.invoke(buffer);
if (cleaner == null) return;
@SuppressWarnings("rawtypes")
Class C = cleaner.getClass();
java.lang.reflect.Method CleanMethod = C.getMethod("clean");
CleanMethod.invoke(cleaner);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception other) {
other.printStackTrace();
}
}
}
The JAVA_VERSION detection is fine and switches nicely, depending on the version my calling code is using. jre6 to jre8 environments use nicely the sun.misc.Cleaner path, but that does not work in java9
You'll probably notice that I'm not an expert in java.reflection.
By guessing, I found .setAccessible(true);
and lance-java's answer (thanks so far) helped a bit:
Version2:
Class B = buffer.getClass();
try {
java.lang.reflect.Method CleanerMethod = B.getDeclaredMethod("cleaner");
CleanerMethod.setAccessible(true);
Object cleaner = CleanerMethod.invoke(buffer);
if (cleaner == null) return;
@SuppressWarnings("rawtypes")
Class C = cleaner.getClass();
java.lang.reflect.Method CleanMethod = C.getDeclaredMethod("clean");
CleanMethod.setAccessible(true); // Now it fails here !
CleanMethod.invoke(cleaner);
} catch (InaccessibleObjectException e) {
// ** causes: Unable to make public void jdk.internal.ref.Cleaner.clean() accessible: module java.base does not "exports jdk.internal.ref" to unnamed module **
}
Additionally the warning with the first CleanerMethod.setAccessible(true)
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by my.package.MyClass (file:/xyz.jar) to method java.nio.DirectByteBuffer.cleaner()
...
WARNING: All illegal access operations will be denied in a future release
...does not sound too healthy? But alas, it's a warning only :)
What else am I missing, or is there a different/better approach to my problem?
In Java 9 concept of modules was introduced (https://blog.codefx.org/java/java-module-system-tutorial/).
A module lists the packages it exports. For code in one module (say org.codefx.demo.jpms) to access types in another (say String in java.base), the following accessibility rules must be fulfilled:
the accessed type ( String) must be public
the package containing the type ( java.lang) must be exported by its module (java.base)
the accessing module (org.codefx.demo.jpms) must read the accessed one (java.base), which is typically achieved by requiring it
If any of these rules are violated at compile or run time, the module systems throws an error. This means that public is no longer really public. A public type in a non-exported package is as inaccessible to the outside world as a non-public type in an exported package. Also note that reflection lost its superpowers.
Now it is possible to define classes/methods that are accessible outside of the package. All other nonspecified classes/methods will not be accessible for other modules even if they are public. This is a link to module-info
for java.base
http://people.redhat.com/mbalaoal/webrevs/jdk_8029661_tls_12_sunpkcs11/2018_02_02/8029661.webrev.02/src/java.base/share/classes/module-info.java.html. As you see jdk.internal.ref
is exported only to these modules: java.desktop
and jdk.unsupported
sun.nio.ch.DirectBuffer
and java.nio.DirectByteBuffer
are part of the internal JDK, so you should NOT rely on these classes. They could disappear at any moment.
If you want them to be cleared, don't keep any references to the DirectByteBuffer
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With