All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metsci.glimpse.util.ugly.CleanerUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020, Metron, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Metron, Inc. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.metsci.glimpse.util.ugly;

import static com.metsci.glimpse.util.ugly.UglyUtils.findClass;
import static com.metsci.glimpse.util.ugly.UglyUtils.firstSuccessfulReturnValue;
import static com.metsci.glimpse.util.ugly.UglyUtils.requireDeclaredMethod;

import java.lang.reflect.Method;
import java.util.concurrent.ThreadFactory;

/**
 * Cleaners serve the same purpose as finalize() methods, with 2 subtle differences:
 * 
    *
  1. finalize() methods are better when resource disposal is non-trivial and/or slow *
  2. The JVM does a better job of running Cleaners promptly *
*

* When a registered Object becomes eligible to be GC-ed, its corresponding Cleaner gets * magically triggered via the JVM's PhantomReference mechanism. *

* It is easy to write code that uses Java 8 Cleaners, and it is easy to write code that * uses Java 9+ Cleaners. However, it is painful to write code that can use either Java 8 * or Java 9+ Cleaners. The goal of this class is to encapsulate and hide that pain. *

* Java 9+ supports creation of Cleaners that use specific {@link ThreadFactory}s. That * capability is not supported by Java 8, and therefore not supported by this class. */ public class CleanerUtils { /** * Like {@code java.lang.ref.Cleaner.Cleanable}, but available in Java 8. */ public static interface Cleanable { void clean( ); } protected static interface CleanerImpl { Cleanable register( Object obj, Runnable action ); } /** * Cleaner impl for Java 8 JVMs. *

* Also works for Java 9+ JVMs when {@link CleanerUtils} is on the classpath (not the modulepath). */ protected static class CleanerImpl1 implements CleanerImpl { protected final Method Cleaner_create; protected final Method Cleaner_clean; public CleanerImpl1( ) throws Exception { Class Cleaner_class = findClass( "jdk.internal.ref.Cleaner", "sun.misc.Cleaner" ); this.Cleaner_create = requireDeclaredMethod( Cleaner_class, "create", Object.class, Runnable.class ); this.Cleaner_clean = requireDeclaredMethod( Cleaner_class, "clean" ); } @Override public Cleanable register( Object obj, Runnable action ) { try { Object cleaner = this.Cleaner_create.invoke( null, obj, action ); return ( ) -> { try { this.Cleaner_clean.invoke( cleaner ); } catch ( ReflectiveOperationException e ) { throw new RuntimeException( e ); } }; } catch ( ReflectiveOperationException e ) { throw new RuntimeException( e ); } } } /** * Cleaner impl for Java 9+ JVMs. */ protected static class CleanerImpl2 implements CleanerImpl { protected final Object cleaner; protected final Method Cleaner_register; protected final Method Cleanable_clean; public CleanerImpl2( ) throws Exception { Class Cleaner_class = Class.forName( "java.lang.ref.Cleaner" ); Method Cleaner_create = requireDeclaredMethod( Cleaner_class, "create" ); this.cleaner = Cleaner_create.invoke( null ); this.Cleaner_register = requireDeclaredMethod( Cleaner_class, "register", Object.class, Runnable.class ); Class Cleanable_class = Class.forName( "java.lang.ref.Cleaner$Cleanable" ); this.Cleanable_clean = requireDeclaredMethod( Cleanable_class, "clean" ); } @Override public Cleanable register( Object obj, Runnable action ) { try { Object cleanable = this.Cleaner_register.invoke( this.cleaner, obj, action ); return ( ) -> { try { this.Cleanable_clean.invoke( cleanable ); } catch ( ReflectiveOperationException e ) { throw new RuntimeException( e ); } }; } catch ( ReflectiveOperationException e ) { throw new RuntimeException( e ); } } } protected static final CleanerImpl cleanerImpl = firstSuccessfulReturnValue( CleanerImpl2::new, CleanerImpl1::new ); /** * IMPORTANT: {@code action} MUST NOT refer to {@code obj} ... if * {@code action} does hold a reference to {@code obj}, it will prevent {@code obj} * from becoming eligible for cleaning. *

* A good way to avoid accidental references to {@code obj} is to avoid using a lambda, * or even an inner class, for {@code action}. Use a static class instead: *

     * public class SomeClass
     * {
     *     // Static class -- not inner class, not lambda
     *     private static class CleaningAction implements Runnable
     *     {
     *         private final Object thingNeededDuringCleanup1;
     *         private final Object thingNeededDuringCleanup2;
     *
     *         public CleaningAction( Object thingNeededDuringCleanup1, Object thingNeededDuringCleanup2 )
     *         {
     *             this.thingNeededDuringCleanup1 = thingNeededDuringCleanup1;
     *             this.thingNeededDuringCleanup2 = thingNeededDuringCleanup2;
     *         }
     *
     *         {@literal @}Override
     *         public void run( )
     *         {
     *             // Do cleanup
     *         }
     *     }
     *
     *     private final Cleanable cleanable;
     *
     *     public SomeClass( )
     *     {
     *         this.cleanable = UglyUtils.registerCleaner( this, new CleaningAction( ... ) );
     *     }
     * }
     * 
*/ public static Cleanable registerCleaner( Object obj, Runnable action ) { return cleanerImpl.register( obj, action ); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy