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

Thu, 15 Aug 2013 17:24:35 +0200

author
erikj
date
Thu, 15 Aug 2013 17:24:35 +0200
changeset 1953
71b0089b146f
parent 1504
22e417cdddee
child 2039
0cfd5baa7154
permissions
-rw-r--r--

8015145: Smartjavac needs more flexibility with linking to sources
Reviewed-by: jjg, ohrstrom

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

mercurial