001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: welker $' 006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 007 * '$Revision: 24234 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.sdm.spa; 031 032import java.io.BufferedReader; 033import java.io.BufferedWriter; 034import java.io.File; 035import java.io.IOException; 036import java.io.InputStream; 037import java.io.InputStreamReader; 038import java.io.OutputStreamWriter; 039import java.util.Iterator; 040import java.util.List; 041 042import ptolemy.actor.IOPort; 043import ptolemy.actor.TypedAtomicActor; 044import ptolemy.actor.TypedIOPort; 045import ptolemy.actor.parameters.FilePortParameter; 046import ptolemy.actor.parameters.PortParameter; 047import ptolemy.data.ArrayToken; 048import ptolemy.data.BooleanToken; 049import ptolemy.data.RecordToken; 050import ptolemy.data.StringToken; 051import ptolemy.data.expr.FileParameter; 052import ptolemy.data.expr.Parameter; 053import ptolemy.data.expr.SingletonParameter; 054import ptolemy.data.type.BaseType; 055import ptolemy.kernel.CompositeEntity; 056import ptolemy.kernel.util.Attribute; 057import ptolemy.kernel.util.IllegalActionException; 058import ptolemy.kernel.util.InternalErrorException; 059import ptolemy.kernel.util.NameDuplicationException; 060import ptolemy.kernel.util.Nameable; 061 062// //////////////////////////////////////////////////////////////////////// 063// // CommandLineExec 064/** 065 * Execute a command string. 066 * <p> 067 * Given a command string, the <I>CommandLineExec</I> actor executes it using 068 * the java Runtime class. 069 * </p> 070 * <p> 071 * <b>SUPPORTED COMMAND TYPES:</b> 072 * <ul> 073 * <li>command</li> 074 * <li>command < infile > outfile</li> 075 * <li>command > outfile</li> 076 * <li>command < infile</li> 077 * <li>command [arg1..argn] > outfile</li> 078 * <li>command [arg1..argn]</li> 079 * <li>command [arg1..argn] < infile > outfile</li> 080 * <li>command1 | command 2 <i>(<B>Warning</B>: This type of commands doesn't 081 * give the output of all the commands. Instead it outputs only the result of 082 * the last one.)</i></li> 083 * </ul> 084 * </p> 085 * <p> 086 * <b>Example commands:</b> 087 * <ul> 088 * <li>C:/Program Files/Internet Explorer/IEXPLORE.EXE <br/> 089 * To generate this command, just double click on the actor and type this in the 090 * <i>command</i> parameter field.</li> 091 * <li>C:/cygwin/bin/perl.exe c:/project/kepler/test/workflows/example.pl > 092 * c:/project/kepler/test/workflows/example.out</li> 093 * <li>C:/cygwin/bin/dir.exe > dirTemp.txt</li> 094 * </ul> 095 * </p> 096 * 097 * @author Ilkay Altintas, Christopher Hylands Brooks, Bilsay Yildirim, 098 * Contributor: Edward A. Lee 099 * @version $Id: CommandLineExec.java 24234 2010-05-06 05:21:26Z welker $ 100 */ 101 102public class CommandLineExec extends TypedAtomicActor { 103 104 /** 105 * Construct a CommandLine actor with the given container and name. 106 * 107 * @param container 108 * The container. 109 * @param name 110 * The name of this actor. 111 * @exception IllegalActionException 112 * If the actor cannot be contained by the proposed 113 * container. 114 * @exception NameDuplicationException 115 * If the container already has an actor with this name. 116 */ 117 public CommandLineExec(CompositeEntity container, String name) 118 throws IllegalActionException, NameDuplicationException { 119 super(container, name); 120 121 // Uncomment the next line to see debugging statements 122 // addDebugListener(new ptolemy.kernel.util.StreamListener()); 123 124 stdString = errString = null; 125 126 // Construct parameter 127 command = new PortParameter(this, "command"); 128 new Attribute(command, "_showName"); 129 130 outputFile = new FilePortParameter(this, "outputFile"); 131 outputFile.setTypeEquals(BaseType.STRING); 132 new Attribute(outputFile, "_showName"); 133 134 // Array with an empty name and value means 135 // default environment of the calling process. 136 environment = new Parameter(this, "environment"); 137 environment.setExpression("{{name = \"\", value = \"\"}}"); 138 139 directory = new FileParameter(this, "directory"); 140 directory.setExpression("$CWD"); 141 142 // Construct input ports. 143 arguments = new TypedIOPort(this, "arguments", true, false); 144 arguments.setMultiport(true); 145 new Attribute(arguments, "_showName"); 146 147 inputStream = new TypedIOPort(this, "inputStream", true, false); 148 new Attribute(inputStream, "_showName"); 149 150 infileHandle = new TypedIOPort(this, "infileHandle", true, false); 151 infileHandle.setTypeEquals(BaseType.STRING); 152 new Attribute(infileHandle, "_showName"); 153 154 trigger = new TypedIOPort(this, "trigger", true, false); 155 156 hide = new SingletonParameter(trigger, "_hide"); 157 hide.setToken(BooleanToken.TRUE); 158 159 // Construct output ports. 160 outfileHandle = new TypedIOPort(this, "outfileHandle", false, true); 161 outfileHandle.setTypeEquals(BaseType.STRING); 162 new Attribute(outfileHandle, "_showName"); 163 164 exitCode = new TypedIOPort(this, "exitCode", false, true); 165 exitCode.setTypeEquals(BaseType.STRING); 166 new Attribute(exitCode, "_showName"); 167 168 output = new TypedIOPort(this, "output", false, true); 169 output.setTypeEquals(BaseType.STRING); 170 new Attribute(output, "_showName"); 171 172 // Set Flags. 173 outputLineByLine = new Parameter(this, "outputLineByLine", 174 new BooleanToken(false)); 175 outputLineByLine.setTypeEquals(BaseType.BOOLEAN); 176 177 waitForProcess = new Parameter(this, "waitForProcess", 178 new BooleanToken(false)); 179 waitForProcess.setTypeEquals(BaseType.BOOLEAN); 180 181 hasTrigger = new Parameter(this, "hasTrigger", new BooleanToken(false)); 182 hasTrigger.setTypeEquals(BaseType.BOOLEAN); 183 184 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" " 185 + "width=\"60\" height=\"30\" " + "style=\"fill:white\"/>\n" 186 + "<text x=\"20\" y=\"25\" " 187 + "style=\"font-size:30; fill:blue; font-family:SansSerif\">" 188 + "$</text>\n" + "</svg>\n"); 189 190 } // constructor 191 192 // //////////////// Public ports and parameters /////////////////////// 193 194 /** 195 * The attribute to hide or show the trigger port without deleting it. 196 */ 197 public SingletonParameter hide; 198 199 /** 200 * The command to execute. 201 */ 202 public PortParameter command; 203 204 /** 205 * Needs to be filled in if the user wants the command to output to a file. 206 */ 207 public FilePortParameter outputFile; 208 209 /** 210 * The arguments to the command. Implemented as a multi/input port to 211 * support more than one argument. It concatanates the inputs in all the 212 * channels. <br/> 213 * <I>If there is an input file, this port can be empty.</I> 214 */ 215 public TypedIOPort arguments; 216 217 /** 218 * Strings to pass to the standard input of the subprocess. This port is an 219 * input port of type String. 220 */ 221 public TypedIOPort inputStream; 222 223 /** 224 * This is an optional port. Used if the file accepts an input file instead 225 * of a list of arguments. 226 */ 227 public TypedIOPort infileHandle; 228 229 /** 230 * The trigger port. The type of this port is undeclared, meaning that it 231 * will resolve to any data type. 232 */ 233 public TypedIOPort trigger; 234 235 /** 236 * A string that forwards the outputFile parameter if it exists. 237 */ 238 public TypedIOPort outfileHandle; 239 /** 240 * The result stream of the command. 241 */ 242 public TypedIOPort output; 243 244 /** 245 * Exit code will be 1 if the command executes successfully. 246 */ 247 public TypedIOPort exitCode; 248 249 /** 250 * If selected, broadcasts the output of the command line by line. 251 * 252 */ 253 public Parameter outputLineByLine; 254 255 /** 256 * Unhide the trigger port when this parameter is true. This Parameter is 257 * type of boolean. 258 * 259 * @UserLevelDocumentation <br/> 260 * <b>NOTE: </b>in fact, user can use the port 261 * configuration window to hide or unhide a port. 262 * This paremeter is here to provide a more 263 * intuitive interface for this actor. 264 */ 265 public Parameter hasTrigger; 266 267 /* 268 * The environment to execute the command. 269 */ 270 public Parameter environment; 271 272 /* 273 * The directory to execute the command 274 */ 275 public FileParameter directory; 276 277 /* 278 * This allows parameter to allow the process to block the threads until it 279 * exits. 280 */ 281 public Parameter waitForProcess; 282 283 // ///////////////////////////////////////////////////////////////// 284 // // public methods //// 285 286 /** 287 * If the specified attribute is <i>showTriggerPort</i>, then get the value 288 * of it and re-render the trigger port. If it is true, show the trigger 289 * port; if it is false, hide the trigger port. 290 * 291 * @param attribute 292 * The attribute that has changed. 293 * @exception IllegalActionException. 294 */ 295 public void attributeChanged(Attribute attribute) 296 throws IllegalActionException { 297 if (attribute == hasTrigger) { 298 _triggerFlag = ((BooleanToken) hasTrigger.getToken()) 299 .booleanValue(); 300 _debug("<TRIGGER_FLAG>" + _triggerFlag + "</TRIGGER_FLAG>"); 301 if (_triggerFlag) { 302 try { 303 trigger.setContainer(this); 304 // new Attribute(trigger, "_showName"); 305 hide.setToken(BooleanToken.FALSE); // DFH 306 } catch (NameDuplicationException ndex) { 307 _debug("111: " + ndex.getMessage()); 308 } 309 } else { 310 List inPortList = this.inputPortList(); 311 Iterator ports = inPortList.iterator(); 312 while (ports.hasNext()) { 313 IOPort p = (IOPort) ports.next(); 314 if (p.isInput()) { 315 try { 316 if (p.getName().equals("trigger")) { 317 // new Attribute(trigger, "_hideName"); //DFH 318 // p.setContainer(null); //DFH 319 hide.setToken(BooleanToken.TRUE); // DFH 320 } 321 } catch (Exception e) { 322 throw new IllegalActionException(this, e 323 .getMessage()); 324 } 325 } 326 }// while 327 }// else 328 } else { 329 super.attributeChanged(attribute); 330 } 331 }// attributeChanged 332 333 /** 334 * Send the exitCode and outputFileHandle(optional) to the result port. 335 * 336 * @exception IllegalActionException 337 * If there is no director. 338 */ 339 public void fire() throws IllegalActionException { 340 341 // update the values in the PortParameters 342 command.update(); 343 outputFile.update(); 344 345 stdString = null; 346 errString = "1"; 347 _lineFlag = ((BooleanToken) outputLineByLine.getToken()).booleanValue(); 348 _debug("<TRIGGER_FLAG>" + _lineFlag + "</TRIGGER_FLAG>"); 349 350 _waitForProcessFlag = ((BooleanToken) waitForProcess.getToken()) 351 .booleanValue(); 352 353 // Get Directory 354 directoryAsFile = directory.asFile(); 355 if (_debugging) { 356 _debug("About to exec \"" 357 + ((StringToken) command.getToken()).stringValue() + "\"" 358 + "\n in \"" + directoryAsFile + "\"\n with environment:"); 359 } 360 // Process the environment parameter. 361 ArrayToken environmentTokens = (ArrayToken) environment.getToken(); 362 363 if (_debugging) { 364 _debug("environmentTokens: " + environmentTokens); 365 } 366 367 environmentArray = null; 368 369 if (environmentTokens.length() >= 1) { 370 environmentArray = new String[environmentTokens.length()]; 371 372 for (int i = 0; i < environmentTokens.length(); i++) { 373 StringToken nameToken = (StringToken) (((RecordToken) environmentTokens 374 .getElement(i)).get("name")); 375 StringToken valueToken = (StringToken) (((RecordToken) environmentTokens 376 .getElement(i)).get("value")); 377 environmentArray[i] = nameToken.stringValue() + "=" 378 + valueToken.stringValue(); 379 380 if (_debugging) { 381 _debug(" " + i + ". \"" + environmentArray[i] + "\""); 382 } 383 384 if ((i == 0) && (environmentTokens.length() == 1) 385 && environmentArray[0].equals("=")) { 386 if (_debugging) { 387 _debug("There is only one element, " 388 + "it is a string of length 0,\n so we " 389 + "pass Runtime.exec() an null " 390 + "environment so that we use\n " 391 + "the default environment"); 392 } 393 environmentArray = null; 394 } 395 } 396 } 397 398 if (((StringToken) command.getToken()).stringValue() != null) { 399 String _commandStr = ((StringToken) command.getToken()) 400 .stringValue(); 401 402 // simply consume the trigger token if there is some. 403 if (_triggerFlag) { 404 if (trigger.getWidth() > 0) { 405 // if (!((BooleanToken) trigger.get(0)).equals(null)) { 406 if (trigger.hasToken(0)) { 407 trigger.get(0); 408 _debug("consume the token at the trigger port."); 409 } 410 } 411 } 412 413 // Consume the input file token if there's one. 414 415 try { 416 if ((infileHandle.numberOfSources() > 0)) { 417 String value = ((StringToken) infileHandle.get(0)) 418 .stringValue(); 419 if (value.length() > 0) { 420 _debug("infileHandle(i) = " + value); 421 if (value.startsWith("file:/")) 422 value = value.substring(7); 423 _commandStr += " < " + value; 424 } 425 } 426 } catch (IllegalActionException ex) { 427 _debug("Input file is null."); 428 } 429 430 // Create the argument string. Consume data in all the channels and 431 // combine them 432 String argString = ""; 433 int i = 0; 434 int width = arguments.getWidth(); 435 436 for (i = 0; i < width; i++) { 437 String value = ((StringToken) arguments.get(i)).stringValue(); 438 if (value.length() > 0) { 439 _debug("arguments(i) = " + value); 440 while (value.indexOf("\\\"") != -1) { 441 int ind = value.indexOf("\\\""); 442 value = value.substring(0, ind) 443 + value.substring(ind + 1, value.length()); 444 _debug(value); 445 } 446 447 argString += value + " "; 448 _debug("argString = " + argString); 449 } 450 } 451 _commandStr += " " + argString; 452 453 int commandCount = getSystemProps(); 454 455 // Get the output file path if there's one and add it to the command 456 // string. 457 if (((StringToken) outputFile.getToken()).stringValue() == "") { 458 _debug("Output file is null."); 459 outfileHandle.broadcast(new StringToken("")); 460 } else { 461 String outFilePath = outputFile.stringValue(); 462 if (outFilePath.startsWith("file:///")) { 463 if (_charsToSkip == 6) { 464 _charsToSkip = 8; 465 } else if (_charsToSkip == 5) { 466 _charsToSkip = 7; 467 } 468 } 469 if (outFilePath.startsWith("file:/")) { 470 outFilePath = outputFile.stringValue().substring( 471 _charsToSkip); 472 } 473 _commandStr += " > " + outFilePath; 474 475 if (!outFilePath.equals("")) 476 outfileHandle.broadcast(new StringToken(outFilePath)); 477 } 478 479 _commandArr[commandCount] = _commandStr; 480 _debug("<COMMAND>" + _commandArr[commandCount] + "</COMMAND>"); 481 // EXECUTION OF THE GENERATED COMMAND. 482 _debug("Executing the command..."); 483 484 try { 485 _stopFireRequested = false; 486 487 // Execute the command 488 Runtime rt = Runtime.getRuntime(); 489 proc = rt.exec(_commandArr, environmentArray, directoryAsFile); 490 491 InputStream inStream = proc.getInputStream(); 492 _outputGobbler = new _StreamReaderThread(inStream, 493 "Exec Stdout Gobbler-" + _streamReaderThreadCount++, 494 this, 1); 495 496 InputStream errStream = proc.getErrorStream(); 497 _errorGobbler = new _StreamReaderThread(errStream, 498 "Exec Stderr Gobbler-" + _streamReaderThreadCount++, 499 this, 2); 500 501 if (_streamReaderThreadCount > 1000) { 502 // Avoid overflow in the thread count. 503 _streamReaderThreadCount = 0; 504 } 505 506 _errorGobbler.start(); 507 _outputGobbler.start(); 508 509 // We could have a parameter that if it was set 510 // we would throw an exception if there was any error data. 511 if (!procDone(proc)) { 512 String line = null; 513 if ((inputStream.numberOfSources() > 0)) { 514 line = ((StringToken) inputStream.get(0)).stringValue(); 515 if (line.length() > 0) { 516 if (_debugging) { 517 _debug("CommandLine: Input: '" + line + "'"); 518 } 519 OutputStreamWriter inputStreamWriter = new OutputStreamWriter( 520 proc.getOutputStream()); 521 _inputBufferedWriter = new BufferedWriter( 522 inputStreamWriter); 523 if (_inputBufferedWriter != null) { 524 try { 525 _inputBufferedWriter.write(line); 526 _inputBufferedWriter.flush(); 527 } catch (IOException ex) { 528 throw new IllegalActionException(this, ex, 529 "Problem writing input '" + line 530 + "'"); 531 } 532 } 533 } 534 } 535 } 536 537 // close the child proc's stdin 538 // (some programs reading stdin expect it to be 539 // closed before excuting, e.g. cat). 540 proc.getOutputStream().close(); 541 542 // Wait for the Process to finish if it is indicated 543 if (_waitForProcessFlag) { 544 try { 545 // The next line waits for the subprocess to finish. 546 int processReturnCode = proc.waitFor(); 547 // wait for stream gobbler threads to finish 548 while (_errorGobbler.isAlive() 549 || _outputGobbler.isAlive()) { 550 Thread.yield(); 551 } 552 if (processReturnCode != 0) { 553 // We could have a parameter that would enable or 554 // disable this. 555 throw new IllegalActionException( 556 this, 557 "Executing command \"" 558 + ((StringToken) command.getToken()) 559 .stringValue() 560 + "\" returned a non-zero return value of " 561 + processReturnCode); 562 } 563 } catch (InterruptedException interrupted) { 564 throw new InternalErrorException(this, interrupted, 565 "_process.waitFor() was interrupted"); 566 } catch (IllegalActionException e) { 567 throw new InternalErrorException(this, e, 568 "exec'd process exited with non zero exit code"); 569 } 570 } 571 572 if (_debugging) { 573 _debug("Exec: Error: '" + errString + "'"); 574 _debug("Exec: Output: '" + stdString + "'"); 575 } 576 } catch (Exception ex) { 577 _debug("<EXCEPTION> An exception occured when executing the command. " 578 + " \n\t Exception: " + ex + "\n</EXCEPTION>"); 579 } 580 // Send error and output to ports 581 output.send(0, new StringToken(stdString)); 582 exitCode.send(0, new StringToken(errString)); 583 } 584 } // end-of-fire 585 586 // Check if the process is done or not 587 private boolean procDone(Process p) { 588 try { 589 p.exitValue(); 590 return true; 591 } catch (IllegalThreadStateException e) { 592 return false; 593 } 594 } 595 596 // Get system properties and set the command array according to it 597 public int getSystemProps() throws IllegalActionException { 598 // Get OS name 599 String osName = System.getProperty("os.name"); 600 // System.out.println(((StringToken) 601 // shell.getToken()).stringValue().toString()); 602 // String sh="sh"; 603 _debug("<OS>" + osName + "</OS>"); 604 605 if (osName.equals("Windows NT") || osName.equals("Windows XP") 606 || osName.equals("Windows 2000")) { 607 608 _commandArr[0] = "cmd.exe"; 609 _commandArr[1] = "/C"; 610 _charsToSkip = 6; 611 /* 612 * } else{ 613 * 614 * _commandArr[0] ="C:'\''cygwin'\''cywgin.bat"; _commandArr[1] = 615 * "-c"; _charsToSkip = 6; } 616 */ 617 return 2; 618 } else if (osName.equals("Windows 95")) { 619 _commandArr[0] = "command.com"; 620 _commandArr[1] = "/C"; 621 _charsToSkip = 6; 622 return 2; 623 } else { 624 _commandArr[0] = "/bin/sh"; 625 _commandArr[1] = "-c"; 626 _charsToSkip = 5; 627 return 2; 628 /* return 0; */ 629 } 630 } // end-of-getSystemProps 631 632 /** 633 * Override the base class to stop waiting for input data. 634 */ 635 public void stopFire() { 636 // NOTE: This method used to be synchronized, as 637 // was the fire() method, but this caused deadlocks. EAL 638 super.stopFire(); 639 _stopFireRequested = true; 640 641 try { 642 _terminateProcess(); 643 } catch (IllegalActionException ex) { 644 throw new InternalErrorException(ex); 645 } 646 } 647 648 private void _terminateProcess() throws IllegalActionException { 649 if (proc != null) { 650 proc.destroy(); 651 proc = null; 652 } 653 } 654 655 /** 656 * Terminate the subprocess. This method is invoked exactly once per 657 * execution of an application. None of the other action methods should be 658 * be invoked after it. 659 * 660 * @exception IllegalActionException 661 * Not thrown in this base class. 662 */ 663 public void wrapup() throws IllegalActionException { 664 _terminateProcess(); 665 } 666 667 // ///////////////////////////////////////////////////////////////// 668 // // inner classes //// 669 // Private class that reads a stream in a thread and updates the 670 // stringBuffer. 671 private class _StreamReaderThread extends Thread { 672 /** 673 * Create a _StreamReaderThread. 674 * 675 * @param inputStream 676 * The stream to read from. 677 * @param name 678 * The name of this StreamReaderThread, which is useful for 679 * debugging. 680 * @param actor 681 * The parent actor of this thread, which is used in error 682 * messages. 683 */ 684 _StreamReaderThread(InputStream inputStream, String name, 685 Nameable actor, int ID) { 686 super(name); 687 _inputStream = inputStream; 688 _inputStreamReader = new InputStreamReader(_inputStream); 689 _actor = actor; 690 _stringBuffer = new StringBuffer(); 691 myID = ID; 692 } 693 694 /** 695 * Read lines from the inputStream and append them to the stringBuffer. 696 */ 697 public void run() { 698 if (!_inputStreamReaderClosed) { 699 _read(); 700 } 701 } 702 703 private void _read() { 704 // We read the data as a char[] instead of using readline() 705 // so that we can get strings that do not end in end of 706 // line chars. 707 708 try { 709 br = new BufferedReader(_inputStreamReader); 710 String line = null; 711 712 while ((line = br.readLine()) != null && !_stopFireRequested) { 713 if (line.length() != 0) { 714 if (_debugging) { 715 // Note that ready might be false here since 716 // we already read the data. 717 _debug("_read(): Gobbler '" + getName() 718 + "' Ready: " + line + " Value: '" + line 719 + "'"); 720 } 721 _stringBuffer.append(line + "\n"); 722 } 723 } 724 } catch (IOException ex) { 725 throw new InternalErrorException(_actor, ex, getName() 726 + ": Failed while reading from " + _inputStream); 727 } 728 // if this thread is reading in stdout, then give this new string to 729 // the actor's stdString 730 if (myID == 1) { 731 stdString = _stringBuffer.toString(); 732 } 733 // if this thread is reading in stderr, then give this new string to 734 // the actor's errString 735 if (myID == 2) { 736 errString = _stringBuffer.toString(); 737 if ((errString == null) || (errString.length() == 0)) 738 errString = "1"; 739 } 740 } 741 742 // Last parameter entered on the constructor. 743 // 1 indicates storing the standard output, 2 indicates coying to the 744 // standard error. 745 private int myID; 746 747 // The actor associated with this stream reader. 748 private Nameable _actor; 749 750 // Stream from which to read. 751 private InputStreamReader _inputStreamReader; 752 753 // Indicator that the stream has been closed. 754 private boolean _inputStreamReaderClosed = false; 755 756 // StringBuffer to update. 757 private StringBuffer _stringBuffer; 758 759 // BufferReader 760 private BufferedReader br; 761 } 762 763 // //////////////////////////////////////////////////////////////////// 764 // // private variables //// 765 766 // StreamReader with which we read stderr. 767 private _StreamReaderThread _errorGobbler; 768 769 // Stream from which to read. 770 private InputStream _inputStream; 771 // StreamReader with which we read stdout. 772 private _StreamReaderThread _outputGobbler; 773 // The subprocess gets its input from this BufferedWriter. 774 private BufferedWriter _inputBufferedWriter; 775 // The combined command to execute. 776 private String _commandArr[] = new String[3]; 777 private boolean _lineFlag = false; 778 private boolean _waitForProcessFlag = false; 779 private String[] environmentArray; 780 private File directoryAsFile; 781 private boolean _triggerFlag = false; 782 // Indicator that stopFire() has been called. 783 private boolean _stopFireRequested = false; 784 private Process proc; 785 private int _charsToSkip = 6; 786 // Instance count of output and error threads, used for debugging. 787 // When the value is greater than 1000, we reset it to 0. 788 private static int _streamReaderThreadCount = 0; 789 790 private String stdString, errString; 791} // end-of-class-CommandLine