Index: main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java	(revision 31588)
+++ main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java	(revision 31591)
@@ -35,8 +35,5 @@
     private int exitValue = -1;
 
-    // user can write custom LineByLineHandler to deal with stdout lines as they come out one line at a time
-    // and stderr lines as they come out one at a time
-    private LineByLineHandler errLineByLineHandler = null;
-    private LineByLineHandler outLineByLineHandler = null;
+    // allow callers to process exceptions of the main process thread if they want
     private ExceptionHandler exceptionHandler = null;
 
@@ -79,14 +76,4 @@
     }
 
-    // register a handler whose gotLine() method will get called as each line is read from the process' stdout
-    public void setStdOutLineByLineHandler(LineByLineHandler out_lbl_handler) {
-	outLineByLineHandler = out_lbl_handler;
-    }
-
-    // register a handler whose gotLine() method will get called as each line is read from the process' stderr
-    public void setStdErrLineByLineHandler(LineByLineHandler err_lbl_handler) {
-	errLineByLineHandler = err_lbl_handler;
-    }
-
     // register a SafeProcess ExceptionHandler whose gotException() method will
     // get called for each exception encountered
@@ -104,6 +91,13 @@
 
 //***************** Copied from gli's gui/FormatConversionDialog.java *************//
+
     public int runProcess() {
-
+	return runProcess(null, null, null); // use default processing of all 3 of the process' iostreams
+    }
+
+    public int runProcess(CustomProcessHandler procInHandler,
+			   CustomProcessHandler procOutHandler,
+			   CustomProcessHandler procErrHandler)
+    {
 	Process prcs = null;
 	SafeProcess.OutputStreamGobbler inputGobbler = null;
@@ -112,5 +106,8 @@
 
 	try {	    
+	    
 	    Runtime rt = Runtime.getRuntime();	    
+	    
+	    
 	    if(this.command != null) {
 		prcs = rt.exec(this.command);
@@ -119,5 +116,5 @@
 
 		// http://stackoverflow.com/questions/5283444/convert-array-of-strings-into-a-string-in-java
-		///logger.info("SafeProcess running: " + Arrays.toString(command_args));
+		logger.info("SafeProcess running: " + Arrays.toString(command_args));
 
 		if(this.envp == null) { 
@@ -136,26 +133,41 @@
 	    }
 
-	    // send inputStr to process. The following constructor can handle inputStr being null
-	    inputGobbler = // WriterToProcessInputStream
-		new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
-	    
-	    // monitor for any error messages
-            errorGobbler // ReaderFromProcessOutputStream
-		= new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);
-
-            // monitor for the expected std output line(s)
-            outputGobbler
-		= new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
-	                
-	    // register line by line handlers, if any were set, for the process stderr and stdout streams
-	    if(this.outLineByLineHandler != null) {
-		outputGobbler.setLineByLineHandler(this.outLineByLineHandler);
-	    }
-	    if(this.errLineByLineHandler != null) {
-		errorGobbler.setLineByLineHandler(this.errLineByLineHandler);
-	    }
-	    if(this.exceptionHandler != null) {
-		inputGobbler.setExceptionHandler(this.exceptionHandler);
-	    }	    
+	    logger.info("### Before creating ProcessInGobbler");
+
+	    // Create the streamgobblers and set any specified handlers on them
+
+	    // PROC INPUT STREAM
+	    if(procInHandler == null) {
+		// send inputStr to process. The following constructor can handle inputStr being null
+		inputGobbler = // WriterToProcessInputStream
+		    new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
+	    } else { // user will do custom handling of process' InputStream 
+		inputGobbler = new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), procInHandler);
+	    }
+
+	    logger.info("### Before creating ProcessErrGobbler");
+	    
+	    // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr
+	    if(procErrHandler == null) {
+		errorGobbler // ReaderFromProcessOutputStream
+		    = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);		
+	    } else {
+		errorGobbler
+		    = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), procErrHandler);
+	    }
+
+	    logger.info("### Before creating ProcessOutGobbler");
+
+            // PROC OUT STREAM to monitor for the expected std output line(s)
+	    if(procOutHandler == null) {
+		outputGobbler
+		    = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
+	    } else {
+		outputGobbler
+		    = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), procOutHandler);
+	    }
+
+	    
+	    logger.info("### Before streamgobblers.start()");
 
             // kick off the stream gobblers
@@ -163,7 +175,14 @@
             errorGobbler.start();
             outputGobbler.start();
+
+	    logger.info("### After streamgobblers.start() - before waitFor");
                                     
             // any error???
-            this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate	    	    
+            this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate
+
+            logger.info("Process exitValue: " + exitValue); 
+
+	    logger.info("### Before streamgobblers.join()");
+
 	    // From the comments of 
 	    // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
@@ -175,8 +194,10 @@
 	    inputGobbler.join(); 
 	    
+	    logger.info("### After streamgobblers.join()");
+
 	    // set the variables the code that created a SafeProcess object may want to inspect
 	    this.outputStr = outputGobbler.getOutput();
 	    this.errorStr = errorGobbler.getOutput();
-
+	    
 	    // Since we didn't have an exception, process should have terminated now (waitFor blocks until then)
 	    // Set process to null so we don't forcibly terminate it below with process.destroy()
