uk.org.retep.util.reference.AbstractDelayedWeakReference Maven / Gradle / Ivy
/*
* 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 uk.org.retep.util.thread.DelayedRunnableAdaptor;
import static uk.org.retep.util.reference.ReferenceFactory.*;
/**
* A weak reference which is held strongly for a set period after the last access
* to it. Once that period has expired it will then be available to the Garbage
* Collector for disposal.
*
* @param Type of the referenced object
* @author peter
* @Since 9.6
*/
public abstract class AbstractDelayedWeakReference
extends CleanableWeakReference
{
/**
* The default timeout of 10 seconds
*/
public static final long DEFAULT_TIMEOUT = 10000L;
/**
* The timeout for this reference
*/
private long timeout;
/**
* Hard reference to the object
*/
private T o;
/**
* Time when the object was last touched
*/
private long touched;
/**
* Our timer which will handle the timeout
*/
private DelayedRunnableAdaptor task;
/**
* Create a weak reference with timeout.
* @param o the referent
*/
public AbstractDelayedWeakReference( final T o )
{
this( DEFAULT_TIMEOUT, o );
}
public AbstractDelayedWeakReference( final long timeout, final T o )
{
super( o );
this.timeout = timeout;
this.o = o;
touched = System.currentTimeMillis();
schedule( timeout );
}
@Override
public void cleanupReference()
{
if( o != null )
{
final long unused = System.currentTimeMillis() - touched;
if( unused > timeout / 2 )
{
o = null;
touched = 0;
unschedule();
// NB: Dont place unscheduled insude unschedule due to locking
unscheduled();
}
else
{
schedule( timeout - (int) unused );
}
}
else
{
remove( super.get() );
}
}
/**
* Called when we remove the hard reference
* @param o Object to remove or null if no longer present
*/
protected abstract void remove( T o );
/**
* {@inheritDoc }
*/
@Override
public synchronized T get()
{
if( o == null )
{
o = super.get();
}
if( o != null )
{
if( touched == 0 )
{
schedule( timeout );
}
touched = System.currentTimeMillis();
touched();
return o;
}
else
{
return null;
}
}
/**
* Called whenever the entry is touched
*/
protected void touched()
{
}
/**
* Return the value held by this reference.
*
* Unlike {@link #get()} which ensures that the object will remain in memory
* for the required delay, this method attempts to allow the object to be
* removed by the garbage collector at its earliest convenience
* @return Value or null if it's already been removed
*/
public synchronized T getAndRelease()
{
// Remove from the schedule, remove the hard reference and set touched
// to the epoch. This way we are giving the gc all the hints to remove
// the object
unschedule();
o = null;
touched = 0L;
return super.get();
}
/**
* Returns the hashCode of the managed Object
* @return hashCode of the managed Object or 0 if null (i.e. removed from
* memory by the Garbage Collector)
*/
public synchronized int getHashCode()
{
final T v = get();
return v == null ? 0 : v.hashCode();
}
private final synchronized void schedule( final long delay )
{
if( task == null )
{
task = new DelayedRunnableAdaptor( delay )
{
@Override
public void run()
{
cleanupReference();
}
};
}
else
{
task.setDelay( delay );
}
getDelayThreadPoolExecutor().execute( task );
}
private final synchronized void unschedule()
{
if( task != null )
{
getDelayThreadPoolExecutor().remove( task );
}
}
/**
* Called whenever we are unscheduled
*/
protected void unscheduled()
{
}
}