com.sun.enterprise.util.io.FileUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2006-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* Portions Copyright [2016] [Payara Foundation]
*/
package com.sun.enterprise.util.io;
import com.sun.enterprise.universal.i18n.LocalStringsImpl;
import com.sun.enterprise.universal.io.SmartFile;
import com.sun.enterprise.util.CULoggerInfo;
import com.sun.enterprise.util.OS;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.inject.Inject;
import org.jvnet.hk2.config.ConfigBean;
public class FileUtils {
final static Logger _utillogger = CULoggerInfo.getLogger();
private final static LocalStringsImpl messages = new LocalStringsImpl(FileUtils.class);
public static void setFileProperties()
{
Integer retryCounter = Integer.getInteger("com.sun.appserv.winFileLockRetryLimit");
Integer retryInterval = Integer.getInteger("com.sun.appserv.winFileLockRetryDelay");
if (retryCounter != null && retryCounter > 0) {
FILE_OPERATION_MAX_RETRIES=retryCounter;
}
if (retryInterval != null && retryInterval > 0) {
FILE_OPERATION_SLEEP_DELAY_MS=retryInterval;
}
}
/**
* The method, java.io.File.getParentFile() does not necessarily do what
* you would think it does. What it really does is to simply chop off the
* final element in the path and return what is left-over. E.g.
* if the file is /foo/. then the "parent" that is returned is /foo
* which is probably not what you expected.
* This method really returns the parent directory - or null if there is none.
* @param f
* @return
*/
public static File getParentFile(File f) {
if (f == null)
return null;
return SmartFile.sanitize(f).getParentFile();
}
/**
* Wrapper for File.mkdirs
* This version will return true if the directory exists when the method returns.
* Unlike File.mkdirs which returns false if the directory already exists.
* @param f The file pointing to the directory to be created
* @return
*/
public static boolean mkdirsMaybe(File f) {
return f != null && (f.isDirectory() || f.mkdirs());
}
/**
* Wrapper for File.delete
* This version will return true if the file does not exist when the method returns.
* Unlike File.delete which returns false if the file does not exist.
* @param f The file to be deleted
* @return
*/
public static boolean deleteFileMaybe(File f) {
return f != null && (!f.exists() || f.delete());
}
/*
* Wrapper for File.listFiles
* Guaranteed to return an array in all cases.
* File.listFiles() returns either null or an empty array. This is annoying and results in harder
* than neccessry to read code -- i.e. there are 3 results possible:
* an array with files in it
* an empty array
* a null
*/
public static File[] listFiles(File f) {
try {
File[] files = f.listFiles();
if(files != null)
return files;
}
catch(Exception e) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, e);
}
return new File[0];
}
public static File[] listFiles(File f, FileFilter ff) {
try {
File[] files = f.listFiles(ff);
if(files != null)
return files;
}
catch(Exception e) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, e);
}
return new File[0];
}
public static File[] listFiles(File f, FilenameFilter fnf) {
try {
File[] files = f.listFiles(fnf);
if(files != null)
return files;
}
catch(Exception e) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, e);
}
return new File[0];
}
///////////////////////////////////////////////////////////////////////////
public static boolean safeIsDirectory(File f) {
if (f == null || !f.exists() || !f.isDirectory())
return false;
return true;
}
///////////////////////////////////////////////////////////////////////////
public static boolean safeIsRealDirectory(String s) {
return safeIsRealDirectory(new File(s));
}
///////////////////////////////////////////////////////////////////////////
public static boolean safeIsRealDirectory(File f) {
if (safeIsDirectory(f) == false)
return false;
String canonical =null;
String absolute=null;
try {
canonical = safeGetCanonicalPath(f);
absolute = f.getParentFile().getCanonicalPath() + File.separator + f.getName();
_utillogger.log(Level.FINE,"Canonical path and abolute path values are " + canonical + " " + absolute);
if(canonical.equals(absolute)){
_utillogger.log(Level.FINE,"The directory " + absolute + " is a symbolic link false");
return true;
}
} catch (IOException ioe) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, ioe);
}
/* Bug 4715043 -- WHOA -- Bug Obscura!!
* In Windows, if you create the File object with, say, "d:/foo", then the
* absolute path will be "d:\foo" and the canonical path will be "D:\foo"
* and they won't match!!!
**/
if (OS.isWindows() && canonical.equalsIgnoreCase(absolute)){
_utillogger.log(Level.FINE,"The directory " + absolute + " is a symbolic link false");
return true;
}
_utillogger.log(Level.FINE,"The directory " + absolute + " is a symbolic link true");
return false;
}
///////////////////////////////////////////////////////////////////////////
public static boolean safeIsDirectory(String s) {
return safeIsDirectory(new File(s));
}
///////////////////////////////////////////////////////////////////////////
public static String safeGetCanonicalPath(File f) {
if (f == null)
return null;
try {
return f.getCanonicalPath();
}
catch (IOException e) {
return f.getAbsolutePath();
}
}
///////////////////////////////////////////////////////////////////////////
public static File safeGetCanonicalFile(File f) {
if (f == null)
return null;
try {
return f.getCanonicalFile();
}
catch (IOException e) {
return f.getAbsoluteFile();
}
}
///////////////////////////////////////////////////////////////////////////
public static boolean hasExtension(String filename, String ext) {
if (filename == null || filename.length() <= 0)
return false;
return filename.endsWith(ext);
}
///////////////////////////////////////////////////////////////////////////
public static boolean hasExtension(File f, String ext) {
if (f == null || !f.exists())
return false;
return f.getName().endsWith(ext);
}
///////////////////////////////////////////////////////////////////////////
public static boolean hasExtensionIgnoreCase(String filename, String ext) {
if (filename == null || filename.length() <= 0)
return false;
return filename.toLowerCase(Locale.ENGLISH).endsWith(ext.toLowerCase(Locale.ENGLISH));
}
///////////////////////////////////////////////////////////////////////////
public static boolean hasExtensionIgnoreCase(File f, String ext) {
if (f == null || !f.exists())
return false;
return f.getName().toLowerCase(Locale.ENGLISH).endsWith(ext.toLowerCase(Locale.ENGLISH));
}
///////////////////////////////////////////////////////////////////////////
public static boolean isLegalFilename(String filename) {
if (!isValidString(filename))
return false;
for (int i = 0; i < ILLEGAL_FILENAME_CHARS.length; i++)
if (filename.indexOf(ILLEGAL_FILENAME_CHARS[i]) >= 0)
return false;
return true;
}
///////////////////////////////////////////////////////////////////////////
public static boolean isFriendlyFilename(String filename) {
if (!isValidString(filename))
return false;
if (filename.indexOf(BLANK) >= 0 || filename.indexOf(DOT) >= 0)
return false;
return isLegalFilename(filename);
}
///////////////////////////////////////////////////////////////////////////
public static String makeLegalFilename(String filename) {
if (isLegalFilename(filename))
return filename;
// let's use "__" to replace "/" and "\" (on Windows) so less chance
// to collide with the actual name when reverting
filename = filename.replaceAll("[/" + Pattern.quote("\\") + "]", "__");
for (int i = 0; i < ILLEGAL_FILENAME_CHARS.length; i++)
filename = filename.replace(ILLEGAL_FILENAME_CHARS[i], REPLACEMENT_CHAR);
return filename;
}
///////////////////////////////////////////////////////////////////////////
public static String makeLegalNoBlankFileName(String filename)
{
return makeLegalFilename(filename).replace(
BLANK, REPLACEMENT_CHAR);
}
///////////////////////////////////////////////////////////////////////////
public static String makeFriendlyFilename(String filename) {
if (isFriendlyFilename(filename))
return filename;
String ret = makeLegalFilename(filename).replace(BLANK, REPLACEMENT_CHAR);
ret = ret.replace(DOT, REPLACEMENT_CHAR);
return ret;
}
///////////////////////////////////////////////////////////////////////////
public static String makeFriendlyFilenameNoExtension(String filename) {
int index = filename.lastIndexOf('.');
if (index > 0)
filename = filename.substring(0, index);
return (makeFriendlyFilename(filename));
}
///////////////////////////////////////////////////////////////////////////
public static String makeFriendlyFilenameExtension(String filename) {
if (filename == null) {
return null;
}
filename = makeLegalNoBlankFileName(filename);
String extension = "";
if (filename.endsWith(".ear")) {
filename = filename.substring(0, filename.indexOf(".ear"));
extension = "_ear";
} else if (filename.endsWith(".war")) {
filename = filename.substring(0, filename.indexOf(".war"));
extension = "_war";
} else if (filename.endsWith(".jar")) {
filename = filename.substring(0, filename.indexOf(".jar"));
extension = "_jar";
} else if (filename.endsWith(".rar")) {
filename = filename.substring(0, filename.indexOf(".rar"));
extension = "_rar";
}
return filename + extension;
}
public static String revertFriendlyFilenameExtension(String filename) {
if (filename == null ||
!(filename.endsWith("_ear") || filename.endsWith("_war") ||
filename.endsWith("_jar") || filename.endsWith("_rar"))) {
return filename;
}
String extension = "";
if (filename.endsWith("_ear")) {
filename = filename.substring(0, filename.indexOf("_ear"));
extension = ".ear";
} else if (filename.endsWith("_war")) {
filename = filename.substring(0, filename.indexOf("_war"));
extension = ".war";
} else if (filename.endsWith("_jar")) {
filename = filename.substring(0, filename.indexOf("_jar"));
extension = ".jar";
} else if (filename.endsWith("_rar")) {
filename = filename.substring(0, filename.indexOf("_rar"));
extension = ".rar";
}
return filename + extension;
}
public static String revertFriendlyFilename(String filename) {
//first, revert the file extension
String name = revertFriendlyFilenameExtension(filename);
//then, revert the rest of the string
return name.replaceAll("__", "/");
}
/////////////////////////////////////////////////////////
public static void liquidate(File parent) {
whack(parent);
}
///////////////////////////////////////////////////////////////////////////
public static boolean isJar(String filename)
{
return hasExtension(filename, ".jar");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isZip(String filename)
{
return hasExtensionIgnoreCase(filename, ".zip");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isJar(File f)
{
return hasExtension(f, ".jar");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isZip(File f)
{
return hasExtensionIgnoreCase(f, ".zip");
}
/**
* Deletes a directory and its contents.
*
* If this method encounters a symbolic link in the subtree below "parent"
* then it deletes the link but not any of the files pointed to by the link.
* Note that whack will delete files if a symbolic link appears in the
* path above the specified parent directory in the path.
*
* @param parent the File at the top of the subtree to delete
* @return success or failure of deleting the directory
*/
public static boolean whack(File parent) {
return whack(parent, null);
}
/**
* Deletes a directory and its contents.
*
* If this method encounters a symbolic link in the subtree below "parent"
* then it deletes the link but not any of the files pointed to by the link.
* Note that whack will delete files if a symbolic link appears in the
* path above the specified parent directory in the path.
*
* @param parent the File at the top of the subtree to delete
* @return success or failure of deleting the directory
*/
public static boolean whack(File parent, Collection undeletedFiles) {
try {
/*
*Resolve any links up-stream from this parent directory and
*then whack the resulting resolved directory.
*/
return whackResolvedDirectory(parent.getCanonicalFile(), undeletedFiles);
} catch (IOException ioe) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, ioe);
return false;
}
}
/**
* Deletes a directory and its contents.
*
* The whackResolvedDirectory method is invoked with a File argument
* in which any upstream file system links have already been resolved.
* This method will treate Any file passed in that does not have the same
* absolute and canonical path - as evaluated in safeIsRealDirectory -
* as a link and will delete the link without deleting any files in the
* linked directory.
*
* @param parent the File at the top of the subtree to delete
* @return success or failure of deleting the directory
*/
private static boolean whackResolvedDirectory(File parent, Collection undeletedFiles) {
/*
*Do not recursively delete the contents if the current parent
*is a symbolic link.
*/
if (safeIsRealDirectory(parent)) {
File[] kids = listFiles(parent);
for (int i = 0; i < kids.length; i++) {
File f = kids[i];
if (f.isDirectory())
whackResolvedDirectory(f, undeletedFiles);
else if (!deleteFile(f) && undeletedFiles != null) {
undeletedFiles.add(f);
}
}
}
/*
*Delete the directory or symbolic link.
*/
return deleteFile(parent);
}
/**
* Delete a file. If impossible to delete then try to delete it when the JVM exits.
* E.g. when Windows is using a jar in the current JVM -- you can not delete the jar until
* the JVM dies.
* @param f file to delete
*/
public static void deleteFileNowOrLater(File f) {
if(!deleteFile(f))
f.deleteOnExit();
}
/**
* Delete a file. Will retry every ten milliseconds for five seconds, doing a
* gc after each second.
*
* @param f file to delete
* @return boolean indicating success or failure of the deletion atttempt; returns true if file is absent
*/
public static boolean deleteFileWithWaitLoop(File f) {
return internalDeleteFile(f, true);
}
/**
* Delete a file. If on Windows and the delete fails, run the gc and retry the deletion.
*
* @param f file to delete
* @return boolean indicating success or failure of the deletion atttempt; returns true if file is absent
*/
public static boolean deleteFile(File f) {
return internalDeleteFile(f, false);
}
/**
* Delete a file. If on Windows and the delete fails, run the gc and retry the deletion.
*
* @param f file to delete
* @return boolean indicating success or failure of the deletion atttempt; returns true if file is absent
*/
private static boolean internalDeleteFile(File f, boolean doWaitLoop) {
/*
*The operation succeeds immediately if the file is deleted
*successfully. On systems that support symbolic links, the file
*will be reported as non-existent if the file is a sym link to a
*non-existent directory. In that case invoke delete to remove
*the link before checking for existence, since File.exists on
*a symlink checks for the existence of the linked-to directory or
*file rather than of the link itself.
*/
if (!doWaitLoop) {
if (f.delete()) {
return true;
}
}
else {
DeleteFileWork work = new DeleteFileWork(f);
doWithRetry(work);
if (work.workComplete()) {
return true;
}
}
boolean log = _utillogger.isLoggable(FILE_OPERATION_LOG_LEVEL);
String filePath = f.getAbsolutePath();
/*
*The deletion failed. This could be simply because the file
*does not exist. In that case, log an appropriate message and
*return.
*/
if (!f.exists()) {
if (log) {
_utillogger.log(Level.FINE, CULoggerInfo.deleteFailedAbsent, filePath);
}
return true;
} else {
/*
*The delete failed and the file exists. Log a message if that
*level is enabled and return false to indicate the failure.
*/
if (log) {
_utillogger.log(FILE_OPERATION_LOG_LEVEL, CULoggerInfo.deleteFailed, filePath);
}
return false;
}
}
/**
* Opens a stream to the specified output file, retrying if necessary.
*
* @param out the output File for which a stream is needed
* @return the FileOutputStream
* @throws IOException for any errors opening the stream
*/
public static FileOutputStream openFileOutputStream(File out) throws IOException {
FileOutputStreamWork work = new FileOutputStreamWork(out);
int retries = doWithRetry(work);
if (retries > 0) {
_utillogger.log(Level.FINE, "Retrying " + retries + " times");
}
if (work.workComplete()) {
return work.getStream();
} else {
IOException ioe = new IOException();
ioe.initCause(work.getLastError());
throw ioe;
}
}
/**
* Return a set of all the files (File objects) under the directory specified, with
* relative pathnames filtered with the filename filter (can be null for all files).
*/
public static Set getAllFilesUnder(File directory, FilenameFilter filenameFilter) throws IOException {
if (!directory.exists() || !directory.isDirectory()) {
throw new IOException("Problem with: " + directory + ". You must supply a directory that exists");
}
return getAllFilesUnder(directory, filenameFilter, true);
}
public static Set getAllFilesUnder(File directory, FilenameFilter filenameFilter, boolean relativize) throws IOException {
Set allFiles = new TreeSet();
File relativizingDir = relativize ? directory : null;
recursiveGetFilesUnder( relativizingDir, directory, filenameFilter,
allFiles, false );
return allFiles;
}
public static Set getAllFilesAndDirectoriesUnder(File directory) throws IOException {
if (!directory.exists() || !directory.isDirectory()) {
throw new IOException("Problem with: " + directory + ". You must supply a directory that exists");
}
Set allFiles = new TreeSet();
recursiveGetFilesUnder(directory, directory, null, allFiles, true);
return allFiles;
}
// relativizingRoot can be null, in which case no relativizing is
// performed.
private static void recursiveGetFilesUnder(File relativizingRoot, File directory, FilenameFilter filenameFilter, Set set, boolean returnDirectories) {
File[] files = listFiles(directory, filenameFilter);
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
recursiveGetFilesUnder(relativizingRoot, files[i], filenameFilter, set, returnDirectories);
if (returnDirectories) {
if( relativizingRoot != null ) {
set.add(relativize(relativizingRoot, files[i]));
} else {
set.add(files[i]);
}
}
} else {
if( relativizingRoot != null ) {
set.add(relativize(relativizingRoot, files[i]));
} else {
set.add(files[i]);
}
}
}
}
/**
* Given a directory and a fully-qualified file somewhere
* under that directory, return the portion of the child
* that is relative to the parent.
*/
public static File relativize(File parent, File child) {
String baseDir = parent.getAbsolutePath();
String baseDirAndChild = child.getAbsolutePath();
String relative = baseDirAndChild.substring(baseDir.length(),
baseDirAndChild.length());
// Strip off any extraneous file separator character.
if( relative.startsWith(File.separator) ) {
relative = relative.substring(1);
}
return new File(relative);
}
/**
* Executes the supplied work object until the work is done or the max.
* retry count is reached.
*
* @param work the RetriableWork implementation to be run
* @return the number of retries performed; 0 indicates the work succeeded without having to retry
*/
private static int doWithRetry(RetriableWork work) {
int retries = 0;
/*
*Try the work the first time. Ideally this will work.
*/
work.run();
/*
*If the work failed and this is Windows - on which running gc may
*unlock the locked file - then begin the retries.
*/
if (!work.workComplete()) {
if(cmdfilepropertieset)
{
setFileProperties();
cmdfilepropertieset=false;
}
_utillogger.log(FILE_OPERATION_LOG_LEVEL, CULoggerInfo.performGC);
while (!work.workComplete() && retries++ < FILE_OPERATION_MAX_RETRIES) {
try {
Thread.sleep(FILE_OPERATION_SLEEP_DELAY_MS);
} catch (InterruptedException ex) {
_utillogger.log(Level.SEVERE, "Thread Interrupted Exception", ex);
}
System.gc();
work.run();
}
}
return retries;
}
/**
* Creates a String listing the absolute paths of files, separated by
* the platform's line separator.
*
* @param files the Collection of File objects to be listed
* @return String containing the absolute paths of the files with the line separator between them
*/
public static String formatFileCollection(Collection files) {
StringBuilder sb = new StringBuilder();
String lineSep = System.getProperty("line.separator");
String prefix = "";
for (File f : files) {
sb.append(prefix).append(f.getAbsolutePath());
prefix = lineSep;
}
return sb.toString();
}
///////////////////////////////////////////////////////////////////////////
public static File getDirectory(File f) {
String filename = f.getAbsolutePath();
return new File((new File(filename)).getParent());
}
///////////////////////////////////////////////////////////////////////////
public static File createTempFile(File directory) {
File f = null;
try {
f = File.createTempFile(TMPFILENAME, "jar", directory);
}
catch (IOException ioe) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, ioe);
}
f.deleteOnExit(); // just in case
return f;
}
/**
* Returns an array of abstract pathnames that matches with the given
* file extension. If the given abstract pathname does not denote a
* directory, then this method returns null. If there is no matching
* file under the given directory and its sub directories,
* it returns null;
*
* @param dirName dir name under which search will begin
* @param ext file extension to look for
* @return an array of abstract pathnames that matches with the extension
*/
public static File[] listAllFiles(File dirName, String ext) {
File[] target = null;
List list = searchDir(dirName, ext);
if ((list != null) && (list.size() > 0)) {
target = new File[list.size()];
target = (File[]) list.toArray(target);
}
return target;
}
/**
* Returns a list of abstract pathnames that matches with the given
* file extension. If the given abstract pathname does not denote a
* directory, then this method returns null. If there is no matching
* file under the given directory and its sub directories, it returns
* an empty list.
*
* @param dirName dir name under which search will begin
* @param ext file extension to look for
* @return a list of abstract pathnames of type java.io.File
* that matches with the given extension
*/
public static List searchDir(File dirName, String ext) {
List targetList = null;
if (dirName.isDirectory()) {
targetList = new ArrayList();
File[] list = listFiles(dirName);
for (int i = 0; i < list.length; i++) {
if (list[i].isDirectory()) {
targetList.addAll(searchDir(list[i], ext));
} else {
String name = list[i].toString();
if (hasExtension(name, ext)) {
targetList.add(list[i]);
}
}
}
}
return targetList;
}
/**
* Copies a file.
*
* @param from Name of file to copy
* @param to Name of new file
* @throws IOException if an error while copying the content
*/
public static void copy(String from, String to) throws IOException {
//if(!StringUtils.ok(from) || !StringUtils.ok(to))
if (from == null || to == null)
throw new IllegalArgumentException("null or empty filename argument");
File fin = new File(from);
File fout = new File(to);
copy(fin, fout);
}
/**
* Copies a file.
*
* @param fin File to copy
* @param fout New file
* @throws IOException if an error while copying the content
*/
public static void copy(File fin, File fout) throws IOException {
if (safeIsDirectory(fin)) {
copyTree(fin, fout);
return;
}
if (!fin.exists())
throw new IllegalArgumentException("File source doesn't exist");
if(!mkdirsMaybe(fout.getParentFile()))
throw new RuntimeException("Can't create parent dir of output file: " + fout);
copyFile(fin, fout);
}
/**
* Copies the entire tree to a new location.
*
* @param din File pointing at root of tree to copy
* @param dout File pointing at root of new tree
* @throws IOException if an error while copying the content
*/
public static void copyTree(File din, File dout)
throws IOException {
if (!safeIsDirectory(din))
throw new IllegalArgumentException("Source isn't a directory");
if(!mkdirsMaybe(dout))
throw new IllegalArgumentException("Can't create destination directory");
FileListerRelative flr = new FileListerRelative(din);
String[] files = flr.getFiles();
for (int i = 0; i < files.length; i++) {
File fin = new File(din, files[i]);
File fout = new File(dout, files[i]);
copy(fin, fout);
}
}
/**
* Returns a String with uniform slashes such that all the
* occurances of '\\' are replaced with '/'.
* In other words, the returned string will have all forward slashes.
* Accepts non-null strings only.
*
* @param inputStr non null String
* @return a String which does not contain `\\` character
*/
public static String makeForwardSlashes(String inputStr) {
if (inputStr == null)
throw new IllegalArgumentException("null String FileUtils.makeForwardSlashes");
return (inputStr.replace('\\', '/'));
}
/**
* Given a string (typically a path), quote the string such that spaces
* are protected from interpretation by a Unix or Windows command shell.
* Note that this method does not handle quoting for all styles of special
* characters. Just for the basic case of strings with spaces.
*
* @param s input string
* @return a String which is quoted to protect spaces
*/
public static String quoteString(String s) {
if (s == null) {
throw new IllegalArgumentException("null string");
}
if (!s.contains("\'")) {
return("\'" + s + "\'");
} else if(!s.contains("\"")) {
return("\"" + s + "\"");
} else {
// Contains a single quote and a double quote. Use backslash
// On Unix. Double quotes on Windows. This method does not claim
// to support this case well if at all
if (OS.isWindows()) {
return("\"" + s + "\"");
} else {
return(s.replaceAll("\040", "\134 "));
}
}
}
///////////////////////////////////////////////////////////////////////////
public static String getIllegalFilenameCharacters() {
return ILLEGAL_FILENAME_STRING;
}
///////////////////////////////////////////////////////////////////////////
static boolean isValidString(String s) {
return ((s != null) && (s.length() != 0));
}
/**
* This method is used to copy a given file to another file
* using the buffer sixe specified
*
* @param fin the source file
* @param fout the destination file
*/
public static void copyFile(File fin, File fout) throws IOException {
InputStream inStream = new BufferedInputStream(new FileInputStream(fin));
FileOutputStream fos = FileUtils.openFileOutputStream(fout);
copy(inStream, fos, fin.length());
}
public static void copy(InputStream in, FileOutputStream out, long size) throws IOException {
try {
copyWithoutClose(in, out, size);
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
public static void copyWithoutClose(InputStream in, FileOutputStream out, long size) throws IOException {
ReadableByteChannel inChannel = Channels.newChannel(in);
FileChannel outChannel = out.getChannel();
outChannel.transferFrom(inChannel, 0, size);
}
public static void copy(InputStream in, OutputStream os, long size) throws IOException {
if (os instanceof FileOutputStream) {
copy(in, (FileOutputStream) os, size);
} else {
ReadableByteChannel inChannel = Channels.newChannel(in);
WritableByteChannel outChannel = Channels.newChannel(os);
if (size==0) {
ByteBuffer byteBuffer = ByteBuffer.allocate(10240);
int read;
do {
read = inChannel.read(byteBuffer);
if (read>0) {
byteBuffer.limit(byteBuffer.position());
byteBuffer.rewind();
outChannel.write(byteBuffer);
byteBuffer.clear();
}
} while (read!=-1);
} else {
ByteBuffer byteBuffer;
try{
byteBuffer = ByteBuffer.allocate(Long.valueOf(size).intValue());
}catch(Throwable err){
throw new IOException(messages.get("allocate.more.than.java.heap.space", err));
}
inChannel.read(byteBuffer);
byteBuffer.rewind();
outChannel.write(byteBuffer);
}
}
}
/**
*Rename, running gc on Windows if needed to try to force open streams to close.
*@param fromFile to be renamed
*@param toFile name for the renamed file
*@return boolean result of the rename attempt
*/
public static boolean renameFile(File fromFile, File toFile) {
boolean log = _utillogger.isLoggable(FILE_OPERATION_LOG_LEVEL) || _utillogger.isLoggable(Level.FINE);
RenameFileWork renameWork = new RenameFileWork(fromFile, toFile);
int retries = doWithRetry(renameWork);
boolean result = renameWork.workComplete();
String fromFilePath = null;
String toFilePath = null;
if (log || ! result) {
fromFilePath = fromFile.getAbsolutePath();
toFilePath = toFile.getAbsolutePath();
}
/*
*If the rename worked, then write an appropriate log message if the
*logging level allows.
*/
if (result) {
if (log) {
/*
*If the rename worked without retries, then log a FINE message.
*If retries were needed then use the configured
*FILE_OPERATION_LOG_LEVEL.
*/
if (retries == 0) {
if (_utillogger.isLoggable(Level.FINE)) {
_utillogger.log(Level.FINE, CULoggerInfo.renameInitialSuccess,
new Object [] {fromFilePath, toFilePath});
}
} else {
_utillogger.log(FILE_OPERATION_LOG_LEVEL, CULoggerInfo.retryRenameSuccess,
new Object [] {fromFilePath, toFilePath, Integer.valueOf(retries)});
}
}
} else {
/*
*The rename has failed. Write a warning message.
*/
_utillogger.log(Level.WARNING, CULoggerInfo.retryRenameFailure,
new Object [] {fromFilePath, toFilePath, Integer.valueOf(retries) });
}
return result;
}
/** Appends the given line at the end of given text file. If the given
* file does not exist, an attempt is made to create it.
* Note that this method can handle only text files.
* @param fileName name of the text file that needs to be appended to
* @param line the line to append to
* @throws RuntimeException in case of any error - that makes it callable
* from a code not within try-catch. Note that NPE will be thrown if either
* argument is null.
* Note that this method is not tested with String containing characters
* with 2 bytes.
*/
public static void appendText(String fileName, String line) throws
RuntimeException {
RandomAccessFile file = null;
try {
final String MODE = "rw";
file = new RandomAccessFile(fileName, MODE);
file.seek(file.getFilePointer() + file.length());
file.writeBytes(line);
}
catch(Exception e) {
throw new RuntimeException("FileUtils.appendText()", e);
}
finally {
try {
if (file != null)
file.close();
}
catch(Exception e){
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, e);
}
}
}
public static void appendText(String fileName, StringBuffer buffer)
throws IOException, FileNotFoundException
{
appendText(fileName, buffer.toString());
}
///////////////////////////////////////////////////////////////////////////
/** A utility routine to read a text file efficiently and return
* the contents as a String. Sometimes while reading log files of spawned
* processes this kind of facility is handy. Instead of opening files, coding
* FileReaders etc. this method could be employed. It is expected that the
* file to be read is small
.
* @param fileName String representing absolute path of the file
* @return String representing the contents of the file, empty String for an empty file
* @throws java.io.IOException if there is an i/o error.
* @throws java.io.FileNotFoundException if the file could not be found
*/
public static String readSmallFile(final String fileName)
throws IOException, FileNotFoundException {
return (readSmallFile(new File(fileName)) );
}
public static String readSmallFile(final File file)
throws IOException {
final BufferedReader bf = new BufferedReader(new FileReader(file));
final StringBuilder sb = new StringBuilder(); //preferred over StringBuffer, no need to synchronize
String line = null;
try {
while ( (line = bf.readLine()) != null ) {
sb.append(line);
sb.append(System.getProperty("line.separator"));
}
}
finally {
try {
bf.close();
}
catch (Exception e) {_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, e);}
finally{
bf.close();
}
}
return ( sb.toString() );
}
/**
* If the path dir/file does not exist, look for it in the classpath. If found
* in classpath, create dir/file.
*
* @param file - path to look for
* @param dir - directory where the path file should exist
* @return the File representing dir/file. If that does not exist, return null.
* @throws IOException
*/
public static File getManagedFile(String file, File dir) throws IOException {
File f = new File(dir, file);
if (f.exists())
return f;
InputStream is = null, bis = null;
OutputStream os = null;
try {
is = Thread.currentThread().getContextClassLoader().getResourceAsStream(file);
if (is == null)
return null;
bis = new BufferedInputStream(is);
if(!mkdirsMaybe(f.getParentFile()))
throw new RuntimeException("Can't create parent dir of output file: " + f);
os = new BufferedOutputStream(FileUtils.openFileOutputStream(f));
byte buf[] = new byte[10240];
int len = 0;
while ((len =bis.read(buf)) > 0) {
os.write(buf, 0, len);
}
return f;
} finally {
if (os != null)
try {
os.close();
} catch (IOException ex) {_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, ex);}
finally{
os.close();
}
if (bis != null)
try {
bis.close();
} catch (IOException ex) {_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, ex);}
finally{
bis.close();
}
if (is != null)
try {
is.close();
} catch (IOException ex) {_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, ex);}
finally{
is.close();
}
}
}
/**
* Write the String to a file. Then make the file readable and writable.
* If the file already exists it will be truncated and the contents replaced
* with the String argument.
* @param s The String to write to the file
* @param f The file to write the String to
* @throws IOException if any errors
*/
public static void writeStringToFile(String s, File f) throws IOException {
Writer writer = null;
try {
writer = new PrintWriter(f);
writer.write(s);
}
finally {
if(writer != null) {
try {
writer.close();
}
catch(Exception e) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, e);
}
f.setReadable(true);
f.setWritable(true);
}
}
}
/**
* Find files matching the regular expression in the given directory
* @param dir the directory to search
* @param regexp the regular expression pattern
* @return either an array of matching File objects or an empty array. Guaranteed
* to never return null
*/
public static File[] findFilesInDir(File dir, final String regexp) {
try {
File[] matches = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.matches(regexp);
}
});
if (matches != null)
return matches;
}
catch (Exception e) {
_utillogger.log(Level.SEVERE,"Exception while matching regular expression", e);
}
return new File[0];
}
/**
* Read in the given resourceName as a resource, and convert to a String
*
* @param resourceName
* @return the contents of the resource as a String or null if absent
*/
public static String resourceToString(String resourceName) {
byte[] bytes = resourceToBytes(resourceName);
return bytes == null ? null : new String(bytes);
}
/**
* Read in the given resourceName as a resource, and convert to a byte array
*
* @param resourceName
* @return the contents of the resource as a byte array or null if absent
*/
public static byte[] resourceToBytes(String resourceName) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
InputStream is = cl.getResourceAsStream(resourceName);
if (is == null)
return null;
try {
is = new BufferedInputStream(is);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int n;
while ((n = is.read(buffer)) != -1) {
baos.write(buffer, 0, n);
}
is.close();
return baos.toByteArray();
}
catch (Exception e) {
try {
is.close();
}
catch (Exception ex) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, ex);
}finally{
try {
is.close();
} catch (IOException io) {
_utillogger.log(Level.SEVERE, CULoggerInfo.exceptionIO, io);
}
}
return null;
}
}
/**
* Represents a unit of work that should be retried, if needed, until it
* succeeds or the configured retry limit is reached.
*
* The run
method required by the Runnable interface is invoked
* to perform the work.
*/
private interface RetriableWork extends Runnable {
/**
* Returns whether the work to be done by this instance of RetriableWork
* has been completed successfully.
*
* This method may be invoked multiple times and so should not have
* side effects.
*
* @return whether the work has been successfully completed
*/
public boolean workComplete();
}
/**
*Retriable work for renaming a file.
*/
private static class RenameFileWork implements RetriableWork {
private File originalFile;
private File newFile;
private boolean renameResult = false;
public RenameFileWork(File originalFile, File newFile) {
this.originalFile = originalFile;
this.newFile = newFile;
}
public boolean workComplete() {
return renameResult;
}
public void run() {
renameResult = originalFile.renameTo(newFile);
}
}
/**
* Retriable work for opening a FileOutputStream.
*/
private static class FileOutputStreamWork implements RetriableWork {
private FileOutputStream fos = null;
private Throwable lastError = null;
private File out;
public FileOutputStreamWork(File out) {
this.out = out;
}
public boolean workComplete() {
return fos != null;
}
public void run() {
try {
fos = new FileOutputStream(out);
lastError = null;
} catch (IOException ioe) {
lastError = ioe;
}
}
public FileOutputStream getStream() {
return fos;
}
public Throwable getLastError() {
return lastError;
}
}
/**
* Retriable work for deleting a file
*/
private static class DeleteFileWork implements RetriableWork {
private final File deleteMe;
private boolean complete = false;
private DeleteFileWork(File deleteMe) {
this.deleteMe = deleteMe;
}
@Override
public void run() {
if (complete) return;
if (deleteMe.delete()) complete = true;
}
@Override
public boolean workComplete() {
return complete;
}
}
///////////////////////////////////////////////////////////////////////////
private final static char[] ILLEGAL_FILENAME_CHARS = {'/', '\\', ':', '*', '?', '"', '<', '>', '|'};
private final static String ILLEGAL_FILENAME_STRING = "\\/:*?\"<>|";
private final static char REPLACEMENT_CHAR = '_';
private final static char BLANK = ' ';
private final static char DOT = '.';
private static String TMPFILENAME = "scratch";
/*
*The following property names are private, unsupported, and unpublished.
*/
private static int FILE_OPERATION_MAX_RETRIES=20;
private static int FILE_OPERATION_SLEEP_DELAY_MS=1000;
private static boolean cmdfilepropertieset=true;
private static final Level FILE_OPERATION_LOG_LEVEL = Level.FINE;
}