/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.util.file; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.tools.tar.TarEntry; import org.apache.tools.tar.TarInputStream; import org.hyperic.util.ArrayUtil; import org.hyperic.util.collection.IntHashMap; public class FileUtil { private static IntHashMap invalidChars = null; private FileUtil(){} /** * Try REALLY hard to find a writable place to create a file. * @param preferredDir The preferred directory. If this is not a * directory, then an IllegalArgumentException will be thrown. * If this names a directory that does not exist, * this method will still return the preferred path if it could be * created. * @param filename The name of the file that should be written to * the directory. This filename may include some preceding directory * names. * @param alternateDirs An array of Strings indicating paths to try * to write the file. If this is null, the default array contains the temp * directory, the user's home directory, and finally the current directory. * @param alternatePrefixDir If the file can't be written to the preferred * directory and must be written to an alternate directory, this prefix * directory is created first. For example, if you wanted to write to * /some/dir/here (with a filename of logs/somefile) but /some/dir/here * was not writable, so /tmp was chosen instead, you might not want * the ultimate file location to be /tmp/logs/somefile. By specifying * an alternatePrefixDir (for example "cam"), the file would then be * written to: /tmp/cam/logs/somefile * NOTE: If this is null it will be ignored. * @return A WritableFile object that can be written to, or null if * nothing can be found. A WritableFile is identical to a regular * File object, with the addition of a method "getOriginalLocationWasUsed" * which can be used to determine if the original desired file location * was used or if an alternate location was used. * @exception IllegalArgumentException if the preferredDir argument * is not actually a directory. */ public static WritableFile findWritableFile ( File preferredDir, String filename, String[] alternateDirs, String alternatePrefixDir) throws IllegalArgumentException { // This class assumes that filename doesn't contain a directory. // To ensure that we create a new File representing the requested // preferredDir/filename and reset preferredDir and filename so that // all dirs are in preferredDir and the filename is a simple // filename. File tempfile = new File(preferredDir, filename); preferredDir = tempfile.getParentFile(); filename = tempfile.getName(); if (alternateDirs == null) { alternateDirs = new String[] { System.getProperty("java.io.tmpdir"), System.getProperty("user.home") + File.separator + "tmp", System.getProperty("user.tmp"), System.getProperty("user.dir") }; } if ( !preferredDir.isDirectory() ) { throw new IllegalArgumentException("preferredDir is not a " + "directory: " + preferredDir); } WritableFile fileAttempt; alternateDirs = (String[]) ArrayUtil.combine(new String[] {preferredDir.getAbsolutePath()}, alternateDirs); File dirToTest; for (int i=0; i<alternateDirs.length; i++) { if (alternateDirs[i] == null) continue; if (alternatePrefixDir == null || i==0) { fileAttempt = new WritableFile(alternateDirs[i], filename); } else { fileAttempt = new WritableFile(alternateDirs[i], alternatePrefixDir + File.separator + filename); } dirToTest = fileAttempt; if ( !dirToTest.isDirectory() ) { dirToTest = dirToTest.getParentFile(); } while (dirToTest != null && !dirToTest.exists()) { dirToTest = dirToTest.getParentFile(); } if (dirToTest != null && dirToTest.exists() && dirToTest.canWrite() ) { fileAttempt.setOriginalLocationWasUsed(i==0); return fileAttempt; } } return null; } /** Copy a file from one file to another */ public static void copyFile(File inFile, File outFile) throws FileNotFoundException, IOException { BufferedInputStream is = null; BufferedOutputStream os = null; try { is = new BufferedInputStream(new FileInputStream(inFile)); os = new BufferedOutputStream(new FileOutputStream(outFile)); copyStream(is, os); } finally { safeCloseStream(is); safeCloseStream(os); } } /** Default buffer size for copyStream method */ public static final int BUFSIZ = 2048; /** * Copy a stream, using a buffer */ public static void copyStream(InputStream is, OutputStream os) throws IOException { copyStream(is, os, new byte[BUFSIZ]); } public static void copyStream(InputStream is, OutputStream os, byte[] buf) throws IOException { int bytesRead = 0; while (true) { bytesRead = is.read(buf); if (bytesRead == -1) break; os.write(buf, 0, bytesRead); } } public static String findString(String fname, String toFind) throws IOException { StringBuffer result = null; BufferedReader in = new BufferedReader(new FileReader(fname)); try { char[] data = new char[8096]; int numread; int toFindIndex = 0; /* Just need to initialize this, because the compiler doesn't * realize that it can't be used before it is assigned a value */ char lastchar = 'a'; while ((numread = in.read(data, 0, 8096)) != -1) { for (int i = 0; i < numread; i++) { /* If we have found the string already or if we our current * character matches the current char in the target string * then we just add the current character to our result * string and move on. */ if (toFindIndex >= toFind.length() || data[i] == toFind.charAt(toFindIndex)) { if (result == null) { result = new StringBuffer(); } if (Character.isISOControl(data[i])) { return result.toString(); } result.append(data[i]); toFindIndex++; } else { /* Otherwise things can get complex. If we haven't * started to match, then just keep going. If we have * started to match, then we need to move backwards * to make sure we don't miss a match. For example: * looking for HI in HHI. If the current character * isn't the same as the last character, then we aren't * going to match, so null everything out and keep * going. Otherwise, decrment everything by one, * because we didn't match the first character, and * go through the loop on this character again. */ if (toFindIndex > 0) { if (data[i] != lastchar) { result = null; toFindIndex = 0; continue; } toFindIndex--; i--; result.deleteCharAt(result.length() - 1); continue; } } lastchar = data[i]; } } } catch (IOException e) { } finally { if (in != null) { try { in.close(); } catch (IOException e) {} } } if (result != null) { return result.toString(); } return null; } /** * The base attribute specifies what the directory base * the relative path should be considered relative to. The base * must be part of the absolute path specified by the path attribute. */ public static String getRelativePath(File path, File base) { String path_abs = path.getAbsolutePath(); String base_abs = base.getAbsolutePath(); int idx = path_abs.indexOf(base_abs); if ( idx == -1 ) { throw new IllegalArgumentException("Path (" + path_abs + ") " + "does not contain " + "base (" + base_abs + ")"); } String relativePath = "." + path_abs.substring(idx + base_abs.length()); return relativePath; } private static void initInvalidChars() { if (invalidChars != null) { return; } invalidChars = new IntHashMap(); char[] invalid = { '\\', '/', ':', '*', '?', '\'', '"', '~', '<', '>', '|', '#', '{', '}', '%', '&', ' ' }; for (int i=0; i<invalid.length; i++) { invalidChars.put(invalid[i], Boolean.TRUE); } } /** * Escape invalid characters in a filename, replacing with "_" */ public static String escape(String name) { initInvalidChars(); int len = name.length(); StringBuffer buf = new StringBuffer(len); char[] chars = name.toCharArray(); for (int i=0; i<len; i++) { char c = chars[i]; if (invalidChars.get(c) == Boolean.TRUE) { buf.append("_"); } else { buf.append(c); } } return buf.toString(); } /** * Test if a directory is writable * * java.io.File#canWrite() has problems on windows for properly detecting * if a directory is writable by the current user. For example, * C:\Program Files is set to read-only, however the Administrator user is * able to write to that directory * * @throws IOException If the File is not a directory */ public static boolean canWrite(File dir) throws IOException { if (!dir.isDirectory()) { throw new IOException(dir.getPath() + " is not a directory"); } File tmp = null; try { tmp = File.createTempFile("hyperic", null, dir); return true; } catch (IOException e) { return false; } finally { if (tmp != null) { tmp.delete(); } } } public static void setReadWriteOnlyByOwner(File file) throws IOException { String path = file.getAbsolutePath(); //For Windows the permissions are inherited from the folder //and the permissions for the 'conf' folder is correct if (System.getProperty("os.name").toLowerCase().contains("win")) { return; } //Unix type systems else { Runtime.getRuntime().exec("chmod 600 " + path); } } /** * Chop the last element off a path. For example, if you pass in * /usr/local/foo then this will return /usr/local * If there is not enough to chop off, this throws IllegalArgumentException */ public static String getParentDir(String path) { int idx = path.lastIndexOf(File.separator); if (idx == -1 || idx == 0) { throw new IllegalArgumentException("Path has no parent: " + path); } return path.substring(0, idx); } /** * Chop the last elements off of a path. */ public static String getParentDir(String path, int levels) { while (levels-- > 0) { path = getParentDir(path); } return path; } /** * Read all the lines from a stream into a list */ public static List<String> readLines(InputStream is) throws IOException { List<String> res = new ArrayList<String>(); InputStreamReader isR = new InputStreamReader(is); BufferedReader br = new BufferedReader(isR); String s; while ((s = br.readLine()) != null) { res.add(s); } return res; } /** * Create a directory and its parent directories if necessary. If directory * creation fails, it will be retried up to the specified number of tries. * * @param dir The directory to create. * @param numTries The number of tries to create the directory. * @return <code>true</code> if directory creation succeeds or the directory * already exists,b<code>false</code> if it fails. * @throws InterruptedException if the operation is interrupted. * @throws IllegalArgumentException if the number of tries is less than one. */ public static boolean makeDirs(File dir, int numTries) throws InterruptedException { if (numTries < 1) { throw new IllegalArgumentException("number of tries must be greater than zero"); } int tries = 0; while (tries++ < numTries) { boolean result = dir.mkdirs(); if (result) { return true; } else { if (dir.exists()) { return true; } else { Thread.sleep(100); } } } return false; } /** * * Deletes all files and subdirectories under dir. * Returns true if all deletions were successful. * If a deletion fails, the method stops attempting to delete and returns * false. * @param dir Directory to delete recursively * @return returns true iff directory was successfully deleted including all its children. */ public static boolean deleteDir(File dir) { if (dir.isDirectory()) { String[] children = dir.list(); for (int i = 0; i < children.length; i++) { boolean success = deleteDir(new File(dir, children[i])); if (!success) { return false; } } } // The directory is now empty so delete it return dir.delete(); } public static void decompress(File compressedFile, File destination) throws IOException { if (compressedFile.getName().endsWith(".zip")) { unzip(compressedFile, destination); } else if (compressedFile.getName().endsWith(".tgz") || compressedFile.getName().endsWith(".tar.gz")) { untar(compressedFile, destination); } else { throw new IllegalArgumentException( "Invalid file format; must be one of zip, tgz, or tar.gz."); } } public static void untar(File tarFile, File destinationDir) throws IOException { TarInputStream tin = null; try { tin = new TarInputStream(new GZIPInputStream(new FileInputStream( tarFile))); // get the first entry in the archive TarEntry tarEntry = tin.getNextEntry(); while (tarEntry != null) { // create a file with the same name as the tarEntry File destPath = new File(destinationDir, tarEntry.getName()); // create any parent directories File parent = destPath.getParentFile(); if (parent != null) { parent.mkdirs(); } // if entry is directory, create it if (tarEntry.isDirectory()) { destPath.mkdirs(); } else { FileOutputStream fout = null; try { // delete the file it already exists if (destPath.exists()) destPath.delete(); fout = new FileOutputStream(destPath); tin.copyEntryContents(fout); } finally { safeCloseStream(fout); } } tarEntry = tin.getNextEntry(); } } finally { safeCloseStream(tin); } } public static void unzip(File tarFile, File destinationDir) throws IOException { ZipInputStream zis = null; final int BUFFER = 2048; try { zis = new ZipInputStream(new FileInputStream(tarFile)); // get the first entry in the archive ZipEntry zipEntry = zis.getNextEntry(); while (zipEntry != null) { // create a file with the same name as the tarEntry File destPath = new File(destinationDir, zipEntry.getName()); // create any parent directories File parent = destPath.getParentFile(); if (parent != null) { parent.mkdirs(); } // if entry is directory, create it if (zipEntry.isDirectory()) { destPath.mkdirs(); } else { int count; byte data[] = new byte[BUFFER]; FileOutputStream fout = null; BufferedOutputStream bos = null; try { // delete the file it already exists if (destPath.exists()) destPath.delete(); fout = new FileOutputStream(destPath); bos = new BufferedOutputStream(fout, BUFFER); while ((count = zis.read(data, 0, BUFFER)) != -1) { bos.write(data, 0, count); } bos.flush(); } finally { safeCloseStream(bos); } } zipEntry = zis.getNextEntry(); } } finally { safeCloseStream(zis); } } public static void safeCloseStream(InputStream in) { if (in != null) { try { in.close(); } catch (IOException e) { // just swallow it } } } public static void safeCloseStream(OutputStream out) { if (out != null) { try { out.close(); } catch (IOException e) { // just swallow it } } } public static boolean safeFileMove(File moveFrom, File moveTo) { if (moveFrom.isDirectory()) { throw new IllegalArgumentException("moveFrom is a directory " + moveFrom); } if (moveTo.isDirectory()) { throw new IllegalArgumentException("moveTo is a directory " + moveTo); } boolean success = moveFrom.renameTo(moveTo); if (!success) { moveTo.delete(); success = moveFrom.renameTo(moveTo); } return success; } public static final void persistObject(final Serializable obj, final String destDir, final String fileName) throws Throwable{ persistObject(obj, new File(destDir + File.separator + fileName)) ; }//EOM public static final void persistObject(final Serializable obj, final File destFile) throws Throwable{ ObjectOutputStream oos = null ; //destFile.createNewFile() ; FileOutputStream fos = null ; try{ fos = new FileOutputStream(destFile) ; oos = new ObjectOutputStream(fos) ; oos.writeObject(obj) ; }finally{ if(oos != null) { oos.flush() ; fos.getFD().sync() ; oos.close() ; }//EO if OOS not null }//EO catch block }//EOM }