src/share/classes/com/sun/tools/sjavac/server/JavacServer.java

changeset 1570
f91144b7da75
parent 1504
22e417cdddee
child 2039
0cfd5baa7154
equal deleted inserted replaced
1569:475eb15dfdad 1570:f91144b7da75
1 /*
2 * Copyright (c) 2011-2012, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.sun.tools.sjavac.server;
26
27 import java.io.BufferedReader;
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.InputStreamReader;
31 import java.io.PrintWriter;
32 import java.io.FileNotFoundException;
33 import java.net.URI;
34 import java.util.HashSet;
35 import java.util.Set;
36 import java.util.HashMap;
37 import java.util.Map;
38
39 import java.net.InetAddress;
40 import java.net.InetSocketAddress;
41 import java.net.ServerSocket;
42 import java.net.Socket;
43 import java.net.SocketAddress;
44 import java.util.ArrayList;
45 import java.util.Random;
46
47 import com.sun.tools.sjavac.Util;
48 import com.sun.tools.sjavac.ProblemException;
49 import java.io.*;
50 import java.util.*;
51
52 /**
53 * The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
54 *
55 * <p><b>This is NOT part of any supported API. If you write code that depends on this, you do so at your own risk. This code and its internal interfaces are
56 * subject to change or deletion without notice.</b></p>
57 */
58 public class JavacServer {
59 // Responding to this tcp/ip port on localhost.
60
61 private final ServerSocket serverSocket;
62 // The secret cookie shared between server and client through the port file.
63 private final long myCookie;
64 // When the server was started.
65 private long serverStart;
66 // Accumulated build time for all requests, not counting idle time.
67 private long totalBuildTime;
68 // The javac server specific log file.
69 PrintWriter theLog;
70 // The compiler pool that maintains the compiler threads.
71 CompilerPool compilerPool;
72 // For the client, all port files fetched, one per started javac server.
73 // Though usually only one javac server is started by a client.
74 private static Map<String, PortFile> allPortFiles;
75 private static Map<String, Long> maxServerMemory;
76 final static int ERROR_FATAL = -1;
77 final static int ERROR_BUT_TRY_AGAIN = -4712;
78 final static String PROTOCOL_COOKIE_VERSION = "----THE-COOKIE-V2----";
79 final static String PROTOCOL_CWD = "----THE-CWD----";
80 final static String PROTOCOL_ID = "----THE-ID----";
81 final static String PROTOCOL_ARGS = "----THE-ARGS----";
82 final static String PROTOCOL_SOURCES_TO_COMPILE = "----THE-SOURCES-TO-COMPILE----";
83 final static String PROTOCOL_VISIBLE_SOURCES = "----THE-VISIBLE-SOURCES----";
84 final static String PROTOCOL_END = "----THE-END----";
85 final static String PROTOCOL_STDOUT = "----THE-STDOUT----";
86 final static String PROTOCOL_STDERR = "----THE-STDERR----";
87 final static String PROTOCOL_PACKAGE_ARTIFACTS = "----THE-PACKAGE_ARTIFACTS----";
88 final static String PROTOCOL_PACKAGE_DEPENDENCIES = "----THE-PACKAGE_DEPENDENCIES----";
89 final static String PROTOCOL_PACKAGE_PUBLIC_APIS = "----THE-PACKAGE-PUBLIC-APIS----";
90 final static String PROTOCOL_SYSINFO = "----THE-SYSINFO----";
91 final static String PROTOCOL_RETURN_CODE = "----THE-RETURN-CODE----";
92 // Check if the portfile is gone, every 5 seconds.
93 static int CHECK_PORTFILE_INTERVAL = 5;
94 // Wait 2 seconds for response, before giving up on javac server.
95 static int CONNECTION_TIMEOUT = 2;
96 static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 1;
97 static int MAX_NUM_CONNECT_ATTEMPTS = 3;
98
99 /**
100 * Acquire the port file. Synchronized since several threads inside an smart javac wrapper client acquires the same port file at the same time.
101 */
102 private static synchronized PortFile getPortFile(String filename) throws FileNotFoundException {
103 if (allPortFiles == null) {
104 allPortFiles = new HashMap<String, PortFile>();
105 }
106 PortFile pf = allPortFiles.get(filename);
107 if (pf == null) {
108 pf = new PortFile(filename);
109 allPortFiles.put(filename, pf);
110 }
111 return pf;
112 }
113
114 /**
115 * Get the cookie used for this server.
116 */
117 long getCookie() {
118 return myCookie;
119 }
120
121 /**
122 * Get the port used for this server.
123 */
124 int getPort() {
125 return serverSocket.getLocalPort();
126 }
127
128 /**
129 * Sum up the total build time for this javac server.
130 */
131 public void addBuildTime(long inc) {
132 totalBuildTime += inc;
133 }
134
135 /**
136 * Log this message.
137 */
138 public void log(String msg) {
139 if (theLog != null) {
140 theLog.println(msg);
141 } else {
142 System.err.println(msg);
143 }
144 }
145
146 /**
147 * Make sure the log is flushed.
148 */
149 public void flushLog() {
150 if (theLog != null) {
151 theLog.flush();
152 }
153 }
154
155 /**
156 * Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3"
157 * is sent as the settings parameter. Returns 0 on success, -1 on failure.
158 */
159 public static int startServer(String settings, PrintStream err) {
160 try {
161 String portfile = Util.extractStringOption("portfile", settings);
162 // The log file collects more javac server specific log information.
163 String logfile = Util.extractStringOption("logfile", settings);
164 // The stdouterr file collects all the System.out and System.err writes to disk.
165 String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
166 // We could perhaps use System.setOut and setErr here.
167 // But for the moment we rely on the client to spawn a shell where stdout
168 // and stderr are redirected already.
169 // The pool size is a limit the number of concurrent compiler threads used.
170 // The server might use less than these to avoid memory problems.
171 int poolsize = Util.extractIntOption("poolsize", settings);
172 if (poolsize <= 0) {
173 // If not set, default to the number of cores.
174 poolsize = Runtime.getRuntime().availableProcessors();
175 }
176
177 // How many seconds of inactivity will the server accept before quitting?
178 int keepalive = Util.extractIntOption("keepalive", settings);
179 if (keepalive <= 0) {
180 keepalive = 120;
181 }
182 // The port file is locked and the server port and cookie is written into it.
183 PortFile portFile = getPortFile(portfile);
184 JavacServer s;
185
186 synchronized (portFile) {
187 portFile.lock();
188 portFile.getValues();
189 if (portFile.containsPortInfo()) {
190 err.println("Javac server not started because portfile exists!");
191 portFile.unlock();
192 return -1;
193 }
194 s = new JavacServer(poolsize, logfile);
195 portFile.setValues(s.getPort(), s.getCookie());
196 portFile.unlock();
197 }
198
199 // Run the server. Will delete the port file when shutting down.
200 // It will shut down automatically when no new requests have come in
201 // during the last 125 seconds.
202 s.run(portFile, err, keepalive);
203 // The run loop for the server has exited.
204 return 0;
205 } catch (Exception e) {
206 e.printStackTrace(err);
207 return -1;
208 }
209 }
210
211 /**
212 * Dispatch a compilation request to a javac server.
213 *
214 * @param args are the command line args to javac and is allowed to contain source files, @file and other command line options to javac.
215 *
216 * The generated classes, h files and other artifacts from the javac invocation are stored by the javac server to disk.
217 *
218 * @param sources_to_compile The sources to compile.
219 *
220 * @param visibleSources If visible sources has a non zero size, then visible_sources are the only files in the file system that the javac server can see!
221 * (Sources to compile are always visible.) The visible sources are those supplied by the (filtered) -sourcepath
222 *
223 * @param visibleClasses If visible classes for a specific root/jar has a non zero size, then visible_classes are the only class files that the javac server
224 * can see, in that root/jar. It maps from a classpath root or a jar file to the set of visible classes for that root/jar.
225 *
226 * The server return meta data about the build in the following parameters.
227 * @param package_artifacts, map from package name to set of created artifacts for that package.
228 * @param package_dependencies, map from package name to set of packages that it depends upon.
229 * @param package_pubapis, map from package name to unique string identifying its pub api.
230 */
231 public static int useServer(String settings, String[] args,
232 Set<URI> sourcesToCompile,
233 Set<URI> visibleSources,
234 Map<URI, Set<String>> visibleClasses,
235 Map<String, Set<URI>> packageArtifacts,
236 Map<String, Set<String>> packageDependencies,
237 Map<String, String> packagePubapis,
238 SysInfo sysinfo,
239 PrintStream out,
240 PrintStream err) {
241 try {
242 // The id can perhaps be used in the future by the javac server to reuse the
243 // JavaCompiler instance for several compiles using the same id.
244 String id = Util.extractStringOption("id", settings);
245 String portfile = Util.extractStringOption("portfile", settings);
246 String logfile = Util.extractStringOption("logfile", settings);
247 String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
248 String background = Util.extractStringOption("background", settings);
249 if (background == null || !background.equals("false")) {
250 background = "true";
251 }
252 // The sjavac option specifies how the server part of sjavac is spawned.
253 // If you have the experimental sjavac in your path, you are done. If not, you have
254 // to point to a com.sun.tools.sjavac.Main that supports --startserver
255 // for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main
256 String sjavac = Util.extractStringOption("sjavac", settings);
257 int poolsize = Util.extractIntOption("poolsize", settings);
258 int keepalive = Util.extractIntOption("keepalive", settings);
259
260 if (keepalive <= 0) {
261 // Default keepalive for server is 120 seconds.
262 // I.e. it will accept 120 seconds of inactivity before quitting.
263 keepalive = 120;
264 }
265 if (portfile == null) {
266 err.println("No portfile was specified!");
267 return -1;
268 }
269 if (logfile == null) {
270 logfile = portfile + ".javaclog";
271 }
272 if (stdouterrfile == null) {
273 stdouterrfile = portfile + ".stdouterr";
274 }
275 // Default to sjavac and hope it is in the path.
276 if (sjavac == null) {
277 sjavac = "sjavac";
278 }
279
280 int attempts = 0;
281 int rc = -1;
282 do {
283 PortFile port_file = getPortFile(portfile);
284 synchronized (port_file) {
285 port_file.lock();
286 port_file.getValues();
287 port_file.unlock();
288 }
289 if (!port_file.containsPortInfo()) {
290 String cmd = fork(sjavac, port_file.getFilename(), logfile, poolsize, keepalive, err, stdouterrfile, background);
291
292 if (background.equals("true") && !port_file.waitForValidValues()) {
293 // Ouch the server did not start! Lets print its stdouterrfile and the command used.
294 printFailedAttempt(cmd, stdouterrfile, err);
295 // And give up.
296 return -1;
297 }
298 }
299 rc = connectAndCompile(port_file, id, args, sourcesToCompile, visibleSources,
300 packageArtifacts, packageDependencies, packagePubapis, sysinfo,
301 out, err);
302 // Try again until we manage to connect. Any error after that
303 // will cause the compilation to fail.
304 if (rc == ERROR_BUT_TRY_AGAIN) {
305 // We could not connect to the server. Try again.
306 attempts++;
307 try {
308 Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS);
309 } catch (InterruptedException e) {
310 }
311 }
312 } while (rc == ERROR_BUT_TRY_AGAIN && attempts < MAX_NUM_CONNECT_ATTEMPTS);
313 return rc;
314 } catch (Exception e) {
315 e.printStackTrace(err);
316 return -1;
317 }
318 }
319
320 private static void printFailedAttempt(String cmd, String f, PrintStream err) {
321 err.println("---- Failed to start javac server with this command -----");
322 err.println(cmd);
323 try {
324 BufferedReader in = new BufferedReader(new FileReader(f));
325 err.println("---- stdout/stderr output from attempt to start javac server -----");
326 for (;;) {
327 String l = in.readLine();
328 if (l == null) {
329 break;
330 }
331 err.println(l);
332 }
333 err.println("------------------------------------------------------------------");
334 } catch (Exception e) {
335 err.println("The stdout/stderr output in file " + f + " does not exist and the server did not start.");
336 }
337 }
338
339 /**
340 * Spawn the server instance.
341 */
342
343 private JavacServer(int poolSize, String logfile) throws IOException {
344 serverStart = System.currentTimeMillis();
345 // Create a server socket on a random port that is bound to the localhost/127.0.0.1 interface.
346 // I.e only local processes can connect to this port.
347 serverSocket = new ServerSocket(0, 128, InetAddress.getByName(null));
348 compilerPool = new CompilerPool(poolSize, this);
349 Random rnd = new Random();
350 myCookie = rnd.nextLong();
351 theLog = new PrintWriter(logfile);
352 log("Javac server started. port=" + getPort() + " date=" + (new java.util.Date()) + " with poolsize=" + poolSize);
353 flushLog();
354 }
355
356 /**
357 * Fork a background process. Returns the command line used that can be printed if something failed.
358 */
359 private static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive,
360 final PrintStream err, String stdouterrfile, String background)
361 throws IOException, ProblemException {
362 if (stdouterrfile != null && stdouterrfile.trim().equals("")) {
363 stdouterrfile = null;
364 }
365 final String startserver = "--startserver:portfile=" + portfile + ",logfile=" + logfile + ",stdouterrfile=" + stdouterrfile + ",poolsize=" + poolsize + ",keepalive="+ keepalive;
366
367 if (background.equals("true")) {
368 sjavac += "%20" + startserver;
369 sjavac = sjavac.replaceAll("%20", " ");
370 sjavac = sjavac.replaceAll("%2C", ",");
371 // If the java/sh/cmd launcher fails the failure will be captured by stdouterr because of the redirection here.
372 String[] cmd = {"/bin/sh", "-c", sjavac + " >> " + stdouterrfile + " 2>&1"};
373 if (!(new File("/bin/sh")).canExecute()) {
374 ArrayList<String> wincmd = new ArrayList<String>();
375 wincmd.add("cmd");
376 wincmd.add("/c");
377 wincmd.add("start");
378 wincmd.add("cmd");
379 wincmd.add("/c");
380 wincmd.add(sjavac + " >> " + stdouterrfile + " 2>&1");
381 cmd = wincmd.toArray(new String[wincmd.size()]);
382 }
383 Process pp = null;
384 try {
385 pp = Runtime.getRuntime().exec(cmd);
386 } catch (Exception e) {
387 e.printStackTrace(err);
388 e.printStackTrace(new PrintWriter(stdouterrfile));
389 }
390 StringBuilder rs = new StringBuilder();
391 for (String s : cmd) {
392 rs.append(s + " ");
393 }
394 return rs.toString();
395 }
396
397 // Do not spawn a background server, instead run it within the same JVM.
398 Thread t = new Thread() {
399 @Override
400 public void run() {
401 try {
402 JavacServer.startServer(startserver, err);
403 } catch (Throwable t) {
404 t.printStackTrace(err);
405 }
406 }
407 };
408 t.start();
409 return "";
410 }
411
412 /**
413 * Expect this key on the next line read from the reader.
414 */
415 private static boolean expect(BufferedReader in, String key) throws IOException {
416 String s = in.readLine();
417 if (s != null && s.equals(key)) {
418 return true;
419 }
420 return false;
421 }
422
423 /**
424 * Make a request to the server only to get the maximum possible heap size to use for compilations.
425 *
426 * @param port_file The port file used to synchronize creation of this server.
427 * @param id The identify of the compilation.
428 * @param out Standard out information.
429 * @param err Standard err information.
430 * @return The maximum heap size in bytes.
431 */
432 public static SysInfo connectGetSysInfo(String serverSettings, PrintStream out, PrintStream err) {
433 SysInfo sysinfo = new SysInfo(-1, -1);
434 String id = Util.extractStringOption("id", serverSettings);
435 String portfile = Util.extractStringOption("portfile", serverSettings);
436 try {
437 PortFile pf = getPortFile(portfile);
438 useServer(serverSettings, new String[0],
439 new HashSet<URI>(),
440 new HashSet<URI>(),
441 new HashMap<URI, Set<String>>(),
442 new HashMap<String, Set<URI>>(),
443 new HashMap<String, Set<String>>(),
444 new HashMap<String, String>(),
445 sysinfo, out, err);
446 } catch (Exception e) {
447 e.printStackTrace(err);
448 }
449 return sysinfo;
450 }
451
452 /**
453 * Connect and compile using the javac server settings and the args. When using more advanced features, the sources_to_compile and visible_sources are
454 * supplied to the server and meta data is returned in package_artifacts, package_dependencies and package_pubapis.
455 */
456 private static int connectAndCompile(PortFile portFile, String id, String[] args,
457 Set<URI> sourcesToCompile,
458 Set<URI> visibleSources,
459 Map<String, Set<URI>> packageArtifacts,
460 Map<String, Set<String>> packageDependencies,
461 Map<String, String> packagePublicApis,
462 SysInfo sysinfo,
463 PrintStream out,
464 PrintStream err) {
465 int rc = -3;
466 try {
467 int port = portFile.getPort();
468 if (port == 0) {
469 return ERROR_BUT_TRY_AGAIN;
470 }
471 long cookie = portFile.getCookie();
472
473 // Acquire the localhost/127.0.0.1 address.
474 InetAddress addr = InetAddress.getByName(null);
475 SocketAddress sockaddr = new InetSocketAddress(addr, port);
476 Socket sock = new Socket();
477 int timeoutMs = CONNECTION_TIMEOUT * 1000;
478 try {
479 sock.connect(sockaddr, timeoutMs);
480 } catch (java.net.ConnectException e) {
481 err.println("Could not connect to javac server found in portfile: " + portFile.getFilename() + " " + e);
482 return ERROR_BUT_TRY_AGAIN;
483 }
484 if (!sock.isConnected()) {
485 err.println("Could not connect to javac server found in portfile: " + portFile.getFilename());
486 return ERROR_BUT_TRY_AGAIN;
487 }
488 BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
489 PrintWriter sockout = new PrintWriter(sock.getOutputStream());
490
491 sockout.println(PROTOCOL_COOKIE_VERSION);
492 sockout.println("" + cookie);
493 sockout.println(PROTOCOL_CWD);
494 sockout.println(System.getProperty("user.dir"));
495 sockout.println(PROTOCOL_ID);
496 sockout.println(id);
497 sockout.println(PROTOCOL_ARGS);
498 for (String s : args) {
499 StringBuffer buf = new StringBuffer();
500 String[] paths = s.split(File.pathSeparator);
501 int c = 0;
502 for (String path : paths) {
503 File f = new File(path);
504 if (f.isFile() || f.isDirectory()) {
505 buf.append(f.getAbsolutePath());
506 c++;
507 if (c < paths.length) {
508 buf.append(File.pathSeparator);
509 }
510 } else {
511 buf = new StringBuffer(s);
512 break;
513 }
514 }
515 sockout.println(buf.toString());
516 }
517 sockout.println(PROTOCOL_SOURCES_TO_COMPILE);
518 for (URI uri : sourcesToCompile) {
519 sockout.println(uri.toString());
520 }
521 sockout.println(PROTOCOL_VISIBLE_SOURCES);
522 for (URI uri : visibleSources) {
523 sockout.println(uri.toString());
524 }
525 sockout.println(PROTOCOL_END);
526 sockout.flush();
527
528 StringBuffer stdout = new StringBuffer();
529 StringBuffer stderr = new StringBuffer();
530
531 if (!expect(in, PROTOCOL_STDOUT)) {
532 return ERROR_FATAL;
533 }
534 // Load stdout
535 for (;;) {
536 String l = in.readLine();
537 if (l == null) {
538 return ERROR_FATAL;
539 }
540 if (l.equals(PROTOCOL_STDERR)) {
541 break;
542 }
543 stdout.append(l);
544 stdout.append('\n');
545 }
546 // Load stderr
547 for (;;) {
548 String l = in.readLine();
549 if (l == null) {
550 return ERROR_FATAL;
551 }
552 if (l.equals(PROTOCOL_PACKAGE_ARTIFACTS)) {
553 break;
554 }
555 stderr.append(l);
556 stderr.append('\n');
557 }
558 // Load the package artifacts
559 Set<URI> lastUriSet = null;
560 for (;;) {
561 String l = in.readLine();
562 if (l == null) {
563 return ERROR_FATAL;
564 }
565 if (l.equals(PROTOCOL_PACKAGE_DEPENDENCIES)) {
566 break;
567 }
568 if (l.length() > 1 && l.charAt(0) == '+') {
569 String pkg = l.substring(1);
570 lastUriSet = new HashSet<URI>();
571 packageArtifacts.put(pkg, lastUriSet);
572 } else if (l.length() > 1 && lastUriSet != null) {
573 lastUriSet.add(new URI(l.substring(1)));
574 }
575 }
576 // Load package dependencies
577 Set<String> lastPackageSet = null;
578 for (;;) {
579 String l = in.readLine();
580 if (l == null) {
581 return ERROR_FATAL;
582 }
583 if (l.equals(PROTOCOL_PACKAGE_PUBLIC_APIS)) {
584 break;
585 }
586 if (l.length() > 1 && l.charAt(0) == '+') {
587 String pkg = l.substring(1);
588 lastPackageSet = new HashSet<String>();
589 packageDependencies.put(pkg, lastPackageSet);
590 } else if (l.length() > 1 && lastPackageSet != null) {
591 lastPackageSet.add(l.substring(1));
592 }
593 }
594 // Load package pubapis
595 Map<String, StringBuffer> tmp = new HashMap<String, StringBuffer>();
596 StringBuffer lastPublicApi = null;
597 for (;;) {
598 String l = in.readLine();
599 if (l == null) {
600 return ERROR_FATAL;
601 }
602 if (l.equals(PROTOCOL_SYSINFO)) {
603 break;
604 }
605 if (l.length() > 1 && l.charAt(0) == '+') {
606 String pkg = l.substring(1);
607 lastPublicApi = new StringBuffer();
608 tmp.put(pkg, lastPublicApi);
609 } else if (l.length() > 1 && lastPublicApi != null) {
610 lastPublicApi.append(l.substring(1));
611 lastPublicApi.append("\n");
612 }
613 }
614 for (String p : tmp.keySet()) {
615 assert (packagePublicApis.get(p) == null);
616 String api = tmp.get(p).toString();
617 packagePublicApis.put(p, api);
618 }
619 // Now reading the max memory possible.
620 for (;;) {
621 String l = in.readLine();
622 if (l == null) {
623 return ERROR_FATAL;
624 }
625 if (l.equals(PROTOCOL_RETURN_CODE)) {
626 break;
627 }
628 if (l.startsWith("num_cores=") && sysinfo != null) {
629 sysinfo.numCores = Integer.parseInt(l.substring(10));
630 }
631 if (l.startsWith("max_memory=") && sysinfo != null) {
632 sysinfo.maxMemory = Long.parseLong(l.substring(11));
633 }
634 }
635 String l = in.readLine();
636 if (l == null) {
637 err.println("No return value from the server!");
638 return ERROR_FATAL;
639 }
640 rc = Integer.parseInt(l);
641 out.print(stdout);
642 err.print(stderr);
643 } catch (Exception e) {
644 e.printStackTrace(err);
645 }
646 return rc;
647 }
648
649 /**
650 * Run the server thread until it exits. Either because of inactivity or because the port file has been deleted by someone else, or overtaken by some other
651 * javac server.
652 */
653 private void run(PortFile portFile, PrintStream err, int keepalive) {
654 boolean fileDeleted = false;
655 long timeSinceLastCompile;
656 try {
657 // Every 5 second (check_portfile_interval) we test if the portfile has disappeared => quit
658 // Or if the last request was finished more than 125 seconds ago => quit
659 // 125 = seconds_of_inactivity_before_shutdown+check_portfile_interval
660 serverSocket.setSoTimeout(CHECK_PORTFILE_INTERVAL*1000);
661 for (;;) {
662 try {
663 Socket s = serverSocket.accept();
664 CompilerThread ct = compilerPool.grabCompilerThread();
665 ct.setSocket(s);
666 compilerPool.execute(ct);
667 flushLog();
668 } catch (java.net.SocketTimeoutException e) {
669 if (compilerPool.numActiveRequests() > 0) {
670 // Never quit while there are active requests!
671 continue;
672 }
673 // If this is the timeout after the portfile
674 // has been deleted by us. Then we truly stop.
675 if (fileDeleted) {
676 log("Quitting because of "+(keepalive+CHECK_PORTFILE_INTERVAL)+" seconds of inactivity!");
677 break;
678 }
679 // Check if the portfile is still there.
680 if (!portFile.exists()) {
681 // Time to quit because the portfile was deleted by another
682 // process, probably by the makefile that is done building.
683 log("Quitting because portfile was deleted!");
684 flushLog();
685 break;
686 }
687 // Check if portfile.stop is still there.
688 if (portFile.markedForStop()) {
689 // Time to quit because another process touched the file
690 // server.port.stop to signal that the server should stop.
691 // This is necessary on some operating systems that lock
692 // the port file hard!
693 log("Quitting because a portfile.stop file was found!");
694 portFile.delete();
695 flushLog();
696 break;
697 }
698 // Does the portfile still point to me?
699 if (!portFile.stillMyValues()) {
700 // Time to quit because another build has started.
701 log("Quitting because portfile is now owned by another javac server!");
702 flushLog();
703 break;
704 }
705
706 // Check how long since the last request finished.
707 long diff = System.currentTimeMillis() - compilerPool.lastRequestFinished();
708 if (diff < keepalive * 1000) {
709 // Do not quit if we have waited less than 120 seconds.
710 continue;
711 }
712 // Ok, time to quit because of inactivity. Perhaps the build
713 // was killed and the portfile not cleaned up properly.
714 portFile.delete();
715 fileDeleted = true;
716 log("" + keepalive + " seconds of inactivity quitting in "
717 + CHECK_PORTFILE_INTERVAL + " seconds!");
718 flushLog();
719 // Now we have a second 5 second grace
720 // period where javac remote requests
721 // that have loaded the data from the
722 // recently deleted portfile can connect
723 // and complete their requests.
724 }
725 }
726 } catch (Exception e) {
727 e.printStackTrace(err);
728 e.printStackTrace(theLog);
729 flushLog();
730 } finally {
731 compilerPool.shutdown();
732 }
733 long realTime = System.currentTimeMillis() - serverStart;
734 log("Shutting down.");
735 log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
736 flushLog();
737 }
738
739 public static void cleanup(String... args) {
740 String settings = Util.findServerSettings(args);
741 if (settings == null) return;
742 String portfile = Util.extractStringOption("portfile", settings);
743 String background = Util.extractStringOption("background", settings);
744 if (background != null && background.equals("false")) {
745 // If the server runs within this jvm, then delete the portfile,
746 // since this jvm is about to exit soon.
747 File f = new File(portfile);
748 f.delete();
749 }
750 }
751 }

mercurial