/* * 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.ode.utils.fs; import org.apache.ode.utils.GUID; import org.apache.ode.utils.SystemUtils; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Convenience class for managing temporary files and cleanup on JVM exit. */ public class TempFileManager { private static final Log __log = LogFactory.getLog(TempFileManager.class); private static TempFileManager __singleton; private static File __baseDir; private static File __workDir; private SortedSet<File> _registeredFiles = new TreeSet<File>(Collections.reverseOrder(null)); private static synchronized TempFileManager getInstance() { if (__singleton == null) { __singleton = new TempFileManager(); } return __singleton; } private TempFileManager() { super(); if (__baseDir == null) { String tmpDirPath = null; try { tmpDirPath = SystemUtils.javaTemporaryDirectory(); } catch (SecurityException se) { __log.error("Unable to read system property for temporary directory setting; " + "will use default configuration."); tmpDirPath = ""; } File tmpDir = new File(tmpDirPath); if (tmpDir.exists()) { __baseDir = tmpDir; } else { throw new IllegalStateException("Odd system configuration - temporary working directory " + tmpDirPath + " does not exist."); } } try { File odeTmp = new File(__baseDir, "ode-" + new GUID().toString()); if (odeTmp.mkdir()) { __workDir = odeTmp; __log.debug("Set working directory to: " + __workDir.getAbsolutePath()); this._registerTemporaryFile(__workDir); } else { throw new IllegalStateException("Unable to create temporary working directory in " + __baseDir.getPath()); } } catch (SecurityException se) { throw new IllegalStateException("The security configuration is preventing the creation of a " + "temporary working directory.", se); } } /** * <p> * Set the working temporary directory. This method can only be invoked when * the singleton instance is uninitialized, and the <code>File</code> passed in * must be both a directory and writable. * </p> * @param f the temporary working directory */ public static synchronized void setWorkingDirectory(File f) { if (__singleton == null) { if (f == null) { __baseDir = null; } else { if (f.isDirectory() && f.canWrite()) { __baseDir = f; __log.debug("Setting base working directory: " + f); } else { throw new IllegalArgumentException("Not a writeable directory: " + f); } } } // cannot set working directory after an instance has been created; // call cleanup() first. else { String msg; if (__baseDir != null) { msg = "Already initialized in base directory: " + __baseDir.getPath(); } else { msg = "Already initialized, but no base directory set."; } throw new IllegalStateException(msg); } } /** * <p> * Get a temporary file, if possible, and register it for cleanup later. In the * event that a temporary file cannot be created, the method will attempt to * create a file in the current working directory instead. * </p> * * @param handle a prefix to use in naming the file; probably only useful for * debugging. * @return the temporary file. */ public static synchronized File getTemporaryFile(String handle) { return getTemporaryFile(handle, __workDir); } public static synchronized File getTemporaryFile(String handle, File parent) { // force initialization if necessary if (__singleton == null) { getInstance(); } if (handle == null) { handle = "temp-"; } if (parent == null) { parent = (__workDir != null ? __workDir : __baseDir); } File tmp; try { tmp = File.createTempFile(handle + Long.toHexString(System.currentTimeMillis()), ".tmp", parent); } catch (IOException ioe) { __log.error("Unable to create temporary file in working directory " + (parent == null ? "<null>; " : (parent.getPath() + "; ")) + "falling back to current working directory.", ioe); tmp = new File(handle + new GUID().toString()); } registerTemporaryFile(tmp); return tmp; } /** * <p> * Get a temporary working directory. * </p> * * @param handle a prefix to use in naming the directory. * @return the temp directory. * @see #getTemporaryFile(String) */ public static synchronized File getTemporaryDirectory(String handle) { return getTemporaryDirectory(handle,null); } public static synchronized File getTemporaryDirectory(String handle, File parent) { File f = getTemporaryFile(handle, parent); f.delete(); f.mkdirs(); return f; } /* * Register an externally created file/directory for later cleanup. */ public static synchronized void registerTemporaryFile(File f) { getInstance()._registerTemporaryFile(f); } private synchronized void _registerTemporaryFile(File f) { _registeredFiles.add(f); __log.debug("Registered temporary file: " + f.getPath()); } /** * <p> * Clear out the temporary working directory. This can be called by, e.g., * a commandline tool or other client when it is known that all temporary * files can be deleted. * </p> */ public static synchronized void cleanup() { if (__singleton != null) { __singleton._cleanup(); __singleton = null; } else { __log.debug("No cleanup necessary."); } } @SuppressWarnings("unchecked") private synchronized void _cleanup() { try { // collect all subdirectory contents that still exist, ordered files-first SortedSet<File> allFiles = new TreeSet(Collections.reverseOrder(null)); for (File f: _registeredFiles) { if (f.exists()) { allFiles.addAll(FileUtils.directoryEntriesInPath(f)); } } __log.debug("cleaning up " + allFiles.size() + " files."); // now delete all files for (File f: allFiles) { __log.debug("deleting: " + f.getAbsolutePath()); if (f.exists() && !f.delete()) { __log.error("Unable to delete file " + f.getAbsolutePath() + "; this may be caused by a descriptor leak and should be reported."); // fall back to deletion on VM shutdown f.deleteOnExit(); } } } finally { _registeredFiles.clear(); __workDir = null; __log.debug("cleanup done."); } } }