/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2001-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.util; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import org.geotools.util.logging.Logging; /** * A thread invoking {@link Reference#clear} on each enqueded reference. * This is usefull only if {@code Reference} subclasses has overridden * their {@code clear()} method in order to perform some cleaning. * This thread is used by {@link WeakHashSet} and {@link WeakValueHashMap}, * which remove their entry from the collection when {@link Reference#clear} * is invoked. * * @since 2.0 * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) */ public final class WeakCollectionCleaner extends Thread { /** * The default thread. */ public static final WeakCollectionCleaner DEFAULT = new WeakCollectionCleaner(); /** * List of reference collected by the garbage collector. * Those elements must be removed from {@link #table}. */ ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); /** * Constructs and starts a new thread as a daemon. This thread will be sleeping * most of the time. It will run only some few nanoseconds each time a new * {@link Reference} is enqueded. */ private WeakCollectionCleaner() { super("WeakCollectionCleaner"); setPriority(MAX_PRIORITY - 2); setDaemon(true); start(); } public synchronized ReferenceQueue<Object> getReferenceQueue() { return referenceQueue; } /** * Loop to be run during the virtual machine lifetime. */ @Override public void run() { ReferenceQueue<Object> rq; while ((rq = getReferenceQueue ()) != null) { try { // Block until a reference is enqueded. final Reference ref = rq.remove(); if (ref == null) { /* * Should never happen according Sun's Javadoc ("Removes the next reference * object in this queue, blocking until one becomes available."). However a * null reference seems to be returned during JVM shutdown on Linux. Wait a * few seconds in order to give the JVM a chance to kill this daemon thread * before the logging at the sever level, and stop the loop. We do not try * to resume the loop since something is apparently going wrong and we want * the user to be notified. See GEOT-1138. */ sleep(15 * 1000L); break; } ref.clear(); // Note: To be usefull, the clear() method must have been overridden in Reference // subclasses. This is what WeakHashSet.Entry and WeakHashMap.Entry do. } catch (InterruptedException exception) { // Somebody doesn't want to lets us sleep... Go back to work. } catch (Exception exception) { Logging.unexpectedException(WeakCollectionCleaner.class, "remove", exception); } catch (AssertionError exception) { Logging.unexpectedException(WeakCollectionCleaner.class, "remove", exception); // Do not kill the thread on assertion failure, in order to // keep the same behaviour as if assertions were turned off. } } Logging.getLogger(WeakCollectionCleaner.class).info("Weak collection cleaner stopped"); } /** * Stops the cleaner thread. Calling this method is recommended in all long running applications * with custom class loaders (e.g., web applications). */ public void exit() { // try to stop it gracefully synchronized (this) { referenceQueue = null; } this.interrupt(); try { this.join(500); } catch (InterruptedException e) { } // last resort tentative to kill the cleaner thread if (this.isAlive()) this.stop(); } }