Index: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/build/GS2PerlConstructor.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/gsdl3/build/GS2PerlConstructor.java	(revision 31590)
+++ main/trunk/greenstone3/src/java/org/greenstone/gsdl3/build/GS2PerlConstructor.java	(revision 31591)
@@ -18,4 +18,5 @@
 import java.io.InputStreamReader;
 import java.io.File;
+import java.io.InputStream;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -334,4 +335,5 @@
 		// http://www.cgi101.com/class/ch3/text.html
 		// setenv QUERY_STRING and REQUEST_METHOD = GET.
+		// Run the perl command as a simple process: no logging to the collection's build log
 		if (runPerlCommand(command_str, envvars, new File(cgi_directory)))
 				   //new File(GlobalProperties.getGSDL3Home() + File.separator + "WEB-INF" + File.separator + "cgi")))
@@ -372,14 +374,5 @@
 	}
 
-	/** returns true if completed correctly, false otherwise */
-    protected boolean runPerlCommand(String[] command) {
-	return runPerlCommand(command, null, null);
-    }
-
-    	
-    protected boolean runPerlCommand(String[] command, String[] envvars, File dir)
-    {
-	boolean success = true;
-	
+    protected SafeProcess createPerlProcess(String[] command, String[] envvars, File dir) {
 	int sepIndex = this.gsdl3home.lastIndexOf(File.separator);
 	String srcHome = this.gsdl3home.substring(0, sepIndex);
@@ -402,4 +395,18 @@
 	    args.add(a + "=" + System.getenv(a));
 	}
+
+	SafeProcess perlProcess 
+	    = new SafeProcess(command, args.toArray(new String[args.size()]), dir); //  dir can be null
+	
+	return perlProcess;
+    }
+
+    // ModifyMetadata operations call runSimplePerlCommand which produces no output in build log
+    protected boolean runSimplePerlCommand(String[] command) {	
+	return runSimplePerlCommand(command, null, null);
+    }
+    
+    protected boolean runSimplePerlCommand(String[] command, String[] envvars, File dir) {
+	boolean success = false;	
 	
 	String command_str = "";
@@ -410,10 +417,50 @@
 	sendMessage(new ConstructionEvent(this, GSStatus.INFO, "command = " + command_str));
 	
-	//logger.info("### Running command = " + command_str);
+	logger.info("### Running simple command = " + command_str);
 
 	// This is where we create and run our perl process safely
-	SafeProcess perlProcess 
-	    = new SafeProcess(command, args.toArray(new String[args.size()]), dir); //  dir can be null
-	
+	SafeProcess perlProcess = createPerlProcess(command, envvars, dir); //  dir can be null
+	
+	perlProcess.setExceptionHandler(this);
+
+	sendProcessBegun(new ConstructionEvent(this, GSStatus.ACCEPTED, "starting"));
+
+	int exitVal = perlProcess.runProcess(); // uses default processing of the perl process' iostreams provided by SafeProcess
+
+	if (exitVal == 0) {
+	    success = true;
+	    sendProcessStatus(new ConstructionEvent(this, GSStatus.CONTINUING, "Success"));
+	} else {
+	    sendProcessStatus(new ConstructionEvent(this, GSStatus.ERROR, "Failure"));	    
+	    success = false; // explicit
+	}
+	
+	return success;
+    }
+
+
+    /** returns true if completed correctly, false otherwise 
+     * Building operations call runPerlCommand which sends build output to collect/log/build_log.#*.txt
+     */
+    protected boolean runPerlCommand(String[] command) {
+	return runPerlCommand(command, null, null);
+    }
+
+    	
+    protected boolean runPerlCommand(String[] command, String[] envvars, File dir)
+    {
+	boolean success = true;
+	
+	String command_str = "";
+	for (int i = 0; i < command.length; i++) {
+	    command_str = command_str + command[i] + " ";
+	}
+	
+	sendMessage(new ConstructionEvent(this, GSStatus.INFO, "command = " + command_str));
+	
+	logger.info("### Running logged command = " + command_str);
+
+	// This is where we create and run our perl process safely
+	SafeProcess perlProcess = createPerlProcess(command, envvars, dir); //  dir can be null
 	
 	sendProcessBegun(new ConstructionEvent(this, GSStatus.ACCEPTED, "starting"));
@@ -428,5 +475,6 @@
 	BufferedWriter bw = null;
 	try {
-	    bw = new BufferedWriter(new FileWriter(GSFile.collectDir(this.site_home) + File.separator + this.collection_name + File.separator + "log" + File.separator + "build_log." + (System.currentTimeMillis()) + ".txt"));		
+
+	    bw = new BufferedWriter(new FileWriter(new File(logDir, "build_log." + (System.currentTimeMillis()) + ".txt")));
 	
 	    bw.write("Document Editor Build \n");		
@@ -434,10 +482,8 @@
 	    
 	    // handle each incoming line from stdout and stderr streams, and any exceptions that occur then
-	    SynchronizedProcessLineByLineHandler outLineByLineHandler
-		= new SynchronizedProcessLineByLineHandler(bw, SynchronizedProcessLineByLineHandler.STDOUT);
-	    SynchronizedProcessLineByLineHandler errLineByLineHandler
-		= new SynchronizedProcessLineByLineHandler(bw, SynchronizedProcessLineByLineHandler.STDERR);
-	    perlProcess.setStdOutLineByLineHandler(outLineByLineHandler);
-	    perlProcess.setStdErrLineByLineHandler(errLineByLineHandler);
+	    SafeProcess.CustomProcessHandler processOutHandler
+		= new SynchronizedProcessHandler(bw, SynchronizedProcessHandler.STDOUT);
+	    SafeProcess.CustomProcessHandler processErrHandler
+		= new SynchronizedProcessHandler(bw, SynchronizedProcessHandler.STDERR);
 	    
 	    // GS2PerlConstructor will do further handling of exceptions that may occur during the perl
@@ -450,5 +496,5 @@
 	    // std in of java, as before.
 
-	    perlProcess.runProcess();
+	    perlProcess.runProcess(null, processOutHandler, processErrHandler); // use default procIn handling
 	    
 	// The original runPerlCommand() code had an ineffective check for whether the cmd had been cancelled
@@ -657,5 +703,6 @@
     // Called when an exception happens during the running of our perl process. However,
     // exceptions when reading from our perl process' stderr and stdout streams are handled by
-    // SynchronizedProcessLineByLineHandler.gotException() below.
+    // SynchronizedProcessHandler.gotException() below, since they happen in separate threads
+    // from this one (the ine from which the perl process is run).
     public synchronized void gotException(Exception e) {
 
@@ -664,15 +711,17 @@
 	e.printStackTrace();
 	sendProcessStatus(new ConstructionEvent(this,GSStatus.ERROR, 
-						"Exception occurred " + e.toString())); // atomic
+						"Exception occurred " + e.toString()));
     }
     
