// #THIRDPARTY# Apache commons-io /* * 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.xipki.commons.console.karaf.intern; import java.io.Closeable; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; import java.util.List; public class FileUtils { /** * The file copy buffer size (30 MB). */ private static final long FILE_COPY_BUFFER_SIZE = 1024L * 1024 * 30; private FileUtils() { } /** * Copied from the apache commons io project * * Deletes a directory recursively. * * @param directory - directory to delete * @throws IOException in case deletion is unsuccessful * @throws IllegalArgumentException if {@code directory} does not exist or is not a directory */ public static void deleteDirectory(final File directory) throws IOException { if (!directory.exists()) { return; } if (!isSymlink(directory)) { cleanDirectory(directory); } if (!directory.delete()) { throw new IOException("Unable to delete directory " + directory + "."); } } /** * Copied from the apache commons io project * * Determines whether the specified file is a Symbolic Link rather than an actual file. * <p> * Will not return true if there is a Symbolic Link anywhere in the path, * only if the specific file is. * <p> * <b>Note:</b> the current implementation always returns {@code false} if the system * is detected as Windows using {@link FilenameUtils#isSystemWindows()} * <p> * For code that runs on Java 1.7 or later, use the following method instead: * <br> * {@code boolean java.nio.file.Files.isSymbolicLink(Path path)} * @param file - the file to check * @return true if the file is a Symbolic Link * @throws IOException if an IO error occurs while checking the file * @since 2.0.0 */ public static boolean isSymlink(final File file) throws IOException { if (file == null) { throw new NullPointerException("File must not be null"); } if (Configuration.isWindows()) { return false; } File fileInCanonicalDir = (file.getParent() == null) ? file : new File(file.getParentFile().getCanonicalFile(), file.getName()); return !fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile()); } /** * Copied from the apache commons io project * * Cleans a directory without deleting it. * * @param directory - directory to clean * @throws IOException in case cleaning is unsuccessful * @throws IllegalArgumentException if {@code directory} does not exist or is not a directory */ public static void cleanDirectory(final File directory) throws IOException { if (!directory.exists()) { throw new IllegalArgumentException(directory + " does not exist"); } if (!directory.isDirectory()) { throw new IllegalArgumentException(directory + " is not a directory"); } final File[] files = directory.listFiles(); // null if security restricted if (files == null) { throw new IOException("Failed to list contents of " + directory); } IOException exception = null; for (final File file : files) { try { forceDelete(file); } catch (final IOException ioe) { exception = ioe; } } if (null != exception) { throw exception; } } /** * Copied from the apache commons io project * * Deletes a file. If file is a directory, delete it and all sub-directories. * <p> * The difference between File.delete() and this method are: * <ul> * <li>A directory to be deleted does not have to be empty.</li> * <li>You get exceptions when a file or directory must not be deleted. * (java.io.File methods returns a boolean)</li> * </ul> * * @param file - file or directory to delete, must not be {@code null} * @throws NullPointerException if the directory is {@code null} * @throws FileNotFoundException if the file was not found * @throws IOException in case deletion is unsuccessful */ public static void forceDelete(final File file) throws IOException { if (file.isDirectory()) { deleteDirectory(file); return; } final boolean filePresent = file.exists(); if (!file.delete()) { if (!filePresent) { throw new FileNotFoundException("File does not exist: " + file); } throw new IOException("Unable to delete file: " + file); } } /** * Copied from the apache commons io project * * Internal copy file method. * This caches the original file length, and an IOException will be thrown * if the output file length is different from the current input file length. * So it may fail if the file changes size. * It may also fail with "IllegalArgumentException: Negative size" if the * input file is truncated part way * through copying the data and the new file size is less than the current position. * * @param srcFile - the validated source file, must not be {@code null} * @param destFile - the validated destination file, must not be {@code null} * @param preserveFileDate whether to preserve the file date * @throws IOException if an error occurs * @throws IOException if the output file length is not the same as the input * file length after the copy completes * @throws IllegalArgumentException "Negative size" if the file is truncated so that the size * is less than the position */ public static void copyFile(final File srcFile, final File destFile, final boolean preserveFileDate) throws IOException { if (destFile.exists() && destFile.isDirectory()) { throw new IOException("Destination '" + destFile + "' exists but is a directory"); } FileInputStream fis = null; FileOutputStream fos = null; FileChannel input = null; FileChannel output = null; try { fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); input = fis.getChannel(); output = fos.getChannel(); final long size = input.size(); // See IO-386 long pos = 0; long count = 0; while (pos < size) { final long remain = size - pos; count = (remain > FILE_COPY_BUFFER_SIZE) ? FILE_COPY_BUFFER_SIZE : remain; final long bytesCopied = output.transferFrom(input, pos, count); if (bytesCopied == 0) { // IO-385 - can happen if file is truncated after caching the size break; // ensure we don't loop forever } pos += bytesCopied; } } finally { closeQuietly(output, fos, input, fis); } final long srcLen = srcFile.length(); // See IO-386 final long dstLen = destFile.length(); // See IO-386 if (srcLen != dstLen) { throw new IOException("Failed to copy full contents from '" + srcFile + "' to '" + destFile + "' Expected length: " + srcLen + " Actual: " + dstLen); } if (preserveFileDate) { destFile.setLastModified(srcFile.lastModified()); } } public static void copyDirectory(final File srcDir, final File destDir) throws IOException { copyDirectory(srcDir, destDir, null, true, null); } /** * Copied from the apache commons io project * * Internal copy directory method. * * @param srcDir - the validated source directory, must not be {@code null} * @param destDir - the validated destination directory, must not be {@code null} * @param filter - the filter to apply, null means copy all directories and files * @param preserveFileDate - whether to preserve the file date * @param exclusionList - List of files and directories to exclude from the copy, may be null * @throws IOException if an error occurs * @since 1.1 */ public static void copyDirectory(final File srcDir, final File destDir, final FileFilter filter, final boolean preserveFileDate, final List<String> exclusionList) throws IOException { // recurse final File[] srcFiles = (filter == null) ? srcDir.listFiles() : srcDir.listFiles(filter); if (srcFiles == null) { // null if abstract pathname does not denote a directory, or if an I/O error occurs throw new IOException("Failed to list contents of " + srcDir); } if (destDir.exists()) { if (!destDir.isDirectory()) { throw new IOException("Destination '" + destDir + "' exists but is not a directory"); } } else { if (!destDir.mkdirs() && !destDir.isDirectory()) { throw new IOException("Destination '" + destDir + "' directory cannot be created"); } } if (!destDir.canWrite()) { throw new IOException("Destination '" + destDir + "' cannot be written to"); } for (final File srcFile : srcFiles) { final File dstFile = new File(destDir, srcFile.getName()); if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) { if (srcFile.isDirectory()) { copyDirectory(srcFile, dstFile, filter, preserveFileDate, exclusionList); } else { copyFile(srcFile, dstFile, preserveFileDate); } } } // Do this last, as the above has probably affected directory metadata if (preserveFileDate) { destDir.setLastModified(srcDir.lastModified()); } } public static void closeQuietly(final Closeable... closeables) { if (closeables == null) { return; } for (final Closeable closeable : closeables) { doCloseQuietly(closeable); } } private static void doCloseQuietly(final Closeable closable) { if (closable == null) { return; } try { closable.close(); } catch (Throwable th) { // CHECKSTYLE:SKIP } } }