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

uk.org.retep.util.reference.ReferenceFactory Maven / Gradle / Ivy

There is a newer version: 10.6
Show newest version
/*
 * 

Copyright (c) 1998-2009, Peter T Mount
* 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 the retep.org.uk 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 THE COPYRIGHT OWNER * OR CONTRIBUTORS 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 uk.org.retep.util.reference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import net.jcip.annotations.ThreadSafe; import uk.org.retep.annotations.NoInstance; import uk.org.retep.logging.Log; import uk.org.retep.logging.LogFactory; import uk.org.retep.util.thread.DelayedRunnableThreadPoolExecutor; import uk.org.retep.util.thread.ExecutorFactory; import uk.org.retep.util.thread.GlobalThreadPool; /** * A factory for managing weak references. * * @author peter * @since 9.6 * @see CleanableWeakReference * @see DelayedWeakReference */ @ThreadSafe @NoInstance public final class ReferenceFactory { private static final Log log = LogFactory.getLog( ReferenceFactory.class ); private static DelayedRunnableThreadPoolExecutor delayPool; private static CleanableReferenceQueue referenceQueue; private static ThreadFactory threadFactory; private ReferenceFactory() { } static synchronized ThreadFactory getThreadFactory() { if( threadFactory == null ) { threadFactory = GlobalThreadPool.createDaemonThreadFactory( "ReferenceFactory" ); } return threadFactory; } /** * DelayedRunnableThreadPoolExecutor used just for Reference's. This * ThreadPoolExecutor is used primarily by {@link DelayedWeakReference} * * @return DelayedRunnableThreadPoolExecutor */ public static synchronized DelayedRunnableThreadPoolExecutor getDelayThreadPoolExecutor() { if( delayPool == null ) { if( log.isDebugEnabled() ) { log.debug( "Creating DelayedRunnableThreadPoolExecutor" ); } delayPool = new DelayedRunnableThreadPoolExecutor( 1, 10, 5L, TimeUnit.MINUTES, getThreadFactory() ); // Ensure we have the core thread started else the first entry will // run immediately delayPool.prestartCoreThread(); } return delayPool; } /** * A {@link ReferenceQueue} which will perform cleanup when a referenced * object is enqueued. * *

* If you have a reference that needs cleanup, make it implement * {@link ReferenceCleanup} and register it with the queue: *

* *
     * class MyReference extends CleanableWeakReference  {
     *     private final OtherInfo dataToCleanUp;
     *     public MyReference(Thing ref, OtherInfo data) {
     *         super(ref );
     *         dataToCleanUp = data;
     *     }
     *     public void cleanupReference() {
     *         dataToCleanUp.releaseOrWhateverYouNeed();
     *     }
     * }
     * 
* *

* Then when the ref object is garbage collected the * {@link CleanableWeakReference#cleanupReference()} method is then * called. *

* * @return */ public static synchronized ReferenceQueue getReferenceQueue() { if( referenceQueue == null ) { if( log.isDebugEnabled() ) { log.debug( "Creating ReferenceQueue" ); } referenceQueue = new CleanableReferenceQueue(); // Start the ReferenceQueue inside our ThreadFactory getThreadFactory().newThread( referenceQueue ).start(); } return referenceQueue; } /** Implementation of the active queue. */ private static final class CleanableReferenceQueue extends ReferenceQueue implements Runnable { /** * We don't support polling so throws {@link UnsupportedOperationException} * * @return */ @Override public Reference poll() { throw new UnsupportedOperationException(); } /** * As we use a background thread this throws {@link InterruptedException} * * @param timeout * @return * @throws java.lang.IllegalArgumentException * @throws java.lang.InterruptedException */ @Override public Reference remove( final long timeout ) throws IllegalArgumentException, InterruptedException { throw new InterruptedException(); } /** * As we use a background thread this throws {@link InterruptedException} * * @return * @throws java.lang.InterruptedException */ @Override public Reference remove() throws InterruptedException { throw new InterruptedException(); } @Override public void run() { if( log.isDebugEnabled() ) { log.debug( "starting ReferenceQueue" ); } while( true ) { try { // Block indefinitely until we get a reference Reference ref = super.remove( 0 ); if( log.isDebugEnabled() ) { log.debug( "dequeued reference %s", ref.toString() ); } if( !(ref instanceof CleanableWeakReference) ) { log.warn( "A reference not a subclass of CleanableWeakReference has been added to the Utilities.activeReferenceQueue(): %s", // NOI18N ref.getClass() ); } else { // do the cleanup try { ((CleanableWeakReference) ref).cleanupReference(); } catch( ThreadDeath td ) { if( log.isDebugEnabled() ) { log.debug( "ReferenceQueue ThreadDeath", td ); } throw td; } catch( Throwable t ) { log.fatal( "Throwable that should not happen has happened, please repoort it as a bug", t ); } finally { // to allow GC ref = null; } } } catch( InterruptedException ex ) { if( log.isDebugEnabled() ) { log.debug( "ReferenceQueue InterruptedException", ex ); } // This can happen occasionally when the VM is shutting down... continue; } } } } }