Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/BindServer.java
41161 views
1
/*
2
* Copyright (c) 2001, 2018, 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 java.io.*;
27
import java.net.*;
28
import java.util.*;
29
30
import nsk.share.*;
31
import nsk.share.jpda.*;
32
33
/**
34
* <code>BindServer</code> is an utility to perform JPDA tests
35
* in remote mode across network.
36
* <p>
37
* This utility should be started on remote host. It listens for connection
38
* from JPDA tests and launches debuggee VM on this host.
39
* <p>
40
* <code>BindServer</code> works together with <code>Binder</code> used in
41
* the tests to incapsulate actions required for launching debuggee VM.
42
* See <code>ProcessBinder</code> and <code>DebugeeArgumentHandler</code>
43
* to know how run tests in local or remote mode across network or
44
* on an single host.
45
* <p>
46
* <code>BindServer</code> is started on the debuggee host.
47
* It recognizes following command line options:
48
* <ul>
49
* <li><code>-bind.file=<i>filename</i></code> - configuration file
50
* <li><code>-verbose</code> - print verbose messages
51
* </ul>
52
* <p>
53
* Only required option is <code>-bind.file</code>, which points to the file
54
* where pairs of particular pathes are presented as they are seen from
55
* both hosts along with some other <code>BindServer</code> options.
56
* See <i>execution.html</i> to read more about format of bind-file.
57
*
58
* @see DebugeeBinder
59
* @see DebugeeArgumentHandler
60
*/
61
public class BindServer implements Finalizable {
62
63
/** Version of <code>BindServer</code> implementation. */
64
public static final long VERSION = 2;
65
66
/** Timeout in milliseconds used for waiting for inner threads. */
67
private static long THREAD_TIMEOUT = DebugeeBinder.THREAD_TIMEOUT; // milliseconds
68
69
private static int PASSED = 0;
70
private static int FAILED = 2;
71
private static int JCK_BASE = 95;
72
73
private static int TRACE_LEVEL_PACKETS = 10;
74
private static int TRACE_LEVEL_THREADS = 20;
75
private static int TRACE_LEVEL_ACTIONS = 30;
76
private static int TRACE_LEVEL_SOCKETS = 40;
77
private static int TRACE_LEVEL_IO = 50;
78
79
private static String pathSeparator = System.getProperty("path.separator");
80
private static String fileSeparator = System.getProperty("file.separator");
81
82
private static char pathSeparatorChar = pathSeparator.charAt(0);
83
private static char fileSeparatorChar = fileSeparator.charAt(0);
84
85
private static Log log = null;
86
private static Log.Logger logger = null;
87
private static ArgumentHandler argHandler = null;
88
89
private static String pathConvertions[][] = null;
90
91
private ListeningThread listeningThread = null;
92
93
private int totalRequests = 0;
94
private int acceptedRequests = 0;
95
private int unauthorizedRequests = 0;
96
private int busyRequests = 0;
97
98
/**
99
* Start <code>BindServer</code> utility from command line.
100
* This method invokes <code>run()</code> and redirects output
101
* to <code>System.err</code>.
102
*
103
* @param argv list of command line arguments
104
*/
105
public static void main (String argv[]) {
106
System.exit(run(argv,System.err) + JCK_BASE);
107
}
108
109
/**
110
* Start <code>BindServer</code> utility from JCK-compatible
111
* environment.
112
*
113
* @param argv list of command line arguments
114
* @param out outpur stream for log messages
115
*
116
* @return FAILED if error occured
117
* PASSED oterwise
118
*/
119
public static int run(String argv[], PrintStream out) {
120
return new BindServer().runIt(argv, out);
121
}
122
123
/**
124
* Perform execution of <code>BindServer</code>.
125
* This method handles command line arguments, starts seperate
126
* thread for listening connection from test on remote host,
127
* and waits for command "exit" from a user.
128
* Finally it closes all conections and prints connections
129
* statiscs.
130
*
131
* @param argv list of command line arguments
132
* @param out outpur stream for log messages
133
*
134
* @return FAILED if error occured
135
* PASSED oterwise
136
*/
137
private int runIt(String argv[], PrintStream out) {
138
try {
139
argHandler = new ArgumentHandler(argv);
140
} catch (ArgumentHandler.BadOption e) {
141
out.println("ERROR: " + e.getMessage());
142
return FAILED;
143
}
144
145
if (argHandler.getArguments().length > 0) {
146
out.println("ERROR: " + "Too many positional arguments in command line");
147
return FAILED;
148
}
149
150
log = new Log(out, argHandler);
151
log.enableErrorsSummary(false);
152
log.enableVerboseOnError(false);
153
logger = new Log.Logger(log, "");
154
155
Finalizer bindFinalizer = new Finalizer(this);
156
bindFinalizer.activate();
157
158
logger.trace(TRACE_LEVEL_THREADS, "BindServer: starting main thread");
159
160
logger.display("Listening to port: " + argHandler.getBindPortNumber());
161
logger.display("Authorizing host: " + argHandler.getDebuggerHost());
162
163
pathConvertions = new String[][] {
164
{ "TESTED_JAVA_HOME", argHandler.getDebuggerJavaHome(), argHandler.getDebugeeJavaHome() },
165
{ "TESTBASE", argHandler.getDebuggerTestbase(), argHandler.getDebugeeTestbase() },
166
{ "WORKDIR", argHandler.getDebuggerWorkDir(), argHandler.getDebugeeWorkDir() }
167
};
168
169
logger.display("Translating pathes:");
170
for (int i = 0; i < pathConvertions.length; i++) {
171
logger.display(pathConvertions[i][0] + ":" +"\n"
172
+ " " + pathConvertions[i][1] + "\n"
173
+ " =>" + "\n"
174
+ " " + pathConvertions[i][2]);
175
}
176
177
String windir = argHandler.getDebugeeWinDir();
178
if (!(windir == null || windir.equals(""))) {
179
logger.display("Using WINDIR: \n"
180
+ " " + argHandler.getDebugeeWinDir());
181
}
182
183
BufferedReader stdIn = new BufferedReader(
184
new InputStreamReader(System.in));
185
186
listeningThread = new ListeningThread(this);
187
listeningThread.bind();
188
listeningThread.start();
189
190
System.out.println("\n"
191
+ "BindServer started" + "\n"
192
+ "Type \"exit\" to shut down BindServer"
193
+ "\n");
194
195
for (;;) {
196
try {
197
String userInput = stdIn.readLine();
198
if (userInput == null || userInput.equals("exit")
199
|| userInput.equals("quit")) {
200
logger.display("Shutting down BindServer");
201
stdIn.close();
202
stdIn = null;
203
break;
204
} else if (userInput.trim().equals("")) {
205
continue;
206
} else {
207
System.out.println("ERROR: Unknown command: " + userInput);
208
}
209
} catch(IOException e) {
210
e.printStackTrace(log.getOutStream());
211
throw new Failure("Caught exception while reading console command:\n\t"
212
+ e);
213
}
214
}
215
216
printSummary(System.out);
217
218
logger.trace(TRACE_LEVEL_THREADS, "BindServer: exiting main thread");
219
try {
220
finalize();
221
} catch (Throwable e) {
222
e.printStackTrace(log.getOutStream());
223
logger.complain("Caught exception while finalization of BindServer:\n\t" + e);
224
}
225
226
return PASSED;
227
}
228
229
/**
230
* Print usefull summary statistics about connections occured.
231
*
232
* @param out output stream for printing statistics
233
*/
234
private void printSummary(PrintStream out) {
235
out.println("\n"
236
+ "Connections summary:" + "\n"
237
+ " Tolal connections: " + totalRequests + "\n"
238
+ " Accepted authorized: " + acceptedRequests + "\n"
239
+ " Rejected unauthorized " + unauthorizedRequests + "\n"
240
+ " Rejected being busy: " + busyRequests + "\n");
241
};
242
243
/**
244
* Check if given <code>path</code> starts with the specified prefix taking
245
* into account difference between <code>slashChar<code> used in <code>path</code>
246
* and <code>fileSeparatorChar</code> used in <code>prefix</code>.
247
*
248
* @param path path to check
249
* @param prefix prefix to compare with
250
* @param slashChar file separator used in <code>path</code>
251
*/
252
private static boolean checkPathPrefix(String path, String prefix, char slashChar) {
253
int prefixLength = prefix.length();
254
if (prefixLength > path.length()) {
255
return false;
256
}
257
for (int i = 0; i < prefixLength; i++) {
258
char pathChar = path.charAt(i);
259
char prefixChar = prefix.charAt(i);
260
261
if (pathChar != prefixChar) {
262
if ((pathChar == slashChar || pathChar == fileSeparatorChar
263
|| pathChar == '\\' || pathChar == '/')
264
&& (prefixChar == slashChar || prefixChar == fileSeparatorChar
265
|| prefixChar == '\\' || prefixChar == '/')) {
266
// do nothing
267
} else {
268
return false;
269
}
270
}
271
}
272
return true;
273
}
274
275
/**
276
* Convert given path according to list of prefixes from
277
* <code>pathConvertions</code> table.
278
*
279
* @param path path for converting
280
* @param slash file separator used in <code>path</code>
281
* @param name path identifier used for error messages
282
* @param strict force throwing Failure if path is not matched
283
*
284
* @return string with the converted path
285
*
286
* @throws Failure if path does not matched for translation
287
*/
288
private static String convertPath(String path, String slash, String name, boolean strict) {
289
if (path == null)
290
return null;
291
292
char slashChar = slash.charAt(0);
293
294
for (int i = 0; i < pathConvertions.length; i++) {
295
String from = pathConvertions[i][1];
296
String to = pathConvertions[i][2];
297
if (checkPathPrefix(path, from, slashChar)) {
298
return (to + path.substring(from.length())).replace(slashChar, fileSeparatorChar);
299
}
300
}
301
if (strict) {
302
throw new Failure("Path not matched for translation " + name + ":\n\t" + path);
303
}
304
return path;
305
}
306
307
/**
308
* Convert given list of pathes according to list of prefixes from
309
* <code>pathConvertions</code> table by invoking <code>convertPath()</code>
310
* for each path from the list.
311
*
312
* @param list list of pathes for converting
313
* @param slash file separator used in pathes
314
* @param name path identifier used for error messages
315
* @param strict force throwing Failure if some path is not matched
316
*
317
* @return list of strings with converted pathes
318
*
319
* @throws Failure if some path does not matched for translation
320
*
321
* @see #convertPath()
322
*/
323
private static String[] convertPathes(String[] list, String slash, String name, boolean strict) {
324
String[] converted = new String[list.length];
325
for (int i = 0; i < list.length; i++) {
326
converted[i] = convertPath(list[i], slash, name, strict);
327
}
328
return converted;
329
}
330
331
/**
332
* Pause current thread for specified amount of time in milliseconds,
333
* This method uses <code>Object.wait(long)</code> method as a reliable
334
* method which prevents whole VM from suspending.
335
*
336
* @param millisecs - amount of time in milliseconds
337
*/
338
private static void sleeping(int millisecs) {
339
Object obj = new Object();
340
341
synchronized(obj) {
342
try {
343
obj.wait(millisecs);
344
} catch (InterruptedException e) {
345
e.printStackTrace(log.getOutStream());
346
new Failure("Thread interrupted while sleeping:\n\t" + e);
347
}
348
}
349
}
350
351
/**
352
* Wait for given thread finished for specified timeout or
353
* interrupt this thread if not finished.
354
*
355
* @param thr thread to wait for
356
* @param millisecs timeout in milliseconds
357
*/
358
private static void waitInterruptThread(Thread thr, long millisecs) {
359
if (thr != null) {
360
String name = thr.getName();
361
try {
362
if (thr.isAlive()) {
363
logger.trace(TRACE_LEVEL_THREADS, "Waiting for thread: " + name);
364
thr.join(millisecs);
365
}
366
} catch (InterruptedException e) {
367
e.printStackTrace(log.getOutStream());
368
throw new Failure ("Thread interrupted while waiting for another thread:\n\t"
369
+ e);
370
} finally {
371
if (thr.isAlive()) {
372
logger.trace(TRACE_LEVEL_THREADS, "Interrupting not finished thread: " + name);
373
thr.interrupt();
374
/*
375
logger.display("Stopping not finished thread: " + thr);
376
thr.stop();
377
*/
378
}
379
}
380
}
381
}
382
383
/**
384
* Wait for given thread finished for default timeout
385
* <code>THREAD_TIMEOUT</code> and
386
* interrupt this thread if not finished.
387
*
388
* @param thr thread to wait for
389
*/
390
private static void waitInterruptThread(Thread thr) {
391
waitInterruptThread(thr, THREAD_TIMEOUT);
392
}
393
394
/**
395
* Close <code>BindServer</code> by finishing all threads and closing
396
* all conections.
397
*/
398
public synchronized void close() {
399
if (listeningThread != null) {
400
listeningThread.close();
401
listeningThread = null;
402
}
403
}
404
405
/**
406
* Make finalization of <code>BindServer</code> object by invoking
407
* method <code>close()</code>.
408
*
409
* @see #close()
410
*/
411
protected void finalize() throws Throwable {
412
close();
413
super.finalize();
414
}
415
416
/**
417
* Make finalization of <code>BindServer</code> object at program exit
418
* by invoking method <code>finalize()</code>.
419
*
420
* @see #finalize()
421
*/
422
public void finalizeAtExit() throws Throwable {
423
finalize();
424
logger.trace(TRACE_LEVEL_THREADS, "BindServer: finalization at exit completed");
425
}
426
427
///////// Thread listening a TCP/IP socket //////////
428
429
/**
430
* An inner thread used for listening connection from remote test
431
* and starting separate serving thread for each accepted connection.
432
*
433
* @see ServingThread
434
*/
435
private static class ListeningThread extends Thread {
436
private volatile boolean shouldStop = false;
437
private volatile boolean closed = false;
438
439
private BindServer owner = null;
440
private volatile ServingThread servingThread = null;
441
private volatile int taskCount = 0;
442
443
private ObjectOutputStream socOut = null;
444
private ObjectInputStream socIn = null;
445
446
private String autorizedHostName = argHandler.getDebuggerHost();
447
private InetAddress autorizedInetAddresses[] = null;
448
private int port = argHandler.getBindPortNumber();
449
private Socket socket = null;
450
private ServerSocket serverSocket = null;
451
private InetAddress clientInetAddr = null;
452
private String clientHostName = null;
453
private SocketConnection connection = null;
454
455
/**
456
* Make listening thread for given <code>BindServer</code> object
457
* as an owner and bind it to listening port by invoking method
458
* <code>bind()</code>.
459
*
460
* @see bind()
461
*/
462
public ListeningThread(BindServer owner) {
463
super("ListeningThread");
464
this.owner = owner;
465
try {
466
autorizedInetAddresses = InetAddress.getAllByName(autorizedHostName);
467
} catch (UnknownHostException e) {
468
e.printStackTrace(log.getOutStream());
469
throw new Failure("Cannot resolve DEBUGGER_HOST value: " + autorizedHostName);
470
}
471
}
472
473
/**
474
* Bind ServerSocket to the specified port.
475
*/
476
public void bind() {
477
for (int i = 0; !shouldStop && i < DebugeeBinder.CONNECT_TRIES; i++) {
478
try {
479
logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: binding to server socket ...");
480
// length of the queue = 2
481
serverSocket = new ServerSocket(port, 2);
482
// timeout for the ServerSocket.accept()
483
serverSocket.setSoTimeout(DebugeeBinder.CONNECT_TRY_DELAY);
484
logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: socket bound: " + serverSocket);
485
logger.display("Bound to listening port");
486
return;
487
} catch (BindException e) {
488
logger.display("Socket binding try #" + i + " failed:\n\t" + e);
489
sleeping(DebugeeBinder.CONNECT_TRY_DELAY);
490
} catch (IOException e) {
491
e.printStackTrace(log.getOutStream());
492
throw new Failure("Caught exception while binding to socket:\n\t"
493
+ e);
494
}
495
}
496
throw new Failure("Unable to bind to socket after "
497
+ DebugeeBinder.CONNECT_TRIES + " tries");
498
}
499
500
/**
501
* Accept socket connection from authorized remote host and
502
* start separate <code>SrvingThread</code> to handle each connection.
503
* Connection from unauthorized hosts or connections made while
504
* current connection is alive are rejected.
505
*
506
* @see ServingThread
507
* @see #llowConnection()
508
* @see allowServing()
509
*/
510
public void run() {
511
String reply = null;
512
513
logger.trace(TRACE_LEVEL_THREADS, "ListeningThread: started");
514
logger.display("Listening for connection from remote host");
515
while(!(shouldStop || isInterrupted())) {
516
try {
517
try {
518
logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: waiting for connection from test");
519
socket = serverSocket.accept();
520
logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: connection accepted");
521
} catch(InterruptedIOException e) {
522
// logger.trace(TRACE_LEVEL_SOCKETS, "ListeningThread: timeout of waiting for connection from test");
523
continue;
524
}
525
owner.totalRequests++;
526
logger.display("");
527
clientInetAddr = socket.getInetAddress();
528
clientHostName = clientInetAddr.getHostName();
529
logger.display("Connection #" + owner.totalRequests
530
+ " requested from host: " + clientHostName);
531
connection = new SocketConnection(logger, "BindServer");
532
// connection.setPingTimeout(DebugeeBinder.PING_TIMEOUT);
533
connection.setSocket(socket);
534
socket = null;
535
if (allowConnection()) {
536
if (allowServing()) {
537
owner.acceptedRequests++;
538
reply = "host authorized: " + clientHostName;
539
logger.display("Accepting connection #" + owner.acceptedRequests
540
+ ": " + reply);
541
servingThread = new ServingThread(this, connection);
542
servingThread.start();
543
cleanHostConnection();
544
} else {
545
owner.busyRequests++;
546
reply = "BindServer is busy";
547
logger.complain("Rejecting connection #" + owner.busyRequests
548
+ ": " + reply);
549
connection.writeObject(new RequestFailed(reply));
550
closeHostConnection();
551
}
552
} else {
553
owner.unauthorizedRequests++;
554
reply = "host unauthorized: " + clientHostName;
555
logger.complain("Rejecting connection #" + owner.unauthorizedRequests
556
+ ": " + reply);
557
connection.writeObject(new RequestFailed(reply));
558
closeHostConnection();
559
}
560
} catch (Exception e) {
561
logger.complain("Caught exception while accepting connection:\n" + e);
562
e.printStackTrace(log.getOutStream());
563
}
564
}
565
logger.trace(TRACE_LEVEL_THREADS, "ListeningThread: exiting");
566
closeConnection();
567
}
568
569
/**
570
* Check if the connection made is from authorized host.
571
*
572
* @return true if connection is allowed because host authorized
573
* false if connection is rejected because host unauthorized
574
*/
575
private boolean allowConnection() {
576
// check if local host from loopback address
577
if (autorizedHostName.equals("localhost"))
578
return clientInetAddr.isLoopbackAddress();
579
580
// check if equal hostname
581
if (autorizedHostName.equals(clientHostName))
582
return true;
583
584
// check if equal host address
585
for (int i = 0; i < autorizedInetAddresses.length; i++) {
586
if (clientInetAddr.equals(autorizedInetAddresses[i])) {
587
return true;
588
}
589
}
590
return false;
591
}
592
593
/**
594
* Check if no current connection exists or it is dead.
595
* If current connection presents it will be tested by pinging
596
* remote host and aborted if host sends no reply. If an alive
597
* connection exists, new connection will be rejected.
598
*
599
* @return true if no alive connection exists
600
* false otherwise
601
*/
602
private boolean allowServing() {
603
if (servingThread == null) {
604
return true;
605
}
606
if (servingThread.done) {
607
return true;
608
}
609
if (!servingThread.isConnectionAlive()) {
610
logger.display("# WARNING: Previous connection from remote host is dead: aborting connection");
611
servingThread.close();
612
servingThread = null;
613
return true;
614
}
615
616
/*
617
logger.complain("Previous connection from remote host is alive: starting new connection");
618
servingThread = null;
619
return true;
620
*/
621
logger.complain("Previous connection from remote host is alive: reject new connection");
622
return false;
623
}
624
625
/**
626
* Wait for this thread finished
627
* for specified timeout or interrupt it.
628
*
629
* @param millis timeout in milliseconds
630
*/
631
public void waitForThread(long millis) {
632
shouldStop = true;
633
waitInterruptThread(this, millis);
634
}
635
636
/**
637
* Close socket connection from remote host.
638
*/
639
private void closeHostConnection() {
640
if (connection != null) {
641
connection.close();
642
}
643
if (socket != null) {
644
try {
645
socket.close();
646
} catch (IOException e) {
647
logger.complain("Caught IOException while closing socket:\n\t"
648
+ e);
649
}
650
socket = null;
651
}
652
}
653
654
/**
655
* Assign <null> to connection and socket objects
656
* but do not close them.
657
*/
658
private void cleanHostConnection() {
659
connection = null;
660
socket = null;
661
}
662
663
/**
664
* Close all connections and sockets.
665
*/
666
private void closeConnection() {
667
closeHostConnection();
668
if (serverSocket != null) {
669
try {
670
serverSocket.close();
671
} catch (IOException e) {
672
logger.complain("Caught IOException while closing ServerSocket:\n\t"
673
+ e);
674
}
675
serverSocket = null;
676
}
677
}
678
679
/**
680
* Close thread by closing all connections and waiting
681
* foor thread finished.
682
*
683
* @see #closeConnection()
684
*/
685
public synchronized void close() {
686
if (closed) {
687
return;
688
}
689
closeHostConnection();
690
if (servingThread != null) {
691
servingThread.close();
692
servingThread = null;
693
}
694
waitForThread(THREAD_TIMEOUT);
695
closeConnection();
696
closed = true;
697
logger.trace(TRACE_LEVEL_THREADS, "ListeningThread closed");
698
}
699
700
} // ListeningThread
701
702
///////// Thread working with a communication channel //////////
703
704
/**
705
* An internal thread for handling each connection from a test
706
* on remote host. It reads requests from test and starts separate
707
* <code>LaunchingThread</code> to execute each request.
708
*
709
* @see LaunchingThread
710
*/
711
private static class ServingThread extends Thread {
712
private volatile boolean shouldStop = false;
713
private volatile boolean closed = false;
714
private volatile boolean done = false;
715
716
private ListeningThread owner = null;
717
private LaunchingThread launchingThread = null;
718
719
private SocketConnection connection = null;
720
721
/**
722
* Make serving thread with specified input/output connection streams
723
* and given <code>Listenerthread</code> as an owner.
724
*
725
* @param owner owner of this thread
726
* @param connection established socket connection with test
727
*/
728
public ServingThread(ListeningThread owner, SocketConnection connection) {
729
super("ServingThread");
730
this.owner = owner;
731
this.connection = connection;
732
}
733
734
/**
735
* Read requests from socket connection and start <code>LaunchingThread</code>
736
* to perform each requested action.
737
*/
738
public void run() {
739
logger.trace(TRACE_LEVEL_THREADS, "ServingThread: starting handling requests from debugger");
740
try {
741
// sending OK(version)
742
logger.trace(TRACE_LEVEL_ACTIONS, "ServingThread: sending initial OK(VERSION) to debugger");
743
connection.writeObject(new OK(VERSION));
744
745
// receiving TaskID(id)
746
logger.trace(TRACE_LEVEL_IO, "ServingThread: waiting for TaskID from debugger");
747
Object taskID = connection.readObject();
748
logger.trace(TRACE_LEVEL_IO, "ServingThread: received TaskID from debugger: " + taskID);
749
if (taskID instanceof TaskID) {
750
String id = ((TaskID)taskID).id;
751
owner.taskCount++;
752
logger.println("[" + owner.taskCount + "/" + owner.owner.totalRequests + "]: " + id);
753
} else {
754
throw new Failure("Unexpected TaskID received form debugger: " + taskID);
755
}
756
757
// starting launching thread
758
launchingThread = new LaunchingThread(this, connection);
759
launchingThread.start();
760
761
// receiving and handling requests
762
while(!(shouldStop || isInterrupted())) {
763
logger.trace(TRACE_LEVEL_IO, "ServingThread: waiting for request from debugger");
764
Object request = connection.readObject();
765
logger.trace(TRACE_LEVEL_IO, "ServingThread: received request from debugger: " + request);
766
if (request == null) {
767
logger.display("Connection closed");
768
break;
769
} else if (request instanceof Disconnect) {
770
logger.display("Closing connection by request");
771
request = null;
772
break;
773
} else {
774
boolean success = false;
775
long timeToFinish = System.currentTimeMillis() + THREAD_TIMEOUT;
776
while (System.currentTimeMillis() < timeToFinish) {
777
if (launchingThread.doneRequest()) {
778
success = true;
779
logger.trace(TRACE_LEVEL_ACTIONS, "ServingThread: asking launching thread to handle request: " + request);
780
launchingThread.handleRequest(request);
781
break;
782
}
783
try {
784
launchingThread.join(DebugeeBinder.TRY_DELAY);
785
} catch (InterruptedException e) {
786
throw new Failure("ServingThread interrupted while waiting for LaunchingThread:\n\t"
787
+ e);
788
}
789
}
790
if (!success) {
791
logger.complain("Rejecting request because of being busy:\n" + request);
792
connection.writeObject(
793
new RequestFailed("Busy with handling previous request"));
794
}
795
}
796
}
797
} catch (Exception e) {
798
e.printStackTrace(log.getOutStream());
799
logger.complain("Caught exception while handling request:\n\t" + e);
800
} finally {
801
logger.trace(TRACE_LEVEL_THREADS, "ServingThread: exiting");
802
closeConnection();
803
done = true;
804
}
805
}
806
807
/**
808
* Check if present socket connection is alive.
809
*/
810
private boolean isConnectionAlive() {
811
return (connection != null && connection.isConnected());
812
}
813
814
/**
815
* Wait for this thread finished
816
* for specified timeout or interrupt it.
817
*
818
* @param millis timeout in milliseconds
819
*/
820
public void waitForThread(long millis) {
821
shouldStop = true;
822
waitInterruptThread(this, millis);
823
}
824
825
/**
826
* Close socket connection from remote host.
827
*/
828
private void closeConnection() {
829
if (connection != null) {
830
connection.close();
831
}
832
if (launchingThread != null) {
833
launchingThread.handleRequest(null);
834
}
835
}
836
837
/**
838
* Close thread closing socket connection and
839
* waiting for thread finished.
840
*/
841
public synchronized void close() {
842
if (closed) {
843
return;
844
}
845
closeConnection();
846
if (launchingThread != null) {
847
launchingThread.close();
848
launchingThread = null;
849
}
850
waitForThread(THREAD_TIMEOUT);
851
closed = true;
852
logger.trace(TRACE_LEVEL_THREADS, "ServingThread closed");
853
}
854
855
} // ServingThread
856
857
///////// Thread serving a particular Binder's request //////////
858
859
/**
860
* An internal thread to execute each request from a test on remote host.
861
* Requests are coming from ServingThread by invoking handleRequest(Object)
862
* method.
863
*/
864
private static class LaunchingThread extends Thread {
865
private volatile boolean shouldStop = false;
866
private volatile boolean closed = false;
867
public volatile boolean done = false;
868
869
private ServingThread owner = null;
870
// private ProcessWaitingThread waitingThread = null;
871
private Process process = null;
872
873
private StreamRedirectingThread stdoutRedirectingThread = null;
874
private StreamRedirectingThread stderrRedirectingThread = null;
875
876
/** Notification about request occurence. */
877
private volatile Object notification = new Object();
878
/** Request to execute. */
879
private volatile Object request = null;
880
/** Socket stream to send replies to. */
881
private SocketConnection connection = null;
882
883
/**
884
* Make thread for executing requests from a test and
885
* send reply.
886
*
887
* @param owner owner of this thread
888
* @connection socket connection for sending replies
889
*/
890
public LaunchingThread(ServingThread owner, SocketConnection connection) {
891
super("LaunchingThread");
892
this.owner = owner;
893
this.connection = connection;
894
}
895
896
/**
897
* Notify this thread that new request has come.
898
*
899
* @param request request to execute
900
*/
901
public void handleRequest(Object request) {
902
synchronized (notification) {
903
this.request = request;
904
notification.notifyAll();
905
}
906
}
907
908
/**
909
* Check if request has been executed.
910
*/
911
public boolean doneRequest() {
912
return done;
913
}
914
915
/**
916
* Wait for request notification from <code>ServingThread</code>
917
* and execute an action according to the request.
918
* Request <i>null</code> means thread should finish.
919
*/
920
public void run() {
921
logger.trace(TRACE_LEVEL_THREADS, "LaunchingThread: started to handle request");
922
done = true;
923
while (!isInterrupted()) {
924
// wait for new request notification
925
logger.trace(TRACE_LEVEL_ACTIONS, "LaunchingThread: waiting for request");
926
synchronized (notification) {
927
try {
928
notification.wait();
929
} catch (InterruptedException e) {
930
logger.complain("LaunchingThread interrupted while waiting for request:\n\t"
931
+ e);
932
break;
933
}
934
}
935
936
// execute the request
937
try {
938
logger.trace(TRACE_LEVEL_ACTIONS, "LaunchingThread: handling request: " + request);
939
if (request == null) {
940
break;
941
} else if (request instanceof LaunchDebugee) {
942
launchDebugee((LaunchDebugee)request);
943
} else if (request instanceof WaitForDebugee) {
944
waitForDebugee((WaitForDebugee)request);
945
} else if (request instanceof DebugeeExitCode) {
946
debugeeExitCode((DebugeeExitCode)request);
947
} else if (request instanceof KillDebugee) {
948
killDebugee((KillDebugee)request);
949
} else {
950
String reason = "Unknown request: " + request;
951
logger.complain(reason);
952
sendReply(new RequestFailed(reason));
953
}
954
} catch (Exception e) {
955
e.printStackTrace(log.getOutStream());
956
logger.complain("Caught exception while handling request:\n\t" + e);
957
}
958
done = true;
959
}
960
done = true;
961
logger.trace(TRACE_LEVEL_THREADS, "LaunchingThread: exiting");
962
closeConnection();
963
}
964
965
/**
966
* Send given reply to remote test.
967
*
968
* @param reply reply object to send
969
*/
970
public void sendReply(Object reply) throws IOException {
971
connection.writeObject(reply);
972
}
973
974
/**
975
* Send given output line to remote test.
976
*
977
* @param reply wrapper object for output line to send
978
*/
979
public void sendStreamMessage(RedirectedStream wrapper) throws IOException {
980
logger.trace(TRACE_LEVEL_ACTIONS, "Sending output line wrapper to debugger: " + wrapper);
981
if (connection.isConnected()) {
982
sendReply(wrapper);
983
} else {
984
logger.complain("NOT redirected: " + wrapper.line);
985
}
986
}
987
988
/**
989
* Launch two <code>StreamRedirectingThread</code> threads to redirect
990
* stdin/stderr output of debuggee VM process via <code>BindServer</code>
991
* connection.
992
*
993
* @param process debuggee VM process
994
*/
995
private void launchStreamRedirectors(Process process) {
996
stdoutRedirectingThread =
997
new StdoutRedirectingThread(this, process.getInputStream(),
998
DebugeeProcess.DEBUGEE_STDOUT_LOG_PREFIX);
999
stdoutRedirectingThread.start();
1000
stderrRedirectingThread =
1001
new StderrRedirectingThread(this, process.getErrorStream(),
1002
DebugeeProcess.DEBUGEE_STDERR_LOG_PREFIX);
1003
stderrRedirectingThread.start();
1004
}
1005
1006
/**
1007
* Execute request for launching debuggee.
1008
*
1009
* @param request request to execute
1010
*/
1011
private void launchDebugee(LaunchDebugee request) throws IOException {
1012
logger.trace(TRACE_LEVEL_ACTIONS, "LaunchDebugee: handle request: " + request);
1013
1014
if (process != null) {
1015
logger.complain("Unable to launch debuggee: process already launched");
1016
sendReply(new RequestFailed("Debuggee process already launched"));
1017
return;
1018
}
1019
1020
try {
1021
String[] cmd = request.cmd;
1022
cmd[0] = convertPath(cmd[0], request.slash, "TESTED_JAVA_HOME", true);
1023
for (int i = 1; i < cmd.length; i++) {
1024
cmd[i] = convertPath(cmd[i], request.slash, "JAVA_ARGS", false);
1025
}
1026
String workDir = convertPath(request.workDir, request.slash, "WORKDIR", true);
1027
String[] classPathes = convertPathes(request.classPathes, request.slash, "CLASSPATH", true);
1028
String windir = argHandler.getDebugeeWinDir();
1029
1030
boolean win = (!(windir == null || windir.equals("")));
1031
String[] envp = new String[win ? 3 : 1] ;
1032
envp[0] = "CLASSPATH=" + ArgumentParser.joinArguments(classPathes, "", pathSeparator);
1033
if (win) {
1034
envp[1] = "WINDIR=" + windir;
1035
envp[2] = "SystemRoot=" + windir;
1036
}
1037
1038
logger.display("Setting environment:\n"
1039
+ " " + ArgumentHandler.joinArguments(envp, "", "\n "));
1040
logger.display("Setting work dir:\n"
1041
+ " " + workDir);
1042
logger.display("Launching debuggee:\n"
1043
+ " " + ArgumentHandler.joinArguments(cmd, "\""));
1044
1045
process = Runtime.getRuntime().exec(cmd, envp, new File(workDir));
1046
logger.display(" debuggee launched successfully");
1047
1048
launchStreamRedirectors(process);
1049
} catch (Exception e) {
1050
if (!(e instanceof Failure)) {
1051
e.printStackTrace(log.getOutStream());
1052
}
1053
logger.complain("Caught exception while launching debuggee:\n\t" + e);
1054
sendReply(new CaughtException(e));
1055
return;
1056
}
1057
1058
sendReply(new OK());
1059
}
1060
1061
/**
1062
* Execute request for waiting for debuggee exited.
1063
*
1064
* @param request request to execute
1065
*/
1066
private void waitForDebugee(WaitForDebugee request) throws IOException {
1067
logger.trace(TRACE_LEVEL_ACTIONS, "WaitForDebugee: handle request: " + request);
1068
1069
if (process == null) {
1070
String reply = "No debuggee process to wait for";
1071
logger.complain(reply);
1072
sendReply(new RequestFailed(reply));
1073
return;
1074
}
1075
1076
logger.display("Waiting for debuggee to exit");
1077
/*
1078
// because timeout is not supported now
1079
// we do not use separate thread for waiting for process
1080
// and so following lines are commented out
1081
1082
waitingThread = new ProcessWaitingThread();
1083
logger.trace(TRACE_LEVEL_ACTIONS, "LaunchingThread: starting thread for waiting for debugee process");
1084
waitingThread.start();
1085
try {
1086
waitingThread.join(request.timeout);
1087
if (waitingThread.isAlive()) {
1088
String reply = "Timeout exceeded while waiting for debuggee to exit";
1089
logger.complain(reply);
1090
waitingThread.interrupt();
1091
sendReply(socOut, new RequestFailed(reply));
1092
return;
1093
}
1094
} catch (InterruptedException e) {
1095
e.printStackTrace(log.getOutStream());
1096
logger.complain("Caught exception while waiting for debuggee:\n\t" + e);
1097
sendReply(new CaughtException(e));
1098
return;
1099
}
1100
int exitStatus = waitingThread.exitStatus;
1101
waitingThread = null;
1102
*/
1103
int exitStatus;
1104
try {
1105
exitStatus = process.waitFor();
1106
waitForRedirectors(THREAD_TIMEOUT);
1107
process.destroy();
1108
} catch (InterruptedException e) {
1109
e.printStackTrace(log.getOutStream());
1110
logger.complain("Caught exception while waiting for debuggee process to exit:\n\t"
1111
+ e);
1112
sendReply(new CaughtException(e));
1113
return;
1114
}
1115
logger.display(" debuggee exited with exit status: " + exitStatus);
1116
sendReply(new OK(exitStatus));
1117
}
1118
1119
/**
1120
* Execute request for returning debuggee exit code.
1121
*
1122
* @param request request to execute
1123
*/
1124
private void debugeeExitCode(DebugeeExitCode request) throws IOException {
1125
logger.trace(TRACE_LEVEL_ACTIONS, "DebugeeExitCode: handle request: " + request);
1126
1127
if (process == null) {
1128
String reply = "No debuggee process to get exit code for";
1129
logger.complain(reply);
1130
sendReply(new RequestFailed(reply));
1131
return;
1132
}
1133
1134
int exitStatus = 0;
1135
try {
1136
exitStatus = process.exitValue();
1137
} catch (IllegalThreadStateException e) {
1138
logger.display("# WARNING: Caught exception while getting exit status of debuggee:\n\t"
1139
+ e);
1140
sendReply(new CaughtException(e));
1141
return;
1142
}
1143
logger.trace(TRACE_LEVEL_ACTIONS, "DebugeeExitCode: return debuggee exit status: " + exitStatus);
1144
sendReply(new OK(exitStatus));
1145
}
1146
1147
/**
1148
* Execute request for unconditional terminating debuggee process.
1149
*
1150
* @param request request to execute
1151
*/
1152
private void killDebugee(KillDebugee request) throws IOException {
1153
logger.trace(TRACE_LEVEL_ACTIONS, "killDebugee: handle request: " + request);
1154
1155
if (process == null) {
1156
String reply = "No debuggee process to kill";
1157
logger.complain(reply);
1158
sendReply(new RequestFailed(reply));
1159
return;
1160
}
1161
1162
logger.trace(TRACE_LEVEL_ACTIONS, "killDebugee: killing debuggee process");
1163
process.destroy();
1164
1165
logger.trace(TRACE_LEVEL_ACTIONS, "killDebugee: debuggee process killed");
1166
sendReply(new OK());
1167
}
1168
1169
/**
1170
* Terminate debigee VM process if still alive.
1171
*/
1172
private void terminateDebugeeAtExit() {
1173
if (process != null) {
1174
logger.trace(TRACE_LEVEL_ACTIONS, "Checking that debuggee process has exited correctly");
1175
try {
1176
int value = process.exitValue();
1177
} catch (IllegalThreadStateException e) {
1178
logger.complain("Debuggee process has not exited correctly: trying to kill it");
1179
process.destroy();
1180
try {
1181
int value = process.exitValue();
1182
} catch (IllegalThreadStateException ie) {
1183
logger.complain("Debuggee process is alive after killing it");
1184
}
1185
process = null;
1186
return;
1187
}
1188
logger.trace(TRACE_LEVEL_ACTIONS, "Debuggee process has exited correctly");
1189
}
1190
}
1191
1192
/**
1193
* Wait for stream redirecting threads finished
1194
* for specified timeout.
1195
*
1196
* @param millis timeout in milliseconds
1197
*/
1198
private void waitForRedirectors(long millis) {
1199
try {
1200
if (stdoutRedirectingThread != null) {
1201
stdoutRedirectingThread.join(millis);
1202
}
1203
if (stderrRedirectingThread != null) {
1204
stderrRedirectingThread.join(millis);
1205
}
1206
} catch (InterruptedException e) {
1207
e.printStackTrace(log.getOutStream());
1208
logger.complain("Caught exception while waiting for debuggee process exited:\n\t"
1209
+ e);
1210
}
1211
}
1212
1213
/**
1214
* Wait for this thread finished
1215
* for specified timeout or interrupt it.
1216
*
1217
* @param millis timeout in milliseconds
1218
*/
1219
public void waitForThread(long millis) {
1220
shouldStop = true;
1221
handleRequest(null);
1222
waitInterruptThread(this, millis);
1223
}
1224
1225
/**
1226
* Close connection with debuggee.
1227
*/
1228
public void closeConnection() {
1229
// no connections to close
1230
}
1231
1232
/**
1233
* Close thread by closing all connections with debuggee,
1234
* finishing all redirectors and wait for thread finished.
1235
*/
1236
public synchronized void close() {
1237
if (closed) {
1238
return;
1239
}
1240
closeConnection();
1241
terminateDebugeeAtExit();
1242
if (stdoutRedirectingThread != null) {
1243
stdoutRedirectingThread.close();
1244
stdoutRedirectingThread = null;
1245
}
1246
if (stderrRedirectingThread != null) {
1247
stderrRedirectingThread.close();
1248
stderrRedirectingThread = null;
1249
}
1250
waitForThread(THREAD_TIMEOUT);
1251
closed = true;
1252
logger.trace(TRACE_LEVEL_THREADS, "LaunchingThread closed");
1253
}
1254
1255
/**
1256
* An inner thread for waiting for debuggee process exited
1257
* and saving its exit status. (currently not used)
1258
*/
1259
/*
1260
private class ProcessWaitingThread extends Thread {
1261
int exitStatus = 0;
1262
1263
ProcessWaitingThread() {
1264
super("ProcessWaitingThread");
1265
}
1266
1267
public void run() {
1268
logger.trace(TRACE_LEVEL_THREADS, "ProcessWaitingThread: starting waiting for process");
1269
try {
1270
exitStatus = process.waitFor();
1271
} catch (InterruptedException e) {
1272
e.printStackTrace(log.getOutStream());
1273
logger.complain("Caught exception while waiting for debuggee process:\n\t"
1274
+ e);
1275
}
1276
logger.trace(TRACE_LEVEL_ACTIONS, "ProcessWaitingThread: process finished with status: " + exitStatus);
1277
logger.trace(TRACE_LEVEL_THREADS, "ProcessWaitingThread: exiting");
1278
}
1279
1280
public synchronized void close() {
1281
logger.trace(TRACE_LEVEL_THREADS, "ProcessWaitingThread closed");
1282
}
1283
1284
} // ProcessWaitingThread
1285
*/
1286
} // LaunchingThread
1287
1288
///////// Redirecting threads /////////
1289
1290
/**
1291
* An abstract base class for internal threads which redirects stderr/stdout
1292
* output from debuggee process via <code>BindServer</code> connection.
1293
* <p>
1294
* Two derived classes will redirect <i>stderr</i> or </i>stdout</i> stream
1295
* by enwrapping stream line by <code>DebugeeStderr</code> or
1296
* <code>DebugeeStderr</code> objects. They should implement only one
1297
* abstract method <code>enwrapLine(String)</code> to make the difference.
1298
*/
1299
public static abstract class StreamRedirectingThread extends Thread {
1300
private volatile boolean shouldStop = false;
1301
private volatile boolean closed = false;
1302
1303
private LaunchingThread owner = null;
1304
1305
private BufferedReader bin = null;
1306
private String prefix = null;
1307
1308
/**
1309
* Make a thread to enwrap and redirect lines from specified
1310
* input stream with given prefix.
1311
*
1312
* @param owner owner of this thread
1313
* @param is input stream to redirect lines from
1314
* @param prefix prefix to add to each line
1315
*/
1316
public StreamRedirectingThread(LaunchingThread owner, InputStream is, String prefix) {
1317
super("StreamRedirectingThread");
1318
this.prefix = prefix;
1319
this.owner = owner;
1320
bin = new BufferedReader(new InputStreamReader(is));
1321
}
1322
1323
/**
1324
* Read lines from an input stream, enwrap them, and send to remote
1325
* test via <code>BindServer</code> connection.
1326
*/
1327
public void run() {
1328
logger.trace(TRACE_LEVEL_THREADS, "StreamRedirectingThread: starting redirect output stream");
1329
try {
1330
String line;
1331
logger.trace(TRACE_LEVEL_IO, "StreamRedirectingThread: waiting for line from debuggee output");
1332
while(!shouldStop) {
1333
line = bin.readLine();
1334
if (line == null)
1335
break;
1336
owner.sendStreamMessage(enwrapLine(prefix + line));
1337
}
1338
} catch (EOFException e) {
1339
logger.display("Debuggee output stream closed by process");
1340
} catch (IOException e) {
1341
e.printStackTrace(log.getOutStream());
1342
logger.display("# WARNING: Connection to debuggee output stream aborted:\n\t" + e);
1343
} catch (Exception e) {
1344
e.printStackTrace(log.getOutStream());
1345
logger.complain("Caught exception while redirecting debuggee output stream:\n\t"
1346
+ e);
1347
}
1348
logger.trace(TRACE_LEVEL_THREADS, "StreamRedirectingThread: exiting");
1349
closeConnection();
1350
}
1351
1352
/**
1353
* Envrap output line by the appropriate wrapper.
1354
* @param line line to enwrap
1355
*/
1356
protected abstract RedirectedStream enwrapLine(String line);
1357
1358
/**
1359
* Wait for this thread finished or interrupt it.
1360
*
1361
* @param millis timeout in milliseconds
1362
*/
1363
public void waitForThread(long millis) {
1364
shouldStop = true;
1365
waitInterruptThread(this, millis);
1366
}
1367
1368
/**
1369
* Close redirected process output stream.
1370
*/
1371
public void closeConnection() {
1372
if (closed) {
1373
return;
1374
}
1375
if (bin != null) {
1376
try {
1377
bin.close();
1378
} catch (IOException e) {
1379
e.printStackTrace(log.getOutStream());
1380
logger.complain("Caught exception while closing debuggee output stream:\n\t"
1381
+ e);
1382
}
1383
bin = null;
1384
}
1385
closed = true;
1386
logger.trace(TRACE_LEVEL_THREADS, "StreamRedirectingThread closed");
1387
}
1388
1389
/**
1390
* Close thread by waiting redirected stream closed
1391
* and finish the thread.
1392
*/
1393
public synchronized void close() {
1394
if (closed) {
1395
return;
1396
}
1397
waitForThread(THREAD_TIMEOUT);
1398
closeConnection();
1399
closed = true;
1400
logger.trace(TRACE_LEVEL_THREADS, "StreamRedirectingThread closed");
1401
}
1402
1403
} // StreamRedirectingThread
1404
1405
/**
1406
* Particalar case of <code>StreamRedirectingThread</code> to redirect
1407
* <i>stderr</i> stream by enwrapping lines into <code>DebugeeStderr</code>
1408
* objects.
1409
*/
1410
private static class StderrRedirectingThread extends StreamRedirectingThread {
1411
1412
/**
1413
* Make a thread to redirect <i>stderr</i> output stream.
1414
*/
1415
StderrRedirectingThread(LaunchingThread owner, InputStream is, String prefix) {
1416
super(owner, is, prefix);
1417
setName("StderrRedirectingThread");
1418
}
1419
1420
/**
1421
* Enwrap given line into <code>DebugeeStderr</code> object.
1422
*/
1423
protected RedirectedStream enwrapLine(String line) {
1424
return new DebugeeStderr(line);
1425
}
1426
1427
}
1428
1429
/**
1430
* Particalar case of <code>StreamRedirectingThread</code> to redirect
1431
* <i>stdout</i> stream by enwrapping lines into <code>DebugeeStdout</code>
1432
* objects.
1433
*/
1434
private static class StdoutRedirectingThread extends StreamRedirectingThread {
1435
1436
/**
1437
* Make a thread to redirect <i>stdout</i> output stream.
1438
*/
1439
StdoutRedirectingThread(LaunchingThread owner, InputStream is, String prefix) {
1440
super(owner, is, prefix);
1441
setName("StdoutRedirectingThread");
1442
}
1443
1444
/**
1445
* Enwrap given line into <code>DebugeeStdout</code> object.
1446
*/
1447
protected RedirectedStream enwrapLine(String line) {
1448
return new DebugeeStdout(line);
1449
}
1450
1451
}
1452
1453
///////// BinderServer's packets //////////
1454
1455
/**
1456
* Base serializable object to transmit request or reply
1457
* via <code>BindServer</code> connection.
1458
*/
1459
public static class Packet implements Serializable {}
1460
1461
///////// Binder's requests //////////
1462
1463
/**
1464
* Base class to represent request to <code>BindServer</code>.
1465
*/
1466
public static abstract class Request extends Packet {}
1467
1468
/**
1469
* This class implements task identification command.
1470
*/
1471
public static class TaskID extends Request {
1472
public String id;
1473
1474
public TaskID(String id) {
1475
this.id = id;
1476
}
1477
1478
public String toString() {
1479
return "TaskID: id=" + id;
1480
}
1481
}
1482
1483
/**
1484
* This class implements a request for launching a debugee.
1485
*/
1486
public static class LaunchDebugee extends Request {
1487
public String slash; // slash symbol used on debugger host
1488
public String[] cmd; // command line arguments as seen on debugger host
1489
public String workDir; // path to working directory as seen on debugger host
1490
public String[] classPathes; // list of class pathes as seen on debugger host
1491
1492
public LaunchDebugee(String[] cmd, String slash, String workDir,
1493
String[] pathes, String[] classPathes,
1494
String[] libPathes) {
1495
this.cmd = cmd;
1496
this.slash = slash;
1497
this.workDir = workDir;
1498
this.classPathes = classPathes;
1499
}
1500
1501
public String toString() {
1502
return "LaunchDebugee:"
1503
+ "\n\tcommand=" + ArgumentParser.joinArguments(cmd, "\"")
1504
+ "\n\tWORKDIR=" + workDir
1505
+ "\n\tCLASSPATH=" + ArgumentParser.joinArguments(classPathes, "", ":")
1506
+ "\n\tslash=" + slash;
1507
}
1508
}
1509
1510
/**
1511
* This class implements a request for waiting for debugee
1512
* termination.
1513
*/
1514
public static class WaitForDebugee extends Request {
1515
public long timeout = 0; // timeout in minutes for waiting
1516
1517
public WaitForDebugee(long value) {
1518
timeout = value;
1519
}
1520
1521
public String toString() {
1522
return "WaitForDebugee: timeout=" + timeout;
1523
}
1524
}
1525
1526
/**
1527
* This class implements a request for exit code of
1528
* debugee process.
1529
*/
1530
public static class DebugeeExitCode extends Request {
1531
public String toString() {
1532
return "SebugeeExitCode";
1533
}
1534
}
1535
1536
/**
1537
* This class implements a request for killing debugee process.
1538
*/
1539
public static class KillDebugee extends Request {
1540
public String toString() {
1541
return "KillDebugee";
1542
}
1543
}
1544
1545
/**
1546
* This class implements a request to disconnect connection with test.
1547
*/
1548
public static class Disconnect extends Request {
1549
public String toString() {
1550
return "Disconnect";
1551
}
1552
}
1553
1554
///////// BindServer's responses //////////
1555
1556
/**
1557
* Base class to represent response from <code>BindServer</code>.
1558
*/
1559
public static abstract class Response extends Packet {}
1560
1561
/**
1562
* This class implements a response that a previoulsy received
1563
* request has been successfully performed.
1564
*/
1565
public static class OK extends Response {
1566
public long info = BindServer.VERSION; // optional additional info
1567
1568
public OK() {
1569
}
1570
1571
public OK(long value) {
1572
info = value;
1573
}
1574
1575
public String toString() {
1576
return "OK(" + info + ")";
1577
}
1578
}
1579
1580
/**
1581
* This class implements a response that the BindServer is
1582
* unable to serve a previoulsy received request.
1583
*/
1584
public static class RequestFailed extends Response {
1585
public String reason; // the short explanation of failure
1586
1587
public RequestFailed(String reason) {
1588
this.reason = reason;
1589
}
1590
1591
public String toString() {
1592
return "RequestFailed(" + reason + ")";
1593
}
1594
}
1595
1596
/**
1597
* This class implements a response that the BindServer is
1598
* unable to serve a previoulsy received request because of
1599
* caught exception.
1600
*/
1601
public static class CaughtException extends RequestFailed {
1602
public CaughtException(Exception cause) {
1603
super("Caught exception: " + cause);
1604
}
1605
}
1606
1607
///////// Wrappers for redirected messages //////////
1608
1609
/**
1610
* Base class to represent wrappers for redirected streams.
1611
*/
1612
public static class RedirectedStream extends Packet {
1613
public String line; // line containing line from redirected stream
1614
1615
public RedirectedStream(String str) {
1616
line = str;
1617
}
1618
1619
public String toString() {
1620
return "RedirectedStream(" + line + ")";
1621
}
1622
}
1623
1624
/**
1625
* This class enwraps redirected line of <i>stdout</i> stream.
1626
*/
1627
public static class DebugeeStdout extends RedirectedStream {
1628
1629
public DebugeeStdout(String str) {
1630
super(str);
1631
}
1632
1633
public String toString() {
1634
return "DebugeeStdout(" + line + ")";
1635
}
1636
}
1637
1638
/**
1639
* This class enwraps redirected line of <i>stderr</i> stream.
1640
*/
1641
public static class DebugeeStderr extends RedirectedStream {
1642
public DebugeeStderr(String str) {
1643
super(str);
1644
}
1645
1646
public String toString() {
1647
return "DebugeeStderr(" + line + ")";
1648
}
1649
}
1650
1651
/////// ArgumentHandler for BindServer command line /////////
1652
1653
/**
1654
* This class is used to parse arguments from command line
1655
* and specified <i>bind-file</i>,
1656
*/
1657
private static class ArgumentHandler extends ArgumentParser {
1658
1659
protected Properties fileOptions;
1660
1661
/**
1662
* Make parser object for command line arguments.
1663
*
1664
* @param args list of command line arguments
1665
*/
1666
public ArgumentHandler(String[] args) {
1667
super(args);
1668
}
1669
1670
/**
1671
* Check if given command line option is aloowed.
1672
*
1673
* @param option option name
1674
* @param value option value
1675
*/
1676
protected boolean checkOption(String option, String value) {
1677
if (option.equals("bind.file")) {
1678
// accept any file name
1679
return true;
1680
}
1681
return super.checkOption(option, value);
1682
}
1683
1684
/**
1685
* Check if all recignized options are compatible.
1686
*/
1687
protected void checkOptions() {
1688
if (getBindFileName() == null) {
1689
throw new BadOption("Option -bind.file is requred ");
1690
}
1691
super.checkOptions();
1692
}
1693
1694
/**
1695
* Check if value of this option points to a existing directory.
1696
*
1697
* @param option option name
1698
* @param dir option value
1699
*/
1700
private void checkDir(String option, String dir) {
1701
File file = new File(dir);
1702
if (!file.exists()) {
1703
throw new BadOption(option + " does not exist: " + dir);
1704
}
1705
if (!file.isAbsolute()) {
1706
throw new BadOption(option + " is not absolute pathname: " + dir);
1707
}
1708
if (!file.isDirectory()) {
1709
throw new BadOption(option + " is not directory: " + dir);
1710
}
1711
}
1712
1713
/**
1714
* Check if option from <i>bind-file</i> is allowed.
1715
*
1716
* @param option option name
1717
* @param value option value
1718
*/
1719
protected boolean checkAdditionalOption(String option, String value) {
1720
1721
if (option.equals("DEBUGGER_HOST")) {
1722
// accept any hostname
1723
return true;
1724
}
1725
1726
if (option.equals("BINDSERVER_PORT")) {
1727
// accept only integer value
1728
try {
1729
int port = Integer.parseInt(value);
1730
} catch (NumberFormatException e) {
1731
throw new Failure("Not integer value of bind-file option " + option
1732
+ ": " + value);
1733
}
1734
return true;
1735
}
1736
1737
if (option.equals("DEBUGGER_TESTED_JAVA_HOME")
1738
|| option.equals("DEBUGGER_WORKDIR")
1739
|| option.equals("DEBUGGER_TESTBASE")) {
1740
if (value == null || value.equals("")) {
1741
throw new BadOption("Empty value of bind-file option " + option);
1742
}
1743
return true;
1744
}
1745
1746
if (option.equals("DEBUGGEE_TESTED_JAVA_HOME")
1747
|| option.equals("DEBUGGEE_WORKDIR")
1748
|| option.equals("DEBUGGEE_TESTBASE")) {
1749
if (value == null || value.equals("")) {
1750
throw new BadOption("Empty value of bind-file option " + option);
1751
}
1752
checkDir(option, value);
1753
return true;
1754
}
1755
1756
if (option.equals("DEBUGGEE_WINDIR")) {
1757
if (!(value == null || value.equals(""))) {
1758
checkDir(option, value);
1759
}
1760
return true;
1761
}
1762
1763
return false;
1764
}
1765
1766
/**
1767
* Check if all recignized options form <i>bind-file</i> are compatible.
1768
*/
1769
protected void checkAdditionalOptions() {
1770
1771
if (getDebuggerJavaHome() == null) {
1772
throw new BadOption("Option DEBUGGER_JAVA_HOME missed from bind-file");
1773
}
1774
if (getDebuggerWorkDir() == null) {
1775
throw new BadOption("Option DEBUGGER_WORKDIR missed from bind-file");
1776
}
1777
if (getDebuggerTestbase() == null) {
1778
throw new BadOption("Option DEBUGGER_TESTBASE missed from bind-file");
1779
}
1780
1781
if (getDebugeeJavaHome() == null) {
1782
throw new BadOption("Option DEBUGGEE_JAVA_HOME missed from bind-file");
1783
}
1784
if (getDebugeeWorkDir() == null) {
1785
throw new BadOption("Option DEBUGGEE_WORKDIR missed from bind-file");
1786
}
1787
if (getDebugeeTestbase() == null) {
1788
throw new BadOption("Option DEBUGGEE_TESTBASE missed from bind-file");
1789
}
1790
}
1791
1792
/**
1793
* Parse options form specified <i>bind-file</i>.
1794
*/
1795
protected void parseAdditionalOptions() {
1796
Enumeration keys = fileOptions.keys();
1797
while (keys.hasMoreElements()) {
1798
String option = (String)keys.nextElement();
1799
String value = fileOptions.getProperty(option);
1800
if (! checkAdditionalOption(option, value)) {
1801
throw new BadOption("Unrecognized bind-file option: " + option);
1802
}
1803
}
1804
checkAdditionalOptions();
1805
}
1806
1807
/**
1808
* Parse all options from command line and specified <i>bind-file</i>.
1809
*/
1810
protected void parseArguments() {
1811
super.parseArguments();
1812
String fileName = getBindFileName();
1813
try {
1814
FileInputStream bindFile = new FileInputStream(fileName);
1815
fileOptions = new Properties();
1816
fileOptions.load(bindFile);
1817
bindFile.close();
1818
} catch(FileNotFoundException e) {
1819
throw new BadOption("Unable to open bind-file " + fileName + ": " + e);
1820
} catch(IOException e) {
1821
e.printStackTrace(log.getOutStream());
1822
throw new Failure("Caught exception while reading bind-file:\n" + e);
1823
}
1824
parseAdditionalOptions();
1825
}
1826
1827
/** Return name of specified <i>bind-file<i>. */
1828
public String getBindFileName() {
1829
return options.getProperty("bind.file");
1830
}
1831
1832
/** Return specified debuggee host name . */
1833
public String getDebuggerHost() {
1834
return fileOptions.getProperty("DEBUGGER_HOST", "localhost");
1835
}
1836
1837
/** Return string representation of port number for <code>BindServer<code> connection. */
1838
public String getBindPort() {
1839
return fileOptions.getProperty("BINDSERVER_PORT", "9000");
1840
}
1841
1842
/** Return specified port number for <code>BindServer<code> connection. */
1843
public int getBindPortNumber() {
1844
try {
1845
return Integer.parseInt(getBindPort());
1846
} catch (NumberFormatException e) {
1847
throw new Failure("Not integer value of BindServer port");
1848
}
1849
}
1850
1851
/** Return specified path to tested JDK used for debuggee VM. */
1852
public String getDebugeeJavaHome() {
1853
return fileOptions.getProperty("DEBUGGEE_TESTED_JAVA_HOME");
1854
}
1855
1856
/** Return specified path to tested JDK used for debugger. */
1857
public String getDebuggerJavaHome() {
1858
return fileOptions.getProperty("DEBUGGER_TESTED_JAVA_HOME");
1859
}
1860
1861
/** Return specified path to working dir from debuggee host. */
1862
public String getDebugeeWorkDir() {
1863
return fileOptions.getProperty("DEBUGGEE_WORKDIR");
1864
}
1865
1866
/** Return specified path to working dir from debugger host. */
1867
public String getDebuggerWorkDir() {
1868
return fileOptions.getProperty("DEBUGGER_WORKDIR");
1869
}
1870
1871
/** Return specified path to testbase dir from debuggee host. */
1872
public String getDebugeeTestbase() {
1873
return fileOptions.getProperty("DEBUGGEE_TESTBASE");
1874
}
1875
1876
/** Return specified path to testbase dir from debugger host. */
1877
public String getDebuggerTestbase() {
1878
return fileOptions.getProperty("DEBUGGER_TESTBASE");
1879
}
1880
1881
/** Return specified path to system directory on Wimdows platform. */
1882
public String getDebugeeWinDir() {
1883
return fileOptions.getProperty("DEBUGGEE_WINDIR");
1884
}
1885
1886
} // ArgumentHandler
1887
1888
} // BindServer
1889
1890