- Timestamp:
- 2017-05-01T18:23:39+12:00 (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/gli/src/org/greenstone/gatherer/shell/GShell.java
r26574 r31638 56 56 import org.greenstone.gatherer.metadata.DocXMLFileManager; 57 57 import org.greenstone.gatherer.remote.RemoteGreenstoneServer; 58 import org.greenstone.gatherer.util.StaticStrings; 58 import org.greenstone.gatherer.util.SafeProcess; 59 import org.greenstone.gatherer.util.StaticStrings; 59 60 import org.greenstone.gatherer.util.Utility; 60 61 … … 63 64 */ 64 65 public class GShell 65 extends Thread {66 extends Thread implements SafeProcess.ExceptionHandler { 66 67 /** A flag used to determine if this process has been asked to cancel. */ 67 68 private boolean cancel = false; … … 84 85 private String commandOutput = null; 85 86 87 /** The process safely run in this GShell. Make sure to set to null when done with. */ 88 SafeProcess prcs = null; 89 86 90 /** Elements in process type enumeration. */ 87 91 static final public int BUILD = 0; … … 113 117 static public String GSHELL_FEDORA_COLDELETE = "gshell_fedora_col_delete"; 114 118 115 /** Determine if the given process is still executing. It does this by attempting to throw an exception - not the most efficient way, but the only one as far as I know116 * @param process the Process to test117 * @return true if it is still executing, false otherwise118 */119 static public boolean processRunning(Process process) {120 boolean process_running = false;121 122 try {123 process.exitValue(); // This will throw an exception if the process hasn't ended yet.124 }125 catch(IllegalThreadStateException itse) {126 process_running = true;127 }128 catch(Exception exception) {129 DebugStream.printStackTrace(exception);130 }131 return process_running;132 }133 119 134 120 /** Constructor gatherer all the data required to create a new process, and emit meaningfull messages. … … 236 222 237 223 238 private void runLocal(String[] args, BufferedOutputStream bos) 224 // old_runLocal uses a potentially unsafe way of running a process and an inefficient way of reading 225 // from the process stdout and stderr streams. Replaced by new runLocal() which uses SafeProcess and 226 // the new CustomProcessHandler inner class instantiated for each of the 2 process output streams. 227 private void old_runLocal(String[] args, BufferedOutputStream bos) 239 228 { 240 229 try { … … 260 249 StringBuffer stdline_buffer = new StringBuffer(); 261 250 262 while(/*type != GShell.NEW &&*/ processRunning(prcs) && !hasSignalledStop()) {251 while(/*type != GShell.NEW &&*/ SafeProcess.processRunning(prcs) && !hasSignalledStop()) { 263 252 // Hopefully this doesn't block if the process is trying to write to STDOUT. 264 253 if((eisr!=null) && eisr.ready()) { … … 348 337 } 349 338 339 private void runLocal(String[] args, BufferedOutputStream bos) 340 { 341 // in case we stop between import and build, let's not launch further processes 342 // that only pressing cancel, which results in interrupts, can subsequently stop. 343 if(hasSignalledStop()) { 344 return; 345 } 346 347 String command = ""; 348 for(int i = 0; i < args.length; i++) { 349 command = command + args[i] + " "; 350 } 351 352 ///ystem.err.println("Command: " + command); 353 fireMessage(type, Dictionary.get("GShell.Command") + ": " + command, status, null); 354 355 prcs = new SafeProcess(args); 356 SafeProcess.CustomProcessHandler processOutHandler 357 = new SynchronizedProcessHandler(bos, SafeProcess.STDOUT); 358 SafeProcess.CustomProcessHandler processErrHandler 359 = new SynchronizedProcessHandler(bos, SafeProcess.STDERR); 360 361 prcs.setExceptionHandler(this); 362 int exitValue = prcs.runProcess(null, processOutHandler, processErrHandler); // use default procIn handling 363 364 if(exitValue == 0) { 365 System.err.println("*** Exited normally"); 366 status = OK; 367 fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Success"), status, null); 368 } 369 else { 370 System.err.println("*** Exited abnormally with exit value " + exitValue); 371 status = ERROR; 372 fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Failure"), status, null); 373 } 374 375 prcs = null; 376 } 377 378 /** When cancel is called on this GShell thread from a separate thread, 379 * This GShell will safely terminate any process it's currently running (by interrupting it) 380 * and will set this.prcs to null at the end. */ 381 public void cancel() { 382 if(prcs != null) { //GShell is running a process, so interrupt the GShell/SafeProcess thread 383 384 SafeProcess.log("********** HAS SIGNALLED STOP. INTERRUPTING THE GSHELL/SAFEPROCESS THREAD"); 385 386 this.interrupt(); // interrupt this thread which is running the SafeProcess prcs 387 // this will propagate the CANCEL status 388 prcs = null; 389 } 390 hasSignalledStop(); // synchronized. Will set status to CANCELLED in thread-safe manner. 391 } 350 392 351 393 … … 589 631 * @return A <strong>boolean</strong> indicating if the user wanted to stop. 590 632 */ 591 public boolean hasSignalledStop() {633 public synchronized boolean hasSignalledStop() { 592 634 boolean has_signalled_stop = false; 593 635 if(progress != null) { … … 639 681 return name; 640 682 } 683 684 // From interface SafeProcess.ExceptionHandler 685 // Called when an exception happens during the running of our perl process, as we want to set 686 // the GShell status to ERROR. 687 // However, exceptions when reading from our perl process' stderr and stdout streams are handled 688 // by SynchronizedProcessHandler.gotException() below, since they happen in separate threads 689 // from this one (the one from which the perl process is run). 690 public void gotException(Exception e) { 691 692 if(e instanceof InterruptedException) { 693 SafeProcess.log("*** InterruptedException in GShell.runLocal()"); 694 setStatus(CANCELLED); // expected exception 695 } else { 696 DebugStream.println("Exception in GShell.runLocal() - unexpected"); 697 DebugStream.printStackTrace(e); 698 setStatus(ERROR); // status particularly needs to be synchronized on 699 } 700 } 701 702 public synchronized void setStatus(int newStatus) { 703 status = newStatus; 704 } 705 706 // Each instance of this class is run in its own thread by class SafeProcess.InputGobbler. 707 // This class deals with each incoming line from the perl process' stderr or stdout streams. One 708 // instance of this class for each stream. However, since multiple instances of this CustomProcessHandler 709 // could be (and in fact, are) writing to the same file in their own threads, several objects, not just 710 // the bufferedwriter object, needed to be made threadsafe. 711 // This class also handles exceptions during the running of the perl process. 712 // The runPerlCommand code originally would do a sendProcessStatus on each exception, so we ensure 713 // we do that here too, to continue original behaviour. These calls are also synchronized to make their 714 // use of the EventListeners threadsafe. 715 protected class SynchronizedProcessHandler extends SafeProcess.CustomProcessHandler 716 { 717 private final BufferedOutputStream bos; // needs to be final to be able to synchronize on the shared object 718 719 public SynchronizedProcessHandler(BufferedOutputStream bos, int src) { 720 super(src); // will set this.source to STDERR or STDOUT 721 this.bos = bos; // caller will close bw, since many more than one 722 // SynchronizedProcessHandlers are using it 723 } 724 725 // trying to keep synchronized methods as short as possible 726 private void logException(Exception e) { 727 String stream = (this.source == SafeProcess.STDERR) ? "stderr" : "stdout"; 728 String msg = "Got exception when processing the perl process' " + stream + " stream."; 729 730 DebugStream.println(msg); // method is already synchronized 731 DebugStream.printStackTrace(e); // method is already synchronized 732 SafeProcess.log("*** " + msg, e); 733 734 GShell.this.setStatus(GShell.ERROR); 735 } 736 737 private void log(String msg) { 738 DebugStream.println(msg); // already synchro 739 System.err.println("@@@@@ " + msg); 740 } 741 742 public void run(Closeable inputStream) { 743 InputStream is = (InputStream) inputStream; 744 745 BufferedReader br = null; 746 try { 747 748 br = new BufferedReader(new InputStreamReader(is, "UTF-8")); 749 String line=null; 750 751 ///while ( !GShell.this.prcs.processRunning() || hasSignalledStop()) { // need to sync on prcs 752 while ( !Thread.currentThread().isInterrupted() && (line = br.readLine()) != null ) { 753 this.gotLine(line); // synchronized 754 } 755 756 } catch (Exception e) { 757 logException(e); 758 } finally { 759 System.err.println("*********"); 760 if(Thread.currentThread().isInterrupted()) { 761 log("We've been asked to stop."); 762 763 } 764 SafeProcess.closeResource(br); 765 String stream = (this.source == SafeProcess.STDERR) ? "stderr" : "stdout"; 766 System.err.println("*** In finally of " + stream + " stream"); 767 System.err.println("*********"); 768 } 769 } 770 771 protected synchronized void gotLine(String line) { 772 fireMessage(GShell.this.type, // not final, needs synchro 773 GShell.this.typeAsString(type) + "> " + line, 774 GShell.this.status, // not final, needs synchro 775 this.bos); // needs synchro 776 } 777 778 } 779 641 780 }
Note:
See TracChangeset
for help on using the changeset viewer.