@@ -192,4 +213,5 @@
 	    }
 	} catch(InterruptedException ie) {
+
 	    if(exceptionHandler != null) {
 		exceptionHandler.gotException(ie);
@@ -197,7 +219,6 @@
 		logger.error("Process InterruptedException: " + ie.getMessage(), ie);
 		//System.err.println("Process InterruptedException " + ie.getMessage());
-		//ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
-	    }
-
+		///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
+	    }
 
 	    // propagate interrupts to worker threads here?
@@ -237,5 +258,5 @@
 	    }
 	}
-
+	
 	return this.exitValue;
     }
@@ -257,9 +278,9 @@
 }
 
-// When reading from a process' stdout or stderr stream, you can create a LineByLineHandler
-// to do something on a line by line basis, such as sending the line to a log
-public static interface LineByLineHandler {
-    public void gotLine(String line);
-    public void gotException(Exception e); // for when an exception occurs instead of getting a line
+// write your own run() body for any StreamGobbler
+// Make sure your implementation is threadsafe if you're sharing immutable objects between the threaded streams
+// example implementation is in the GS2PerlConstructor.SynchronizedProcessHandler class.
+public static interface CustomProcessHandler {
+    public void run(Closeable stream); //InputStream or OutputStream
 }
 
@@ -270,13 +291,13 @@
 public static class InputStreamGobbler extends Thread
 {
-    InputStream is = null;
-    StringBuffer outputstr = new StringBuffer();
-    boolean split_newlines = false;
-    LineByLineHandler lineByLineHandler = null;
-    
+    private InputStream is = null;
+    private StringBuffer outputstr = new StringBuffer();
+    private boolean split_newlines = false;
+    private CustomProcessHandler customHandler = null;
+
     public InputStreamGobbler(InputStream is)
     {
 	this.is = is;
-	split_newlines = false;
+	this.split_newlines = false;
     }
     
@@ -287,10 +308,12 @@
     }
     
-    public void setLineByLineHandler(LineByLineHandler lblHandler) {
-	lineByLineHandler = lblHandler;
-    }
-
-
-    public void run()
+    public InputStreamGobbler(InputStream is, CustomProcessHandler customHandler)
+    {
+	this.is = is;
+	this.customHandler = customHandler;
+    }
+
+    // default run() behaviour
+    public void runDefault()
     {
 	BufferedReader br = null;
@@ -307,20 +330,12 @@
 		//System.out.println("@@@ GOT LINE: " + line);
 		outputstr.append(line);
-		
 		if(split_newlines) {
 		    outputstr.append(Misc.NEWLINE); // "\n" is system dependent (Win must be "\r\n")
 		}
-
-		if(lineByLineHandler != null) { // let handler deal with newlines
-		    lineByLineHandler.gotLine(line);
-		}		
-	    }
-	} catch (IOException ioe) {
-	    if(lineByLineHandler != null) {
-		lineByLineHandler.gotException(ioe);
-	    } else {
-		logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe);
-		//ioe.printStackTrace();
-	    }
+	    }
+	} catch (IOException ioe) {	    
+	    logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe);
+	    //System.err.println("Exception when reading from a process' stdout/stderr stream: ");
+	    //ioe.printStackTrace();  
 	    
 	} finally {
@@ -329,4 +344,16 @@
     }
     
+    public void runCustom() {
+	this.customHandler.run(is);
+    }
+    
+    public void run() {
+	if(this.customHandler == null) {
+	    runDefault();
+	} else {
+	    runCustom();
+	}
+    }
+
     public String getOutput() { 
 	return outputstr.toString(); // implicit toString() call anyway. //return outputstr; 
@@ -340,7 +367,7 @@
 public static class OutputStreamGobbler extends Thread
 {
-    OutputStream os = null;
-    String inputstr = "";
-    ExceptionHandler exceptionHandler = null;
+    private OutputStream os = null;
+    private String inputstr = "";
+    private CustomProcessHandler customHandler = null;
 
     public OutputStreamGobbler(OutputStream os) {
@@ -353,11 +380,13 @@
 	this.inputstr = inputstr;
     }
-    
-    public void setExceptionHandler(ExceptionHandler eHandler) {
-	exceptionHandler = eHandler;
-    }
-
-    public void run()
-    {	
+
+    public OutputStreamGobbler(OutputStream os, CustomProcessHandler customHandler) {
+	this.os = os;
+	this.customHandler = customHandler;
+    }
+
+    // default run() behaviour
+    public void runDefault() {
+	
 	if (inputstr == null) {
 		return;
@@ -365,5 +394,5 @@
 	
 	BufferedWriter osw = null;
-	try	{
+	try {
 	    osw = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
 	    //System.out.println("@@@ SENDING LINE: " + inputstr);
@@ -385,14 +414,25 @@
 	    */
 	} catch (IOException ioe) {
-	    if (this.exceptionHandler != null) {
-		this.exceptionHandler.gotException(ioe);
-	    } else {
-		logger.error("Exception writing to SafeProcess' inputstream: ", ioe);	    
-		//ioe.printStackTrace();
-	    }
-	    
+	    logger.error("Exception writing to SafeProcess' inputstream: ", ioe);
+	    //System.err.println("Exception writing to SafeProcess' inputstream: ");
+	    //ioe.printStackTrace();	
 	} finally {
 	    SafeProcess.closeResource(osw);
 	}
+    }
+
+    // call the user's custom handler for the run() method
+    public void runCustom() {
+	this.customHandler.run(os);	
+    }
+
+    public void run()
+    {
+	if(this.customHandler == null) {
+	    runDefault();
+	} else {
+	    runCustom();
+	}
+
     }	
 } // end static inner class OutputStreamGobbler
