1 /*
   2  * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package nsk.share.jdi;
  25 
  26 import jdk.test.lib.Platform;
  27 import nsk.share.*;
  28 import nsk.share.jpda.*;
  29 
  30 import com.sun.jdi.*;
  31 import com.sun.jdi.connect.*;
  32 
  33 import com.sun.jdi.connect.Connector.Argument;
  34 import java.io.*;
  35 import java.net.*;
  36 import java.util.*;
  37 
  38 /**
  39  * This class provides debugger with connection to debugee VM
  40  * using JDI connectors.
  41  *<p>
  42  * This class provides abilities to launch and bind to debugee VM
  43  * as described for base <code>DebugeeBinder</code> class,
  44  * using JDI connectors and <code>com.sun.VirtualMachine</code> mirror.
  45  * <p>
  46  * When <code>Binder</code> is asked to bind to debugee by invoking
  47  * <code>bindToBebugee()</code> method it uses
  48  * <code>com.sun.jdi.Connector</code> object corresponding to
  49  * value of command line options <code>-connector</code> and
  50  * <code>-transport</code> to launch and connect to debugee VM.
  51  * After debugee is launched and connection is established
  52  * <code>Binder</code> uses <code>com.sun.jdi.VirtualMachine</code>
  53  * object to construct <code>Debugee</code> object, that
  54  * provides abilities to interact with debugee VM.
  55  *
  56  * @see Debugee
  57  * @see DebugeeBinder
  58  */
  59 public class Binder extends DebugeeBinder {
  60 
  61     /**
  62      * Default message prefix for <code>Binder</code> object.
  63      */
  64     public static final String LOG_PREFIX = "binder> ";
  65 
  66     /**
  67      * Get version string.
  68      */
  69     public static String getVersion () {
  70         return "@(#)Binder.java 1.14 03/10/08";
  71     }
  72 
  73     // -------------------------------------------------- //
  74 
  75     /**
  76      * Handler of command line arguments.
  77      */
  78     private ArgumentHandler argumentHandler = null;
  79 
  80     /**
  81      * Return <code>argumentHandler</code> of this binder.
  82      */
  83     public ArgumentHandler getArgumentHandler() {
  84         return argumentHandler;
  85     }
  86 
  87     // -------------------------------------------------- //
  88 
  89     /**
  90      * Make <code>Binder</code> object and pass raw command line arguments.
  91      *
  92      * @deprecated  Use newer
  93      *              <code>Binder(ArgumentHandler,Log)</code>
  94      *              constructor.
  95      */
  96     @Deprecated
  97     public Binder (String args[]) {
  98         this(args, new Log(System.err));
  99     }
 100 
 101     /**
 102      * Make <code>Binder</code> object for raw command line arguments
 103      * and specified <code>log</code> object.
 104      *
 105      * @deprecated  Use newer
 106      *              <code>Binder(ArgumentHandler,Log)</code>
 107      *              constructor.
 108      */
 109     @Deprecated
 110     public Binder (String args[], Log log) {
 111         this(new ArgumentHandler(args), log);
 112     }
 113 
 114     /**
 115      * Make <code>Binder</code> object for specified command line arguments
 116      * and <code>log</code> object.
 117      */
 118     public Binder (ArgumentHandler argumentHandler, Log log) {
 119         super(argumentHandler, log);
 120         this.argumentHandler = argumentHandler;
 121     }
 122 
 123     // -------------------------------------------------- //
 124 
 125     /**
 126      * Make initial <code>Debugee</code> object for local debuggee process
 127      * started with launching connector.
 128      */
 129     public Debugee makeLocalDebugee(Process process) {
 130         LocalLaunchedDebugee debugee = new LocalLaunchedDebugee(process, this);
 131 
 132         Finalizer finalizer = new Finalizer(debugee);
 133         finalizer.activate();
 134 
 135         return debugee;
 136     }
 137 
 138     /**
 139      * Launch local debuggee process with specified command line
 140      * and make initial <code>Debugee</code> object.
 141      */
 142     public Debugee startLocalDebugee(String cmd) {
 143         Process process = null;
 144 
 145         try {
 146             process = launchProcess(cmd);
 147         } catch (IOException e) {
 148             e.printStackTrace(log.getOutStream());
 149             throw new Failure("Caught exception while launching local debuggee VM process:\n\t"
 150                             + e);
 151         }
 152 
 153         return makeLocalDebugee(process);
 154     }
 155 
 156     /**
 157      * Make debuggee wrapper for already launched debuggee VM.
 158      * After enwraping debugee's output is redirected to Binder's log,
 159      * VMStartEvent is received and debuggee is initialized.
 160      */
 161     public Debugee enwrapDebugee(VirtualMachine vm, Process proc) {
 162         Debugee debugee = makeLocalDebugee(proc);
 163 
 164         display("Redirecting VM output");
 165         debugee.redirectOutput(log);
 166         debugee.setupVM(vm);
 167 
 168         long timeout = argumentHandler.getWaitTime() * 60 * 1000; // milliseconds
 169 
 170         display("Waiting for VM initialized");
 171         debugee.waitForVMInit(timeout);
 172 
 173         return debugee;
 174     }
 175 
 176     /**
 177      * Launch debugee VM and establish connection to it without waiting for VMStartEvent.
 178      * After launching debugee's output is redirected to Binder's log,
 179      * but VMStartEvent is not received and so debuggee is not fully initialized.
 180      *
 181      * @see #bindToDebugee(String)
 182      */
 183     public Debugee bindToDebugeeNoWait(String classToExecute) {
 184 
 185         VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
 186         display("VirtualMachineManager: version "
 187                 + vmm.majorInterfaceVersion() + "."
 188                 + vmm.minorInterfaceVersion());
 189 
 190         Debugee debugee = null;
 191 
 192         String classPath = null;
 193         classPath = System.getProperty("java.class.path");
 194 
 195         prepareForPipeConnection(argumentHandler);
 196 
 197         if (argumentHandler.isLaunchedLocally()) {
 198 
 199             if (argumentHandler.isDefaultConnector()) {
 200                 debugee = localDefaultLaunchDebugee(vmm, classToExecute, classPath);
 201             } else if (argumentHandler.isRawLaunchingConnector()) {
 202                 debugee = localRawLaunchDebugee(vmm, classToExecute, classPath);
 203             } else if (argumentHandler.isLaunchingConnector()) {
 204                 debugee = localLaunchDebugee(vmm, classToExecute, classPath);
 205             } else if (argumentHandler.isAttachingConnector()) {
 206                 debugee = localLaunchAndAttachDebugee(vmm, classToExecute, classPath);
 207             } else if (argumentHandler.isListeningConnector()) {
 208                 debugee = localLaunchAndListenDebugee(vmm, classToExecute, classPath);
 209             } else {
 210                 throw new TestBug("Unexpected connector type for local debugee launch mode"
 211                                   + argumentHandler.getConnectorType());
 212             }
 213 
 214         } else if (argumentHandler.isLaunchedRemotely()) {
 215 
 216             connectToBindServer(classToExecute);
 217 
 218             if (argumentHandler.isAttachingConnector()) {
 219                 debugee = remoteLaunchAndAttachDebugee(vmm, classToExecute, classPath);
 220             } else if (argumentHandler.isListeningConnector()) {
 221                 debugee = remoteLaunchAndListenDebugee(vmm, classToExecute, classPath);
 222             } else {
 223                 throw new TestBug("Unexpected connector type for remote debugee launch mode"
 224                                   + argumentHandler.getConnectorType());
 225             }
 226 
 227         } else if (argumentHandler.isLaunchedManually()) {
 228 
 229             if (argumentHandler.isAttachingConnector()) {
 230                 debugee = manualLaunchAndAttachDebugee(vmm, classToExecute, classPath);
 231             } else if (argumentHandler.isListeningConnector()) {
 232                 debugee = manualLaunchAndListenDebugee(vmm, classToExecute, classPath);
 233             } else {
 234                 throw new TestBug("Unexpected connector type for manual debugee launch mode"
 235                                   + argumentHandler.getConnectorType());
 236             }
 237 
 238         } else {
 239             throw new Failure("Unexpected debugee launching mode: " + argumentHandler.getLaunchMode());
 240         }
 241 
 242         return debugee;
 243     }
 244 
 245     /**
 246      * Launch debugee VM and establish JDI connection.
 247      * After launching debugee's output is redirected to Binder's log,
 248      * VMStart event is received and debuggee is initialized.
 249      *
 250      * @see #bindToDebugeeNoWait(String)
 251      */
 252     public Debugee bindToDebugee(String classToExecute) {
 253         Debugee debugee = bindToDebugeeNoWait(classToExecute);
 254 
 255         if(argumentHandler.getOptions().getProperty("traceAll") != null)
 256             debugee.VM().setDebugTraceMode(VirtualMachine.TRACE_ALL);
 257 
 258         long timeout = argumentHandler.getWaitTime() * 60 * 1000; // milliseconds
 259 
 260         display("Waiting for VM initialized");
 261         debugee.waitForVMInit(timeout);
 262 
 263         return debugee;
 264     }
 265 
 266     // -------------------------------------------------- //
 267 
 268     /**
 269      * Launch debugee locally via the default LaunchingConnector.
 270      */
 271     private Debugee localDefaultLaunchDebugee (VirtualMachineManager vmm,
 272                                                 String classToExecute,
 273                                                 String classPath) {
 274         display("Finding connector: " + "default" );
 275         LaunchingConnector connector = vmm.defaultConnector();
 276         Map<String,? extends Argument> arguments = setupLaunchingConnector(connector, classToExecute, classPath);
 277 
 278         VirtualMachine vm;
 279         try {
 280             display("Launching debugee");
 281             vm = connector.launch(arguments);
 282         } catch (IllegalConnectorArgumentsException e) {
 283             e.printStackTrace(log.getOutStream());
 284             throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e);
 285         } catch (VMStartException e) {
 286             e.printStackTrace(log.getOutStream());
 287             String msg = readVMStartExceptionOutput(e, log.getOutStream());
 288             throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\n" + msg);
 289         } catch (IOException e) {
 290             e.printStackTrace(log.getOutStream());
 291             throw new Failure("Caught exception while launching debugee VM:\n\t" + e);
 292         };
 293 
 294         Process process = vm.process();
 295         Debugee debugee = makeLocalDebugee(process);
 296         debugee.redirectOutput(log);
 297         debugee.setupVM(vm);
 298 
 299         return debugee;
 300     }
 301 
 302 
 303     /**
 304      * Launch debugee locally via the default LaunchingConnector.
 305      */
 306     private Debugee localLaunchDebugee (VirtualMachineManager vmm,
 307                                             String classToExecute,
 308                                             String classPath) {
 309 
 310         display("Finding connector: " + argumentHandler.getConnectorName() );
 311         LaunchingConnector connector =
 312             (LaunchingConnector) findConnector(argumentHandler.getConnectorName(),
 313                                                 vmm.launchingConnectors());
 314         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupLaunchingConnector(connector, classToExecute, classPath);
 315 
 316         VirtualMachine vm;
 317         try {
 318             display("Launching debugee");
 319             vm = connector.launch(arguments);
 320         } catch (IllegalConnectorArgumentsException e) {
 321             e.printStackTrace(log.getOutStream());
 322             throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e);
 323         } catch (VMStartException e) {
 324             e.printStackTrace(log.getOutStream());
 325             String msg = readVMStartExceptionOutput(e, log.getOutStream());
 326             throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\nProcess output:\n\t" + msg);
 327         } catch (IOException e) {
 328             e.printStackTrace(log.getOutStream());
 329             throw new Failure("Caught exception while launching debugee VM:\n\t" + e);
 330         };
 331 
 332         Process process = vm.process();
 333         Debugee debugee = makeLocalDebugee(process);
 334         debugee.redirectOutput(log);
 335         debugee.setupVM(vm);
 336 
 337         return debugee;
 338     }
 339 
 340     /**
 341      * Launch debugee locally via the RawLaunchingConnector.
 342      */
 343     private Debugee localRawLaunchDebugee (VirtualMachineManager vmm,
 344                                             String classToExecute,
 345                                             String classPath) {
 346         display("Finding connector: " + argumentHandler.getConnectorName() );
 347         LaunchingConnector connector =
 348             (LaunchingConnector) findConnector(argumentHandler.getConnectorName(),
 349                                                 vmm.launchingConnectors());
 350         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupRawLaunchingConnector(connector, classToExecute, classPath);
 351 
 352         VirtualMachine vm;
 353         try {
 354             display("Launching debugee");
 355             vm = connector.launch(arguments);
 356         } catch (IllegalConnectorArgumentsException e) {
 357             e.printStackTrace(log.getOutStream());
 358             throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e);
 359         } catch (VMStartException e) {
 360             e.printStackTrace(log.getOutStream());
 361             String msg = readVMStartExceptionOutput(e, log.getOutStream());
 362             throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\nProcess output:\n\t" + msg);
 363         } catch (IOException e) {
 364             e.printStackTrace(log.getOutStream());
 365             throw new Failure("Caught exception while launching debugee VM:\n\t" + e);
 366         };
 367 
 368         Process process = vm.process();
 369         Debugee debugee = makeLocalDebugee(process);
 370         debugee.redirectOutput(log);
 371         debugee.setupVM(vm);
 372 
 373         return debugee;
 374     }
 375 
 376     /**
 377      * Launch debugee VM locally as a local process and connect to it using
 378      * <code>AttachingConnector</code>.
 379      */
 380     private Debugee localLaunchAndAttachDebugee (VirtualMachineManager vmm,
 381                                                     String classToExecute,
 382                                                     String classPath) {
 383         display("FindingConnector: " + argumentHandler.getConnectorName() );
 384         AttachingConnector connector =
 385             (AttachingConnector) findConnector(argumentHandler.getConnectorName(),
 386                                                 vmm.attachingConnectors());
 387         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath);
 388 
 389         String address = makeTransportAddress();
 390         String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address);
 391         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
 392 
 393         display("Starting java process:\n\t" + javaCmdLine);
 394         Debugee debugee = startLocalDebugee(cmdLineArgs);
 395         debugee.redirectOutput(log);
 396 
 397         display("Attaching to debugee");
 398         VirtualMachine vm = null;
 399         IOException ioe = null;
 400         for (int i = 0; i < CONNECT_TRIES; i++) {
 401             try {
 402                 vm = connector.attach(arguments);
 403                 display("Debugee attached");
 404                 debugee.setupVM(vm);
 405                 return debugee;
 406             } catch (IOException e) {
 407                 display("Attempt #" + i + " to connect to debugee VM failed:\n\t" + e);
 408                 ioe = e;
 409                 if (debugee.terminated()) {
 410                     throw new Failure("Unable to connect to debuggee VM: VM process is terminated");
 411                 }
 412                 try {
 413                     Thread.currentThread().sleep(CONNECT_TRY_DELAY);
 414                 } catch (InterruptedException ie) {
 415                     ie.printStackTrace(log.getOutStream());
 416                     throw new Failure("Thread interrupted while pausing connection attempts:\n\t"
 417                                     + ie);
 418                 }
 419             } catch (IllegalConnectorArgumentsException e) {
 420                 e.printStackTrace(log.getOutStream());
 421                 throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e);
 422             }
 423         }
 424         throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES
 425                         + " tries:\n\t" + ioe);
 426     }
 427 
 428     /**
 429      * Launch debugee VM locally as a local process and connect to it using
 430      * <code>ListeningConnector</code>.
 431      */
 432     private Debugee localLaunchAndListenDebugee (VirtualMachineManager vmm,
 433                                                     String classToExecute,
 434                                                     String classPath) {
 435         display("Finding connector: " + argumentHandler.getConnectorName() );
 436         ListeningConnector connector =
 437             (ListeningConnector) findConnector(argumentHandler.getConnectorName(),
 438                                                 vmm.listeningConnectors());
 439         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath);
 440 
 441         String address = null;
 442         try {
 443             display("Listening for connection from debugee");
 444             address = connector.startListening(arguments);
 445         } catch (IllegalConnectorArgumentsException e) {
 446             e.printStackTrace(log.getOutStream());
 447             throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
 448         } catch (IOException e) {
 449             e.printStackTrace(log.getOutStream());
 450             throw new Failure("Caught exception while starting listening debugee VM:\n\t" + e);
 451         };
 452 
 453         String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address);
 454         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
 455 
 456         display("Starting java process:\n\t" + javaCmdLine);
 457         Debugee debugee = startLocalDebugee(cmdLineArgs);
 458         debugee.redirectOutput(log);
 459 
 460         display("Waiting for connection from debugee");
 461         VirtualMachine vm = null;
 462         IOException ioe = null;
 463         for (int i = 0; i < CONNECT_TRIES; i++) {
 464             try {
 465                 vm = connector.accept(arguments);
 466                 connector.stopListening(arguments);
 467                 display("Debugee attached");
 468                 debugee.setupVM(vm);
 469                 return debugee;
 470             } catch (IOException e) {
 471                 display("Attempt #" + i + " to listen debugee VM failed:\n\t" + e);
 472                 ioe = e;
 473                 if (debugee.terminated()) {
 474                     throw new Failure("Unable to connect to debuggee VM: VM process is terminated");
 475                 }
 476                 try {
 477                     Thread.currentThread().sleep(CONNECT_TRY_DELAY);
 478                 } catch (InterruptedException ie) {
 479                     ie.printStackTrace(log.getOutStream());
 480                     throw new Failure("Thread interrupted while pausing connection attempts:\n\t"
 481                                     + ie);
 482                 }
 483             } catch (IllegalConnectorArgumentsException e) {
 484                 e.printStackTrace(log.getOutStream());
 485                 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
 486             }
 487         }
 488         throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES
 489                         + " tries:\n\t" + ioe);
 490     }
 491 
 492     // -------------------------------------------------- //
 493 
 494     /**
 495      * Launch debugee VM remotely via <code>BindServer</code> and connect to it using
 496      * <code>AttachingConnector</code>.
 497      */
 498     private Debugee remoteLaunchAndAttachDebugee (VirtualMachineManager vmm,
 499                                                     String classToExecute,
 500                                                     String classPath) {
 501         display("Finding connector: " + argumentHandler.getConnectorName() );
 502         AttachingConnector connector =
 503             (AttachingConnector) findConnector(argumentHandler.getConnectorName(),
 504                                                 vmm.attachingConnectors());
 505 
 506         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath);
 507 
 508         String address = makeTransportAddress();
 509         String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address);
 510         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
 511 
 512         display("Starting remote java process:\n\t" + javaCmdLine);
 513         Debugee debugee = startRemoteDebugee(cmdLineArgs);
 514 
 515         display("Attaching to debugee");
 516         VirtualMachine vm;
 517         IOException ioe = null;
 518         for (int i = 0; i < CONNECT_TRIES; i++) {
 519             try {
 520                 vm = connector.attach(arguments);
 521                 display("Debugee attached");
 522                 debugee.setupVM(vm);
 523                 return debugee;
 524             } catch (IOException e) {
 525                 display("Attempt #" + i + " to connect to debugee VM failed:\n\t" + e);
 526                 ioe = e;
 527                 if (debugee.terminated()) {
 528                     throw new Failure("Unable to connect to debuggee VM: VM process is terminated");
 529                 }
 530                 try {
 531                     Thread.currentThread().sleep(CONNECT_TRY_DELAY);
 532                 } catch (InterruptedException ie) {
 533                     ie.printStackTrace(log.getOutStream());
 534                     throw new Failure("Thread interrupted while pausing connection attempts:\n\t"
 535                                     + ie);
 536                 }
 537             } catch (IllegalConnectorArgumentsException e) {
 538                 e.printStackTrace(log.getOutStream());
 539                 throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e);
 540             }
 541         }
 542         throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES
 543                         + " tries:\n\t" + ioe);
 544     }
 545 
 546     /**
 547      * Launch debugee VM remotely via <code>BindServer</code> and connect to it using
 548      * <code>ListeningConnector</code>.
 549      */
 550     private Debugee remoteLaunchAndListenDebugee (VirtualMachineManager vmm,
 551                                                     String classToExecute,
 552                                                     String classPath) {
 553         display("Finding connector: " + argumentHandler.getConnectorName() );
 554         ListeningConnector connector =
 555             (ListeningConnector) findConnector(argumentHandler.getConnectorName(),
 556                                                 vmm.listeningConnectors());
 557         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath);
 558 
 559         String address = null;
 560         try {
 561             display("Listening for connection from debugee");
 562             address = connector.startListening(arguments);
 563         } catch (IllegalConnectorArgumentsException e) {
 564             e.printStackTrace(log.getOutStream());
 565             throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
 566         } catch (IOException e) {
 567             e.printStackTrace(log.getOutStream());
 568             throw new Failure("Caught exception while starting listening debugee VM:\n\t" + e);
 569         };
 570 
 571         String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address);
 572         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
 573 
 574         display("Starting remote java process:\n\t" + javaCmdLine);
 575         Debugee debugee = startRemoteDebugee(cmdLineArgs);
 576 
 577         display("Waiting for connection from debugee");
 578         VirtualMachine vm;
 579         IOException ioe = null;
 580         for (int i = 0; i < CONNECT_TRIES; i++) {
 581             try {
 582                 vm = connector.accept(arguments);
 583                 connector.stopListening(arguments);
 584                 display("Debugee attached");
 585                 debugee.setupVM(vm);
 586                 return debugee;
 587             } catch (IOException e) {
 588                 display("Attempt #" + i + " to listen debugee VM failed:\n\t" + e);
 589                 ioe = e;
 590                 if (debugee.terminated()) {
 591                     throw new Failure("Unable to connect to debuggee VM: VM process is terminated");
 592                 }
 593                 try {
 594                     Thread.currentThread().sleep(CONNECT_TRY_DELAY);
 595                 } catch (InterruptedException ie) {
 596                     ie.printStackTrace(log.getOutStream());
 597                     throw new Failure("Thread interrupted while pausing connection attempts:\n\t"
 598                                     + ie);
 599                 }
 600             } catch (IllegalConnectorArgumentsException e) {
 601                 e.printStackTrace(log.getOutStream());
 602                 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
 603             }
 604         }
 605         throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES
 606                         + " tries:\n\t" + ioe);
 607     }
 608 
 609     // -------------------------------------------------- //
 610 
 611     /**
 612      * Prompt to manually launch debugee VM and connect to it using
 613      * <code>AttachingConnector</code>.
 614      */
 615     private Debugee manualLaunchAndAttachDebugee (VirtualMachineManager vmm,
 616                                                     String classToExecute,
 617                                                     String classPath) {
 618         display("Finding connector: " + argumentHandler.getConnectorName() );
 619         AttachingConnector connector =
 620             (AttachingConnector) findConnector(argumentHandler.getConnectorName(),
 621                                                 vmm.attachingConnectors());
 622         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath);
 623 
 624         String address = makeTransportAddress();
 625         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
 626 
 627         display("Starting manual java process:\n\t" + javaCmdLine);
 628         ManualLaunchedDebugee debugee = startManualDebugee(javaCmdLine);
 629 
 630         VirtualMachine vm;
 631         try {
 632             display("Attaching to debugee");
 633             vm = connector.attach(arguments);
 634         } catch (IllegalConnectorArgumentsException e) {
 635             e.printStackTrace(log.getOutStream());
 636             throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e);
 637         } catch (IOException e) {
 638             e.printStackTrace(log.getOutStream());
 639             throw new Failure("Caught exception while attaching to debugee VM:\n\t" + e);
 640         };
 641         display("Debugee attached");
 642 
 643         debugee.setupVM(vm);
 644         return debugee;
 645     }
 646 
 647     /**
 648      * Prompt to manually launch debugee VM and connect to it using
 649      * <code>ListeningConnector</code>.
 650      */
 651     private Debugee manualLaunchAndListenDebugee (VirtualMachineManager vmm,
 652                                                     String classToExecute,
 653                                                     String classPath) {
 654         display("Finding connector: " + argumentHandler.getConnectorName() );
 655         ListeningConnector connector =
 656             (ListeningConnector) findConnector(argumentHandler.getConnectorName(),
 657                                                 vmm.listeningConnectors());
 658         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath);
 659 
 660         VirtualMachine vm;
 661         try {
 662             display("Listening for connection from debugee");
 663             String address = connector.startListening(arguments);
 664             String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
 665             display("Starting manual java process:\n\t" + javaCmdLine);
 666             ManualLaunchedDebugee debugee = startManualDebugee(javaCmdLine);
 667             display("Waiting for connection from debugee");
 668             vm = connector.accept(arguments);
 669             display("Debugee attached");
 670             connector.stopListening(arguments);
 671             debugee.setupVM(vm);
 672             return debugee;
 673         } catch (IllegalConnectorArgumentsException e) {
 674             e.printStackTrace(log.getOutStream());
 675             throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
 676         } catch (IOException e) {
 677             e.printStackTrace(log.getOutStream());
 678             throw new Failure("Caught exception while listening to debugee VM:\n\t" + e);
 679         }
 680     }
 681 
 682     // -------------------------------------------------- //
 683 
 684     /**
 685      * Make proper arguments for LaunchingConnector.
 686      */
 687     private Map<String,? extends Argument> setupLaunchingConnector(LaunchingConnector connector,
 688                                                 String classToExecute,
 689                                                 String classPath) {
 690         display("ClassPath: " + classPath);
 691         display("LaunchingConnector:");
 692         display("    name: " + connector.name());
 693         display("    description: " + connector.description());
 694         display("    transport: " + connector.transport());
 695 
 696         Hashtable<String,? extends Argument> arguments = new Hashtable<String,Argument>(connector.defaultArguments());
 697 
 698         Connector.Argument arg;
 699 
 700         arg = (Connector.StringArgument) arguments.get("quote");
 701         String quote = "\0";
 702         arg.setValue(quote);
 703 
 704         String[] rawArgs = argumentHandler.getRawArguments();
 705         if (Platform.isWindows()) {
 706             // " has to be escaped on windows
 707             rawArgs = Arrays.stream(rawArgs)
 708                             .map(s -> s.replace("\"", "\\\""))
 709                             .toArray(String[]::new);
 710         }
 711 
 712         String cmdline = classToExecute + " " + ArgumentHandler.joinArguments(rawArgs, quote);
 713 
 714         if(System.getProperty("main.wrapper") != null) {
 715             cmdline = MainWrapper.class.getName() + " " + System.getProperty("main.wrapper") + " " + cmdline;
 716         }
 717 
 718         arg = (Connector.StringArgument) arguments.get("main");
 719         arg.setValue(cmdline);
 720 
 721         if (! argumentHandler.willDebugeeSuspended()) {
 722             Connector.BooleanArgument barg = (Connector.BooleanArgument) arguments.get("suspend");
 723             barg.setValue(true);
 724         }
 725 
 726 /*
 727         if (! argumentHandler.isJVMDIStrictMode()) {
 728             arg = (Connector.StringArgument) arguments.get("options");
 729             arg.setValue("strict=y");
 730         }
 731  */
 732 
 733         if (! argumentHandler.isDefaultDebugeeJavaHome()) {
 734             arg = (Connector.StringArgument) arguments.get("home");
 735             arg.setValue(argumentHandler.getDebugeeJavaHome());
 736         }
 737 
 738         if (! argumentHandler.isDefaultLaunchExecName()) {
 739             arg = (Connector.StringArgument) arguments.get("vmexec");
 740             arg.setValue(argumentHandler.getLaunchExecName());
 741         }
 742 
 743         // vthread fixme: at some point we are going to have to be able to pass tests
 744         // without having this flag turned on.
 745         arg = (Connector.StringArgument) arguments.get("enumeratevthreads");
 746         arg.setValue("y");
 747 
 748         String vmArgs = "";
 749 
 750         String vmUserArgs = argumentHandler.getLaunchOptions();
 751 
 752         if (vmUserArgs != null) {
 753             vmArgs = vmUserArgs;
 754         }
 755 
 756 
 757         if (classPath != null) {
 758             vmArgs += " -classpath " + quote + classPath + quote;
 759         }
 760 
 761         if (vmArgs.length() > 0) {
 762             arg = (Connector.StringArgument) arguments.get("options");
 763             arg.setValue(vmArgs);
 764         }
 765 
 766         display("Connector arguments:");
 767         Iterator iterator = arguments.values().iterator();
 768         while (iterator.hasNext()) {
 769             display("    " + iterator.next());
 770         }
 771         return arguments;
 772     }
 773 
 774     /**
 775      * Make proper arguments for RawLaunchingConnector.
 776      */
 777     private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupRawLaunchingConnector(LaunchingConnector connector,
 778                                                 String classToExecute,
 779                                                 String classPath) {
 780         display("RawLaunchingConnector:");
 781         display("    name: " + connector.name());
 782         display("    description: " + connector.description());
 783         display("    transport: " + connector.transport());
 784 
 785         Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String, com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments());
 786 
 787         String connectorAddress;
 788         String vmAddress;
 789 
 790         if (argumentHandler.isSocketTransport()) {
 791             connectorAddress = argumentHandler.getTransportPort();
 792             vmAddress = argumentHandler.getDebugeeHost()
 793                         + ":" + argumentHandler.getTransportPort();
 794         } else if (argumentHandler.isShmemTransport() ) {
 795             connectorAddress = argumentHandler.getTransportSharedName();
 796             vmAddress=connectorAddress;
 797         } else {
 798             throw new TestBug("Undefined transport type for AttachingConnector");
 799         }
 800 
 801         Connector.Argument arg;
 802 
 803         arg = (Connector.StringArgument) arguments.get("quote");
 804         String quote = arg.value();
 805 
 806         String javaCmdLine = makeCommandLineString(classToExecute, quote);
 807 
 808         arg = (Connector.StringArgument) arguments.get("command");
 809         arg.setValue(javaCmdLine);
 810 
 811         arg = (Connector.StringArgument) arguments.get("address");
 812         arg.setValue(connectorAddress);
 813 
 814         display("Connector arguments:");
 815         Iterator iterator = arguments.values().iterator();
 816         while (iterator.hasNext()) {
 817             display("    " + iterator.next());
 818         }
 819         return arguments;
 820     }
 821 
 822     /**
 823      * Make proper arguments for AttachingConnector.
 824      */
 825     private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupAttachingConnector(AttachingConnector connector,
 826                                                 String classToExecute,
 827                                                 String classPath) {
 828         display("AttachingConnector:");
 829         display("    name: " + connector.name());
 830         display("    description: " + connector.description());
 831         display("    transport: " + connector.transport());
 832 
 833         Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String,com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments());
 834 
 835         Connector.Argument arg;
 836         if (argumentHandler.isSocketTransport()) {
 837             arg = (Connector.StringArgument) arguments.get("hostname");
 838             arg.setValue(argumentHandler.getDebugeeHost());
 839             Connector.IntegerArgument iarg = (Connector.IntegerArgument) arguments.get("port");
 840             iarg.setValue(argumentHandler.getTransportPortNumber());
 841         } else {
 842             arg = (Connector.StringArgument) arguments.get("name");
 843             arg.setValue(argumentHandler.getTransportSharedName());
 844         }
 845 
 846         display("Connector arguments:");
 847         Iterator iterator = arguments.values().iterator();
 848         while (iterator.hasNext()) {
 849             display("    " + iterator.next());
 850         }
 851         return arguments;
 852     }
 853 
 854     /**
 855      * Make proper arguments for ListeningConnector.
 856      */
 857     private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupListeningConnector(ListeningConnector connector,
 858                                                 String classToExecute,
 859                                                 String classPath) {
 860         display("ListeningConnector:");
 861         display("    name: " + connector.name());
 862         display("    description: " + connector.description());
 863         display("    transport: " + connector.transport());
 864 
 865         Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String,com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments());
 866 
 867         Connector.Argument arg;
 868         if (argumentHandler.isSocketTransport()) {
 869             if (!argumentHandler.isTransportAddressDynamic()) {
 870                 int port = argumentHandler.getTransportPortNumber();
 871                 Connector.IntegerArgument iarg = (Connector.IntegerArgument) arguments.get("port");
 872                 iarg.setValue(port);
 873             }
 874         } else {
 875             String sharedName = argumentHandler.getTransportSharedName();
 876             arg = (Connector.StringArgument) arguments.get("name");
 877             arg.setValue(sharedName);
 878         }
 879 
 880         display("Connector arguments:");
 881         Iterator iterator = arguments.values().iterator();
 882         while (iterator.hasNext()) {
 883             display("    " + iterator.next());
 884         }
 885         return arguments;
 886     }
 887 
 888     // -------------------------------------------------- //
 889 
 890     /**
 891      * Find connector by name from given connectors list.
 892      */
 893     private Connector findConnector(String connectorName, List connectors) {
 894         Iterator iter = connectors.iterator();
 895 
 896         while (iter.hasNext()) {
 897             Connector connector = (Connector) iter.next();
 898             if (connector.name().equals(connectorName)) {
 899                 return connector;
 900             }
 901         }
 902         throw new Failure("JDI connector not found: " + connectorName);
 903     }
 904 
 905     // -------------------------------------------------- //
 906 
 907     /**
 908      * Launch local debuggee process with specified command line arguments
 909      * and make initial <code>Debugee</code> mirror.
 910      */
 911     protected Debugee startLocalDebugee(String[] cmdArgs) {
 912         Process process = null;
 913 
 914         try {
 915             process = launchProcess(cmdArgs);
 916         } catch (IOException e) {
 917             e.printStackTrace(log.getOutStream());
 918             throw new Failure("Caught exception while launching local debuggee VM process:\n\t"
 919                             + e);
 920         }
 921 
 922         return makeLocalDebugee(process);
 923     }
 924 
 925     /**
 926      * Launch remote debuggee process with specified command line arguments
 927      * and make initial <code>Debugee</code> mirror.
 928      */
 929     protected RemoteLaunchedDebugee startRemoteDebugee(String[] cmdArgs) {
 930         try {
 931             launchRemoteProcess(cmdArgs);
 932         } catch (IOException e) {
 933             e.printStackTrace(log.getOutStream());
 934             throw new Failure("Caught exception while launching remote debuggee VM process:\n\t"
 935                             + e);
 936         }
 937 
 938         RemoteLaunchedDebugee debugee = new RemoteLaunchedDebugee(this);
 939 
 940         Finalizer finalizer = new Finalizer(debugee);
 941         finalizer.activate();
 942 
 943         return debugee;
 944     }
 945 
 946     /**
 947      * Launch manual debuggee process with specified command line arguments
 948      * and make initial <code>Debugee</code> mirror.
 949      */
 950     protected ManualLaunchedDebugee startManualDebugee(String cmd) {
 951         ManualLaunchedDebugee debugee = new ManualLaunchedDebugee(this);
 952         debugee.launchDebugee(cmd);
 953 
 954         Finalizer finalizer = new Finalizer(debugee);
 955         finalizer.activate();
 956 
 957         return debugee;
 958     }
 959 
 960     public static String readVMStartExceptionOutput(VMStartException e, PrintStream log) {
 961         StringBuffer msg = new StringBuffer();
 962         try (InputStream is = e.process().getInputStream()) {
 963             msg.append("\tstdout: ").append(new String(readAllBytes(is))).append('\n');
 964         } catch (IOException e1) {
 965             log.println("Could not read normal output from launched process:" + e1);
 966         }
 967         try (InputStream is = e.process().getErrorStream()) {
 968             msg.append("\tstderr: ").append(new String(readAllBytes(is)));
 969         } catch (IOException e1) {
 970             log.println("Could not read error output from launched process:" + e1);
 971         }
 972         return msg.toString();
 973     }
 974 
 975     /**
 976      * Copied from the JDK 9 implementation in InputStream.java
 977      */
 978     private static byte[] readAllBytes(InputStream is) throws IOException {
 979         final int DEFAULT_BUFFER_SIZE = 8192;
 980         final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
 981 
 982         byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
 983         int capacity = buf.length;
 984         int nread = 0;
 985         int n;
 986         for (;;) {
 987             // read to EOF which may read more or less than initial buffer size
 988             while ((n = is.read(buf, nread, capacity - nread)) > 0)
 989                 nread += n;
 990 
 991             // if the last call to read returned -1, then we're done
 992             if (n < 0)
 993                 break;
 994 
 995             // need to allocate a larger buffer
 996             if (capacity <= MAX_BUFFER_SIZE - capacity) {
 997                 capacity = capacity << 1;
 998             } else {
 999                 if (capacity == MAX_BUFFER_SIZE)
1000                     throw new OutOfMemoryError("Required array size too large");
1001                 capacity = MAX_BUFFER_SIZE;
1002             }
1003             buf = Arrays.copyOf(buf, capacity);
1004         }
1005         return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
1006     }
1007 
1008 }
1009 
1010 
1011 /**
1012  * Mirror of locally launched debugee.
1013  */
1014 final class LocalLaunchedDebugee extends Debugee {
1015 
1016     /** Enwrap the locally started VM process. */
1017     public LocalLaunchedDebugee (Process process, Binder binder) {
1018         super(binder);
1019         this.process = process;
1020         checkTermination = true;
1021     }
1022 
1023     // ---------------------------------------------- //
1024 
1025     /** Return exit status of the debugee VM. */
1026     public int getStatus () {
1027         return process.exitValue();
1028     }
1029 
1030     /** Check whether the debugee VM has been terminated. */
1031     public boolean terminated () {
1032         if (process == null)
1033             return true;
1034 
1035         try {
1036             int value = process.exitValue();
1037             return true;
1038         } catch (IllegalThreadStateException e) {
1039             return false;
1040         }
1041     }
1042 
1043     // ---------------------------------------------- //
1044 
1045     /** Kill the debugee VM. */
1046     protected void killDebugee () {
1047         super.killDebugee();
1048         if (!terminated()) {
1049             log.display("Killing debugee VM process");
1050             process.destroy();
1051         }
1052     }
1053 
1054     /** Wait until the debugee VM shutdown or crash. */
1055     protected int waitForDebugee () throws InterruptedException {
1056         int code = process.waitFor();
1057         return code;
1058     }
1059 
1060     /** Get a pipe to write to the debugee's stdin stream. */
1061     protected OutputStream getInPipe () {
1062         return process.getOutputStream();
1063     }
1064 
1065     /** Get a pipe to read the debugee's stdout stream. */
1066     protected InputStream getOutPipe () {
1067         return process.getInputStream();
1068     }
1069 
1070     /** Get a pipe to read the debugee's stderr stream. */
1071     protected InputStream getErrPipe () {
1072         return process.getErrorStream();
1073     }
1074 }
1075 
1076 
1077 /**
1078  * Mirror of remotely launched debugee.
1079  */
1080 final class RemoteLaunchedDebugee extends Debugee {
1081 
1082     /** Enwrap the remotely started VM process. */
1083     public RemoteLaunchedDebugee (Binder binder) {
1084         super(binder);
1085     }
1086 
1087     // ---------------------------------------------- //
1088 
1089     /** Return exit status of the debugee VM. */
1090     public int getStatus () {
1091         return binder.getRemoteProcessStatus();
1092     }
1093 
1094     /** Check whether the debugee VM has been terminated. */
1095     public boolean terminated () {
1096         return binder.isRemoteProcessTerminated();
1097     }
1098 
1099     // ---------------------------------------------- //
1100 
1101     /** Kill the debugee VM. */
1102     protected void killDebugee () {
1103         super.killDebugee();
1104         if (!terminated()) {
1105             binder.killRemoteProcess();
1106         }
1107     }
1108 
1109     /** Wait until the debugee VM shutdown or crash. */
1110     protected int waitForDebugee () {
1111         return binder.waitForRemoteProcess();
1112     }
1113 
1114     /** Get a pipe to write to the debugee's stdin stream. */
1115     protected OutputStream getInPipe () {
1116         return null;
1117     }
1118 
1119     /** Get a pipe to read the debugee's stdout stream. */
1120     protected InputStream getOutPipe () {
1121         return null;
1122     }
1123 
1124     /** Get a pipe to read the debugee's stderr stream. */
1125     protected InputStream getErrPipe () {
1126         return null;
1127     }
1128 
1129     public void redirectStdout(OutputStream out) {
1130     }
1131 
1132     public void redirectStdout(Log log, String prefix) {
1133     }
1134 
1135     public void redirectStderr(OutputStream out) {
1136     }
1137 
1138     public void redirectStderr(Log log, String prefix) {
1139     }
1140 }
1141 
1142 
1143 /**
1144  * Mirror of manually launched debugee.
1145  */
1146 final class ManualLaunchedDebugee extends Debugee {
1147     /** Enwrap the manually started VM process. */
1148     public ManualLaunchedDebugee (Binder binder) {
1149         super(binder);
1150         makeInputReader();
1151     }
1152 
1153     // ---------------------------------------------- //
1154 
1155     private int exitCode = 0;
1156     private boolean finished = false;
1157     private static BufferedReader bin = null;
1158 
1159     public void launchDebugee(String commandLine) {
1160         makeInputReader();
1161 
1162         putMessage("Launch target VM using such command line:\n"
1163                     + commandLine);
1164         String answer = askQuestion("Has the VM successfully started? (yes/no)", "yes");
1165         for ( ; ; ) {
1166             if (answer.equals("yes"))
1167                 break;
1168             if (answer.equals("no"))
1169                 throw new Failure ("Unable to manually launch debugee VM");
1170             answer = askQuestion("Wrong answer. Please type yes or no", "yes");
1171         }
1172     }
1173 
1174     private static void makeInputReader() {
1175         if (bin == null) {
1176             bin = new BufferedReader(new InputStreamReader(System.in));
1177         }
1178     }
1179 
1180     private static void destroyInputReader() {
1181         if (bin != null) {
1182             try {
1183                 bin.close();
1184             } catch (IOException e) {
1185 //                e.printStackTrace(log.getOutStream());
1186                 throw new Failure("Caught exception while closing input stream:\n\t" + e);
1187             }
1188             bin = null;
1189         }
1190     }
1191 
1192     private static void putMessage(String msg) {
1193         System.out.println("\n>>> " + msg);
1194     }
1195 
1196     private static String askQuestion(String question, String defaultAnswer) {
1197         try {
1198             System.out.print("\n>>> " + question);
1199             System.out.print(" [" + defaultAnswer + "] ");
1200             System.out.flush();
1201             String answer = bin.readLine();
1202             if (answer.equals(""))
1203                 return defaultAnswer;
1204             return answer;
1205         } catch (IOException e) {
1206 //            e.printStackTrace(log.getOutStream());
1207             throw new Failure("Caught exception while reading answer:\n\t" + e);
1208         }
1209     }
1210 
1211     /** Return exit status of the debugee VM. */
1212     public int getStatus () {
1213         if (! finished) {
1214             throw new Failure("Unable to get status of debugee VM: process still alive");
1215         }
1216         return exitCode;
1217     }
1218 
1219     /** Check whether the debugee VM has been terminated. */
1220     public boolean terminated () {
1221         return finished;
1222     }
1223 
1224     // ---------------------------------------------- //
1225 
1226     /** Kill the debugee VM. */
1227     protected void killDebugee () {
1228         super.killDebugee();
1229         if (!terminated()) {
1230             putMessage("Kill launched VM");
1231             String answer = askQuestion("Has the VM successfully terminated? (yes/no)", "yes");
1232             for ( ; ; ) {
1233                 if (answer.equals("yes")) {
1234                     finished = true;
1235                     break;
1236                 }
1237                 if (answer.equals("no"))
1238                     throw new Failure ("Unable to manually kill debugee VM");
1239                 answer = askQuestion("Wrong answer. Please type yes or no", "yes");
1240             }
1241         }
1242     }
1243 
1244     /** Wait until the debugee VM shutdown or crash. */
1245     protected int waitForDebugee () {
1246         putMessage("Wait for launched VM to exit.");
1247         String answer = askQuestion("What is VM exit code?", "95");
1248         for ( ; ; ) {
1249             try {
1250                 exitCode = Integer.parseInt(answer);
1251                 break;
1252             } catch (NumberFormatException e) {
1253                 answer = askQuestion("Wrong answer. Please type integer value", "95");
1254             }
1255         }
1256         finished = true;
1257         return exitCode;
1258     }
1259 
1260     /** Get a pipe to write to the debugee's stdin stream. */
1261     protected OutputStream getInPipe () {
1262         return null;
1263     }
1264 
1265     /** Get a pipe to read the debugee's stdout stream. */
1266     protected InputStream getOutPipe () {
1267         return null;
1268     }
1269 
1270     /** Get a pipe to read the debugee's stderr stream. */
1271     protected InputStream getErrPipe () {
1272         return null;
1273     }
1274 
1275     public void redirectStdout(OutputStream out) {
1276     }
1277 
1278     public void redirectStdout(Log log, String prefix) {
1279     }
1280 
1281     public void redirectStderr(OutputStream out) {
1282     }
1283 
1284     public void redirectStderr(Log log, String prefix) {
1285     }
1286 
1287     public void close() {
1288         destroyInputReader();
1289         super.close();
1290     }
1291 }