/* Copyright (c) 2015, Adam Retter All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Adam Retter Consulting nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Adam Retter BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.exist.util.io; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Stack; import java.util.UUID; import java.util.stream.Stream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.exist.util.FileUtils; /** * Temporary File Manager * * Attempts to create and delete temporary files working around the issues of * some JDK platforms (e.g. Windows). Where deleting files is impossible, used * but finished with temporary files will be re-used where possible if they * cannot be deleted. * * @version 1.0 * * @author Adam Retter <adam.retter@googlemail.com> */ public class TemporaryFileManager { private final static Log LOG = LogFactory.getLog(TemporaryFileManager.class); private final static String FOLDER_PREFIX = "_mmtfm_"; private final Stack<Path> available = new Stack<>(); private final Path tmpFolder; private final static TemporaryFileManager instance = new TemporaryFileManager(); public static TemporaryFileManager getInstance() { return instance; } private TemporaryFileManager() { cleanupOldTempFolders(); try { this.tmpFolder = Files.createTempDirectory(FOLDER_PREFIX + UUID.randomUUID().toString()); } catch(final IOException ioe) { throw new RuntimeException("Unable to create temporary folder", ioe); } //add hook to JVM to delete the file on exit //unfortunately this does not always work on all (e.g. Windows) platforms //will be recovered on restart by cleanupOldTempFolders tmpFolder.toFile().deleteOnExit(); LOG.info("Temporary folder is: " + tmpFolder.toAbsolutePath().toString()); } public final Path getTemporaryFile() throws IOException { Path tempFile = null; synchronized(available) { if(!available.empty()) { tempFile = available.pop(); } } if(tempFile == null) { tempFile = Files.createTempFile(tmpFolder, "mmtf_" + System.currentTimeMillis(), ".tmp"); //add hook to JVM to delete the file on exit //unfortunately this does not always work on all (e.g. Windows) platforms tempFile.toFile().deleteOnExit(); } return tempFile; } public void returnTemporaryFile(final Path tempFile) { //Check if tempFile is still present .. if (Files.exists(tempFile)) { boolean deleted = false; try { deleted = Files.deleteIfExists(tempFile); } catch(final IOException e) { LOG.error("Unable to delete temporary file: " + tempFile.toAbsolutePath().toString(), e); } if(deleted) { LOG.debug("Deleted temporary file: " + tempFile.toAbsolutePath().toString()); } else { LOG.debug("Could not delete temporary file: " + tempFile.toAbsolutePath().toString() + ". Returning to stack for re-use."); //if we couldnt delete it, add it to the stack of available files //for reuse in the future. //Typically there are problems deleting these files on Windows //platforms which is why this facility was added synchronized(available) { //Check if tempFile is not allready present in stack ... if (available.contains(tempFile)) { LOG.debug("Temporary file: " + tempFile.toAbsolutePath().toString() + " already in stack. Skipping."); } else { available.push(tempFile); } } } } else { LOG.debug("Trying to delete non existing file: " + tempFile.toAbsolutePath().toString()); } } /** * Called at startup to attempt to cleanup * any left-over temporary folders * from the last time this was run */ private void cleanupOldTempFolders() { final Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir")); try(final Stream<Path> tmpFiles = Files.list(tmpDir)) { tmpFiles .filter(path -> Files.isDirectory(path) && path.startsWith(FOLDER_PREFIX)) .forEach(FileUtils::deleteQuietly); } catch(final IOException ioe) { LOG.error("Unable to delete old temporary folders", ioe); } } @Override protected void finalize() throws Throwable { try { //remove references to available files available.clear(); //try and remove our temporary folder FileUtils.deleteQuietly(tmpFolder); } finally { super.finalize(); } } }