Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/BindServer.java
41161 views
/*1* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package nsk.share.jpda;2425import java.io.*;26import java.net.*;27import java.util.*;2829import nsk.share.*;30import nsk.share.jpda.*;3132/**33* <code>BindServer</code> is an utility to perform JPDA tests34* in remote mode across network.35* <p>36* This utility should be started on remote host. It listens for connection37* from JPDA tests and launches debuggee VM on this host.38* <p>39* <code>BindServer</code> works together with <code>Binder</code> used in40* the tests to incapsulate actions required for launching debuggee VM.41* See <code>ProcessBinder</code> and <code>DebugeeArgumentHandler</code>42* to know how run tests in local or remote mode across network or43* on an single host.44* <p>45* <code>BindServer</code> is started on the debuggee host.46* It recognizes following command line options:47* <ul>48* <li><code>-bind.file=<i>filename</i></code> - configuration file49* <li><code>-verbose</code> - print verbose messages50* </ul>51* <p>52* Only required option is <code>-bind.file</code>, which points to the file53* where pairs of particular pathes are presented as they are seen from54* both hosts along with some other <code>BindServer</code> options.55* See <i>execution.html</i> to read more about format of bind-file.56*57* @see DebugeeBinder58* @see DebugeeArgumentHandler59*/60public class BindServer implements Finalizable {6162/** Version of <code>BindServer</code> implementation. */63public static final long VERSION = 2;6465/** Timeout in milliseconds used for waiting for inner threads. */66private static long THREAD_TIMEOUT = DebugeeBinder.THREAD_TIMEOUT; // milliseconds6768private static int PASSED = 0;69private static int FAILED = 2;70private static int JCK_BASE = 95;7172private static int TRACE_LEVEL_PACKETS = 10;73private static int TRACE_LEVEL_THREADS = 20;74private static int TRACE_LEVEL_ACTIONS = 30;75private static int TRACE_LEVEL_SOCKETS = 40;76private static int TRACE_LEVEL_IO = 50;7778private static String pathSeparator = System.getProperty("path.separator");79private static String fileSeparator = System.getProperty("file.separator");8081private static char pathSeparatorChar = pathSeparator.charAt(0);82private static char fileSeparatorChar = fileSeparator.charAt(0);8384private static Log log = null;85private static Log.Logger logger = null;86private static ArgumentHandler argHandler = null;8788private static String pathConvertions[][] = null;8990private ListeningThread listeningThread = null;9192private int totalRequests = 0;93private int acceptedRequests = 0;94private int unauthorizedRequests = 0;95private int busyRequests = 0;9697/**98* Start <code>BindServer</code> utility from command line.99* This method invokes <code>run()</code> and redirects output100* to <code>System.err</code>.101*102* @param argv list of command line arguments103*/104public static void main (String argv[]) {105System.exit(run(argv,System.err) + JCK_BASE);106}107108/**109* Start <code>BindServer</code> utility from JCK-compatible110* environment.111*112* @param argv list of command line arguments113* @param out outpur stream for log messages114*115* @return FAILED if error occured116* PASSED oterwise117*/118public static int run(String argv[], PrintStream out) {119return new BindServer().runIt(argv, out);120}121122/**123* Perform execution of <code>BindServer</code>.124* This method handles command line arguments, starts seperate125* thread for listening connection from test on remote host,126* and waits for command "exit" from a user.127* Finally it closes all conections and prints connections128* statiscs.129*130* @param argv list of command line arguments131* @param out outpur stream for log messages132*133* @return FAILED if error occured134* PASSED oterwise135*/136private int runIt(String argv[], PrintStream out) {137try {138argHandler = new ArgumentHandler(argv);139} catch (ArgumentHandler.BadOption e) {140out.println("ERROR: " + e.getMessage());141return FAILED;142}143144if (argHandler.getArguments().length > 0) {145out.println("ERROR: " + "Too many positional arguments in command line");146return FAILED;147}148149log = new Log(out, argHandler);150log.enableErrorsSummary(false);151log.enableVerboseOnError(false);152logger = new Log.Logger(log, "");153154Finalizer bindFinalizer = new Finalizer(this);155bindFinalizer.activate();156157logger.trace(TRACE_LEVEL_THREADS, "BindServer: starting main thread");158159logger.display("Listening to port: " + argHandler.getBindPortNumber());160logger.display("Authorizing host: " + argHandler.getDebuggerHost());161162pathConvertions = new String[][] {163{ "TESTED_JAVA_HOME", argHandler.getDebuggerJavaHome(), argHandler.getDebugeeJavaHome() },164{ "TESTBASE", argHandler.getDebuggerTestbase(), argHandler.getDebugeeTestbase() },165{ "WORKDIR", argHandler.getDebuggerWorkDir(), argHandler.getDebugeeWorkDir() }166};167168logger.display("Translating pathes:");169for (int i = 0; i < pathConvertions.length; i++) {170logger.display(pathConvertions[i][0] + ":" +"\n"171+ " " + pathConvertions[i][1] + "\n"172+ " =>" + "\n"173+ " " + pathConvertions[i][2]);174}175176String windir = argHandler.getDebugeeWinDir();177if (!(windir == null || windir.equals(""))) {178logger.display("Using WINDIR: \n"179+ " " + argHandler.getDebugeeWinDir());180}181182BufferedReader stdIn = new BufferedReader(183new InputStreamReader(System.in));184185listeningThread = new ListeningThread(this);186listeningThread.bind();187listeningThread.start();188189System.out.println("\n"190+ "BindServer started" + "\n"191+ "Type \"exit\" to shut down BindServer"192+ "\n");193194for (;;) {195try {196String userInput = stdIn.readLine();197if (userInput == null || userInput.equals("exit")198|| userInput.equals("quit")) {199logger.display("Shutting down BindServer");200stdIn.close();201stdIn = null;202break;203} else if (userInput.trim().equals("")) {204continue;205} else {206System.out.println("ERROR: Unknown command: " + userInput);207}208} catch(IOException e) {209e.printStackTrace(log.getOutStream());210throw new Failure("Caught exception while reading console command:\n\t"211+ e);212}213}214215printSummary(System.out);216217logger.trace(TRACE_LEVEL_THREADS, "BindServer: exiting main thread");218try {219finalize();220} catch (Throwable e) {221e.printStackTrace(log.getOutStream());222logger.complain("Caught exception while finalization of BindServer:\n\t" + e);223}224225return PASSED;226}227228/**229* Print usefull summary statistics about connections occured.230*231* @param out output stream for printing statistics232*/233private void printSummary(PrintStream out) {234out.println("\n"235+ "Connections summary:" + "\n"236+ " Tolal connections: " + totalRequests + "\n"237+ " Accepted authorized: " + acceptedRequests + "\n"238+ " Rejected unauthorized " + unauthorizedRequests + "\n"239+ " Rejected being busy: " + busyRequests + "\n");240};241242/**243* Check if given <code>path</code> starts with the specified prefix taking244* into account difference between <code>slashChar<code> used in <code>path</code>245* and <code>fileSeparatorChar</code> used in <code>prefix</code>.246*247* @param path path to check248* @param prefix prefix to compare with249* @param slashChar file separator used in <code>path</code>250*/251private static boolean checkPathPrefix(String path, String prefix, char slashChar) {252int prefixLength = prefix.length();253if (prefixLength > path.length()) {254return false;255}256for (int i = 0; i < prefixLength; i++) {257char pathChar = path.charAt(i);258char prefixChar = prefix.charAt(i);259260if (pathChar != prefixChar) {261if ((pathChar == slashChar || pathChar == fileSeparatorChar262|| pathChar == '\\' || pathChar == '/')263&& (prefixChar == slashChar || prefixChar == fileSeparatorChar264|| prefixChar == '\\' || prefixChar == '/')) {265// do nothing266} else {267return false;268}269}270}271return true;272}273274/**275* Convert given path according to list of prefixes from276* <code>pathConvertions</code> table.277*278* @param path path for converting279* @param slash file separator used in <code>path</code>280* @param name path identifier used for error messages281* @param strict force throwing Failure if path is not matched282*283* @return string with the converted path284*285* @throws Failure if path does not matched for translation286*/287private static String convertPath(String path, String slash, String name, boolean strict) {288if (path == null)289return null;290291char slashChar = slash.charAt(0);292293for (int i = 0; i < pathConvertions.length; i++) {294String from = pathConvertions[i][1];295String to = pathConvertions[i][2];296if (checkPathPrefix(path, from, slashChar)) {297return (to + path.substring(from.length())).replace(slashChar, fileSeparatorChar);298}299}300if (strict) {301throw new Failure("Path not matched for translation " + name + ":\n\t" + path);302}303return path;304}305306/**307* Convert given list of pathes according to list of prefixes from308* <code>pathConvertions</code> table by invoking <code>convertPath()</code>309* for each path from the list.310*311* @param list list of pathes for converting312* @param slash file separator used in pathes313* @param name path identifier used for error messages314* @param strict force throwing Failure if some path is not matched315*316* @return list of strings with converted pathes317*318* @throws Failure if some path does not matched for translation319*320* @see #convertPath()321*/322private static String[] convertPathes(String[] list, String slash, String name, boolean strict) {323String[] converted = new String[list.length];324for (int i = 0; i < list.length; i++) {325converted[i] = convertPath(list[i], slash, name, strict);326}327return converted;328}329330/**331* Pause current thread for specified amount of time in milliseconds,332* This method uses <code>Object.wait(long)</code> method as a reliable333* method which prevents whole VM from suspending.334*335* @param millisecs - amount of time in milliseconds336*/337private static void sleeping(int millisecs) {338Object obj = new Object();339340synchronized(obj) {341try {342obj.wait(millisecs);343} catch (InterruptedException e) {344e.printStackTrace(log.getOutStream());345new Failure("Thread interrupted while sleeping:\n\t" + e);346}347}348}349350/**351* Wait for given thread finished for specified timeout or352* interrupt this thread if not finished.353*354* @param thr thread to wait for355* @param millisecs timeout in milliseconds356*/357private static void waitInterruptThread(Thread thr, long millisecs) {358if (thr != null) {359String name = thr.getName();360try {361if (thr.isAlive()) {362logger.trace(TRACE_LEVEL_THREADS, "Waiting for thread: " + name);363thr.join(millisecs);364}365} catch (InterruptedException e) {366e.printStackTrace(log.getOutStream());367throw new Failure ("Thread interrupted while waiting for another thread:\n\t"368+ e);369} finally {370if (thr.isAlive()) {371logger.trace(TRACE_LEVEL_THREADS, "Interrupting not finished thread: " + name);372thr.interrupt();373/*374logger.display("Stopping not finished thread: " + thr);375thr.stop();376*/377}378}379}380}381382/**383* Wait for given thread finished for default timeout384* <code>THREAD_TIMEOUT</code> and385* interrupt this thread if not finished.386*387* @param thr thread to wait for388*/389private static void waitInterruptThread(Thread thr) {390waitInterruptThread(thr, THREAD_TIMEOUT);391}392393/**394* Close <code>BindServer</code> by finishing all threads and closing395* all conections.396*/397public synchronized void close() {398if (listeningThread != null) {399listeningThread.close();400listeningThread = null;401}402}403404/**405* Make finalization of <code>BindServer</code> object by invoking406* method <code>close()</code>.407*408* @see #close()409*/410protected void finalize() throws Throwable {411close();412super.finalize();413}414415/**416* Make finalization of <code>BindServer</code> object at program exit417* by invoking method <code>finalize()</code>.418*419* @see #finalize()420*/421public void finalizeAtExit() throws Throwable {422finalize();423logger.trace(TRACE_LEVEL_THREADS, "BindServer: finalization at exit completed");424}425426///////// Thread listening a TCP/IP socket //////////427428/**429* An inner thread used for listening connection from remote test430* and starting separate serving thread for each accepted connection.431*432* @see ServingThread433*/434private static class ListeningThread extends Thread {435private volatile boolean shouldStop = false;436private volatile boolean closed = false;437438private BindServer owner = null;439private volatile ServingThread servingThread = null;440private volatile int taskCount = 0;441442private ObjectOutputStream socOut = null;443private ObjectInputStream socIn = null;444445private String autorizedHostName = argHandler.getDebuggerHost();446private InetAddress autorizedInetAddresses[] = null;447private int port = argHandler.getBindPortNumber();448private Socket socket = null;449private ServerSocket serverSocket = null;450private InetAddress clientInetAddr = null;451private String clientHostName = null;452private SocketConnection connection = null;453454/**455* Make listening thread for given <code>BindServer</code> object456* as an owner and bind it to listening port by invoking method457* <code>bind()</code>.458*459* @see bind()460*/461public ListeningThread(BindServer owner) {462super("ListeningThread");463this.owner = owner;464try {465autorizedInetAddresses = InetAddress.getAllByName(autorizedHostName);466} catch (UnknownHostException e) {467e.printStackTrace(log.getOutStream());468throw new Failure("Cannot resolve DEBUGGER_HOST value: " + autorizedHostName);469}470}471472/**473* Bind ServerSocket to the specified port.474*/475public void bind() {476for (int i = 0; !shouldStop && i < DebugeeBinder.CONNECT_TRIES; i++) {477try {478logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: binding to server socket ...");479// length of the queue = 2480serverSocket = new ServerSocket(port, 2);481// timeout for the ServerSocket.accept()482serverSocket.setSoTimeout(DebugeeBinder.CONNECT_TRY_DELAY);483logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: socket bound: " + serverSocket);484logger.display("Bound to listening port");485return;486} catch (BindException e) {487logger.display("Socket binding try #" + i + " failed:\n\t" + e);488sleeping(DebugeeBinder.CONNECT_TRY_DELAY);489} catch (IOException e) {490e.printStackTrace(log.getOutStream());491throw new Failure("Caught exception while binding to socket:\n\t"492+ e);493}494}495throw new Failure("Unable to bind to socket after "496+ DebugeeBinder.CONNECT_TRIES + " tries");497}498499/**500* Accept socket connection from authorized remote host and501* start separate <code>SrvingThread</code> to handle each connection.502* Connection from unauthorized hosts or connections made while503* current connection is alive are rejected.504*505* @see ServingThread506* @see #llowConnection()507* @see allowServing()508*/509public void run() {510String reply = null;511512logger.trace(TRACE_LEVEL_THREADS, "ListeningThread: started");513logger.display("Listening for connection from remote host");514while(!(shouldStop || isInterrupted())) {515try {516try {517logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: waiting for connection from test");518socket = serverSocket.accept();519logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: connection accepted");520} catch(InterruptedIOException e) {521// logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: timeout of waiting for connection from test");522continue;523}524owner.totalRequests++;525logger.display("");526clientInetAddr = socket.getInetAddress();527clientHostName = clientInetAddr.getHostName();528logger.display("Connection #" + owner.totalRequests529+ " requested from host: " + clientHostName);530connection = new SocketConnection(logger, "BindServer");531// connection.setPingTimeout(DebugeeBinder.PING_TIMEOUT);532connection.setSocket(socket);533socket = null;534if (allowConnection()) {535if (allowServing()) {536owner.acceptedRequests++;537reply = "host authorized: " + clientHostName;538logger.display("Accepting connection #" + owner.acceptedRequests539+ ": " + reply);540servingThread = new ServingThread(this, connection);541servingThread.start();542cleanHostConnection();543} else {544owner.busyRequests++;545reply = "BindServer is busy";546logger.complain("Rejecting connection #" + owner.busyRequests547+ ": " + reply);548connection.writeObject(new RequestFailed(reply));549closeHostConnection();550}551} else {552owner.unauthorizedRequests++;553reply = "host unauthorized: " + clientHostName;554logger.complain("Rejecting connection #" + owner.unauthorizedRequests555+ ": " + reply);556connection.writeObject(new RequestFailed(reply));557closeHostConnection();558}559} catch (Exception e) {560logger.complain("Caught exception while accepting connection:\n" + e);561e.printStackTrace(log.getOutStream());562}563}564logger.trace(TRACE_LEVEL_THREADS, "ListeningThread: exiting");565closeConnection();566}567568/**569* Check if the connection made is from authorized host.570*571* @return true if connection is allowed because host authorized572* false if connection is rejected because host unauthorized573*/574private boolean allowConnection() {575// check if local host from loopback address576if (autorizedHostName.equals("localhost"))577return clientInetAddr.isLoopbackAddress();578579// check if equal hostname580if (autorizedHostName.equals(clientHostName))581return true;582583// check if equal host address584for (int i = 0; i < autorizedInetAddresses.length; i++) {585if (clientInetAddr.equals(autorizedInetAddresses[i])) {586return true;587}588}589return false;590}591592/**593* Check if no current connection exists or it is dead.594* If current connection presents it will be tested by pinging595* remote host and aborted if host sends no reply. If an alive596* connection exists, new connection will be rejected.597*598* @return true if no alive connection exists599* false otherwise600*/601private boolean allowServing() {602if (servingThread == null) {603return true;604}605if (servingThread.done) {606return true;607}608if (!servingThread.isConnectionAlive()) {609logger.display("# WARNING: Previous connection from remote host is dead: aborting connection");610servingThread.close();611servingThread = null;612return true;613}614615/*616logger.complain("Previous connection from remote host is alive: starting new connection");617servingThread = null;618return true;619*/620logger.complain("Previous connection from remote host is alive: reject new connection");621return false;622}623624/**625* Wait for this thread finished626* for specified timeout or interrupt it.627*628* @param millis timeout in milliseconds629*/630public void waitForThread(long millis) {631shouldStop = true;632waitInterruptThread(this, millis);633}634635/**636* Close socket connection from remote host.637*/638private void closeHostConnection() {639if (connection != null) {640connection.close();641}642if (socket != null) {643try {644socket.close();645} catch (IOException e) {646logger.complain("Caught IOException while closing socket:\n\t"647+ e);648}649socket = null;650}651}652653/**654* Assign <null> to connection and socket objects655* but do not close them.656*/657private void cleanHostConnection() {658connection = null;659socket = null;660}661662/**663* Close all connections and sockets.664*/665private void closeConnection() {666closeHostConnection();667if (serverSocket != null) {668try {669serverSocket.close();670} catch (IOException e) {671logger.complain("Caught IOException while closing ServerSocket:\n\t"672+ e);673}674serverSocket = null;675}676}677678/**679* Close thread by closing all connections and waiting680* foor thread finished.681*682* @see #closeConnection()683*/684public synchronized void close() {685if (closed) {686return;687}688closeHostConnection();689if (servingThread != null) {690servingThread.close();691servingThread = null;692}693waitForThread(THREAD_TIMEOUT);694closeConnection();695closed = true;696logger.trace(TRACE_LEVEL_THREADS, "ListeningThread closed");697}698699} // ListeningThread700701///////// Thread working with a communication channel //////////702703/**704* An internal thread for handling each connection from a test705* on remote host. It reads requests from test and starts separate706* <code>LaunchingThread</code> to execute each request.707*708* @see LaunchingThread709*/710private static class ServingThread extends Thread {711private volatile boolean shouldStop = false;712private volatile boolean closed = false;713private volatile boolean done = false;714715private ListeningThread owner = null;716private LaunchingThread launchingThread = null;717718private SocketConnection connection = null;719720/**721* Make serving thread with specified input/output connection streams722* and given <code>Listenerthread</code> as an owner.723*724* @param owner owner of this thread725* @param connection established socket connection with test726*/727public ServingThread(ListeningThread owner, SocketConnection connection) {728super("ServingThread");729this.owner = owner;730this.connection = connection;731}732733/**734* Read requests from socket connection and start <code>LaunchingThread</code>735* to perform each requested action.736*/737public void run() {738logger.trace(TRACE_LEVEL_THREADS, "ServingThread: starting handling requests from debugger");739try {740// sending OK(version)741logger.trace(TRACE_LEVEL_ACTIONS, "ServingThread: sending initial OK(VERSION) to debugger");742connection.writeObject(new OK(VERSION));743744// receiving TaskID(id)745logger.trace(TRACE_LEVEL_IO, "ServingThread: waiting for TaskID from debugger");746Object taskID = connection.readObject();747logger.trace(TRACE_LEVEL_IO, "ServingThread: received TaskID from debugger: " + taskID);748if (taskID instanceof TaskID) {749String id = ((TaskID)taskID).id;750owner.taskCount++;751logger.println("[" + owner.taskCount + "/" + owner.owner.totalRequests + "]: " + id);752} else {753throw new Failure("Unexpected TaskID received form debugger: " + taskID);754}755756// starting launching thread757launchingThread = new LaunchingThread(this, connection);758launchingThread.start();759760// receiving and handling requests761while(!(shouldStop || isInterrupted())) {762logger.trace(TRACE_LEVEL_IO, "ServingThread: waiting for request from debugger");763Object request = connection.readObject();764logger.trace(TRACE_LEVEL_IO, "ServingThread: received request from debugger: " + request);765if (request == null) {766logger.display("Connection closed");767break;768} else if (request instanceof Disconnect) {769logger.display("Closing connection by request");770request = null;771break;772} else {773boolean success = false;774long timeToFinish = System.currentTimeMillis() + THREAD_TIMEOUT;775while (System.currentTimeMillis() < timeToFinish) {776if (launchingThread.doneRequest()) {777success = true;778logger.trace(TRACE_LEVEL_ACTIONS, "ServingThread: asking launching thread to handle request: " + request);779launchingThread.handleRequest(request);780break;781}782try {783launchingThread.join(DebugeeBinder.TRY_DELAY);784} catch (InterruptedException e) {785throw new Failure("ServingThread interrupted while waiting for LaunchingThread:\n\t"786+ e);787}788}789if (!success) {790logger.complain("Rejecting request because of being busy:\n" + request);791connection.writeObject(792new RequestFailed("Busy with handling previous request"));793}794}795}796} catch (Exception e) {797e.printStackTrace(log.getOutStream());798logger.complain("Caught exception while handling request:\n\t" + e);799} finally {800logger.trace(TRACE_LEVEL_THREADS, "ServingThread: exiting");801closeConnection();802done = true;803}804}805806/**807* Check if present socket connection is alive.808*/809private boolean isConnectionAlive() {810return (connection != null && connection.isConnected());811}812813/**814* Wait for this thread finished815* for specified timeout or interrupt it.816*817* @param millis timeout in milliseconds818*/819public void waitForThread(long millis) {820shouldStop = true;821waitInterruptThread(this, millis);822}823824/**825* Close socket connection from remote host.826*/827private void closeConnection() {828if (connection != null) {829connection.close();830}831if (launchingThread != null) {832launchingThread.handleRequest(null);833}834}835836/**837* Close thread closing socket connection and838* waiting for thread finished.839*/840public synchronized void close() {841if (closed) {842return;843}844closeConnection();845if (launchingThread != null) {846launchingThread.close();847launchingThread = null;848}849waitForThread(THREAD_TIMEOUT);850closed = true;851logger.trace(TRACE_LEVEL_THREADS, "ServingThread closed");852}853854} // ServingThread855856///////// Thread serving a particular Binder's request //////////857858/**859* An internal thread to execute each request from a test on remote host.860* Requests are coming from ServingThread by invoking handleRequest(Object)861* method.862*/863private static class LaunchingThread extends Thread {864private volatile boolean shouldStop = false;865private volatile boolean closed = false;866public volatile boolean done = false;867868private ServingThread owner = null;869// private ProcessWaitingThread waitingThread = null;870private Process process = null;871872private StreamRedirectingThread stdoutRedirectingThread = null;873private StreamRedirectingThread stderrRedirectingThread = null;874875/** Notification about request occurence. */876private volatile Object notification = new Object();877/** Request to execute. */878private volatile Object request = null;879/** Socket stream to send replies to. */880private SocketConnection connection = null;881882/**883* Make thread for executing requests from a test and884* send reply.885*886* @param owner owner of this thread887* @connection socket connection for sending replies888*/889public LaunchingThread(ServingThread owner, SocketConnection connection) {890super("LaunchingThread");891this.owner = owner;892this.connection = connection;893}894895/**896* Notify this thread that new request has come.897*898* @param request request to execute899*/900public void handleRequest(Object request) {901synchronized (notification) {902this.request = request;903notification.notifyAll();904}905}906907/**908* Check if request has been executed.909*/910public boolean doneRequest() {911return done;912}913914/**915* Wait for request notification from <code>ServingThread</code>916* and execute an action according to the request.917* Request <i>null</code> means thread should finish.918*/919public void run() {920logger.trace(TRACE_LEVEL_THREADS, "LaunchingThread: started to handle request");921done = true;922while (!isInterrupted()) {923// wait for new request notification924logger.trace(TRACE_LEVEL_ACTIONS, "LaunchingThread: waiting for request");925synchronized (notification) {926try {927notification.wait();928} catch (InterruptedException e) {929logger.complain("LaunchingThread interrupted while waiting for request:\n\t"930+ e);931break;932}933}934935// execute the request936try {937logger.trace(TRACE_LEVEL_ACTIONS, "LaunchingThread: handling request: " + request);938if (request == null) {939break;940} else if (request instanceof LaunchDebugee) {941launchDebugee((LaunchDebugee)request);942} else if (request instanceof WaitForDebugee) {943waitForDebugee((WaitForDebugee)request);944} else if (request instanceof DebugeeExitCode) {945debugeeExitCode((DebugeeExitCode)request);946} else if (request instanceof KillDebugee) {947killDebugee((KillDebugee)request);948} else {949String reason = "Unknown request: " + request;950logger.complain(reason);951sendReply(new RequestFailed(reason));952}953} catch (Exception e) {954e.printStackTrace(log.getOutStream());955logger.complain("Caught exception while handling request:\n\t" + e);956}957done = true;958}959done = true;960logger.trace(TRACE_LEVEL_THREADS, "LaunchingThread: exiting");961closeConnection();962}963964/**965* Send given reply to remote test.966*967* @param reply reply object to send968*/969public void sendReply(Object reply) throws IOException {970connection.writeObject(reply);971}972973/**974* Send given output line to remote test.975*976* @param reply wrapper object for output line to send977*/978public void sendStreamMessage(RedirectedStream wrapper) throws IOException {979logger.trace(TRACE_LEVEL_ACTIONS, "Sending output line wrapper to debugger: " + wrapper);980if (connection.isConnected()) {981sendReply(wrapper);982} else {983logger.complain("NOT redirected: " + wrapper.line);984}985}986987/**988* Launch two <code>StreamRedirectingThread</code> threads to redirect989* stdin/stderr output of debuggee VM process via <code>BindServer</code>990* connection.991*992* @param process debuggee VM process993*/994private void launchStreamRedirectors(Process process) {995stdoutRedirectingThread =996new StdoutRedirectingThread(this, process.getInputStream(),997DebugeeProcess.DEBUGEE_STDOUT_LOG_PREFIX);998stdoutRedirectingThread.start();999stderrRedirectingThread =1000new StderrRedirectingThread(this, process.getErrorStream(),1001DebugeeProcess.DEBUGEE_STDERR_LOG_PREFIX);1002stderrRedirectingThread.start();1003}10041005/**1006* Execute request for launching debuggee.1007*1008* @param request request to execute1009*/1010private void launchDebugee(LaunchDebugee request) throws IOException {1011logger.trace(TRACE_LEVEL_ACTIONS, "LaunchDebugee: handle request: " + request);10121013if (process != null) {1014logger.complain("Unable to launch debuggee: process already launched");1015sendReply(new RequestFailed("Debuggee process already launched"));1016return;1017}10181019try {1020String[] cmd = request.cmd;1021cmd[0] = convertPath(cmd[0], request.slash, "TESTED_JAVA_HOME", true);1022for (int i = 1; i < cmd.length; i++) {1023cmd[i] = convertPath(cmd[i], request.slash, "JAVA_ARGS", false);1024}1025String workDir = convertPath(request.workDir, request.slash, "WORKDIR", true);1026String[] classPathes = convertPathes(request.classPathes, request.slash, "CLASSPATH", true);1027String windir = argHandler.getDebugeeWinDir();10281029boolean win = (!(windir == null || windir.equals("")));1030String[] envp = new String[win ? 3 : 1] ;1031envp[0] = "CLASSPATH=" + ArgumentParser.joinArguments(classPathes, "", pathSeparator);1032if (win) {1033envp[1] = "WINDIR=" + windir;1034envp[2] = "SystemRoot=" + windir;1035}10361037logger.display("Setting environment:\n"1038+ " " + ArgumentHandler.joinArguments(envp, "", "\n "));1039logger.display("Setting work dir:\n"1040+ " " + workDir);1041logger.display("Launching debuggee:\n"1042+ " " + ArgumentHandler.joinArguments(cmd, "\""));10431044process = Runtime.getRuntime().exec(cmd, envp, new File(workDir));1045logger.display(" debuggee launched successfully");10461047launchStreamRedirectors(process);1048} catch (Exception e) {1049if (!(e instanceof Failure)) {1050e.printStackTrace(log.getOutStream());1051}1052logger.complain("Caught exception while launching debuggee:\n\t" + e);1053sendReply(new CaughtException(e));1054return;1055}10561057sendReply(new OK());1058}10591060/**1061* Execute request for waiting for debuggee exited.1062*1063* @param request request to execute1064*/1065private void waitForDebugee(WaitForDebugee request) throws IOException {1066logger.trace(TRACE_LEVEL_ACTIONS, "WaitForDebugee: handle request: " + request);10671068if (process == null) {1069String reply = "No debuggee process to wait for";1070logger.complain(reply);1071sendReply(new RequestFailed(reply));1072return;1073}10741075logger.display("Waiting for debuggee to exit");1076/*1077// because timeout is not supported now1078// we do not use separate thread for waiting for process1079// and so following lines are commented out10801081waitingThread = new ProcessWaitingThread();1082logger.trace(TRACE_LEVEL_ACTIONS, "LaunchingThread: starting thread for waiting for debugee process");1083waitingThread.start();1084try {1085waitingThread.join(request.timeout);1086if (waitingThread.isAlive()) {1087String reply = "Timeout exceeded while waiting for debuggee to exit";1088logger.complain(reply);1089waitingThread.interrupt();1090sendReply(socOut, new RequestFailed(reply));1091return;1092}1093} catch (InterruptedException e) {1094e.printStackTrace(log.getOutStream());1095logger.complain("Caught exception while waiting for debuggee:\n\t" + e);1096sendReply(new CaughtException(e));1097return;1098}1099int exitStatus = waitingThread.exitStatus;1100waitingThread = null;1101*/1102int exitStatus;1103try {1104exitStatus = process.waitFor();1105waitForRedirectors(THREAD_TIMEOUT);1106process.destroy();1107} catch (InterruptedException e) {1108e.printStackTrace(log.getOutStream());1109logger.complain("Caught exception while waiting for debuggee process to exit:\n\t"1110+ e);1111sendReply(new CaughtException(e));1112return;1113}1114logger.display(" debuggee exited with exit status: " + exitStatus);1115sendReply(new OK(exitStatus));1116}11171118/**1119* Execute request for returning debuggee exit code.1120*1121* @param request request to execute1122*/1123private void debugeeExitCode(DebugeeExitCode request) throws IOException {1124logger.trace(TRACE_LEVEL_ACTIONS, "DebugeeExitCode: handle request: " + request);11251126if (process == null) {1127String reply = "No debuggee process to get exit code for";1128logger.complain(reply);1129sendReply(new RequestFailed(reply));1130return;1131}11321133int exitStatus = 0;1134try {1135exitStatus = process.exitValue();1136} catch (IllegalThreadStateException e) {1137logger.display("# WARNING: Caught exception while getting exit status of debuggee:\n\t"1138+ e);1139sendReply(new CaughtException(e));1140return;1141}1142logger.trace(TRACE_LEVEL_ACTIONS, "DebugeeExitCode: return debuggee exit status: " + exitStatus);1143sendReply(new OK(exitStatus));1144}11451146/**1147* Execute request for unconditional terminating debuggee process.1148*1149* @param request request to execute1150*/1151private void killDebugee(KillDebugee request) throws IOException {1152logger.trace(TRACE_LEVEL_ACTIONS, "killDebugee: handle request: " + request);11531154if (process == null) {1155String reply = "No debuggee process to kill";1156logger.complain(reply);1157sendReply(new RequestFailed(reply));1158return;1159}11601161logger.trace(TRACE_LEVEL_ACTIONS, "killDebugee: killing debuggee process");1162process.destroy();11631164logger.trace(TRACE_LEVEL_ACTIONS, "killDebugee: debuggee process killed");1165sendReply(new OK());1166}11671168/**1169* Terminate debigee VM process if still alive.1170*/1171private void terminateDebugeeAtExit() {1172if (process != null) {1173logger.trace(TRACE_LEVEL_ACTIONS, "Checking that debuggee process has exited correctly");1174try {1175int value = process.exitValue();1176} catch (IllegalThreadStateException e) {1177logger.complain("Debuggee process has not exited correctly: trying to kill it");1178process.destroy();1179try {1180int value = process.exitValue();1181} catch (IllegalThreadStateException ie) {1182logger.complain("Debuggee process is alive after killing it");1183}1184process = null;1185return;1186}1187logger.trace(TRACE_LEVEL_ACTIONS, "Debuggee process has exited correctly");1188}1189}11901191/**1192* Wait for stream redirecting threads finished1193* for specified timeout.1194*1195* @param millis timeout in milliseconds1196*/1197private void waitForRedirectors(long millis) {1198try {1199if (stdoutRedirectingThread != null) {1200stdoutRedirectingThread.join(millis);1201}1202if (stderrRedirectingThread != null) {1203stderrRedirectingThread.join(millis);1204}1205} catch (InterruptedException e) {1206e.printStackTrace(log.getOutStream());1207logger.complain("Caught exception while waiting for debuggee process exited:\n\t"1208+ e);1209}1210}12111212/**1213* Wait for this thread finished1214* for specified timeout or interrupt it.1215*1216* @param millis timeout in milliseconds1217*/1218public void waitForThread(long millis) {1219shouldStop = true;1220handleRequest(null);1221waitInterruptThread(this, millis);1222}12231224/**1225* Close connection with debuggee.1226*/1227public void closeConnection() {1228// no connections to close1229}12301231/**1232* Close thread by closing all connections with debuggee,1233* finishing all redirectors and wait for thread finished.1234*/1235public synchronized void close() {1236if (closed) {1237return;1238}1239closeConnection();1240terminateDebugeeAtExit();1241if (stdoutRedirectingThread != null) {1242stdoutRedirectingThread.close();1243stdoutRedirectingThread = null;1244}1245if (stderrRedirectingThread != null) {1246stderrRedirectingThread.close();1247stderrRedirectingThread = null;1248}1249waitForThread(THREAD_TIMEOUT);1250closed = true;1251logger.trace(TRACE_LEVEL_THREADS, "LaunchingThread closed");1252}12531254/**1255* An inner thread for waiting for debuggee process exited1256* and saving its exit status. (currently not used)1257*/1258/*1259private class ProcessWaitingThread extends Thread {1260int exitStatus = 0;12611262ProcessWaitingThread() {1263super("ProcessWaitingThread");1264}12651266public void run() {1267logger.trace(TRACE_LEVEL_THREADS, "ProcessWaitingThread: starting waiting for process");1268try {1269exitStatus = process.waitFor();1270} catch (InterruptedException e) {1271e.printStackTrace(log.getOutStream());1272logger.complain("Caught exception while waiting for debuggee process:\n\t"1273+ e);1274}1275logger.trace(TRACE_LEVEL_ACTIONS, "ProcessWaitingThread: process finished with status: " + exitStatus);1276logger.trace(TRACE_LEVEL_THREADS, "ProcessWaitingThread: exiting");1277}12781279public synchronized void close() {1280logger.trace(TRACE_LEVEL_THREADS, "ProcessWaitingThread closed");1281}12821283} // ProcessWaitingThread1284*/1285} // LaunchingThread12861287///////// Redirecting threads /////////12881289/**1290* An abstract base class for internal threads which redirects stderr/stdout1291* output from debuggee process via <code>BindServer</code> connection.1292* <p>1293* Two derived classes will redirect <i>stderr</i> or </i>stdout</i> stream1294* by enwrapping stream line by <code>DebugeeStderr</code> or1295* <code>DebugeeStderr</code> objects. They should implement only one1296* abstract method <code>enwrapLine(String)</code> to make the difference.1297*/1298public static abstract class StreamRedirectingThread extends Thread {1299private volatile boolean shouldStop = false;1300private volatile boolean closed = false;13011302private LaunchingThread owner = null;13031304private BufferedReader bin = null;1305private String prefix = null;13061307/**1308* Make a thread to enwrap and redirect lines from specified1309* input stream with given prefix.1310*1311* @param owner owner of this thread1312* @param is input stream to redirect lines from1313* @param prefix prefix to add to each line1314*/1315public StreamRedirectingThread(LaunchingThread owner, InputStream is, String prefix) {1316super("StreamRedirectingThread");1317this.prefix = prefix;1318this.owner = owner;1319bin = new BufferedReader(new InputStreamReader(is));1320}13211322/**1323* Read lines from an input stream, enwrap them, and send to remote1324* test via <code>BindServer</code> connection.1325*/1326public void run() {1327logger.trace(TRACE_LEVEL_THREADS, "StreamRedirectingThread: starting redirect output stream");1328try {1329String line;1330logger.trace(TRACE_LEVEL_IO, "StreamRedirectingThread: waiting for line from debuggee output");1331while(!shouldStop) {1332line = bin.readLine();1333if (line == null)1334break;1335owner.sendStreamMessage(enwrapLine(prefix + line));1336}1337} catch (EOFException e) {1338logger.display("Debuggee output stream closed by process");1339} catch (IOException e) {1340e.printStackTrace(log.getOutStream());1341logger.display("# WARNING: Connection to debuggee output stream aborted:\n\t" + e);1342} catch (Exception e) {1343e.printStackTrace(log.getOutStream());1344logger.complain("Caught exception while redirecting debuggee output stream:\n\t"1345+ e);1346}1347logger.trace(TRACE_LEVEL_THREADS, "StreamRedirectingThread: exiting");1348closeConnection();1349}13501351/**1352* Envrap output line by the appropriate wrapper.1353* @param line line to enwrap1354*/1355protected abstract RedirectedStream enwrapLine(String line);13561357/**1358* Wait for this thread finished or interrupt it.1359*1360* @param millis timeout in milliseconds1361*/1362public void waitForThread(long millis) {1363shouldStop = true;1364waitInterruptThread(this, millis);1365}13661367/**1368* Close redirected process output stream.1369*/1370public void closeConnection() {1371if (closed) {1372return;1373}1374if (bin != null) {1375try {1376bin.close();1377} catch (IOException e) {1378e.printStackTrace(log.getOutStream());1379logger.complain("Caught exception while closing debuggee output stream:\n\t"1380+ e);1381}1382bin = null;1383}1384closed = true;1385logger.trace(TRACE_LEVEL_THREADS, "StreamRedirectingThread closed");1386}13871388/**1389* Close thread by waiting redirected stream closed1390* and finish the thread.1391*/1392public synchronized void close() {1393if (closed) {1394return;1395}1396waitForThread(THREAD_TIMEOUT);1397closeConnection();1398closed = true;1399logger.trace(TRACE_LEVEL_THREADS, "StreamRedirectingThread closed");1400}14011402} // StreamRedirectingThread14031404/**1405* Particalar case of <code>StreamRedirectingThread</code> to redirect1406* <i>stderr</i> stream by enwrapping lines into <code>DebugeeStderr</code>1407* objects.1408*/1409private static class StderrRedirectingThread extends StreamRedirectingThread {14101411/**1412* Make a thread to redirect <i>stderr</i> output stream.1413*/1414StderrRedirectingThread(LaunchingThread owner, InputStream is, String prefix) {1415super(owner, is, prefix);1416setName("StderrRedirectingThread");1417}14181419/**1420* Enwrap given line into <code>DebugeeStderr</code> object.1421*/1422protected RedirectedStream enwrapLine(String line) {1423return new DebugeeStderr(line);1424}14251426}14271428/**1429* Particalar case of <code>StreamRedirectingThread</code> to redirect1430* <i>stdout</i> stream by enwrapping lines into <code>DebugeeStdout</code>1431* objects.1432*/1433private static class StdoutRedirectingThread extends StreamRedirectingThread {14341435/**1436* Make a thread to redirect <i>stdout</i> output stream.1437*/1438StdoutRedirectingThread(LaunchingThread owner, InputStream is, String prefix) {1439super(owner, is, prefix);1440setName("StdoutRedirectingThread");1441}14421443/**1444* Enwrap given line into <code>DebugeeStdout</code> object.1445*/1446protected RedirectedStream enwrapLine(String line) {1447return new DebugeeStdout(line);1448}14491450}14511452///////// BinderServer's packets //////////14531454/**1455* Base serializable object to transmit request or reply1456* via <code>BindServer</code> connection.1457*/1458public static class Packet implements Serializable {}14591460///////// Binder's requests //////////14611462/**1463* Base class to represent request to <code>BindServer</code>.1464*/1465public static abstract class Request extends Packet {}14661467/**1468* This class implements task identification command.1469*/1470public static class TaskID extends Request {1471public String id;14721473public TaskID(String id) {1474this.id = id;1475}14761477public String toString() {1478return "TaskID: id=" + id;1479}1480}14811482/**1483* This class implements a request for launching a debugee.1484*/1485public static class LaunchDebugee extends Request {1486public String slash; // slash symbol used on debugger host1487public String[] cmd; // command line arguments as seen on debugger host1488public String workDir; // path to working directory as seen on debugger host1489public String[] classPathes; // list of class pathes as seen on debugger host14901491public LaunchDebugee(String[] cmd, String slash, String workDir,1492String[] pathes, String[] classPathes,1493String[] libPathes) {1494this.cmd = cmd;1495this.slash = slash;1496this.workDir = workDir;1497this.classPathes = classPathes;1498}14991500public String toString() {1501return "LaunchDebugee:"1502+ "\n\tcommand=" + ArgumentParser.joinArguments(cmd, "\"")1503+ "\n\tWORKDIR=" + workDir1504+ "\n\tCLASSPATH=" + ArgumentParser.joinArguments(classPathes, "", ":")1505+ "\n\tslash=" + slash;1506}1507}15081509/**1510* This class implements a request for waiting for debugee1511* termination.1512*/1513public static class WaitForDebugee extends Request {1514public long timeout = 0; // timeout in minutes for waiting15151516public WaitForDebugee(long value) {1517timeout = value;1518}15191520public String toString() {1521return "WaitForDebugee: timeout=" + timeout;1522}1523}15241525/**1526* This class implements a request for exit code of1527* debugee process.1528*/1529public static class DebugeeExitCode extends Request {1530public String toString() {1531return "SebugeeExitCode";1532}1533}15341535/**1536* This class implements a request for killing debugee process.1537*/1538public static class KillDebugee extends Request {1539public String toString() {1540return "KillDebugee";1541}1542}15431544/**1545* This class implements a request to disconnect connection with test.1546*/1547public static class Disconnect extends Request {1548public String toString() {1549return "Disconnect";1550}1551}15521553///////// BindServer's responses //////////15541555/**1556* Base class to represent response from <code>BindServer</code>.1557*/1558public static abstract class Response extends Packet {}15591560/**1561* This class implements a response that a previoulsy received1562* request has been successfully performed.1563*/1564public static class OK extends Response {1565public long info = BindServer.VERSION; // optional additional info15661567public OK() {1568}15691570public OK(long value) {1571info = value;1572}15731574public String toString() {1575return "OK(" + info + ")";1576}1577}15781579/**1580* This class implements a response that the BindServer is1581* unable to serve a previoulsy received request.1582*/1583public static class RequestFailed extends Response {1584public String reason; // the short explanation of failure15851586public RequestFailed(String reason) {1587this.reason = reason;1588}15891590public String toString() {1591return "RequestFailed(" + reason + ")";1592}1593}15941595/**1596* This class implements a response that the BindServer is1597* unable to serve a previoulsy received request because of1598* caught exception.1599*/1600public static class CaughtException extends RequestFailed {1601public CaughtException(Exception cause) {1602super("Caught exception: " + cause);1603}1604}16051606///////// Wrappers for redirected messages //////////16071608/**1609* Base class to represent wrappers for redirected streams.1610*/1611public static class RedirectedStream extends Packet {1612public String line; // line containing line from redirected stream16131614public RedirectedStream(String str) {1615line = str;1616}16171618public String toString() {1619return "RedirectedStream(" + line + ")";1620}1621}16221623/**1624* This class enwraps redirected line of <i>stdout</i> stream.1625*/1626public static class DebugeeStdout extends RedirectedStream {16271628public DebugeeStdout(String str) {1629super(str);1630}16311632public String toString() {1633return "DebugeeStdout(" + line + ")";1634}1635}16361637/**1638* This class enwraps redirected line of <i>stderr</i> stream.1639*/1640public static class DebugeeStderr extends RedirectedStream {1641public DebugeeStderr(String str) {1642super(str);1643}16441645public String toString() {1646return "DebugeeStderr(" + line + ")";1647}1648}16491650/////// ArgumentHandler for BindServer command line /////////16511652/**1653* This class is used to parse arguments from command line1654* and specified <i>bind-file</i>,1655*/1656private static class ArgumentHandler extends ArgumentParser {16571658protected Properties fileOptions;16591660/**1661* Make parser object for command line arguments.1662*1663* @param args list of command line arguments1664*/1665public ArgumentHandler(String[] args) {1666super(args);1667}16681669/**1670* Check if given command line option is aloowed.1671*1672* @param option option name1673* @param value option value1674*/1675protected boolean checkOption(String option, String value) {1676if (option.equals("bind.file")) {1677// accept any file name1678return true;1679}1680return super.checkOption(option, value);1681}16821683/**1684* Check if all recignized options are compatible.1685*/1686protected void checkOptions() {1687if (getBindFileName() == null) {1688throw new BadOption("Option -bind.file is requred ");1689}1690super.checkOptions();1691}16921693/**1694* Check if value of this option points to a existing directory.1695*1696* @param option option name1697* @param dir option value1698*/1699private void checkDir(String option, String dir) {1700File file = new File(dir);1701if (!file.exists()) {1702throw new BadOption(option + " does not exist: " + dir);1703}1704if (!file.isAbsolute()) {1705throw new BadOption(option + " is not absolute pathname: " + dir);1706}1707if (!file.isDirectory()) {1708throw new BadOption(option + " is not directory: " + dir);1709}1710}17111712/**1713* Check if option from <i>bind-file</i> is allowed.1714*1715* @param option option name1716* @param value option value1717*/1718protected boolean checkAdditionalOption(String option, String value) {17191720if (option.equals("DEBUGGER_HOST")) {1721// accept any hostname1722return true;1723}17241725if (option.equals("BINDSERVER_PORT")) {1726// accept only integer value1727try {1728int port = Integer.parseInt(value);1729} catch (NumberFormatException e) {1730throw new Failure("Not integer value of bind-file option " + option1731+ ": " + value);1732}1733return true;1734}17351736if (option.equals("DEBUGGER_TESTED_JAVA_HOME")1737|| option.equals("DEBUGGER_WORKDIR")1738|| option.equals("DEBUGGER_TESTBASE")) {1739if (value == null || value.equals("")) {1740throw new BadOption("Empty value of bind-file option " + option);1741}1742return true;1743}17441745if (option.equals("DEBUGGEE_TESTED_JAVA_HOME")1746|| option.equals("DEBUGGEE_WORKDIR")1747|| option.equals("DEBUGGEE_TESTBASE")) {1748if (value == null || value.equals("")) {1749throw new BadOption("Empty value of bind-file option " + option);1750}1751checkDir(option, value);1752return true;1753}17541755if (option.equals("DEBUGGEE_WINDIR")) {1756if (!(value == null || value.equals(""))) {1757checkDir(option, value);1758}1759return true;1760}17611762return false;1763}17641765/**1766* Check if all recignized options form <i>bind-file</i> are compatible.1767*/1768protected void checkAdditionalOptions() {17691770if (getDebuggerJavaHome() == null) {1771throw new BadOption("Option DEBUGGER_JAVA_HOME missed from bind-file");1772}1773if (getDebuggerWorkDir() == null) {1774throw new BadOption("Option DEBUGGER_WORKDIR missed from bind-file");1775}1776if (getDebuggerTestbase() == null) {1777throw new BadOption("Option DEBUGGER_TESTBASE missed from bind-file");1778}17791780if (getDebugeeJavaHome() == null) {1781throw new BadOption("Option DEBUGGEE_JAVA_HOME missed from bind-file");1782}1783if (getDebugeeWorkDir() == null) {1784throw new BadOption("Option DEBUGGEE_WORKDIR missed from bind-file");1785}1786if (getDebugeeTestbase() == null) {1787throw new BadOption("Option DEBUGGEE_TESTBASE missed from bind-file");1788}1789}17901791/**1792* Parse options form specified <i>bind-file</i>.1793*/1794protected void parseAdditionalOptions() {1795Enumeration keys = fileOptions.keys();1796while (keys.hasMoreElements()) {1797String option = (String)keys.nextElement();1798String value = fileOptions.getProperty(option);1799if (! checkAdditionalOption(option, value)) {1800throw new BadOption("Unrecognized bind-file option: " + option);1801}1802}1803checkAdditionalOptions();1804}18051806/**1807* Parse all options from command line and specified <i>bind-file</i>.1808*/1809protected void parseArguments() {1810super.parseArguments();1811String fileName = getBindFileName();1812try {1813FileInputStream bindFile = new FileInputStream(fileName);1814fileOptions = new Properties();1815fileOptions.load(bindFile);1816bindFile.close();1817} catch(FileNotFoundException e) {1818throw new BadOption("Unable to open bind-file " + fileName + ": " + e);1819} catch(IOException e) {1820e.printStackTrace(log.getOutStream());1821throw new Failure("Caught exception while reading bind-file:\n" + e);1822}1823parseAdditionalOptions();1824}18251826/** Return name of specified <i>bind-file<i>. */1827public String getBindFileName() {1828return options.getProperty("bind.file");1829}18301831/** Return specified debuggee host name . */1832public String getDebuggerHost() {1833return fileOptions.getProperty("DEBUGGER_HOST", "localhost");1834}18351836/** Return string representation of port number for <code>BindServer<code> connection. */1837public String getBindPort() {1838return fileOptions.getProperty("BINDSERVER_PORT", "9000");1839}18401841/** Return specified port number for <code>BindServer<code> connection. */1842public int getBindPortNumber() {1843try {1844return Integer.parseInt(getBindPort());1845} catch (NumberFormatException e) {1846throw new Failure("Not integer value of BindServer port");1847}1848}18491850/** Return specified path to tested JDK used for debuggee VM. */1851public String getDebugeeJavaHome() {1852return fileOptions.getProperty("DEBUGGEE_TESTED_JAVA_HOME");1853}18541855/** Return specified path to tested JDK used for debugger. */1856public String getDebuggerJavaHome() {1857return fileOptions.getProperty("DEBUGGER_TESTED_JAVA_HOME");1858}18591860/** Return specified path to working dir from debuggee host. */1861public String getDebugeeWorkDir() {1862return fileOptions.getProperty("DEBUGGEE_WORKDIR");1863}18641865/** Return specified path to working dir from debugger host. */1866public String getDebuggerWorkDir() {1867return fileOptions.getProperty("DEBUGGER_WORKDIR");1868}18691870/** Return specified path to testbase dir from debuggee host. */1871public String getDebugeeTestbase() {1872return fileOptions.getProperty("DEBUGGEE_TESTBASE");1873}18741875/** Return specified path to testbase dir from debugger host. */1876public String getDebuggerTestbase() {1877return fileOptions.getProperty("DEBUGGER_TESTBASE");1878}18791880/** Return specified path to system directory on Wimdows platform. */1881public String getDebugeeWinDir() {1882return fileOptions.getProperty("DEBUGGEE_WINDIR");1883}18841885} // ArgumentHandler18861887} // BindServer188818891890