/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * FileSystemHelper.java * Creation date: Nov 18, 2004. * By: Edward Lam */ package org.openquark.util; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; /** * This class contains static helper methods to aid in interactions with the file system. * @author Edward Lam * @author Joseph Wong */ public final class FileSystemHelper { /* * Not intended to be instantiated. */ private FileSystemHelper() { } /** * Determine if a file exists. * Workaround for java.io.File lack of support for path names > 260 chars in Windows. * * Note: Creation of the following will correctly throw a FileNotFoundException: * - FileInputStream * - FileReader * - RandomAccessFile * * if the file truly does not exist. * * @param file a file object. May be null, in which case false is returned. * @return whether the file exists. */ public static boolean fileExists(File file) { /* * TODOEL: Only works for files. Doesn't work with folders. * For folders: File.canRead() or create a URL and try to read it? */ if (file == null) { return false; } // We can just use the file.exists() method if the path name isn't too long. if (file.getAbsolutePath().length() < 255) { return file.exists(); } // As far as I can tell, there are two ways to tell if a file with a too-long pathname exists. // 1) Create a FileInputStream, and see if it causes an exception. // 2) Create a RandomAccessFile, and see if it causes an exception. // Testing shows that, at the moment, 2) is faster. RandomAccessFile raFile = null; try { raFile = new RandomAccessFile(file, "r"); return true; } catch (FileNotFoundException e) { } finally { if (raFile != null) { try { raFile.close(); } catch (IOException ioe) { } } } return false; } /** * Return the extension portion of the file's name . */ public static String getExtension(File f) { if (f != null) { String filename = f.getName(); int i = filename.lastIndexOf('.'); if (i > 0 && i < filename.length() - 1) { return filename.substring(i + 1).toLowerCase(); } } return null; } /** * Delete an entire directory tree. * @param f - the root of the tree. * @return - true if all deletions were successful, false otherwise. */ public static boolean delTree (File f) { if (f.isDirectory()) { File[] files = f.listFiles(); for (int i = 0; i < files.length; ++i) { if (!delTree(files[i])) { return false; } } } return f.delete(); } /** * Attempts to create the specified directory (and all its ancestors) if it does * not already exists. This method is intended as a workaround for * {@link File#mkdirs()}, which has a known race condition where it would unnecessary * fail in scenarios where: * <ul> * <li>mkdirs is called on the path /a/b * <li>the directory /a does not exist * <li>mkdirs attempts to create /a, but in the mean time some other thread (or process) creates it first * <li>mkdirs realizes that it failed to create /a (since it now exists), and decides to give up creating /a/b * </ul> * * This bug is filed as Bug 4742723 in Sun's bug database: * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4742723"> * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4742723 * </a> * * @param f the directory * @return true if the directory exists or has been created successfully. */ public static boolean ensureDirectoryExists(File f) { if (f.isDirectory()) { return true; } if (f.mkdir()) { return true; } // We obtain the canonical path so that we can obtain a proper // path for the parent (which we'll try to ensure exists) File canonicalFile; try { canonicalFile = f.getCanonicalFile(); } catch (IOException e) { // getCanonicalFile() had some problems, but we really cannot say // that the directory does not exist (because it may have been created // by another process or thread), so the return value should be another // check of isDirectory() return f.isDirectory(); } // The directory could not be made as is, so try // to create its parent (recursively) and then // try again File parent = canonicalFile.getParentFile(); if (parent != null) { ensureDirectoryExists(parent); } // we tried our best creating the parent, so regardless // or whether that succeeded or not, we try creating // the requested directory again if (canonicalFile.mkdir()) { return true; } else { // the final mkdir() attempt "failed", but that could // mean that the directory really exists, so the // return value should be another check of isDirectory() return f.isDirectory(); } } /** * Returns a subfolder in the user's "Application Data" folder with the * given name. The folder will be created if it does not already exists. If * the clean flag is set and the folder already exists it will be deleted * first and then recreated. * * @param folderName The name of the folder to create. * @param clean True means the folder will be empty. False means existing data will be preserved. * @return The directory File. * @throws BusinessObjectsException */ public static File getApplicationDataFolder(String folderName, boolean clean) throws BusinessObjectsException { // Get the system user.home property. String userHomePath = System.getProperty("user.home"); if (userHomePath == null) { throw new BusinessObjectsException( "Could not find java.home system property."); } // Create the file object for the application's data folder. File userHome = new File(userHomePath); File appFolder = new File(userHome, "Application Data/"+folderName); // Clean the folder if requested. if (appFolder.exists() && clean) { delTree(appFolder); } // Ensure the the folder exists. if (!FileSystemHelper.ensureDirectoryExists(appFolder)) { throw new BusinessObjectsException("Could not create folder "+appFolder); } return appFolder; } }