blob: 2967472563e66071429800f3fd5fca7434b640e6 [file] [log] [blame]
// Copyright 2004 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry.util;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.tapestry.Tapestry;
/**
* A basic kind of janitor, an object that periodically invokes {@link ICleanable#executeCleanup()}
* on a set of objects.
* <p>
* The JanitorThread holds a <em>weak reference</em> to the objects it operates on.
*
* @author Howard Lewis Ship
* @version $Id$
* @since 1.0.5
*/
public class JanitorThread extends Thread
{
/**
* Default number of seconds between janitor runs, about 30 seconds.
*/
public static final long DEFAULT_INTERVAL_MILLIS = 30 * 1024;
private long interval = DEFAULT_INTERVAL_MILLIS;
private boolean lockInterval = false;
private static JanitorThread shared = null;
/**
* A {@link List}of {@link WeakReference}s to {@link ICleanable}instances.
*/
private List references = new ArrayList();
/**
* Creates a new daemon Janitor.
*/
public JanitorThread()
{
this(null);
}
/**
* Creates new Janitor with the given name. The thread will have minimum priority and be a
* daemon.
*/
public JanitorThread(String name)
{
super(name);
setDaemon(true);
setPriority(MIN_PRIORITY);
}
/**
* Returns a shared instance of JanitorThread. In most cases, the shared instance should be
* used, rather than creating a new instance; the exception being when particular scheduling is
* of concern. It is also bad policy to change the sleep interval on the shared janitor (though
* nothing prevents this, either).
*/
public synchronized static JanitorThread getSharedJanitorThread()
{
if (shared == null)
{
shared = new JanitorThread("Shared-JanitorThread");
shared.lockInterval = true;
shared.start();
}
return shared;
}
public long getInterval()
{
return interval;
}
/**
* Updates the property, which may not take effect until the next time the thread finishes
* sleeping.
*
* @param value
* the interval, in milliseconds, between sweeps.
* @throws IllegalStateException
* always, if the receiver is the shared JanitorThread
* @throws IllegalArgumentException
* if value is less than 1
*/
public void setInterval(long value)
{
if (lockInterval)
throw new IllegalStateException(Tapestry.getMessage("JanitorThread.interval-locked"));
if (value < 1)
throw new IllegalArgumentException(Tapestry
.getMessage("JanitorThread.illegal-interval"));
interval = value;
}
/**
* Adds a new cleanable object to the list of references. Care should be taken that objects are
* not added multiple times; they will be cleaned too often.
*/
public void add(ICleanable cleanable)
{
WeakReference reference = new WeakReference(cleanable);
synchronized (references)
{
references.add(reference);
}
}
/**
* Runs through the list of targets and invokes {@link ICleanable#executeCleanup()}on each of
* them. {@link WeakReference}s that have been invalidated are weeded out.
*/
protected void sweep()
{
synchronized (references)
{
Iterator i = references.iterator();
while (i.hasNext())
{
WeakReference ref = (WeakReference) i.next();
ICleanable cleanable = (ICleanable) ref.get();
if (cleanable == null)
i.remove();
else
cleanable.executeCleanup();
}
}
}
/**
* Waits for the next run, by sleeping for the desired period. Returns true if the sleep was
* successful, or false if the thread was interrupted (and should shut down).
*/
protected void waitForNextPass()
{
try
{
sleep(interval);
}
catch (InterruptedException ex)
{
interrupt();
}
}
/**
* Alternates between {@link #waitForNextPass()}and {@link #sweep()}.
*/
public void run()
{
while (!isInterrupted())
{
waitForNextPass();
sweep();
}
}
public String toString()
{
StringBuffer buffer = new StringBuffer("JanitorThread@");
buffer.append(Integer.toHexString(hashCode()));
buffer.append("[interval=");
buffer.append(interval);
buffer.append(" count=");
synchronized (references)
{
buffer.append(references.size());
}
buffer.append(']');
return buffer.toString();
}
}