Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cannot access a member of class java.nio.DirectByteBuffer (in module java.base) with modifiers "public"

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?

like image 972
datafiddler Avatar asked Dec 19 '17 16:12

datafiddler


2 Answers

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

like image 56
Ivan Avatar answered Oct 12 '22 23:10

Ivan


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.

like image 37
Sorin Avatar answered Oct 12 '22 22:10

Sorin