/* FileOps.java This utility class provides a number of static methods for doing file operations. Created: 2 December 2000 Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2012 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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, see <http://www.gnu.org/licenses/>. */ package arlut.csd.Util; import java.io.InputStream; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; /*------------------------------------------------------------------------------ class FileOps ------------------------------------------------------------------------------*/ /** * This utility class provides a number of static methods for doing * file operations. */ public class FileOps { private static int MAXSIZE = 0xFFFFF; // 16 megabytes /** * Copies a file named inputFileName to the location outputFileName * in an operating system independent fashion. */ public static boolean copyFile(String inputFileName, String outputFileName) throws IOException { File outFile = new File(outputFileName); if (outFile.exists()) { throw new IllegalArgumentException("Error, copyFile called with a pre-existing target"); } BufferedInputStream inStream = new BufferedInputStream(new FileInputStream(inputFileName)); BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(outputFileName)); byte buffer[] = new byte[32767]; int length = 0; /* -- */ try { length = inStream.read(buffer); while (length != -1) { outStream.write(buffer, 0, length); length = inStream.read(buffer); } } finally { outStream.close(); inStream.close(); if (length == -1) { return true; } else { return false; } } } public static boolean deleteFile(String filename) throws IOException { File file = new File(filename); return file.delete(); } public static boolean deleteDirectory(String directoryName) throws IOException { boolean success = true; String filenames[]; File file; /* -- */ file = new File(directoryName); if (!file.isDirectory()) { throw new IOException("Error, deleteDirectory called on non-directory " + directoryName); } filenames = file.list(); if (filenames == null) { return success; } directoryName = PathComplete.completePath(directoryName); for (int i = 0; i < filenames.length; i++) { File subfile = new File(directoryName, filenames[i]); if (subfile.isDirectory()) { try { if (!deleteDirectory(directoryName + filenames[i])) { success = false; } } catch (IOException ex) { ex.printStackTrace(); success = false; } } else { try { if (!deleteFile(directoryName + filenames[i])) { success = false; } } catch (IOException ex) { ex.printStackTrace(); success = false; } } } // all sub files should be deleted, delete the directory if (success) { return file.delete(); } else { return false; } } /** * <p>This method is used to run an external process line for the * Ganymede server. This method waits until the external command * completes before returning, and all file handles opened to * communicate with the process will be closed before returning.</p> * * <p>While this method is waiting for the process to exit, it will * spin on the stdout and stderr from the process, consuming and * discarding anything written by the subprocess so as to prevent * the subprocess from blocking due to its output piling up.</p> */ public static int runProcess(String[] cmdArgs) throws IOException, InterruptedException { Process p = java.lang.Runtime.getRuntime().exec(cmdArgs); return runProcess(p); } /** * <p>This method is used to run an external process line for the * Ganymede server. This method waits until the external command * completes before returning, and all file handles opened to * communicate with the process will be closed before returning.</p> * * <p>While this method is waiting for the process to exit, it will * spin on the stdout and stderr from the process, consuming and * discarding anything written by the subprocess so as to prevent * the subprocess from blocking due to its output piling up.</p> */ public static int runProcess(String commandLine) throws IOException, InterruptedException { Process p = java.lang.Runtime.getRuntime().exec(commandLine); return runProcess(p); } /** * <p>This method is used to run an external process line for the * Ganymede server. This method waits until the external command * completes before returning, and all file handles opened to * communicate with the process will be closed before returning.</p> * * <p>While this method is waiting for the process to exit, it will * spin on the stdout and stderr from the process, consuming and * discarding anything written by the subprocess so as to prevent * the subprocess from blocking due to its output piling up.</p> */ private static int runProcess(Process p) throws IOException, InterruptedException { InputStream iStream = p.getInputStream(); InputStream eStream = p.getErrorStream(); byte[] buffer = new byte[4096]; // our own skip buffer try { while (true) { // this bletcherousness is so that we can consume anything // the sub process writes to its stdout or stderr, rather // than allowing the subprocess to block waiting in vain // for us to read from it. // really if we see anything coming from subprocesses // here, it means that somebody didn't properly do // redirection on their sync stuff. try { return p.exitValue(); } catch (IllegalThreadStateException ex) { while (iStream.available() > 0 || eStream.available() > 0) { try { iStream.read(buffer, 0, (int) Math.min(buffer.length, iStream.available())); } catch (IOException exc) { // so we couldn't eat the bytes, what else can we do? } try { eStream.read(buffer, 0, (int) Math.min(buffer.length, eStream.available())); } catch (IOException exc) { // screw you, copper } } } try { Thread.currentThread().sleep(100); // 100 milliseconds } catch (InterruptedException ex) { // screw you, copper } } } finally { FileOps.cleanupProcess(p); } } /** * <p>This method is used to run an external process line for the * Ganymede server. This method waits until the external command * completes before returning, and all file handles opened to * communicate with the process will be closed before returning.</p> * * @return a List comprising the Integer result code, the String * stdout text, and the String Stderr text. */ public static List runCaptureOutputProcess(String[] cmdArgs) throws IOException, InterruptedException { Process p = java.lang.Runtime.getRuntime().exec(cmdArgs); return runCaptureOutputProcess(p); } /** * <p>This method is used to run an external process line for the * Ganymede server. This method waits until the external command * completes before returning, and all file handles opened to * communicate with the process will be closed before returning.</p> * * @return a List comprising the Integer result code, the String * stdout text, and the String Stderr text. */ public static List runCaptureOutputProcess(String commandLine) throws IOException, InterruptedException { Process p = java.lang.Runtime.getRuntime().exec(commandLine); return runCaptureOutputProcess(p); } /** * <p>This method is used to run an external process line for the * Ganymede server. This method waits until the external command * completes before returning, and all file handles opened to * communicate with the process will be closed before returning.</p> * * @return a List comprising the Integer result code, the String * stdout text, and the String Stderr text. */ private static List runCaptureOutputProcess(Process p) throws IOException, InterruptedException { InputStream iStream = p.getInputStream(); InputStream eStream = p.getErrorStream(); StringBuffer stdoutBuffer = new StringBuffer(); StringBuffer stderrBuffer = new StringBuffer(); boolean done = false; int result_code = 0; try { while (!done) { slurpStreams(iStream, eStream, stdoutBuffer, stderrBuffer); try { result_code = p.exitValue(); done = true; } catch (IllegalThreadStateException ex) { slurpStreams(iStream, eStream, stdoutBuffer, stderrBuffer); } try { Thread.currentThread().sleep(100); // 100 milliseconds } catch (InterruptedException ex) { // screw you, copper } } } finally { slurpStreams(iStream, eStream, stdoutBuffer, stderrBuffer); FileOps.cleanupProcess(p); List resultList = new ArrayList(3); resultList.add(Integer.valueOf(result_code)); resultList.add(stdoutBuffer.toString()); resultList.add(stderrBuffer.toString()); return resultList; } } /** * Read everything we can from the passed stdout and stderr streams, * copy the data into outBuffer and errBuffer. */ private static void slurpStreams(InputStream outStream, InputStream errStream, StringBuffer outBuffer, StringBuffer errBuffer) { byte[] buffer = new byte[4096]; try { while (outStream.available() > 0 || errStream.available() > 0) { try { int count = (int) Math.min(buffer.length, outStream.available()); if (outBuffer.length() < MAXSIZE) { outBuffer.append(new String(buffer, 0, outStream.read(buffer, 0, count))); } else { outStream.read(buffer, 0, count); } } catch (IOException exc) { // so we couldn't eat the bytes, what else can we do? } try { int count = (int) Math.min(buffer.length, errStream.available()); if (errBuffer.length() < MAXSIZE) { errBuffer.append(new String(buffer, 0, errStream.read(buffer, 0, count))); } else { errStream.read(buffer, 0, count); } } catch (IOException exc) { // screw you, copper } } } catch (IOException ex) { // shrug } } /** * <p>This method shuts down / cleans up all resources related to * Process p. The following is mentioned as a work-around for the * fact that Process keeps its file descriptors open by default * until Garbage Collection.</p> */ public static void cleanupProcess(Process p) { try { p.getInputStream().close(); } catch (NullPointerException ex) { } catch (IOException ex) { } try { p.getOutputStream().close(); } catch (NullPointerException ex) { } catch (IOException ex) { } try { p.getErrorStream().close(); } catch (NullPointerException ex) { } catch (IOException ex) { } p.destroy(); } /** * * Test rig * */ public static void main (String args[]) { boolean result = false; /* File x = new File(args[0]); try { if (x.isDirectory()) { result = deleteDirectory(args[0]); } else { result = deleteFile(args[0]); } } catch (IOException ex) { ex.printStackTrace(); } */ /* String inName = args[0]; String outName = args[1]; try { result = FileOps.copyFile(inName, outName); } catch (IOException ex) { ex.printStackTrace(); } */ if (result) { System.exit(0); } else { System.exit(1); } } }