1 /* 2 * Copyright (c) 2001, 2019, 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.jpda; 25 26 import nsk.share.*; 27 28 import java.io.*; 29 import java.net.*; 30 import java.util.*; 31 32 /** 33 * This class provides debugger with ability to launch 34 * debuggee VM and to make connection to it using JDI connector or 35 * JDWP transport. 36 * <p> 37 * The present version of <code>Binder</code> allows 38 * to launch debuggee VM either on local machine (<i>local</i> launch mode), 39 * or on remote host using <code>BindServer</code> utility 40 * (<i>remote</i> launch mode). Also there is an ability to launch 41 * debuggee VM manually as a separate process on local or remote machine 42 * (<i>manual</i> launch mode), which is usefull for debugging. 43 * All these launching modes are specified by command line option 44 * <code>-debugee.launch</code> recognized by <code>DebugeeArgumentHandler</code>. 45 * <p> 46 * <code>Binder</code> also makes it possible to establish TCP/IP 47 * connection between debugger and debuggee throw <code>IOPipe</code> 48 * object. This connection allows debugger to communicate with debuggee 49 * by exchanging with synchronization messages and data. 50 * <p> 51 * To launch debuggee VM and bind to it use <code>bindToDebugee()</code> 52 * method. This method construct mirror of debugee VM, represented by 53 * object of <code>DebugeeProcess</code> class or derived. This mirror object 54 * allows to control debuggee VM. 55 * <p> 56 * See also <code>nsk.share.jdi.Binder</code> and <code>nsk.share.jdwp.Binder</code> 57 * classes which provide launching and binding to debuggee VM using specific 58 * JDI and JDWP features. 59 * 60 * @see DebugeeArgumentHandler 61 * @see DebugeeProcess 62 * @see IOPipe 63 * @see BindServer 64 * 65 * @see nsk.share.jdi.Binder 66 * @see nsk.share.jdwp.Binder 67 */ 68 public class DebugeeBinder extends Log.Logger implements Finalizable { 69 70 private static final boolean IS_WINDOWS = System.getProperty("os.name") 71 .toLowerCase() 72 .startsWith("win"); 73 74 public static int TRY_DELAY = 1000; // milliseconds 75 76 public static int CONNECT_TIMEOUT = 1 * 60 * 1000; // milliseconds 77 public static int CONNECT_TRY_DELAY = 2 * 1000; // milliseconds 78 public static int CONNECT_TRIES = CONNECT_TIMEOUT / CONNECT_TRY_DELAY; 79 80 public static int THREAD_TIMEOUT = 2 * CONNECT_TRY_DELAY; // milliseconds 81 public static int PING_TIMEOUT = 30 * 1000; // milliseconds 82 83 public static int SOCKET_TIMEOUT = 2 * 1000; // milliseconds 84 public static int SOCKET_LINGER = 1; // milliseconds 85 86 private static int TRACE_LEVEL_PACKETS = 10; 87 private static int TRACE_LEVEL_THREADS = 20; 88 private static int TRACE_LEVEL_ACTIONS = 30; 89 private static int TRACE_LEVEL_SOCKETS = 40; 90 private static int TRACE_LEVEL_IO = 50; 91 92 /** 93 * Default message prefix for <code>Binder</code> object. 94 */ 95 public static final String LOG_PREFIX = "binder> "; 96 97 private DebugeeArgumentHandler argumentHandler = null; 98 99 /** 100 * Get version string. 101 */ getVersion()102 public static String getVersion () { 103 return "@(#)Binder.java %I% %E%"; 104 } 105 106 // -------------------------------------------------- // 107 108 private BindServerListener bindServerListener = null; 109 private ServerSocket pipeServerSocket = null; 110 111 // -------------------------------------------------- // 112 113 /** 114 * Incarnate new Binder obeying the given 115 * <code>argumentHandler</code>; and assign the given 116 * <code>log</code>. 117 */ DebugeeBinder(DebugeeArgumentHandler argumentHandler, Log log)118 public DebugeeBinder (DebugeeArgumentHandler argumentHandler, Log log) { 119 super(log, LOG_PREFIX); 120 this.argumentHandler = argumentHandler; 121 Finalizer finalizer = new Finalizer(this); 122 finalizer.activate(); 123 } 124 125 /** 126 * Get argument handler of this binder object. 127 */ getArgumentHandler()128 DebugeeArgumentHandler getArgumentHandler() { 129 return argumentHandler; 130 } 131 132 // -------------------------------------------------- // 133 134 /** 135 * Wait for given thread finished for THREAD_TIMEOUT timeout and 136 * interrupt this thread if not finished. 137 * 138 * @param thr thread to wait for 139 * @param logger to write log messages to 140 */ waitForThread(Thread thr, Log.Logger logger)141 public static void waitForThread(Thread thr, Log.Logger logger) { 142 waitForThread(thr, THREAD_TIMEOUT, logger); 143 } 144 145 /** 146 * Wait for given thread finished for specified timeout and 147 * interrupt this thread if not finished. 148 * 149 * @param thr thread to wait for 150 * @param millisecs timeout in milliseconds 151 * @param logger to write log messages to 152 */ waitForThread(Thread thr, long millisecs, Log.Logger logger)153 public static void waitForThread(Thread thr, long millisecs, Log.Logger logger) { 154 if (thr != null) { 155 if (thr.isAlive()) { 156 try { 157 logger.trace(TRACE_LEVEL_THREADS, "Waiting for thread: " + thr.getName()); 158 thr.join(millisecs); 159 } catch (InterruptedException e) { 160 e.printStackTrace(logger.getOutStream()); 161 throw new Failure ("Thread interrupted while waiting for another thread:\n\t" 162 + e); 163 } finally { 164 if (thr.isAlive()) { 165 logger.trace(TRACE_LEVEL_THREADS, "Interrupting not finished thread: " + thr); 166 thr.interrupt(); 167 } 168 } 169 } 170 } 171 } 172 173 174 /** 175 * Make preperation for IOPipe connection before starting debugee VM process. 176 * May change options in the passed <code>argumentHandler</code>. 177 */ prepareForPipeConnection(DebugeeArgumentHandler argumentHandler)178 public void prepareForPipeConnection(DebugeeArgumentHandler argumentHandler) { 179 if (argumentHandler.isTransportAddressDynamic()) { 180 try { 181 pipeServerSocket = new ServerSocket(); 182 pipeServerSocket.setReuseAddress(false); 183 pipeServerSocket.bind(null); 184 185 } catch (IOException e) { 186 e.printStackTrace(getOutStream()); 187 throw new Failure("Caught IOException while binding for IOPipe connection: \n\t" 188 + e); 189 } 190 191 int port = pipeServerSocket.getLocalPort(); 192 argumentHandler.setPipePortNumber(port); 193 } 194 } 195 196 /** 197 * Return already bound ServerSocket for IOPipe connection or null. 198 */ getPipeServerSocket()199 protected ServerSocket getPipeServerSocket() { 200 return pipeServerSocket; 201 } 202 203 /** 204 * Close ServerSocket used for IOPipeConnection if any. 205 */ closePipeServerSocket()206 private void closePipeServerSocket() { 207 if (pipeServerSocket != null) { 208 try { 209 pipeServerSocket.close(); 210 } catch (IOException e) { 211 println("# WARNING: Caught IOException while closing ServerSocket used for IOPipe connection: \n\t" 212 + e); 213 } 214 } 215 } 216 217 // -------------------------------------------------- // 218 219 /** 220 * Make environment for launching JVM process. 221 */ makeProcessEnvironment()222 public String[] makeProcessEnvironment() { 223 /* 224 String env = new String[0]; 225 return env; 226 */ 227 return null; 228 } 229 230 /** 231 * Launch process by the specified command line. 232 * 233 * @throws IOException if I/O error occured while launching process 234 */ launchProcess(String cmdLine)235 public Process launchProcess(String cmdLine) throws IOException { 236 String env[] = makeProcessEnvironment(); 237 return Runtime.getRuntime().exec(cmdLine, env); 238 } 239 240 /** 241 * Launch process by the arguments array. 242 * 243 * @throws IOException if I/O error occured while launching process 244 */ launchProcess(String[] args)245 public Process launchProcess(String[] args) throws IOException { 246 String env[] = makeProcessEnvironment(); 247 return Runtime.getRuntime().exec(args, env); 248 } 249 250 /** 251 * Make string representation of debuggee VM transport address according 252 * to current command line options. 253 */ makeTransportAddress()254 public String makeTransportAddress() { 255 String address = null; 256 if (argumentHandler.isSocketTransport()) { 257 if (argumentHandler.isListeningConnector()) { 258 address = argumentHandler.getTestHost() 259 + ":" + argumentHandler.getTransportPort(); 260 } else { 261 address = argumentHandler.getTransportPort(); 262 } 263 } else if (argumentHandler.isShmemTransport() ) { 264 address = argumentHandler.getTransportSharedName(); 265 } else { 266 throw new TestBug("Undefined transport type: " 267 + argumentHandler.getTransportType()); 268 } 269 return address; 270 } 271 272 /** 273 * Make command line to launch debugee VM as a string using given quote symbol, 274 * using specified <code>transportAddress</code> for JDWP connection. 275 */ makeCommandLineString(String classToExecute, String transportAddress, String quote)276 public String makeCommandLineString(String classToExecute, String transportAddress, String quote) { 277 String[] args = makeCommandLineArgs(classToExecute, transportAddress); 278 return ArgumentParser.joinArguments(args, quote); 279 } 280 281 /** 282 * Make command line to launch debugee VM as a string using given quote symbol. 283 */ makeCommandLineString(String classToExecute, String quote)284 public String makeCommandLineString(String classToExecute, String quote) { 285 return makeCommandLineString(classToExecute, makeTransportAddress(), quote); 286 } 287 288 /** 289 * Make command line to launch debugee VM as a string using default quote symbol, 290 * using specified <code>transportAddress</code> for JDWP connection. 291 */ 292 /* 293 public String makeCommandLineString(String classToExecute, String transportAddress) { 294 return makeCommandLineString(classToExecute, transportAddress, "\""); 295 } 296 */ 297 298 /** 299 * Make command line to launch debugee VM as a string using default quote symbol. 300 */ 301 /* 302 public String makeCommandLineString(String classToExecute) { 303 return makeCommandLineString(classToExecute, makeTransportAddress()); 304 } 305 */ 306 307 /** 308 * Make command line to launch debugee VM as an array of arguments, 309 * using specified <code>transportAddress</code> for JDWP connection. 310 */ makeCommandLineArgs(String classToExecute, String transportAddress)311 public String[] makeCommandLineArgs(String classToExecute, String transportAddress) { 312 Vector<String> args = new Vector<String>(); 313 314 args.add(argumentHandler.getLaunchExecPath()); 315 316 String javaOpts = argumentHandler.getLaunchOptions(); 317 if (javaOpts != null && javaOpts.length() > 0) { 318 StringTokenizer st = new StringTokenizer(javaOpts); 319 320 while (st.hasMoreTokens()) { 321 args.add(st.nextToken()); 322 } 323 } 324 325 /* 326 String classPath = System.getProperty("java.class.path"); 327 args.add("-classpath") 328 args.add(classPath); 329 */ 330 331 args.add("-Xdebug"); 332 333 String server; 334 if (argumentHandler.isAttachingConnector()) { 335 server = "y"; 336 } else { 337 server = "n"; 338 } 339 340 String jdwpArgs = "-Xrunjdwp:" 341 + "server=" + server 342 + ",transport=" + argumentHandler.getTransportName() 343 + ",address=" + transportAddress; 344 345 if (! argumentHandler.isDefaultJVMDIStrictMode()) { 346 if (argumentHandler.isJVMDIStrictMode()) 347 jdwpArgs += ",strict=y"; 348 else 349 jdwpArgs += ",strict=n"; 350 } 351 352 args.add(jdwpArgs); 353 354 if (classToExecute != null) { 355 StringTokenizer st = new StringTokenizer(classToExecute); 356 357 while (st.hasMoreTokens()) { 358 args.add(st.nextToken()); 359 } 360 } 361 362 String[] rawArgs = argumentHandler.getRawArguments(); 363 for (int i = 0; i < rawArgs.length; i++) { 364 String rawArg = rawArgs[i]; 365 // " has to be escaped on windows 366 if (IS_WINDOWS) { 367 rawArg = rawArg.replace("\"", "\\\""); 368 } 369 args.add(rawArg); 370 } 371 372 String[] argsArray = new String[args.size()]; 373 for (int i = 0; i < args.size(); i++) { 374 argsArray[i] = (String) args.elementAt(i); 375 } 376 377 return argsArray; 378 } 379 380 /** 381 * Make command line to launch debugee VM as an array of arguments. 382 */ makeCommandLineArgs(String classToExecute)383 public String[] makeCommandLineArgs(String classToExecute) { 384 return makeCommandLineArgs(classToExecute, makeTransportAddress()); 385 } 386 387 /** 388 * Make connection to remote BindServer and start BindServerListener thread. 389 * 390 * @throws IOException if I/O error occured while connecting 391 */ connectToBindServer(String taskID)392 public void connectToBindServer(String taskID) { 393 if (bindServerListener != null) { 394 throw new Failure("Connection to BindServer already exists"); 395 } 396 try { 397 bindServerListener = new BindServerListener(this); 398 bindServerListener.setDaemon(true); 399 bindServerListener.connect(taskID); 400 bindServerListener.start(); 401 } catch (IOException e) { 402 e.printStackTrace(getOutStream()); 403 throw new Failure("Caught exception while connecting to BindServer:\n\t" + e); 404 } 405 } 406 407 /** 408 * Split string into list of substrings using specified separator. 409 */ splitString(String givenString, String separator)410 private static String[] splitString(String givenString, String separator) { 411 Vector<String> tmpList = new Vector<String>(); 412 StringTokenizer tokenizer = new StringTokenizer(givenString, separator); 413 while(tokenizer.hasMoreTokens()) { 414 tmpList.add(tokenizer.nextToken()); 415 } 416 String[] list = new String[tmpList.size()]; 417 for (int i = 0; i < tmpList.size(); i++) { 418 list[i] = tmpList.elementAt(i); 419 } 420 return list; 421 } 422 423 /** 424 * Send command to remote <code>BindServer</code> and receive reply. 425 * 426 * @throws IOException if I/O error occured while launching process 427 */ sendRemoteCommand(Object command)428 public synchronized Object sendRemoteCommand(Object command) { 429 try { 430 bindServerListener.sendCommand(command); 431 Object reply = bindServerListener.getReply(); 432 return reply; 433 } catch (IOException e) { 434 e.printStackTrace(log.getOutStream()); 435 throw new Failure("Unexpected exception while sending command to BindServer:\n\t" 436 + e); 437 } 438 } 439 440 /** 441 * Launch remote process using request to <code>BindServer</code>. 442 * 443 * @throws IOException if I/O error occured 444 */ launchRemoteProcess(String[] args)445 public void launchRemoteProcess(String[] args) throws IOException { 446 String pathSeparator = System.getProperty("path.separator"); 447 BindServer.LaunchDebugee command = 448 new BindServer.LaunchDebugee(args, 449 System.getProperty("file.separator"), 450 System.getProperty("user.dir"), 451 splitString(System.getProperty("java.library.path"), pathSeparator), 452 splitString(System.getProperty("java.class.path"), pathSeparator), 453 splitString(System.getProperty("java.library.path"), pathSeparator)); 454 455 Object reply = sendRemoteCommand(command); 456 if (reply instanceof BindServer.OK) { 457 // do nothing 458 } else if (reply instanceof BindServer.RequestFailed) { 459 BindServer.RequestFailed castedReply = (BindServer.RequestFailed)reply; 460 throw new Failure("BindServer error: " + castedReply.reason); 461 } else { 462 throw new Failure("Wrong reply from BindServer: " + reply); 463 } 464 } 465 466 /** 467 * Return exit status of the remotely launched process 468 * using request to <code>BindServer</code>. 469 */ getRemoteProcessStatus()470 public int getRemoteProcessStatus () { 471 Object reply = sendRemoteCommand(new BindServer.DebugeeExitCode()); 472 if (reply instanceof BindServer.OK) { 473 BindServer.OK castedReply = (BindServer.OK)reply; 474 return (int)castedReply.info; 475 } else if (reply instanceof BindServer.CaughtException) { 476 BindServer.CaughtException castedReply = (BindServer.CaughtException)reply; 477 throw new IllegalThreadStateException(castedReply.reason); 478 } else if (reply instanceof BindServer.RequestFailed) { 479 BindServer.RequestFailed castedReply = (BindServer.RequestFailed)reply; 480 throw new Failure("BindServer error: " + castedReply.reason); 481 } else { 482 throw new Failure("Wrong reply from BindServer: " + reply); 483 } 484 } 485 486 /** 487 * Check whether the remotely launched process has been terminated 488 * using request to <code>BindServer</code>. 489 */ isRemoteProcessTerminated()490 public boolean isRemoteProcessTerminated () { 491 try { 492 int value = getRemoteProcessStatus(); 493 return true; 494 } catch (IllegalThreadStateException e) { 495 return false; 496 } 497 } 498 499 // ---------------------------------------------- // 500 501 /** 502 * Kill the remotely launched process 503 * using request to <code>BindServer</code>. 504 */ killRemoteProcess()505 public void killRemoteProcess () { 506 Object reply = sendRemoteCommand(new BindServer.KillDebugee()); 507 if (reply instanceof BindServer.OK) { 508 return; 509 } else if (reply instanceof BindServer.RequestFailed) { 510 BindServer.RequestFailed castedReply = (BindServer.RequestFailed)reply; 511 throw new Failure("BindServer error: " + castedReply.reason); 512 } else { 513 throw new Failure("Wrong reply from BindServer: " + reply); 514 } 515 } 516 517 /** 518 * Wait until the remotely launched process exits or crashes 519 * using request to <code>BindServer</code>. 520 */ waitForRemoteProcess()521 public int waitForRemoteProcess () { 522 523 Object reply = sendRemoteCommand(new BindServer.WaitForDebugee(0)); 524 if (reply instanceof BindServer.OK) { 525 BindServer.OK castedReply = (BindServer.OK)reply; 526 return (int)castedReply.info; 527 } else if (reply instanceof BindServer.RequestFailed) { 528 BindServer.RequestFailed castedReply = (BindServer.RequestFailed)reply; 529 throw new Failure("BindServer error: " + castedReply.reason); 530 } else { 531 throw new Failure("Wrong reply from BindServer: " + reply); 532 } 533 } 534 535 /** 536 * Close binder by closing all started threads. 537 */ close()538 public void close() { 539 if (bindServerListener != null) { 540 bindServerListener.close(); 541 } 542 closePipeServerSocket(); 543 } 544 545 /** 546 * Finalize binder by invoking <code>close()</code>. 547 * 548 * @throws Throwable if any throwable exception is thrown during finalization 549 */ finalize()550 protected void finalize() throws Throwable { 551 close(); 552 super.finalize(); 553 } 554 555 /** 556 * Finalize binder at exit by invoking <code>finalize()</code>. 557 * 558 * @throws Throwable if any throwable exception is thrown during finalization 559 */ finalizeAtExit()560 public void finalizeAtExit() throws Throwable { 561 finalize(); 562 } 563 564 /** 565 * Separate thread for listening connection from <code>BindServer</code>. 566 */ 567 private class BindServerListener extends Thread { 568 private SocketConnection connection = null; 569 private Log.Logger logger = null; 570 571 /** List of received responses from <code>BindServer</code>. */ 572 private LinkedList<BindServer.Response> replies = new LinkedList<BindServer.Response>(); 573 574 /** 575 * Make thread. 576 */ BindServerListener(Log.Logger logger)577 public BindServerListener(Log.Logger logger) { 578 this.logger = logger; 579 } 580 581 /** 582 * Establish connection to <code>BindServer</code>. 583 */ connect(String taskID)584 public void connect(String taskID) throws IOException { 585 String host = argumentHandler.getDebugeeHost(); 586 int port = argumentHandler.getBindPortNumber(); 587 display("Connecting to BindServer: " + host + ":" + port); 588 connection = new SocketConnection(logger, "BindServer"); 589 // connection.setPingTimeout(DebugeeBinder.PING_TIMEOUT); 590 connection.attach(host, port); 591 handshake(taskID); 592 } 593 594 /** 595 * Receive OK(version) from BindServer and check received version number. 596 */ handshake(String taskID)597 private void handshake(String taskID) { 598 // receive OK(version) 599 trace(TRACE_LEVEL_ACTIONS, "Waiting for initial OK(version) from BindServer"); 600 Object reply = connection.readObject(); 601 trace(TRACE_LEVEL_ACTIONS, "Got initial OK(version) from BindServer: " + reply); 602 if (reply instanceof BindServer.RequestFailed) { 603 BindServer.RequestFailed castedReply = (BindServer.RequestFailed)reply; 604 trace(TRACE_LEVEL_ACTIONS, "Reply is RequestFailed: throw Failure"); 605 throw new Failure("BindServer error: " + castedReply.reason); 606 } else if (reply instanceof BindServer.OK) { 607 BindServer.OK castedReply = (BindServer.OK)reply; 608 trace(TRACE_LEVEL_ACTIONS, "Reply is OK: check BindServer version"); 609 if (castedReply.info != BindServer.VERSION) { 610 throw new Failure("Wrong version of BindServer: " + castedReply.info 611 + " (expected: " + BindServer.VERSION + ")"); 612 } 613 display("Connected to BindServer: version " + castedReply.info); 614 } else { 615 trace(TRACE_LEVEL_ACTIONS, "Reply is unknown: throw Failure"); 616 throw new Failure("Wrong reply from BindServer: " + reply); 617 } 618 619 // send TaskID(id) 620 try { 621 trace(TRACE_LEVEL_ACTIONS, "Sending TaskID(id) to BindServer"); 622 sendCommand(new BindServer.TaskID(taskID)); 623 trace(TRACE_LEVEL_ACTIONS, "Sent TaskID(id) to BindServer"); 624 } catch (IOException e) { 625 throw new Failure("Caught IOException while sending TaskID(id) to BindServer:\n\t" 626 + e); 627 } 628 } 629 630 /** 631 * Check if thread is connected to <code>BindServer</code>. 632 */ isConnected()633 public boolean isConnected() { 634 return (connection != null && connection.isConnected()); 635 } 636 637 /** 638 * Send a command to </code>BindServer</code>. 639 */ sendCommand(Object command)640 public synchronized void sendCommand(Object command) throws IOException { 641 connection.writeObject(command); 642 } 643 644 /** 645 * Receive response from <code>BindServer</code>. 646 */ getReply()647 public Object getReply() { 648 synchronized (replies) { 649 while (replies.isEmpty()) { 650 if (!isConnected()) { 651 throw new Failure("No reply from BindServer: connection lost"); 652 } 653 try { 654 replies.wait(TRY_DELAY); 655 } catch (InterruptedException e) { 656 e.printStackTrace(getOutStream()); 657 throw new Failure("Thread interrupted while waiting for reply from BindServer:\n\t" 658 + e); 659 } 660 } 661 Object reply = replies.removeFirst(); 662 if (reply == null) { 663 throw new Failure("No reply from BindServer: connection lost"); 664 } 665 return reply; 666 } 667 } 668 669 /** 670 * Add response object to the list of received responses. 671 */ addReply(BindServer.Response reply)672 private void addReply(BindServer.Response reply) { 673 synchronized (replies) { 674 replies.add(reply); 675 replies.notifyAll(); 676 } 677 } 678 679 /** 680 * Read packets from <code>BindServer<code> connection and 681 * notify waiting thread if response or IOPipe message received. 682 * Received lines of redirected streams are put into log. 683 */ run()684 public void run() { 685 trace(TRACE_LEVEL_THREADS, "BindServerListener thread started"); 686 try { 687 for (;;) { 688 Object reply = connection.readObject(); 689 if (reply == null) { 690 break; 691 } else if (reply instanceof BindServer.Disconnect) { 692 reply = null; 693 trace(TRACE_LEVEL_ACTIONS, "Packet is Disconnect: close connection"); 694 break; 695 } else if (reply instanceof BindServer.RedirectedStream) { 696 BindServer.RedirectedStream castedReply = (BindServer.RedirectedStream)reply; 697 trace(TRACE_LEVEL_ACTIONS, "Packet is RedirectedStream: put message into log"); 698 log.println(castedReply.line); 699 } else if (reply instanceof BindServer.Response) { 700 BindServer.Response castedReply = (BindServer.Response)reply; 701 trace(TRACE_LEVEL_ACTIONS, "Packet is reply: notify all threads waiting for reply"); 702 addReply(castedReply); 703 } else { 704 trace(TRACE_LEVEL_ACTIONS, "Packet is unknown: throw Failure"); 705 throw new Failure("Wrong reply from BindServer: " + reply); 706 } 707 } 708 } catch (Exception e) { 709 e.printStackTrace(getOutStream()); 710 complain("Caught exception while reading packets from BindServer:\n\t" + e); 711 } finally { 712 closeConnection(); 713 addReply(null); 714 trace(TRACE_LEVEL_THREADS, "BindServerListener thread finished"); 715 } 716 } 717 718 /** 719 * Send Disconnect command to </code>BindServer</code>. 720 */ disconnect()721 public void disconnect() { 722 if (connection == null) return; 723 try { 724 sendCommand(new BindServer.Disconnect()); 725 } catch (IOException e) { 726 display("Caught IOException while requesting disconnection with BindServer"); 727 } 728 } 729 730 /** 731 * Close socket connection. 732 */ closeConnection()733 public void closeConnection() { 734 if (connection != null) { 735 connection.close(); 736 } 737 } 738 739 /** 740 * Wait for thread finished in the specified timeout or interrupt it. 741 */ waitForThread(long millis)742 public void waitForThread(long millis) { 743 DebugeeBinder.waitForThread(this, millis, logger); 744 } 745 746 /** 747 * Close this thread by waiting for it finishes or interrupt it 748 * and close socket connection. 749 */ close()750 public void close() { 751 disconnect(); 752 waitForThread(DebugeeBinder.THREAD_TIMEOUT); 753 closeConnection(); 754 } 755 756 } // BindServerListener 757 758 } // DebugeeBinder 759