Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/build/GS2PerlConstructor.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/build/GS2PerlConstructor.java	(revision 31630)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/build/GS2PerlConstructor.java	(revision 31631)
@@ -485,7 +485,7 @@
 	    // handle each incoming line from stdout and stderr streams, and any exceptions that occur then
 	    SafeProcess.CustomProcessHandler processOutHandler
-		= new SynchronizedProcessHandler(bw, SynchronizedProcessHandler.STDOUT);
+		= new SynchronizedProcessHandler(bw, SafeProcess.STDOUT);
 	    SafeProcess.CustomProcessHandler processErrHandler
-		= new SynchronizedProcessHandler(bw, SynchronizedProcessHandler.STDERR);
+		= new SynchronizedProcessHandler(bw, SafeProcess.STDERR);
 	    
 	    // GS2PerlConstructor will do further handling of exceptions that may occur during the perl
@@ -700,5 +700,5 @@
     // exceptions when reading from our perl process' stderr and stdout streams are handled by
     // SynchronizedProcessHandler.gotException() below, since they happen in separate threads
-    // from this one (the ine from which the perl process is run).
+    // from this one (the one from which the perl process is run).
     public synchronized void gotException(Exception e) {
 
@@ -719,17 +719,12 @@
     // 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
+    protected class SynchronizedProcessHandler extends SafeProcess.CustomProcessHandler
     {
-	public static final int STDERR = 0;
-	public static final int STDOUT = 1;
-
-	private final int source;
 	private final BufferedWriter bwHandle; // needs to be final to synchronize on the object
 		
-
 	public SynchronizedProcessHandler(BufferedWriter bw, int src) {
+	    super(src); // will set this.source to STDERR or STDOUT
 	    this.bwHandle = bw; // caller will close bw, since many more than one
 	                        // SynchronizedProcessHandlers are using it
-	    this.source = src; // STDERR or STDOUT
 	}
 
@@ -751,5 +746,5 @@
 
 
-		    //if(this.source == STDERR) {
+		    //if(this.source == SafeProcess.STDERR) {
 		    ///System.err.println("ERROR: " + line);
 		    //} else {
@@ -767,5 +762,5 @@
 			} 
 		    } catch(IOException ioe) {
-			String msg = (source == STDERR) ? "stderr" : "stdout";
+			String msg = (this.source == SafeProcess.STDERR) ? "stderr" : "stdout";
 			msg = "Exception when writing out a line read from perl process' " + msg + " stream.";
 			GS2PerlConstructor.logger.error(msg, ioe);
@@ -780,5 +775,5 @@
 	    } catch (IOException ioe) { // problem with reading in from process with BufferedReader br
 
-		String msg = (source == STDERR) ? "stderr" : "stdout";
+		String msg = (this.source == SafeProcess.STDERR) ? "stderr" : "stdout";
 		msg = "Got exception when processing the perl process' " + msg + " stream.";
 		GS2PerlConstructor.logger.error(msg, ioe);
@@ -821,5 +816,5 @@
 		// http://stackoverflow.com/questions/14211629/java-util-logger-write-synchronization
 		
-		String msg = (source == STDERR) ? "stderr" : "stdout";
+		String msg = (this.source == SafeProcess.STDERR) ? "stderr" : "stdout";
 		msg = "IOException when writing out a line read from perl process' " + msg + " stream.";
 		msg += "\nGot line: " + line + "\n";
Index: /main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java	(revision 31630)
+++ /main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java	(revision 31631)
@@ -20,4 +20,8 @@
 
 public class SafeProcess {
+    
+    public static final int STDERR = 0;
+    public static final int STDOUT = 1;
+    public static final int STDIN = 2;    
 
     static Logger logger = Logger.getLogger(org.greenstone.util.SafeProcess.class.getName());
@@ -204,5 +208,5 @@
     }
 
-    // Runs a process with default stream processing
+    // Runs a process with default stream processing. Returns the exitValue
     public int runProcess() {
 	return runProcess(null, null, null); // use default processing of all 3 of the process' iostreams
@@ -210,5 +214,6 @@
 
     // Run a process with custom stream processing (any custom handlers passed in that are null
-    // will use the default stream processing)
+    // will use the default stream processing). 
+    // Returns the exitValue from running the Process
     public int runProcess(CustomProcessHandler procInHandler,
 			   CustomProcessHandler procOutHandler,
@@ -239,5 +244,5 @@
 	    if(procErrHandler == null) {
 		errorGobbler // ReaderFromProcessOutputStream
-		    = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);		
+		    = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);
 	    } else {
 		errorGobbler
@@ -414,5 +419,5 @@
 
 
-//******************** Inner class interface definitions ********************//
+//******************** Inner class and interface definitions ********************//
 // Static inner classes can be instantiated without having to instantiate an object of the outer class first
 
@@ -423,26 +428,43 @@
 public static interface ExceptionHandler {
 
-    // SHOULD I DECLARE THIS SYNCHRONIZED?
-    // It ends up being thread safe for the particular instance I'm using it for, but that doesn't
-    // make it future proof...
-    public void gotException(Exception e);
+    // when implementing ExceptionHandler.gotException(), if it manipulates anything that's
+    // not threadsafe, declare gotException() as a synchronized method to ensure thread safety
+    public void gotException(Exception e); // can't declare as synchronized in interface method declaration
 }
 
 // Write your own run() body for any StreamGobbler. You need to create an instance of a class
-// implementing CustomProcessHandler for EACH IOSTREAM of the process that you want to handle.
+// extending CustomProcessHandler for EACH IOSTREAM of the process that you want to handle.
 // Do not create a single CustomProcessHandler instance and reuse it for all three streams,
 // i.e. don't call SafeProcess' runProcess(x, x, x); It should be runProcess(x, y, z).
 // 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
+// CustomProcessHandler is made an abstract class instead of an interface to force classes that want
+// to use a CustomProcessHandler to create a separate class that extends CustomProcessHandler, rather than
+// that the classes that wish to use it "implementing" the CustomProcessHandler interface itself: the
+// CustomProcessHandler.run() method may then be called in the major thread from which the Process is being
+// executed, rather than from the individual threads that deal with each iostream of the Process.
+public static abstract class CustomProcessHandler {
+
+    protected final int source;
+
+    protected CustomProcessHandler(int src) {
+	this.source = src; // STDERR or STDOUT or STDIN
+    }
+    
+    public abstract void run(Closeable stream); //InputStream or OutputStream
 }
 
-// When using the default stream processing to read from a process' stdout or stderr stream,
-// you can create a LineByLineHandler for the process' err and out streams
+// When using the default stream processing to read from a process' stdout or stderr stream, you can
+// create a class extending LineByLineHandler for the process' err stream and one for its output stream
 // 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
+public static abstract class LineByLineHandler {
+    protected final int source;
+
+    protected LineByLineHandler(int src) {
+	this.source = src; // STDERR or STDOUT
+    }
+
+    public abstract void gotLine(String line); // first non-null line
+    public abstract void gotException(Exception e); // for when an exception occurs instead of getting a line
 }
 
