All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jackrabbit.util.TransientFileFactory Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.jackrabbit.util;

import java.io.File;
import java.io.IOException;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Collections;

/**
 * The TransientFileFactory utility class can be used to create
 * transient files, i.e. temporary files that are automatically
 * removed once the associated File object is reclaimed by the
 * garbage collector.
 * 

* File deletion is handled by a low-priority background thread. *

*/ public class TransientFileFactory { /** * The singleton factory instance */ private static TransientFileFactory INSTANCE; /** * Queue where MoribundFileReference instances will be enqueued * once the associated target File objects have been gc'ed. */ private final ReferenceQueue phantomRefQueue = new ReferenceQueue(); /** * Collection of MoribundFileReference instances currently * being tracked. */ private final Collection trackedRefs = Collections.synchronizedList(new ArrayList()); /** * The reaper thread responsible for removing files awaiting deletion */ private final ReaperThread reaper; /** * Shutdown hook which removes all files awaiting deletion */ private static Thread shutdownHook = null; /** * Returns the singleton TransientFileFactory instance. */ public static TransientFileFactory getInstance() { synchronized (TransientFileFactory.class) { if (INSTANCE == null) { INSTANCE = new TransientFileFactory(); } return INSTANCE; } } /** * Hidden constructor. */ private TransientFileFactory() { // instantiate & start low priority reaper thread reaper = new ReaperThread("Transient File Reaper"); reaper.setPriority(Thread.MIN_PRIORITY); reaper.setDaemon(true); reaper.start(); // register shutdownhook for final cleaning up try { shutdownHook = new Thread() { public void run() { doShutdown(); } }; Runtime.getRuntime().addShutdownHook(shutdownHook); } catch (IllegalStateException e) { // can't register shutdownhook because // jvm shutdown sequence has already begun, // silently ignore... } } //------------------------------------------------------< factory methods > /** * Same as {@link File#createTempFile(String, String, File)} except that * the newly-created file will be automatically deleted once the * returned File object has been gc'ed. * * @param prefix The prefix string to be used in generating the file's * name; must be at least three characters long * @param suffix The suffix string to be used in generating the file's * name; may be null, in which case the * suffix ".tmp" will be used * @param directory The directory in which the file is to be created, or * null if the default temporary-file * directory is to be used * @return the newly-created empty file * @throws IOException If a file could not be created */ public File createTransientFile(String prefix, String suffix, File directory) throws IOException { File f = File.createTempFile(prefix, suffix, directory); trackedRefs.add(new MoribundFileReference(f, phantomRefQueue)); return f; } /** * Shuts this factory down removing all temp files and removes shutdown hook. *

* Warning!!! *

* This should be called by a web-application IF it is unloaded * AND IF jackrabbit-jcr-commons.jar had been loaded by * the webapp classloader. This must be called after all repositories had * been stopped, so use with great care! *

* See http://issues.apache.org/jira/browse/JCR-1636 for details. */ 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();) { it.next().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... } shutdownHook = null; } reaper.stopWorking(); } //--------------------------------------------------------< inner classes > /** * The reaper thread that will remove the files that are ready for deletion. */ private class ReaperThread extends Thread { private volatile boolean stopping = false; ReaperThread(String name) { super(name); } /** * Run the reaper thread that will delete files as their associated * marker objects are reclaimed by the garbage collector. */ public void run() { while (!stopping) { MoribundFileReference fileRef = null; try { // wait until a MoribundFileReference is ready for deletion fileRef = (MoribundFileReference) phantomRefQueue.remove(); } catch (InterruptedException e) { if (stopping) { break; } } catch (Exception e) { // silently ignore... continue; } // delete target fileRef.delete(); fileRef.clear(); trackedRefs.remove(fileRef); } } /** * Stops the reaper thread. */ public void stopWorking() { stopping = true; interrupt(); } } /** * Tracker object for a file pending deletion. */ private static class MoribundFileReference extends PhantomReference { /** * The full path to the file being tracked. */ private final String path; /** * Constructs an instance of this class from the supplied parameters. * * @param file The file to be tracked. * @param queue The queue on to which the tracker will be pushed. */ MoribundFileReference(File file, ReferenceQueue queue) { super(file, queue); this.path = file.getPath(); } /** * Deletes the file associated with this instance. * * @return true if the file was deleted successfully; * false otherwise. */ boolean delete() { return new File(path).delete(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy