/*
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);
}
}
}