Index: src/main/java/org/apache/jackrabbit/util/TransientFileFactory.java =================================================================== --- src/main/java/org/apache/jackrabbit/util/TransientFileFactory.java (revision 662061) +++ src/main/java/org/apache/jackrabbit/util/TransientFileFactory.java (working copy) @@ -59,6 +59,11 @@ private final Thread reaper; /** + * Shutdown hook which removes all files awaiting deletion + */ + private static Thread shutdownHook = null; + + /** * Returns the singleton TransientFileFactory instance. */ public static TransientFileFactory getInstance() { @@ -81,20 +86,12 @@ reaper.start(); // register shutdownhook for final cleaning up try { - Runtime.getRuntime().addShutdownHook(new Thread() { + shutdownHook = new Thread() { public void run() { - // synchronize on the list before iterating over it in order - // to avoid ConcurrentModificationException (JCR-549) - // @see java.lang.util.Collections.synchronizedList(java.util.List) - synchronized(trackedRefs) { - for (Iterator it = trackedRefs.iterator(); it.hasNext();) { - MoribundFileReference fileRef = (MoribundFileReference) it.next(); - fileRef.delete(); - } - - } + doShutdown(); } - }); + }; + Runtime.getRuntime().addShutdownHook(shutdownHook); } catch (IllegalStateException e) { // can't register shutdownhook because // jvm shutdown sequence has already begun, @@ -126,6 +123,45 @@ return f; } + /** + * Shuts this factory down removing all temp files. Also removes shutdown + * hook. + * This should be called by web-application IF jackrabbit-jcr-commons.jar + * was loaded by webapp classloader WHEN webapp is unloaded. + * This must be called after all repositories had been stopped, so use with + * care. + */ + public static void shutdown() { + getInstance().doShutdown(); + } + + /** + * Actually shuts factory down removing all temp files. This happens when + * VM shutdown hook works or when explicitly requested. + * Shutdown hook is removed. + */ + private synchronized void doShutdown() { + // synchronize on the list before iterating over it in order + // to avoid ConcurrentModificationException (JCR-549) + // @see java.lang.util.Collections.synchronizedList(java.util.List) + synchronized(trackedRefs) { + for (Iterator it = trackedRefs.iterator(); it.hasNext();) { + MoribundFileReference fileRef = (MoribundFileReference) it.next(); + fileRef.delete(); + } + + } + if (shutdownHook != null) { + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } catch (IllegalStateException e) { + // can't unregister shutdownhook because + // jvm shutdown sequence has already begun, + // silently ignore... + } + } + } + //--------------------------------------------------------< inner classes > /** * The reaper thread that will remove the files that are ready for deletion.