Just cast Cleaner to Runnable in Java9+ to prevent IllegalAccessException

Motivation:

When try to call Cleaner.run() via reflection on Java9 you may see an IllegalAccessException.

Modifications:

Just cast the Cleaner to Runnable to prevent IllegalAccessException to be raised.

Result:

Free direct buffers also work on Java9+ as expected.
This commit is contained in:
Norman Maurer 2016-08-06 21:01:16 +02:00
parent cb5f71782e
commit be77dfb1ca

View File

@ -32,6 +32,8 @@ import java.nio.ByteBuffer;
final class Cleaner0 { final class Cleaner0 {
private static final long CLEANER_FIELD_OFFSET; private static final long CLEANER_FIELD_OFFSET;
private static final Method CLEAN_METHOD; private static final Method CLEAN_METHOD;
private static final boolean CLEANER_IS_RUNNABLE;
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Cleaner0.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(Cleaner0.class);
static { static {
@ -39,6 +41,7 @@ final class Cleaner0 {
Field cleanerField; Field cleanerField;
long fieldOffset = -1; long fieldOffset = -1;
Method clean = null; Method clean = null;
boolean cleanerIsRunnable = false;
if (PlatformDependent0.hasUnsafe()) { if (PlatformDependent0.hasUnsafe()) {
try { try {
cleanerField = direct.getClass().getDeclaredField("cleaner"); cleanerField = direct.getClass().getDeclaredField("cleaner");
@ -48,20 +51,23 @@ final class Cleaner0 {
try { try {
// Cleaner implements Runnable from JDK9 onwards. // Cleaner implements Runnable from JDK9 onwards.
Runnable runnable = (Runnable) cleaner; Runnable runnable = (Runnable) cleaner;
clean = Runnable.class.getDeclaredMethod("run"); runnable.run();
cleanerIsRunnable = true;
} catch (ClassCastException ignored) { } catch (ClassCastException ignored) {
clean = cleaner.getClass().getDeclaredMethod("clean"); clean = cleaner.getClass().getDeclaredMethod("clean");
clean.invoke(cleaner);
} }
clean.invoke(cleaner);
} catch (Throwable t) { } catch (Throwable t) {
// We don't have ByteBuffer.cleaner(). // We don't have ByteBuffer.cleaner().
fieldOffset = -1; fieldOffset = -1;
clean = null; clean = null;
cleanerIsRunnable = false;
} }
} }
logger.debug("java.nio.ByteBuffer.cleaner(): {}", fieldOffset != -1? "available" : "unavailable"); logger.debug("java.nio.ByteBuffer.cleaner(): {}", fieldOffset != -1? "available" : "unavailable");
CLEANER_FIELD_OFFSET = fieldOffset; CLEANER_FIELD_OFFSET = fieldOffset;
CLEAN_METHOD = clean; CLEAN_METHOD = clean;
CLEANER_IS_RUNNABLE = cleanerIsRunnable;
// free buffer if possible // free buffer if possible
freeDirectBuffer(direct); freeDirectBuffer(direct);
@ -71,11 +77,16 @@ final class Cleaner0 {
if (CLEANER_FIELD_OFFSET == -1 || !buffer.isDirect()) { if (CLEANER_FIELD_OFFSET == -1 || !buffer.isDirect()) {
return; return;
} }
assert CLEAN_METHOD != null : "CLEANER_FIELD_OFFSET != -1 implies CLEAN_METHOD != null"; assert CLEAN_METHOD != null || CLEANER_IS_RUNNABLE:
"CLEANER_FIELD_OFFSET != -1 implies CLEAN_METHOD != null or CLEANER_IS_RUNNABLE == true";
try { try {
Object cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET); Object cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
if (cleaner != null) { if (cleaner != null) {
CLEAN_METHOD.invoke(cleaner); if (CLEANER_IS_RUNNABLE) {
((Runnable) cleaner).run();
} else {
CLEAN_METHOD.invoke(cleaner);
}
} }
} catch (Throwable t) { } catch (Throwable t) {
// Nothing we can do here. // Nothing we can do here.