/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. 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.html
* or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [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.
*/
/*
* FileUtils.java
*
* Created on November 16, 2001, 6:44 PM
*
* @author bnevins
*
* Copyright 2000-2001 by iPlanet/Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of iPlanet/Sun Microsystems, Inc. ("Confidential Information").
* You shall not disclose such Confidential Information and shall
* use it only in accordance with the terms of the license
* agreement you entered into with iPlanet/Sun Microsystems.
*
*/
/*
* KEDAR/MURALI has made some changes to this class
* so that it works with installer(LogDomains esp.).
*/
package com.sun.enterprise.util.io;
import java.io.*;
import java.util.*;
//import com.sun.enterprise.util.diagnostics.Reporter;
//import com.sun.enterprise.util.StringUtils;
import com.sun.enterprise.util.OS;
//Bug 4677074 begin
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
//import com.sun.logging.LogDomains;
//Bug 4677074 end
public class FileUtils
{
public static native int recursiveChownNative(String dirOrFile, String user);
public static native int recursiveChownNative(String dirOrFile);
public static native int recursiveChmodNative(String dirOrFile, String permissions);
//Bug 4677074 begin
//static Logger _logger=LogDomains.getLogger(LogDomains.UTIL_LOGGER);
//static Logger _logger=Logger.getLogger("javax.enterprise.system.util");
static Logger _logger=IOLogger.getLogger();
static Logger _utillogger=com.sun.logging.LogDomains.getLogger(com.sun.logging.LogDomains.UTIL_LOGGER);
//Bug 4677074 end
private FileUtils()
{
}
///////////////////////////////////////////////////////////////////////////
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;
// these 2 values while be different for symbolic links
String canonical = safeGetCanonicalPath(f);
String absolute = f.getAbsolutePath();
if(canonical.equals(absolute))
return true;
/* 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))
return 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 isEar(String filename)
{
return hasExtension(filename, ".ear");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isJar(String filename)
{
return hasExtension(filename, ".jar");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isZip(String filename)
{
return hasExtensionIgnoreCase(filename, ".zip");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isArchive(String filename)
{
return isWar(filename) || isRar(filename) || isJar(filename) || isZip(filename) || isEar(filename);
}
///////////////////////////////////////////////////////////////////////////
public static boolean isArchive(File f)
{
return isWar(f) || isRar(f) || isJar(f) || isZip(f) || isEar(f);
}
///////////////////////////////////////////////////////////////////////////
public static boolean isWar(String filename)
{
return hasExtension(filename, ".war");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isRar(String filename)
{
return hasExtension(filename, ".rar");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isEar(File f)
{
return hasExtension(f, ".ear");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isJar(File f)
{
return hasExtension(f, ".jar");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isZip(File f)
{
return hasExtensionIgnoreCase(f, ".zip");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isWar(File f)
{
return hasExtension(f, ".war");
}
///////////////////////////////////////////////////////////////////////////
public static boolean isRar(File f)
{
return hasExtension(f, ".rar");
}
///////////////////////////////////////////////////////////////////////////
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().endsWith(ext.toLowerCase());
}
///////////////////////////////////////////////////////////////////////////
public static boolean hasExtensionIgnoreCase(File f, String ext)
{
if(f == null || !f.exists())
return false;
return f.getName().toLowerCase().endsWith(ext.toLowerCase());
}
///////////////////////////////////////////////////////////////////////////
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;
for(int i = 0; i < ILLEGAL_FILENAME_CHARS.length; i++)
filename = filename.replace(ILLEGAL_FILENAME_CHARS[i], REPLACEMENT_CHAR);
return filename;
}
///////////////////////////////////////////////////////////////////////////
public static String makeFriendlyFileName(String filename)
{
// OK -- I hate having to go back in here to see if the 'N'
// is uppercase or lowercase. Let's have both!!
return makeFriendlyFilename(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 = makeLegalNoBlankFileName(filename);
// only replace the "." in the archive type suffix
if (ret.endsWith(".ear"))
{
ret = ret.substring(0, ret.indexOf(".ear"));
ret = ret + "_ear";
}
else if (ret.endsWith(".war"))
{
ret = ret.substring(0, ret.indexOf(".war"));
ret = ret + "_war";
}
else if (ret.endsWith(".jar"))
{
ret = ret.substring(0, ret.indexOf(".jar"));
ret = ret + "_jar";
}
else if (ret.endsWith(".rar"))
{
ret = ret.substring(0, ret.indexOf(".rar"));
ret = ret + "_rar";
}
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 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.replace(REPLACEMENT_CHAR, '/');
}
///////////////////////////////////////////////////////////////////////////
public static String makeFriendlyFileNameNoExtension(String filename)
{
// OK -- I hate having to go back in here to see if the 'N'
// is uppercase or lowercase. Let's have both!!
return makeFriendlyFilenameNoExtension(filename);
}
/////////////////////////////////////////////////////////
public static void liquidate(File parent)
{
whack(parent);
}
/////////////////////////////////////////////////////////
/**
*Deletes a directory and its contents.
*<p>
*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.
*<p>
*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<File> 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, "iplanet_util.io_exception", ioe);
return false;
}
}
/**
*Deletes a directory and its contents.
*<p>
*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<File> undeletedFiles)
{
/*
*Do not recursively delete the contents if the current parent
*is a symbolic link.
*/
if (safeIsRealDirectory(parent))
{
File[] kids = parent.listFiles();
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 on Windows and the delete fails, run the gc and retry the deletion.
*@param 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)
{
/*
*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 (f.delete())
{
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, "enterprise_util.delete_failed_absent", 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, "enterprise_util.error_deleting_file", filePath);
}
return false;
}
}
/**
*Attempts to delete files that could not be deleted earlier and were not overwritten.
*<p>
*On Windows, the method requests garbage collection which may unlock locked
*files. (The JarFile finalizer closes the file.)
*
*On any platform, any remaining leftover files that cannot be cleaned
*up are marked for delete-on-exit.
*<p>
*Note that the <code>leftovers</code> collection may be modified; a File
*object is removed if that leftover file is deleted successfully.
*@param leftovers a Collection of File objects for undeleted and non-overwritten files
*@return whether or not all files were deleted successfully
*/
public static boolean deleteLeftoverFiles(final Collection<File> leftovers)
{
if (leftovers.size() == 0)
{
return true;
}
/*
*If all leftover files are removed then logging at the end of the method
*(if the logging level is set fine enough) reports all the files that
*were cleaned up. To do that create a copy of the original leftover
*files now because the logic below will remove items from the collection
*as it successfully removes files.
*/
Collection<File> originalLeftovers = null;
boolean log = _utillogger.isLoggable(FILE_OPERATION_LOG_LEVEL);
if (log)
{
originalLeftovers = new ArrayList<File>(leftovers);
}
/*
*On Windows, as long as not all leftover files have been cleaned and we have not
*run the max. number of retries, try again to trigger gc and delete
*each remaining leftover file.
*/
int retries = 0;
if (OS.isWindows())
{
retries = doWithRetry(new RetriableWork()
{
public boolean workComplete()
{
/*
*The removal of the leftover files is done if the collection
*is empty.
*/
return leftovers.size() == 0;
}
public void run()
{
/*
*Try to delete all remaining files. For each successful deletion
*remove the corresponding File object from the leftovers collection.
*/
for (Iterator<File> files = leftovers.iterator(); files.hasNext(); )
{
File f = files.next();
if (f.delete())
{
files.remove();
}
}
}
});
}
/*
*Either all leftovers have been removed or at least one remains and we've
*hit the retry limit...or this is not even a Windows platform.
*/
if (leftovers.size() == 0 && log)
{
_utillogger.log(FILE_OPERATION_LOG_LEVEL, "enterprise_util.leftover_delete_success", new Object [] {
Integer.valueOf(retries), formatFileCollection(originalLeftovers) } );
}
else
{
/*
*Mark all remaining leftovers as delete-on-exit.
*/
for (File f : leftovers)
{
f.deleteOnExit();
}
/*
*No retries are performed on non-Windows systems, so log a message
*that is appropriate to the platform.
*/
if (OS.isWindows())
{
_utillogger.log(Level.WARNING, "enterprise_util.leftover_delete_failure_Windows", new Object [] {
Integer.valueOf(retries), formatFileCollection(leftovers)} );
}
else
{
_utillogger.log(Level.WARNING, "enterprise_util.leftover_delete_failure_nonWindows", new Object [] {
formatFileCollection(leftovers)} );
}
}
return leftovers.size() == 0;
}
/**
*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
*@exception 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 (work.workComplete())
{
return work.getStream();
}
else
{
IOException ioe = new IOException();
ioe.initCause(work.getLastError());
throw ioe;
}
}
/**
*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() && OS.isWindows())
{
_utillogger.log(FILE_OPERATION_LOG_LEVEL, "enterprise_util.perform_gc");
while ( ! work.workComplete() && retries++ < FILE_OPERATION_MAX_RETRIES)
{
try
{
Thread.currentThread().sleep(FILE_OPERATION_SLEEP_DELAY_MS);
}
catch (InterruptedException 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<File> 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)
{
//Bug 4677074 ioe.printStackTrace();
//Bug 4677074 begin
_logger.log(Level.SEVERE,"iplanet_util.io_exception",ioe);
//Bug 4677074 end
}
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 = dirName.listFiles();
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
* @exception 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 from File to copy
* @param to New file
*
* @exception 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(fout.exists() && overwrite == false)
//throw new IOException("File destination exists, overwrite not specified");
if(!safeIsDirectory(fout.getParentFile()))
fout.getParentFile().mkdirs();
copy(new FileInputStream(fin), new FileOutputStream(fout));
}
/**
* Copies the entire tree to a new location. Also copies all symlink directories.
*
* @param sourceTree File pointing at root of tree to copy
* @param destTree File pointing at root of new tree
*
* @exception IOException if an error while copying the content
*/
public static void copyTree(File din, File dout)
throws IOException
{
copyTree(din, dout, true);
}
/**
* Copies the entire tree to a new location.
* Symlink directories are only copied when copySymLinksDir is true.
*
* @param sourceTree File pointing at root of tree to copy
* @param destTree File pointing at root of new tree
* @param copySymLinkDirs if true -- recursively copy symlink directories; otherwise ignore them.
* @exception IOException if an error while copying the content
*/
public static void copyTree(File din, File dout, boolean copySymLinkDirs)
throws IOException
{
if(!safeIsDirectory(din))
throw new IllegalArgumentException("Source isn't a directory");
dout.mkdirs();
if(!safeIsDirectory(dout))
throw new IllegalArgumentException("Can't create destination directory");
FileListerRelative flr = new FileListerRelative(din);
flr.setCopySymLinks(copySymLinkDirs);
flr.setCopyEmptyDirs(true);
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]);
if(fin.isDirectory())
fout.mkdirs();
else
copy(fin, fout);
//System.out.println("Copied " + fin.getPath() + " to " + fout.getPath());
//Bug 4677074 System.out.print(".");
//Bug 4677074 begin
//_logger.log(Level.FINE,"Copied " + fin.getPath() + " to "+ fout.getPath());
_logger.log(Level.FINE,".");
//Bug 4677074 end
}
}
/**
* Copies the bytes from the given input stream to the output stream.
* It closes the streams afterwards.
*
* @param inStream input stream from the src
* @param outStream output stream to the destination
*
* @exception IOException if an error while copying the content
*/
public static void copy(InputStream inStream, OutputStream outStream)
throws IOException
{
copyWithoutClose(inStream, outStream);
// closes the streams
inStream.close();
outStream.close();
}
/**
* Copies the bytes from the given input stream to the output stream.
* It does not close the streams afterwards.
*
* @param inStream input stream from the src
* @param outStream output stream to the destination
*
* @exception IOException if an error while copying the content
*/
public static void copyWithoutClose(InputStream inStream,
OutputStream outStream) throws IOException
{
BufferedInputStream bis =
new BufferedInputStream(inStream, BUFFER_SIZE);
BufferedOutputStream bos =
new BufferedOutputStream(outStream, BUFFER_SIZE);
byte[] buf = new byte[BUFFER_SIZE];
int len = 0;
while (len != -1)
{
try
{
len = bis.read(buf, 0, buf.length);
}
catch (EOFException eof)
{
break;
}
if (len != -1)
{
bos.write(buf, 0, len);
}
}
bos.flush();
}
/**
* 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 a non null String
* @return a String which <code> does not contain `\\` character </code>
*/
public static String makeForwardSlashes(String inputStr)
{
if(inputStr == null)
throw new IllegalArgumentException("null String FileUtils.makeForwardSlashes");
return ( inputStr.replace('\\', '/') );
}
///////////////////////////////////////////////////////////////////////////
public static String getIllegalFilenameCharacters()
{
return ILLEGAL_FILENAME_STRING;
}
/**
* This version will set the owner to the current user
* @param dirOrFile - File or Directory to change owner
* @throws NativeIOException if there was an error
* @return a success String suitable for logging.
*/
public static String recursiveChown(File dirOrFile) throws NativeIOException
{
return recursiveChown(dirOrFile, null);
}
///////////////////////////////////////////////////////////////////////////
/**
* This version will set the owner to the specified user
* @param dirOrFile - File or Directory to change owner
* @throws NativeIOException if there was an error
* @return a success String suitable for logging.
*/
public static String recursiveChown(File dirOrFile, String user) throws NativeIOException
{
if(dirOrFile == null || !dirOrFile.exists())
throw new NativeIOException("File doesn't exist: " + dirOrFile);
String fname = dirOrFile.getAbsolutePath();
int ret = 0;
if(!isValidString(user))
ret = recursiveChownNative(fname);
else
ret = recursiveChownNative(fname, user);
NativeResults nr = new NativeResults(ret);
NativeIOException ne = nr.getResultException();
if(ne == null)
return nr.getResultString();
else
throw ne;
}
/**
* Recursively changes the file permissions via native code.
* @param dirOrFile the directory or file to chmod (recursively if directory)
* @param permissions a String representing permissions.
* <p> permissions must be exactly like so:
* "rwxrwxrwx" which is user-group-others
* put a dash (or anything else) in a position that you don't want the permission set.
* e.g. rwx -> user, r -> group, nothing -> others
* "rwxr-----"
* @throws NativeIOException -- if there was an error
* @return String suitable for logging about the success
*/
public static String recursiveChmod(File dirOrFile, String permissions) throws NativeIOException
{
if(dirOrFile == null || !dirOrFile.exists())
throw new NativeIOException("File doesn't exist: " + dirOrFile);
if(permissions == null)
throw new NativeIOException("null permissions string.");
permissions = permissions.toLowerCase(); // using R,W,X should not be an error!
verifyPermissions(permissions);
int ret = recursiveChmodNative(dirOrFile.getAbsolutePath(), permissions);
NativeResults nr = new NativeResults(ret);
NativeIOException ne = nr.getResultException();
if(ne == null)
return nr.getResultString();
else
throw ne;
}
///////////////////////////////////////////////////////////////////////////
static boolean isValidString(String s)
{
return ((s != null) && (s.length() != 0));
}
///////////////////////////////////////////////////////////////////////////
/**
* Windows has BIG issues renaming a directory that is open somnewhere -- e.g. if
* a DOS box is opened anywhere in that directory.
* This method will try to do a "virtual renaming" if there are problems
* I.e. it attempts to do a simple rename, if that fails it will copy everything under
* the original directory to the renamed directory. Then it will delete everything
* under the original directory that the OS will allow it to.
* NOTE: if the proposed new name already exists - I will try to whack it.
* bnevins April 2003
* @returns the renamed directory-name
*/
public static File smartRename(File original) throws IOException
{
if(original == null)
throw new IllegalArgumentException("null argument");
String originalName = original.getAbsolutePath();
if(!safeIsDirectory(original))
throw new IOException("Directory doesn't exist: " + originalName);
// let's try to work with "_old" added to name...
String renamedName = originalName + "_old";
File renamed = new File(renamedName);
if(renamed.exists())
{
whack(renamed);
// it might have failed:
if(renamed.exists())
{
for(int i = 0; i < 1000; i++)
{
String name = renamedName + i;
renamed = new File(name);
try
{ whack(renamed); }
catch(Throwable t)
{ /* ignore!! */ }
if(!renamed.exists())
{
// found one!!!
renamedName = name;
break;
}
}
// once more in case we went through all 1000!
if(renamed.exists())
throw new IOException("Tried 1000 possible rename directory names. None worked!");
}
}
// whew!
// try a simple rename...
if(original.renameTo(renamed))
return renamed;
// OK -- we'return probably in Windows and something is referencing original.
copyTree(original, renamed);
whack(original); // this will delete whatever is possible to delete...
return renamed;
}
///////////////////////////////////////////////////////////////////////////
/**
*Rename, running gc on Windows if needed to try to force open streams to close.
*@param file to be renamed
*@param new 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, "enterprise_util.rename_initial_success", new Object [] {
fromFilePath, toFilePath } );
}
}
else
{
_utillogger.log(FILE_OPERATION_LOG_LEVEL, "enterprise_util.retry_rename_success", new Object []
{ fromFilePath, toFilePath, Integer.valueOf(retries) } );
}
}
}
else
{
/*
*The rename has failed. Write a warning message.
*/
_utillogger.log(Level.WARNING, "enterprise_util.retry_rename_failure", new Object []
{ fromFilePath, toFilePath, Integer.valueOf(retries) } );
}
return result;
}
///////////////////////////////////////////////////////////////////////////
private static void verifyPermissions(String per) throws NativeIOException
{
// brace yourself for some involved argument checking.
// It's not pretty but it *is* neccessary.
if(per == null)
throw new NativeIOException("null permissions string.");
if(per.length() != 9)
throw new NativeIOException("permissions string must be exactly" +
" 9 characters long. It is " + per.length() + " characters long.");
// make sure every character is r, w, x, or -
String err1 = "permissions string has a bad character ('";
String err2 = "') at position #";
String err3 = ". Expected '-' or '";
String err4 = "'";
for(int i = 0; i < 9; i++)
{
char c = per.charAt(i);
int pos = i % 3; // 0->r, 1->w, 2->x
if(pos == 0)
{
if(c != 'r' && c != '-')
throw new NativeIOException(err1 + c + err2 + (i + 1) + err3 + 'r' + err4);
}
else if(pos == 1)
{
if(c != 'w' && c != '-')
throw new NativeIOException(err1 + c + err2 + (i + 1) + err3 + 'w' + err4);
}
else if(pos == 2)
{
if(c != 'x' && c != '-')
throw new NativeIOException(err1 + c + err2 + (i + 1) + err3 + 'x' + err4);
}
}
}
/** 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)
{}
}
}
public static void appendText(String fileName, StringBuffer buffer)
throws IOException, FileNotFoundException
{
appendText(fileName, buffer.toString());
}
///////////////////////////////////////////////////////////////////////////
/** A utility routine to read a <b> text file </b> 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 <code> small </code>.
* @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)
{}
}
return ( sb.toString() );
}
public static void main(String[] args)
{
try
{
System.out.println(smartRename(new File("C:/temp/test")));
}
catch(Throwable t)
{
t.printStackTrace();
}
/*
try
{
String ret = recursiveChmod(new File("c:/temp"), "rwxr-----");
System.out.println("recursiveChmod returned: " + ret);
}
catch(Throwable t)
{
t.printStackTrace();
}
//_logger.log(Level.SEVERE,"iplanet_util.io_exception", new IOException());
//System.out.println("xxxxxxxxxx");
**/
appendText("empty.txt", "text line");
}
/**
* Method to read a file and return it as String
*/
public static String getFileContents(String fileName)
throws IOException, FileNotFoundException
{
FileReader fr = null;
fr = new FileReader(fileName);
StringWriter sr = new StringWriter();
try
{
char[] buf = new char[1024];
int len = 0;
while (len != -1)
{
try
{
len = fr.read(buf, 0, buf.length);
}
catch (EOFException eof)
{
break;
}
if (len != -1)
{
sr.write(buf, 0, len);
}
}
fr.close();
sr.close();
return sr.toString();
}
finally
{
if (fr != null)
{
try
{
fr.close();
}
catch (IOException ioe)
{
}
}
}
}
/**
*Make sure the specified path is a legal length on Windows.
*@param fullPath the String holding the path to check
*@throws IOException if the path is too long
*/
public static void validateWindowsFilePathLength(String fullPath) throws IOException
{
/*
*No longer needed because 1.5.0-06 now avoids the Windows file path
*length limitation.
*/
return;
}
/**
*Make sure the specified path is a legal length on Windows.
*@param file the File to check
*@throws IOException if the path is too long
*/
public static void validateWindowsFilePathLength(File file) throws IOException
{
validateWindowsFilePathLength(file.getAbsolutePath());
}
/**
*Represents a unit of work that should be retried, if needed, until it
*succeeds or the configured retry limit is reached.
*<p>
*The <code>run</code> 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.
*<p>
*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;
}
}
///////////////////////////////////////////////////////////////////////////
private static final int BUFFER_SIZE = 0x10000; // 64k
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 final int FILE_OPERATION_MAX_RETRIES = Integer.getInteger("com.sun.appserv.winFileLockRetryLimit", 4).intValue();
private static final int FILE_OPERATION_SLEEP_DELAY_MS = Integer.getInteger("com.sun.appserv.winFileLockRetryDelay", 100).intValue();
private static final Level FILE_OPERATION_LOG_LEVEL = Level.FINE;
}
|