datafiddler
datafiddler

Reputation: 1835

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?

Upvotes: 8

Views: 13564

Answers (4)

Avik Ray
Avik Ray

Reputation: 109

Due to the reasons mentioned in Ivan's answer, you will need to add few JVM args from the list here: https://github.com/apache/ignite/issues/10747#issuecomment-1566646439

Upvotes: 0

Sorin
Sorin

Reputation: 19

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.

Upvotes: 1

lance-java
lance-java

Reputation: 27984

Class.getMethod(), Class.getField() and etc. will only return the public methods/fields. I think you want Class.getDeclaredMethod().

Upvotes: 0

Ivan
Ivan

Reputation: 8758

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

Upvotes: 9

Related Questions