/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* 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.geotoolkit.factory;
import java.util.Iterator;
import javax.imageio.spi.ServiceRegistry;
import org.apache.sis.util.ArraysExt;
import org.geotoolkit.internal.Threads;
/**
* Disposes every factories on JVM shutdown. Performs also other shutdown service that
* are better to be executed only after factories disposal, like executors shutdown.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.19
*
* @see Threads#ensureShutdownHookRegistered()
*
* @since 3.00
* @module
*/
public final class ShutdownHook extends Thread {
/**
* The shutdown hook registered to the JVM, stored for allowing unregistration.
*/
private static ShutdownHook SHUTDOWN_HOOK = new ShutdownHook();
static {
Runtime.getRuntime().addShutdownHook(SHUTDOWN_HOOK);
}
/**
* The registries on which to dispose the factories on shutdown.
*/
private ServiceRegistry[] registries;
/**
* A hook for deleting temporary files.
*
* Rational: we do not invoke {@code TemporaryFile.deleteAll()} directly because we do not want
* to trig class loading at shutdown time if the application did not created any temporary file.
* This is important for avoiding NoClassDefError in environments like Tomcat which may not give
* access to the classloader able to load TemporaryFile at this point.
*/
private Runnable deleteTemporaryFiles;
/**
* Creates the singleton instance.
*/
private ShutdownHook() {
super(Threads.RESOURCE_DISPOSERS, "ShutdownHook");
}
/**
* Run hook and remove it from JVM shutdown.
*/
public synchronized static void runAndremove() {
if (Runtime.getRuntime().removeShutdownHook(SHUTDOWN_HOOK)) {
SHUTDOWN_HOOK.run();
SHUTDOWN_HOOK = null;
}
}
/**
* Adds the given registry to the list of registry to dispose on shutdown.
*/
static synchronized void register(final ServiceRegistry registry) {
final ShutdownHook hook = SHUTDOWN_HOOK;
if (hook != null) {
ServiceRegistry[] registries = hook.registries;
if (registries == null) {
registries = new ServiceRegistry[] {registry};
} else {
registries = ArraysExt.append(registries, registry);
}
hook.registries = registries;
}
}
/**
* Register a runnable to execute for deleting temporary files.
* This is used by {@link org.geotoolkit.internal.io.TemporaryFile} only.
*
* @param runnable Executable to run for deleting temporary files.
*/
public static synchronized void registerFileDeletor(final Runnable runnable) {
final ShutdownHook hook = SHUTDOWN_HOOK;
if (hook != null) {
hook.deleteTemporaryFiles = runnable;
}
}
/**
* Disposes every factories. Note that some factories perform their disposal work in a
* background thread, so we need to shutdown the thread executor only after we finished
* to requested the disposal of every factories.
*/
@Override
public void run() {
final ServiceRegistry[] registries;
final Runnable deleteTemporaryFiles;
synchronized (ShutdownHook.class) {
registries = this.registries;
deleteTemporaryFiles = this.deleteTemporaryFiles;
this.registries = null;
this.deleteTemporaryFiles = deleteTemporaryFiles;
}
if (registries != null) {
for (final ServiceRegistry registry : registries) {
for (final Iterator<Class<?>> it=registry.getCategories(); it.hasNext();) {
final Class<?> category = it.next();
for (final Iterator<?> i=registry.getServiceProviders(category, false); i.hasNext();) {
final Object factory = i.next();
if (factory instanceof Factory) {
((Factory) factory).dispose(true);
}
}
}
}
}
/*
* The following method should be invoked only when we think there is not any code still
* runnning that may invoke Threads.executor(boolean). It is actually hard to ensure that,
* but a search on Threads.SHUTDOWN_HOOKS and Threads.executeDisposal(Runnable) is helpful.
*/
Threads.shutdown();
/*
* Delete the temporary file after there is presumably no running service.
*/
if (deleteTemporaryFiles != null) {
deleteTemporaryFiles.run();
}
}
}