5050 * File utilities.
5151 */
5252public final class FileUtils {
53+ /** The DirectByteBuffer.cleaner() method. */
54+ private static Method directByteBufferCleanerMethod ;
5355
54- /** The clean() method. */
55- private static Method cleanMethod ;
56+ /** The Cleaner.clean() method. */
57+ private static Method cleanerCleanMethod ;
58+
59+ // /** The jdk.incubator.foreign.MemorySegment class (JDK14+). */
60+ // private static Class<?> memorySegmentClass;
61+ //
62+ // /** The jdk.incubator.foreign.MemorySegment.ofByteBuffer method (JDK14+). */
63+ // private static Method memorySegmentOfByteBufferMethod;
64+ //
65+ // /** The jdk.incubator.foreign.MemorySegment.ofByteBuffer method (JDK14+). */
66+ // private static Method memorySegmentCloseMethod;
5667
5768 /** The attachment() method. */
5869 private static Method attachmentMethod ;
@@ -425,46 +436,66 @@ public static String getParentDirPath(final String path) {
425436 private static void lookupCleanMethodPrivileged () {
426437 if (VersionFinder .JAVA_MAJOR_VERSION < 9 ) {
427438 try {
428- // See: https://stackoverflow.com/a/19447758/3950982
429- cleanMethod = Class .forName ("sun.misc.Cleaner" ).getMethod ("clean" );
430- cleanMethod .setAccessible (true );
431- attachmentMethod = Class .forName ("sun.nio.ch.DirectBuffer" ).getMethod ("attachment" );
439+ // See:
440+ // https://stackoverflow.com/a/19447758/3950982
441+ cleanerCleanMethod = Class .forName ("sun.misc.Cleaner" ).getDeclaredMethod ("clean" );
442+ cleanerCleanMethod .setAccessible (true );
443+ final Class <?> directByteBufferClass = Class .forName ("sun.nio.ch.DirectBuffer" );
444+ directByteBufferCleanerMethod = directByteBufferClass .getDeclaredMethod ("cleaner" );
445+ attachmentMethod = directByteBufferClass .getMethod ("attachment" );
432446 attachmentMethod .setAccessible (true );
433447 } catch (final SecurityException e ) {
434448 throw ClassGraphException .newClassGraphException (
435- "You need to grant classgraph RuntimePermission(\" accessClassInPackage.sun.misc\" ), "
436- + "RuntimePermission(\" accessClassInPackage.sun.nio.ch\" ), "
449+ "You need to grant classgraph RuntimePermission(\" accessClassInPackage.sun.misc\" ) "
437450 + "and ReflectPermission(\" suppressAccessChecks\" )" ,
438451 e );
439452 } catch (final ReflectiveOperationException | LinkageError e ) {
440453 // Ignore
441454 }
442455 } else {
443- // In JDK9+, calling sun.misc.Cleaner.clean() gives a reflection warning on stderr,
444- // so we need to call Unsafe.theUnsafe.invokeCleaner(byteBuffer) instead, which makes
445- // the same call, but does not print the reflection warning.
446- try {
447- Class <?> unsafeClass ;
456+ boolean jdk14Success = false ;
457+ // // TODO: This feature is in incubation now -- enable after it leaves incubation.
458+ // // To enable this feature, need to:
459+ // // -- add whatever the "jdk.incubator.foreign" module name is replaced with to <Import-Package>
460+ // // in pom.xml, as an optional dependency
461+ // // -- add the same module name to module-info.java as a "requires static" optional dependency
462+ // // -- build two versions of module.java: the existing one, for --release=9, and a new version,
463+ // // for --release=15 (or whatever the final release version ends up being when the feature is
464+ // // moved out of incubation).
465+ // try {
466+ // // JDK 14+ Invoke MemorySegment.ofByteBuffer(myByteBuffer).close()
467+ // // https://stackoverflow.com/a/26777380/3950982
468+ // memorySegmentClass = Class.forName("jdk.incubator.foreign.MemorySegment");
469+ // memorySegmentCloseMethod = AutoCloseable.class.getDeclaredMethod("close");
470+ // memorySegmentOfByteBufferMethod = memorySegmentClass.getMethod("ofByteBuffer",
471+ // ByteBuffer.class);
472+ // jdk14Success = true;
473+ // } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e1) {
474+ // // Fall through
475+ // }
476+ if (!jdk14Success ) { // In JDK9+, calling sun.misc.Cleaner.clean() gives a reflection warning on stderr,
477+ // so we need to call Unsafe.theUnsafe.invokeCleaner(byteBuffer) instead, which makes
478+ // the same call, but does not print the reflection warning.
448479 try {
449- unsafeClass = Class .forName ("sun.misc.Unsafe" );
450- } catch (final ReflectiveOperationException | LinkageError e ) {
451- // jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method,
452- // but that method should be added if sun.misc.Unsafe is removed.
453- unsafeClass = Class .forName ("jdk.internal.misc.Unsafe" );
480+ Class <?> unsafeClass ;
481+ try {
482+ unsafeClass = Class .forName ("sun.misc.Unsafe" );
483+ } catch (final ReflectiveOperationException | LinkageError e ) {
484+ throw ClassGraphException .newClassGraphException ("Could not get class sun.misc.Unsafe" , e );
485+ }
486+ final Field theUnsafeField = unsafeClass .getDeclaredField ("theUnsafe" );
487+ theUnsafeField .setAccessible (true );
488+ theUnsafe = theUnsafeField .get (null );
489+ cleanerCleanMethod = unsafeClass .getMethod ("invokeCleaner" , ByteBuffer .class );
490+ cleanerCleanMethod .setAccessible (true );
491+ } catch (final SecurityException e ) {
492+ throw ClassGraphException .newClassGraphException (
493+ "You need to grant classgraph RuntimePermission(\" accessClassInPackage.sun.misc\" ) "
494+ + "and ReflectPermission(\" suppressAccessChecks\" )" ,
495+ e );
496+ } catch (final ReflectiveOperationException | LinkageError ex ) {
497+ // Ignore
454498 }
455- final Field theUnsafeField = unsafeClass .getDeclaredField ("theUnsafe" );
456- theUnsafeField .setAccessible (true );
457- theUnsafe = theUnsafeField .get (null );
458- cleanMethod = unsafeClass .getMethod ("invokeCleaner" , ByteBuffer .class );
459- cleanMethod .setAccessible (true );
460- } catch (final SecurityException e ) {
461- throw ClassGraphException .newClassGraphException (
462- "You need to grant classgraph RuntimePermission(\" accessClassInPackage.sun.misc\" ), "
463- + "RuntimePermission(\" accessClassInPackage.jdk.internal.misc\" ) "
464- + "and ReflectPermission(\" suppressAccessChecks\" )" ,
465- e );
466- } catch (final ReflectiveOperationException | LinkageError ex ) {
467- // Ignore
468499 }
469500 }
470501 }
@@ -489,13 +520,11 @@ public Object run() {
489520 * @return true if successful
490521 */
491522 private static boolean closeDirectByteBufferPrivileged (final ByteBuffer byteBuffer , final LogNode log ) {
523+ if (!byteBuffer .isDirect ()) {
524+ // Nothing to do
525+ return true ;
526+ }
492527 try {
493- if (cleanMethod == null ) {
494- if (log != null ) {
495- log .log ("Could not unmap ByteBuffer, cleanMethod == null" );
496- }
497- return false ;
498- }
499528 if (VersionFinder .JAVA_MAJOR_VERSION < 9 ) {
500529 if (attachmentMethod == null ) {
501530 if (log != null ) {
@@ -512,46 +541,68 @@ private static boolean closeDirectByteBufferPrivileged(final ByteBuffer byteBuff
512541 return false ;
513542 }
514543 // Invoke ((DirectBuffer) byteBuffer).cleaner().clean()
515- final Method cleanerMethod = byteBuffer .getClass ().getMethod ("cleaner" );
516- if (cleanerMethod == null ) {
544+ if (directByteBufferCleanerMethod == null ) {
517545 if (log != null ) {
518546 log .log ("Could not unmap ByteBuffer, cleanerMethod == null" );
519547 }
520548 return false ;
521549 }
522550 try {
523- cleanerMethod .setAccessible (true );
551+ directByteBufferCleanerMethod .setAccessible (true );
524552 } catch (final Exception e ) {
525553 if (log != null ) {
526554 log .log ("Could not unmap ByteBuffer, cleanerMethod.setAccessible(true) failed" );
527555 }
528556 return false ;
529557 }
530- final Object cleaner = cleanerMethod .invoke (byteBuffer );
531- if (cleaner == null ) {
558+ final Object cleanerInstance = directByteBufferCleanerMethod .invoke (byteBuffer );
559+ if (cleanerInstance == null ) {
532560 if (log != null ) {
533561 log .log ("Could not unmap ByteBuffer, cleaner == null" );
534562 }
535563 return false ;
536564 }
565+ if (cleanerCleanMethod == null ) {
566+ if (log != null ) {
567+ log .log ("Could not unmap ByteBuffer, cleanMethod == null" );
568+ }
569+ return false ;
570+ }
537571 try {
538- cleanMethod .invoke (cleaner );
572+ cleanerCleanMethod .invoke (cleanerInstance );
539573 return true ;
540574 } catch (final Exception e ) {
541575 if (log != null ) {
542576 log .log ("Could not unmap ByteBuffer, cleanMethod.invoke(cleaner) failed: " + e );
543577 }
544578 return false ;
545579 }
580+ // } else if (memorySegmentOfByteBufferMethod != null) {
581+ // // JDK 14+
582+ // final Object memorySegment = memorySegmentOfByteBufferMethod.invoke(null, byteBuffer);
583+ // if (memorySegment == null) {
584+ // if (log != null) {
585+ // log.log("Got null MemorySegment, could not unmap ByteBuffer");
586+ // }
587+ // return false;
588+ // }
589+ // memorySegmentCloseMethod.invoke(memorySegment);
590+ // return true;
546591 } else {
547592 if (theUnsafe == null ) {
548593 if (log != null ) {
549594 log .log ("Could not unmap ByteBuffer, theUnsafe == null" );
550595 }
551596 return false ;
552597 }
598+ if (cleanerCleanMethod == null ) {
599+ if (log != null ) {
600+ log .log ("Could not unmap ByteBuffer, cleanMethod == null" );
601+ }
602+ return false ;
603+ }
553604 try {
554- cleanMethod .invoke (theUnsafe , byteBuffer );
605+ cleanerCleanMethod .invoke (theUnsafe , byteBuffer );
555606 return true ;
556607 } catch (final IllegalArgumentException e ) {
557608 // Buffer is a duplicate or slice
0 commit comments