+    // Each instance of this class is run in its own thread by class SafeProcess.InputGobbler.
     // This class deals with each incoming line from the perl process' stderr or stdout streams. One
-    // instance of this class for each stream. However, since multiple instances of this LineByLineHandler 
-    // could be (and in fact, are) writing to the same file in their own threads, the writer object needs 
-    // to be made threadsafe.
+    // instance of this class for each stream. However, since multiple instances of this CustomProcessHandler
+    // could be (and in fact, are) writing to the same file in their own threads, several objects, not just
+    // the bufferedwriter object, needed to be made threadsafe.
     // This class also handles exceptions during the running of the perl process.
     // The runPerlCommand code originally would do a sendProcessStatus on each exception, so we ensure
-    // we do that here too, to continue original behaviour.
-    protected class SynchronizedProcessLineByLineHandler implements SafeProcess.LineByLineHandler
+    // we do that here too, to continue original behaviour. These calls are also synchronized to make their
+    // use of the EventListeners threadsafe.
+    protected class SynchronizedProcessHandler implements SafeProcess.CustomProcessHandler
     {
 	public static final int STDERR = 0;
@@ -683,15 +732,78 @@
 		
 
-	public SynchronizedProcessLineByLineHandler(BufferedWriter bw, int src) {
-	    this.bwHandle = bw;
+	public SynchronizedProcessHandler(BufferedWriter bw, int src) {
+	    this.bwHandle = bw; // caller will close bw, since many more than one
+	                        // SynchronizedProcessHandlers are using it
 	    this.source = src; // STDERR or STDOUT
 	}
 
-	public synchronized void gotLine(String line) {
-	    //if(this.source == STDERR) {
-		///System.err.println("ERROR: " + line);
-	    //} else {
-		///System.err.println("OUT: " + line);
-	    //}
+	public void run(Closeable inputStream) {
+	    InputStream is = (InputStream) inputStream;
+
+	    BufferedReader br = null;
+	    try {
+		br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+		String line=null;
+		while ( (line = br.readLine()) != null ) {
+		    
+		    if(Thread.currentThread().isInterrupted()) { // should we not instead check if SafeProcess thread was interrupted?
+			System.err.println("Got interrupted when reading lines from process err/out stream.");
+			break; // will go to finally block
+		    }
+		    
+///		    System.out.println("@@@ GOT LINE: " + line);
+///		    GS2PerlConstructor.logger.info("@@@ GOT LINE: " + line + " STDOUT=1: " + source);
+
+		    //if(this.source == STDERR) {
+		    ///System.err.println("ERROR: " + line);
+		    //} else {
+		    ///System.err.println("OUT: " + line);
+		    //}	
+		    
+
+		    this.gotLine(line); // synchronized
+		    
+		    /*
+		    try {
+			synchronized(bwHandle) { // get a lock on the writer handle, then write
+			    
+			    bwHandle.write(line + "\n");
+			} 
+		    } catch(IOException ioe) {
+			String msg = (source == STDERR) ? "stderr" : "stdout";
+			msg = "Exception when writing out a line read from perl process' " + msg + " stream.";
+			GS2PerlConstructor.logger.error(msg, ioe);
+		    }
+		    
+		    // this next method is thread safe since only synchronized methods are invoked.
+		    // and only immutable (final) vars are used. 
+		    // NO, What about the listeners???
+		    sendProcessStatus(new ConstructionEvent(GS2PerlConstructor.this, GSStatus.CONTINUING, line));		    
+		    */
+		}
+	    } catch (IOException ioe) { // problem with reading in from process with BufferedReader br
+
+		String msg = (source == STDERR) ? "stderr" : "stdout";
+		msg = "Got exception when processing the perl process' " + msg + " stream.";
+		GS2PerlConstructor.logger.error(msg, ioe);
+		// now do what the original runPerlCommand() code always did:
+		ioe.printStackTrace();
+		logException(ioe); // synchronized
+
+	    } catch (Exception e) { // problem with BufferedWriter bwHandle on processing each line
+		e.printStackTrace();
+		logException(e); // synchronized
+	    } finally {
+		SafeProcess.closeResource(br);
+	    }
+	}
+
+	// trying to keep synchronized methods as short as possible
+	private synchronized void logException(Exception e) {
+	    sendProcessStatus(new ConstructionEvent(this, GSStatus.ERROR, "Exception occurred " + e.toString()));
+	}
+
+	// trying to keep synchronized methods as short as possible
+	private synchronized void gotLine(String line) throws Exception {
 
 	    // BufferedWriter writes may not be atomic
@@ -699,33 +811,27 @@
 	    // Choosing to put try-catch outside of sync block, since it's okay to give up lock on exception
 	    // http://stackoverflow.com/questions/14944551/it-is-better-to-have-a-synchronized-block-inside-a-try-block-or-a-try-block-insi
-	    // "All methods on Logger are multi-thread safe", see
-	    // http://stackoverflow.com/questions/14211629/java-util-logger-write-synchronization
-	    
-	    try {		
-		bwHandle.write(line + "\n");
+	    try {					    
+		bwHandle.write(line + "\n");	
+	    
+///		System.out.println("@@@ WROTE LINE: " + line);
+///		GS2PerlConstructor.logger.info("@@@ WROTE LINE: " + line);
+
+		// this next method is thread safe since only synchronized methods are invoked.
+		// and only immutable (final) vars are used.
+		sendProcessStatus(new ConstructionEvent(GS2PerlConstructor.this, GSStatus.CONTINUING, line));
+	    
+	    } catch(IOException ioe) { // can't throw Exceptions, but are forced to handle Exceptions here
+		// since our method definition doesn't specify a throws list.
+		// "All methods on Logger are multi-thread safe", see
+		// http://stackoverflow.com/questions/14211629/java-util-logger-write-synchronization
 		
-	    } catch(IOException ioe) {
 		String msg = (source == STDERR) ? "stderr" : "stdout";
-		msg = "Exception when writing out a line read from perl process' " + msg + " stream.";
-		GS2PerlConstructor.logger.error(msg, ioe);
-	    }
-	    
-	    // this next method is thread safe since only synchronized methods are invoked.
-	    // and only immutable (final) vars are used.
-	    sendProcessStatus(new ConstructionEvent(GS2PerlConstructor.this, GSStatus.CONTINUING, line));
-	}
-
-	// This is called when we get an exception during the processing of a perl's
-	// input-, err- or output stream
-	public synchronized void gotException(Exception e) {
-	    String msg = (source == STDERR) ? "stderr" : "stdout";
-	    msg = "Got exception when processing the perl process' " + msg + " stream.";
-
-	    // now do what the original runPerlCommand() code always did:
-	    e.printStackTrace();
-	    sendProcessStatus(new ConstructionEvent(this, GSStatus.ERROR, "Exception occurred " + e.toString())); // atomic
-	}
-
-    } // end inner class SynchronizedProcessLineByLineHandler
+		msg = "IOException when writing out a line read from perl process' " + msg + " stream.";
+		msg += "\nGot line: " + line + "\n";
+		throw new Exception(msg, ioe);
+	    }		    
+	}
+
+    } // end inner class SynchronizedProcessHandler
 
 }
Index: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Construct.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Construct.java	(revision 31590)
+++ main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Construct.java	(revision 31591)
@@ -1187,15 +1187,25 @@
 		// now we know we have an archives folder
 		String old_value = coll_db.getValue(oid);
-		String new_value = old_value.replace("<index-status>B", "<index-status>" + mark);
+		String new_value = "<index-status>" + mark;
+		if(old_value == null) {
+		    logger.error("### null old_value in flat DB for oid " + oid);
+		} else {
+		    new_value = old_value.replace("<index-status>B", "<index-status>" + mark);
+		    logger.info("### Replacing db entry for oid " + oid + " which has old_value " + old_value);
+		    logger.info("### with new value " + new_value);
+				    
+		}
 		// Close database for reading
 		coll_db.closeDatabase();
+
 		if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.WRITE))
-		{
+		    {
 			logger.error("Could not open collection archives database. Somebody already using this database!");
 			return;
-		}
+		    }
+		
 		coll_db.setValue(oid, new_value);
-		coll_db.closeDatabase();
 		
+		coll_db.closeDatabase();		
 	}
 }
Index: main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java	(revision 31590)
+++ 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
