8147569: Error messages from sjavac server does not always get relayed back to client
Refactored how logging works in sjavac. Reviewed-by: jlahoda
This commit is contained in:
parent
c63980511c
commit
49850dd82f
@ -75,9 +75,7 @@ public class CleanProperties implements Transformer {
|
||||
Map<String, PubApi> dependencyPublicApis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
Writer out,
|
||||
Writer err) {
|
||||
int numCores) {
|
||||
boolean rc = true;
|
||||
for (String pkgName : pkgSrcs.keySet()) {
|
||||
String pkgNameF = pkgName.replace('.',File.separatorChar);
|
||||
|
@ -42,6 +42,8 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.sun.tools.sjavac.comp.CompilationService;
|
||||
import com.sun.tools.sjavac.options.Options;
|
||||
@ -89,9 +91,7 @@ public class CompileJavaPackages implements Transformer {
|
||||
final Map<String, PubApi> dependencyPubapis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
final Writer out,
|
||||
final Writer err) {
|
||||
int numCores) {
|
||||
|
||||
Log.debug("Performing CompileJavaPackages transform...");
|
||||
|
||||
@ -219,7 +219,9 @@ public class CompileJavaPackages implements Transformer {
|
||||
}
|
||||
|
||||
String chunkId = id + "-" + String.valueOf(i);
|
||||
Log log = Log.get();
|
||||
compilationCalls.add(() -> {
|
||||
Log.setLogForCurrentThread(log);
|
||||
CompilationSubResult result = sjavac.compile("n/a",
|
||||
chunkId,
|
||||
args.prepJavacArgs(),
|
||||
@ -227,8 +229,8 @@ public class CompileJavaPackages implements Transformer {
|
||||
cc.srcs,
|
||||
visibleSources);
|
||||
synchronized (lock) {
|
||||
safeWrite(result.stdout, out);
|
||||
safeWrite(result.stderr, err);
|
||||
Util.getLines(result.stdout).forEach(Log::info);
|
||||
Util.getLines(result.stderr).forEach(Log::error);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
@ -246,8 +248,10 @@ public class CompileJavaPackages implements Transformer {
|
||||
subResults.add(fut.get());
|
||||
} catch (ExecutionException ee) {
|
||||
Log.error("Compilation failed: " + ee.getMessage());
|
||||
} catch (InterruptedException ee) {
|
||||
Log.error("Compilation interrupted: " + ee.getMessage());
|
||||
Log.error(ee);
|
||||
} catch (InterruptedException ie) {
|
||||
Log.error("Compilation interrupted: " + ie.getMessage());
|
||||
Log.error(ie);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
@ -292,16 +296,6 @@ public class CompileJavaPackages implements Transformer {
|
||||
return rc;
|
||||
}
|
||||
|
||||
private void safeWrite(String str, Writer w) {
|
||||
if (str.length() > 0) {
|
||||
try {
|
||||
w.write(str);
|
||||
} catch (IOException e) {
|
||||
Log.error("Could not print compilation output.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split up the sources into compile chunks. If old package dependents information
|
||||
* is available, sort the order of the chunks into the most dependent first!
|
||||
|
@ -83,9 +83,7 @@ public class CompileProperties implements Transformer {
|
||||
Map<String, PubApi> dependencyPublicApis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
Writer out,
|
||||
Writer err) {
|
||||
int numCores) {
|
||||
boolean rc = true;
|
||||
for (String pkgName : pkgSrcs.keySet()) {
|
||||
String pkgNameF = Util.toFileSystemPath(pkgName);
|
||||
|
@ -70,9 +70,7 @@ public class CopyFile implements Transformer {
|
||||
Map<String, PubApi> dependencyPubapis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
Writer out,
|
||||
Writer err)
|
||||
int numCores)
|
||||
{
|
||||
boolean rc = true;
|
||||
String dest_filename;
|
||||
|
@ -123,16 +123,11 @@ public class JavacState {
|
||||
// Setup transform that always exist.
|
||||
private CompileJavaPackages compileJavaPackages = new CompileJavaPackages();
|
||||
|
||||
// Where to send stdout and stderr.
|
||||
private Writer out, err;
|
||||
|
||||
// Command line options.
|
||||
private Options options;
|
||||
|
||||
JavacState(Options op, boolean removeJavacState, Writer o, Writer e) {
|
||||
JavacState(Options op, boolean removeJavacState) {
|
||||
options = op;
|
||||
out = o;
|
||||
err = e;
|
||||
numCores = options.getNumCores();
|
||||
theArgs = options.getStateArgsString();
|
||||
binDir = Util.pathToFile(options.getDestDir());
|
||||
@ -294,8 +289,8 @@ public class JavacState {
|
||||
/**
|
||||
* Load a javac_state file.
|
||||
*/
|
||||
public static JavacState load(Options options, Writer out, Writer err) {
|
||||
JavacState db = new JavacState(options, false, out, err);
|
||||
public static JavacState load(Options options) {
|
||||
JavacState db = new JavacState(options, false);
|
||||
Module lastModule = null;
|
||||
Package lastPackage = null;
|
||||
Source lastSource = null;
|
||||
@ -367,22 +362,22 @@ public class JavacState {
|
||||
noFileFound = true;
|
||||
} catch (IOException e) {
|
||||
Log.info("Dropping old javac_state because of errors when reading it.");
|
||||
db = new JavacState(options, true, out, err);
|
||||
db = new JavacState(options, true);
|
||||
foundCorrectVerNr = true;
|
||||
newCommandLine = false;
|
||||
syntaxError = false;
|
||||
}
|
||||
if (foundCorrectVerNr == false && !noFileFound) {
|
||||
Log.info("Dropping old javac_state since it is of an old version.");
|
||||
db = new JavacState(options, true, out, err);
|
||||
db = new JavacState(options, true);
|
||||
} else
|
||||
if (newCommandLine == true && !noFileFound) {
|
||||
Log.info("Dropping old javac_state since a new command line is used!");
|
||||
db = new JavacState(options, true, out, err);
|
||||
db = new JavacState(options, true);
|
||||
} else
|
||||
if (syntaxError == true) {
|
||||
Log.info("Dropping old javac_state since it contains syntax errors.");
|
||||
db = new JavacState(options, true, out, err);
|
||||
db = new JavacState(options, true);
|
||||
}
|
||||
db.prev.calculateDependents();
|
||||
return db;
|
||||
@ -812,9 +807,7 @@ public class JavacState {
|
||||
dependencyPublicApis,
|
||||
0,
|
||||
isIncremental(),
|
||||
numCores,
|
||||
out,
|
||||
err);
|
||||
numCores);
|
||||
if (!r)
|
||||
rc = false;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,11 +26,24 @@
|
||||
package com.sun.tools.sjavac;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utility class only for sjavac logging.
|
||||
* The log level can be set using for example --log=DEBUG on the sjavac command line.
|
||||
*
|
||||
* Logging in sjavac has special requirements when running in server/client
|
||||
* mode. Most of the log messages is generated server-side, but the server
|
||||
* is typically spawned by the client in the background, so the user usually
|
||||
* does not see the server stdout/stderr. For this reason log messages needs
|
||||
* to relayed back to the client that performed the request that generated the
|
||||
* log message. To support this use case this class maintains a per-thread log
|
||||
* instance so that each connected client can have its own instance that
|
||||
* relays messages back to the requesting client.
|
||||
*
|
||||
* On the client-side (or when running sjavac without server-mode) there will
|
||||
* typically just be one Log instance.
|
||||
*
|
||||
* <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.
|
||||
@ -38,61 +51,94 @@ import java.io.Writer;
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
public class Log {
|
||||
private static PrintWriter out, err;
|
||||
|
||||
public final static int WARN = 1;
|
||||
public final static int INFO = 2;
|
||||
public final static int DEBUG = 3;
|
||||
public final static int TRACE = 4;
|
||||
private static int level = WARN;
|
||||
public enum Level {
|
||||
ERROR,
|
||||
WARN,
|
||||
INFO,
|
||||
DEBUG,
|
||||
TRACE;
|
||||
}
|
||||
|
||||
private static Log stdOutErr = new Log(new PrintWriter(System.out), new PrintWriter(System.err));
|
||||
private static ThreadLocal<Log> loggers = new ThreadLocal<>();
|
||||
|
||||
protected PrintWriter err; // Used for error and warning messages
|
||||
protected PrintWriter out; // Used for other messages
|
||||
protected Level level = Level.INFO;
|
||||
|
||||
public Log(Writer out, Writer err) {
|
||||
this.out = out == null ? null : new PrintWriter(out, true);
|
||||
this.err = err == null ? null : new PrintWriter(err, true);
|
||||
}
|
||||
|
||||
public static void setLogForCurrentThread(Log log) {
|
||||
loggers.set(log);
|
||||
}
|
||||
|
||||
public static void setLogLevel(String l) {
|
||||
setLogLevel(Level.valueOf(l.toUpperCase(Locale.US)));
|
||||
}
|
||||
|
||||
public static void setLogLevel(Level l) {
|
||||
get().level = l;
|
||||
}
|
||||
|
||||
static public void trace(String msg) {
|
||||
if (level >= TRACE) {
|
||||
out.println(msg);
|
||||
}
|
||||
log(Level.TRACE, msg);
|
||||
}
|
||||
|
||||
static public void debug(String msg) {
|
||||
if (level >= DEBUG) {
|
||||
out.println(msg);
|
||||
}
|
||||
log(Level.DEBUG, msg);
|
||||
}
|
||||
|
||||
static public void info(String msg) {
|
||||
if (level >= INFO) {
|
||||
out.println(msg);
|
||||
}
|
||||
log(Level.INFO, msg);
|
||||
}
|
||||
|
||||
static public void warn(String msg) {
|
||||
err.println(msg);
|
||||
log(Level.WARN, msg);
|
||||
}
|
||||
|
||||
static public void error(String msg) {
|
||||
err.println(msg);
|
||||
log(Level.ERROR, msg);
|
||||
}
|
||||
|
||||
static public void initializeLog(Writer o, Writer e) {
|
||||
out = new PrintWriter(o);
|
||||
err = new PrintWriter(e);
|
||||
static public void error(Throwable t) {
|
||||
log(Level.ERROR, t);
|
||||
}
|
||||
|
||||
static public void setLogLevel(String l) {
|
||||
switch (l) {
|
||||
case "warn": level = WARN; break;
|
||||
case "info": level = INFO; break;
|
||||
case "debug": level = DEBUG; break;
|
||||
case "trace": level = TRACE; break;
|
||||
default:
|
||||
throw new IllegalArgumentException("No such log level \"" + l + "\"");
|
||||
}
|
||||
static public void log(Level l, String msg) {
|
||||
get().printLogMsg(l, msg);
|
||||
}
|
||||
|
||||
static public boolean isTracing() {
|
||||
return level >= TRACE;
|
||||
public static void debug(Throwable t) {
|
||||
log(Level.DEBUG, t);
|
||||
}
|
||||
|
||||
public static void log(Level l, Throwable t) {
|
||||
StringWriter sw = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(sw, true));
|
||||
log(l, sw.toString());
|
||||
}
|
||||
|
||||
static public boolean isDebugging() {
|
||||
return level >= DEBUG;
|
||||
return get().isLevelLogged(Level.DEBUG);
|
||||
}
|
||||
|
||||
protected boolean isLevelLogged(Level l) {
|
||||
return l.ordinal() <= level.ordinal();
|
||||
}
|
||||
|
||||
public static Log get() {
|
||||
Log log = loggers.get();
|
||||
return log != null ? log : stdOutErr;
|
||||
}
|
||||
|
||||
protected void printLogMsg(Level msgLevel, String msg) {
|
||||
if (isLevelLogged(msgLevel)) {
|
||||
PrintWriter pw = msgLevel.ordinal() <= Level.WARN.ordinal() ? err : out;
|
||||
pw.println(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,9 +95,7 @@ public interface Transformer {
|
||||
Map<String, PubApi> dependencyApis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
Writer out,
|
||||
Writer err);
|
||||
int numCores);
|
||||
|
||||
void setExtra(String e);
|
||||
void setExtra(Options args);
|
||||
|
@ -36,7 +36,9 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Utilities.
|
||||
@ -236,4 +238,10 @@ public class Util {
|
||||
int dotIndex = fileNameStr.indexOf('.');
|
||||
return dotIndex == -1 ? "" : fileNameStr.substring(dotIndex);
|
||||
}
|
||||
|
||||
public static Stream<String> getLines(String str) {
|
||||
return str.isEmpty()
|
||||
? Stream.empty()
|
||||
: Stream.of(str.split(Pattern.quote(System.lineSeparator())));
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public class ClientMain {
|
||||
|
||||
public static int run(String[] args, Writer out, Writer err) {
|
||||
|
||||
Log.initializeLog(out, err);
|
||||
Log.setLogForCurrentThread(new Log(out, err));
|
||||
|
||||
Options options;
|
||||
try {
|
||||
@ -61,6 +61,8 @@ public class ClientMain {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Log.setLogLevel(options.getLogLevel());
|
||||
|
||||
Log.debug("==========================================================");
|
||||
Log.debug("Launching sjavac client with the following parameters:");
|
||||
Log.debug(" " + options.getStateArgsString());
|
||||
@ -81,7 +83,7 @@ public class ClientMain {
|
||||
sjavac = new SjavacImpl();
|
||||
}
|
||||
|
||||
int rc = sjavac.compile(args, out, err);
|
||||
int rc = sjavac.compile(args);
|
||||
|
||||
// If sjavac is running in the foreground we should shut it down at this point
|
||||
if (!useServer)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,6 +32,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@ -40,6 +41,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
import com.sun.tools.sjavac.Util;
|
||||
@ -50,6 +52,8 @@ import com.sun.tools.sjavac.server.PortFile;
|
||||
import com.sun.tools.sjavac.server.Sjavac;
|
||||
import com.sun.tools.sjavac.server.SjavacServer;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
/**
|
||||
* Sjavac implementation that delegates requests to a SjavacServer.
|
||||
*
|
||||
@ -64,8 +68,6 @@ public class SjavacClient implements Sjavac {
|
||||
// JavaCompiler instance for several compiles using the same id.
|
||||
private final String id;
|
||||
private final PortFile portFile;
|
||||
private final String logfile;
|
||||
private final String stdouterrfile;
|
||||
|
||||
// Default keepalive for server is 120 seconds.
|
||||
// I.e. it will accept 120 seconds of inactivity before quitting.
|
||||
@ -102,8 +104,6 @@ public class SjavacClient implements Sjavac {
|
||||
Log.error("Port file inaccessable: " + e);
|
||||
throw e;
|
||||
}
|
||||
logfile = Util.extractStringOption("logfile", serverConf, portfileName + ".javaclog");
|
||||
stdouterrfile = Util.extractStringOption("stdouterrfile", serverConf, portfileName + ".stdouterr");
|
||||
sjavacForkCmd = Util.extractStringOption("sjavac", serverConf, "sjavac");
|
||||
int poolsize = Util.extractIntOption("poolsize", serverConf);
|
||||
keepalive = Util.extractIntOption("keepalive", serverConf, 120);
|
||||
@ -121,7 +121,7 @@ public class SjavacClient implements Sjavac {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compile(String[] args, Writer stdout, Writer stderr) {
|
||||
public int compile(String[] args) {
|
||||
int result = -1;
|
||||
try (Socket socket = tryConnect()) {
|
||||
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
@ -136,32 +136,33 @@ public class SjavacClient implements Sjavac {
|
||||
// Read server response line by line
|
||||
String line;
|
||||
while (null != (line = in.readLine())) {
|
||||
if (!line.contains(":")) {
|
||||
throw new AssertionError("Could not parse protocol line: >>\"" + line + "\"<<");
|
||||
}
|
||||
String[] typeAndContent = line.split(":", 2);
|
||||
String type = typeAndContent[0];
|
||||
String content = typeAndContent[1];
|
||||
switch (type) {
|
||||
case SjavacServer.LINE_TYPE_STDOUT:
|
||||
stdout.write(content);
|
||||
stdout.write('\n');
|
||||
break;
|
||||
case SjavacServer.LINE_TYPE_STDERR:
|
||||
stderr.write(content);
|
||||
stderr.write('\n');
|
||||
break;
|
||||
case SjavacServer.LINE_TYPE_RC:
|
||||
|
||||
try {
|
||||
Log.log(Log.Level.valueOf(type), "[server] " + content);
|
||||
continue;
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Parsing of 'type' as log level failed.
|
||||
}
|
||||
|
||||
if (type.equals(SjavacServer.LINE_TYPE_RC)) {
|
||||
result = Integer.parseInt(content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
Log.error("[CLIENT] Exception caught: " + ioe);
|
||||
Log.error("IOException caught during compilation: " + ioe.getMessage());
|
||||
Log.debug(ioe);
|
||||
result = CompilationSubResult.ERROR_FATAL;
|
||||
ioe.printStackTrace(new PrintWriter(stderr));
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt(); // Restore interrupt
|
||||
Log.error("[CLIENT] compile interrupted.");
|
||||
Log.error("Compilation interrupted.");
|
||||
Log.debug(ie);
|
||||
result = CompilationSubResult.ERROR_FATAL;
|
||||
ie.printStackTrace(new PrintWriter(stderr));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -215,11 +216,8 @@ public class SjavacClient implements Sjavac {
|
||||
// Fork a new server and wait for it to start
|
||||
SjavacClient.fork(sjavacForkCmd,
|
||||
portFile,
|
||||
logfile,
|
||||
poolsize,
|
||||
keepalive,
|
||||
System.err,
|
||||
stdouterrfile);
|
||||
keepalive);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -230,51 +228,53 @@ public class SjavacClient implements Sjavac {
|
||||
/*
|
||||
* Fork a server process process and wait for server to come around
|
||||
*/
|
||||
public static void fork(String sjavacCmd,
|
||||
PortFile portFile,
|
||||
String logfile,
|
||||
int poolsize,
|
||||
int keepalive,
|
||||
final PrintStream err,
|
||||
String stdouterrfile)
|
||||
throws IOException, InterruptedException {
|
||||
public static void fork(String sjavacCmd, PortFile portFile, int poolsize, int keepalive)
|
||||
throws IOException, InterruptedException {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.addAll(Arrays.asList(OptionHelper.unescapeCmdArg(sjavacCmd).split(" ")));
|
||||
cmd.add("--startserver:"
|
||||
+ "portfile=" + portFile.getFilename()
|
||||
+ ",logfile=" + logfile
|
||||
+ ",stdouterrfile=" + stdouterrfile
|
||||
+ ",poolsize=" + poolsize
|
||||
+ ",keepalive="+ keepalive);
|
||||
|
||||
Process p = null;
|
||||
Process serverProcess;
|
||||
Log.info("Starting server. Command: " + String.join(" ", cmd));
|
||||
try {
|
||||
// If the cmd for some reason can't be executed (file not found, or
|
||||
// is not executable) this will throw an IOException with a decent
|
||||
// error message.
|
||||
p = new ProcessBuilder(cmd)
|
||||
.redirectErrorStream(true)
|
||||
.redirectOutput(new File(stdouterrfile))
|
||||
.start();
|
||||
// If the cmd for some reason can't be executed (file is not found,
|
||||
// or is not executable for instance) this will throw an
|
||||
// IOException and p == null.
|
||||
serverProcess = new ProcessBuilder(cmd)
|
||||
.redirectErrorStream(true)
|
||||
.start();
|
||||
} catch (IOException ex) {
|
||||
// Message is typically something like:
|
||||
// Cannot run program "xyz": error=2, No such file or directory
|
||||
Log.error("Failed to create server process: " + ex.getMessage());
|
||||
Log.debug(ex);
|
||||
throw new IOException(ex);
|
||||
}
|
||||
|
||||
// serverProcess != null at this point.
|
||||
try {
|
||||
// Throws an IOException if no valid values materialize
|
||||
portFile.waitForValidValues();
|
||||
|
||||
} catch (IOException ex) {
|
||||
// Log and rethrow exception
|
||||
Log.error("Faild to launch server.");
|
||||
Log.error(" Message: " + ex.getMessage());
|
||||
String rc = p == null || p.isAlive() ? "n/a" : "" + p.exitValue();
|
||||
Log.error(" Server process exit code: " + rc);
|
||||
Log.error("Server log:");
|
||||
Log.error("------- Server log start -------");
|
||||
try (Scanner s = new Scanner(new File(stdouterrfile))) {
|
||||
while (s.hasNextLine())
|
||||
Log.error(s.nextLine());
|
||||
// Process was started, but server failed to initialize. This could
|
||||
// for instance be due to the JVM not finding the server class,
|
||||
// or the server running in to some exception early on.
|
||||
Log.error("Sjavac server failed to initialize: " + ex.getMessage());
|
||||
Log.error("Process output:");
|
||||
Reader serverStdoutStderr = new InputStreamReader(serverProcess.getInputStream());
|
||||
try (BufferedReader br = new BufferedReader(serverStdoutStderr)) {
|
||||
br.lines().forEach(Log::error);
|
||||
}
|
||||
Log.error("------- Server log end ---------");
|
||||
throw ex;
|
||||
Log.error("<End of process output>");
|
||||
try {
|
||||
Log.error("Process exit code: " + serverProcess.exitValue());
|
||||
} catch (IllegalThreadStateException e) {
|
||||
// Server is presumably still running.
|
||||
}
|
||||
throw new IOException("Server failed to initialize: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,15 +25,14 @@
|
||||
|
||||
package com.sun.tools.sjavac.comp;
|
||||
|
||||
import java.io.Writer;
|
||||
import com.sun.tools.sjavac.Log;
|
||||
import com.sun.tools.sjavac.server.Sjavac;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
import com.sun.tools.sjavac.server.Sjavac;
|
||||
|
||||
/**
|
||||
* An sjavac implementation that limits the number of concurrent calls by
|
||||
* wrapping invocations in Callables and delegating them to a FixedThreadPool.
|
||||
@ -55,10 +54,12 @@ public class PooledSjavac implements Sjavac {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compile(String[] args, Writer out, Writer err) {
|
||||
public int compile(String[] args) {
|
||||
Log log = Log.get();
|
||||
try {
|
||||
return pool.submit(() -> {
|
||||
return delegate.compile(args, out, err);
|
||||
Log.setLogForCurrentThread(log);
|
||||
return delegate.compile(args);
|
||||
}).get();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -27,6 +27,7 @@ package com.sun.tools.sjavac.comp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -68,7 +69,7 @@ import javax.tools.JavaFileManager;
|
||||
public class SjavacImpl implements Sjavac {
|
||||
|
||||
@Override
|
||||
public int compile(String[] args, Writer out, Writer err) {
|
||||
public int compile(String[] args) {
|
||||
Options options;
|
||||
try {
|
||||
options = Options.parseArgs(args);
|
||||
@ -77,8 +78,6 @@ public class SjavacImpl implements Sjavac {
|
||||
return RC_FATAL;
|
||||
}
|
||||
|
||||
Log.setLogLevel(options.getLogLevel());
|
||||
|
||||
if (!validateOptions(options))
|
||||
return RC_FATAL;
|
||||
|
||||
@ -100,18 +99,21 @@ public class SjavacImpl implements Sjavac {
|
||||
if (stateDir == null) {
|
||||
// Prepare context. Direct logging to our byte array stream.
|
||||
Context context = new Context();
|
||||
PrintWriter writer = new PrintWriter(err);
|
||||
com.sun.tools.javac.util.Log.preRegister(context, writer);
|
||||
StringWriter strWriter = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(strWriter);
|
||||
com.sun.tools.javac.util.Log.preRegister(context, printWriter);
|
||||
JavacFileManager.preRegister(context);
|
||||
|
||||
// Prepare arguments
|
||||
String[] passThroughArgs = Stream.of(args)
|
||||
.filter(arg -> !arg.startsWith(Option.SERVER.arg))
|
||||
.toArray(String[]::new);
|
||||
|
||||
// Compile
|
||||
com.sun.tools.javac.main.Main compiler = new com.sun.tools.javac.main.Main("javac", writer);
|
||||
Main.Result result = compiler.compile(passThroughArgs, context);
|
||||
Main.Result result = new Main("javac", printWriter).compile(passThroughArgs, context);
|
||||
|
||||
// Process compiler output (which is always errors)
|
||||
printWriter.flush();
|
||||
Util.getLines(strWriter.toString()).forEach(Log::error);
|
||||
|
||||
// Clean up
|
||||
JavaFileManager fileManager = context.get(JavaFileManager.class);
|
||||
@ -126,7 +128,7 @@ public class SjavacImpl implements Sjavac {
|
||||
|
||||
} else {
|
||||
// Load the prev build state database.
|
||||
JavacState javac_state = JavacState.load(options, out, err);
|
||||
JavacState javac_state = JavacState.load(options);
|
||||
|
||||
// Setup the suffix rules from the command line.
|
||||
Map<String, Transformer> suffixRules = new HashMap<>();
|
||||
@ -288,10 +290,12 @@ public class SjavacImpl implements Sjavac {
|
||||
|
||||
return rc[0] ? RC_OK : RC_FATAL;
|
||||
} catch (ProblemException e) {
|
||||
// For instance make file list mismatch.
|
||||
Log.error(e.getMessage());
|
||||
Log.debug(e);
|
||||
return RC_FATAL;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(new PrintWriter(err));
|
||||
Log.error(e);
|
||||
return RC_FATAL;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,6 +25,10 @@
|
||||
|
||||
package com.sun.tools.sjavac.server;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@ -53,8 +57,8 @@ public class IdleResetSjavac implements Sjavac {
|
||||
private TimerTask idlenessTimerTask;
|
||||
|
||||
public IdleResetSjavac(Sjavac delegate,
|
||||
Terminable toShutdown,
|
||||
long idleTimeout) {
|
||||
Terminable toShutdown,
|
||||
long idleTimeout) {
|
||||
this.delegate = delegate;
|
||||
this.toShutdown = toShutdown;
|
||||
this.idleTimeout = idleTimeout;
|
||||
@ -62,10 +66,10 @@ public class IdleResetSjavac implements Sjavac {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compile(String[] args, Writer out, Writer err) {
|
||||
public int compile(String[] args) {
|
||||
startCall();
|
||||
try {
|
||||
return delegate.compile(args, out, err);
|
||||
return delegate.compile(args);
|
||||
} finally {
|
||||
endCall();
|
||||
}
|
||||
@ -95,6 +99,7 @@ public class IdleResetSjavac implements Sjavac {
|
||||
throw new IllegalStateException("Idle timeout already scheduled");
|
||||
idlenessTimerTask = new TimerTask() {
|
||||
public void run() {
|
||||
Log.setLogForCurrentThread(ServerMain.getErrorLog());
|
||||
toShutdown.shutdown("Server has been idle for " + (idleTimeout / 1000) + " seconds.");
|
||||
}
|
||||
};
|
||||
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.sjavac.server;
|
||||
|
||||
import java.io.FilterWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Inserts {@literal prefix} in front of each line written.
|
||||
*
|
||||
* A line is considered to be terminated by any one of a line feed, a carriage
|
||||
* return, or a carriage return followed immediately by a line feed.
|
||||
*/
|
||||
public class LinePrefixFilterWriter extends FilterWriter {
|
||||
|
||||
private final String prefix;
|
||||
private boolean atBeginningOfLine = true;
|
||||
private char lastChar = '\0';
|
||||
|
||||
protected LinePrefixFilterWriter(Writer out, String prefix) {
|
||||
super(out);
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String str, int off, int len) throws IOException {
|
||||
for (int i = 0; i < len; i++) {
|
||||
write(str.charAt(off + i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
for (int i = 0; i < len; i++) {
|
||||
write(cbuf[off + i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int c) throws IOException {
|
||||
if (lastChar == '\r' && c == '\n') {
|
||||
// Second character of CR+LF sequence.
|
||||
// Do nothing. We already started a new line on last character.
|
||||
} else {
|
||||
if (atBeginningOfLine) {
|
||||
super.write(prefix, 0, prefix.length());
|
||||
}
|
||||
super.write(c);
|
||||
atBeginningOfLine = c == '\r' || c == '\n';
|
||||
}
|
||||
lastChar = (char) c;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,6 +25,8 @@
|
||||
|
||||
package com.sun.tools.sjavac.server;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@ -56,8 +58,11 @@ public class PortFileMonitor {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
Log log = Log.get();
|
||||
TimerTask shutdownCheck = new TimerTask() {
|
||||
public void run() {
|
||||
Log.setLogForCurrentThread(log);
|
||||
Log.debug("Checking port file status...");
|
||||
try {
|
||||
if (!portFile.exists()) {
|
||||
// Time to quit because the portfile was deleted by another
|
||||
@ -74,12 +79,11 @@ public class PortFileMonitor {
|
||||
server.shutdown("Quitting because portfile is now owned by another javac server!");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(server.theLog);
|
||||
server.flushLog();
|
||||
Log.error("IOException caught in PortFileMonitor.");
|
||||
Log.debug(e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
e.printStackTrace(server.theLog);
|
||||
server.flushLog();
|
||||
Log.error(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,19 +25,16 @@
|
||||
|
||||
package com.sun.tools.sjavac.server;
|
||||
|
||||
import static com.sun.tools.sjavac.server.SjavacServer.LINE_TYPE_RC;
|
||||
import static com.sun.tools.sjavac.server.SjavacServer.LINE_TYPE_STDERR;
|
||||
import static com.sun.tools.sjavac.server.SjavacServer.LINE_TYPE_STDOUT;
|
||||
import com.sun.tools.sjavac.Log;
|
||||
import com.sun.tools.sjavac.Util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.Socket;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import com.sun.tools.sjavac.AutoFlushWriter;
|
||||
import com.sun.tools.sjavac.Log;
|
||||
import static com.sun.tools.sjavac.server.SjavacServer.LINE_TYPE_RC;
|
||||
|
||||
|
||||
/**
|
||||
@ -56,7 +53,7 @@ import com.sun.tools.sjavac.Log;
|
||||
* This code and its internal interfaces are subject to change or
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
public class RequestHandler implements Runnable {
|
||||
public class RequestHandler extends Thread {
|
||||
|
||||
private final Socket socket;
|
||||
private final Sjavac sjavac;
|
||||
@ -68,9 +65,30 @@ public class RequestHandler implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
|
||||
|
||||
// Set up logging for this thread. Stream back logging messages to
|
||||
// client on the format format "level:msg".
|
||||
Log.setLogForCurrentThread(new Log(out, out) {
|
||||
@Override
|
||||
protected boolean isLevelLogged(Level l) {
|
||||
// Make sure it is up to the client to decide whether or
|
||||
// not this message should be displayed.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printLogMsg(Level msgLevel, String msg) {
|
||||
// Follow sjavac server/client protocol: Send one line
|
||||
// at a time and prefix with message with "level:".
|
||||
Util.getLines(msg)
|
||||
.map(line -> msgLevel + ":" + line)
|
||||
.forEach(line -> super.printLogMsg(msgLevel, line));
|
||||
}
|
||||
});
|
||||
|
||||
// Read argument array
|
||||
int n = Integer.parseInt(in.readLine());
|
||||
String[] args = new String[n];
|
||||
@ -78,23 +96,32 @@ public class RequestHandler implements Runnable {
|
||||
args[i] = in.readLine();
|
||||
}
|
||||
|
||||
// If there has been any internal errors, notify client
|
||||
checkInternalErrorLog();
|
||||
|
||||
// Perform compilation
|
||||
Writer stdout = new LinePrefixFilterWriter(new AutoFlushWriter(out), LINE_TYPE_STDOUT + ":");
|
||||
Writer stderr = new LinePrefixFilterWriter(new AutoFlushWriter(out), LINE_TYPE_STDERR + ":");
|
||||
int rc = sjavac.compile(args, stdout, stderr);
|
||||
stdout.flush();
|
||||
stderr.flush();
|
||||
int rc = sjavac.compile(args);
|
||||
|
||||
// Send return code back to client
|
||||
out.println(LINE_TYPE_RC + ":" + rc);
|
||||
|
||||
// Check for internal errors again.
|
||||
checkInternalErrorLog();
|
||||
} catch (Exception ex) {
|
||||
// Not much to be done at this point. The client side request
|
||||
// code will most likely throw an IOException and the
|
||||
// compilation will fail.
|
||||
StringWriter sw = new StringWriter();
|
||||
ex.printStackTrace(new PrintWriter(sw));
|
||||
Log.error(sw.toString());
|
||||
Log.error(ex);
|
||||
} finally {
|
||||
Log.setLogForCurrentThread(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkInternalErrorLog() {
|
||||
Path errorLog = ServerMain.getErrorLog().getLogDestination();
|
||||
if (errorLog != null) {
|
||||
Log.error("Server has encountered an internal error. See " + errorLog.toAbsolutePath()
|
||||
+ " for details.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,10 +25,20 @@
|
||||
|
||||
package com.sun.tools.sjavac.server;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.FilterWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
import com.sun.tools.sjavac.Log.Level;
|
||||
import com.sun.tools.sjavac.server.log.LazyInitFileLog;
|
||||
import com.sun.tools.sjavac.server.log.LoggingOutputStream;
|
||||
|
||||
import static com.sun.tools.sjavac.Log.Level.ERROR;
|
||||
import static com.sun.tools.sjavac.Log.Level.INFO;
|
||||
|
||||
/**
|
||||
* <p><b>This is NOT part of any supported API.
|
||||
@ -37,20 +47,40 @@ import com.sun.tools.sjavac.Log;
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
public class ServerMain {
|
||||
|
||||
// For logging server internal (non request specific) errors.
|
||||
private static LazyInitFileLog errorLog;
|
||||
|
||||
public static int run(String[] args) {
|
||||
|
||||
Log.initializeLog(new OutputStreamWriter(System.out),
|
||||
new OutputStreamWriter(System.err));
|
||||
// Under normal operation, all logging messages generated server-side
|
||||
// are due to compilation requests. These logging messages should
|
||||
// be relayed back to the requesting client rather than written to the
|
||||
// server log. The only messages that should be written to the server
|
||||
// log (in production mode) should be errors,
|
||||
Log.setLogForCurrentThread(errorLog = new LazyInitFileLog("server.log"));
|
||||
Log.setLogLevel(ERROR); // should be set to ERROR.
|
||||
|
||||
// Make sure no exceptions go under the radar
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
||||
Log.setLogForCurrentThread(errorLog);
|
||||
Log.error(e);
|
||||
});
|
||||
|
||||
// Inevitably someone will try to print messages using System.{out,err}.
|
||||
// Make sure this output also ends up in the log.
|
||||
System.setOut(new PrintStream(new LoggingOutputStream(System.out, INFO, "[stdout] ")));
|
||||
System.setErr(new PrintStream(new LoggingOutputStream(System.err, ERROR, "[stderr] ")));
|
||||
|
||||
// Any options other than --startserver?
|
||||
if (args.length > 1) {
|
||||
System.err.println("When spawning a background server, only a single --startserver argument is allowed.");
|
||||
Log.error("When spawning a background server, only a single --startserver argument is allowed.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int exitCode;
|
||||
try {
|
||||
SjavacServer server = new SjavacServer(args[0], System.err);
|
||||
SjavacServer server = new SjavacServer(args[0]);
|
||||
exitCode = server.startServer();
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
@ -59,4 +89,8 @@ public class ServerMain {
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public static LazyInitFileLog getErrorLog() {
|
||||
return errorLog;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -42,6 +42,6 @@ public interface Sjavac {
|
||||
final static int RC_FATAL = -1;
|
||||
final static int RC_OK = 0;
|
||||
|
||||
int compile(String[] args, Writer stdout, Writer stderr);
|
||||
int compile(String[] args);
|
||||
void shutdown();
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
package com.sun.tools.sjavac.server;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
@ -39,6 +40,7 @@ import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
import com.sun.tools.sjavac.Util;
|
||||
import com.sun.tools.sjavac.client.PortFileInaccessibleException;
|
||||
import com.sun.tools.sjavac.comp.PooledSjavac;
|
||||
@ -54,17 +56,12 @@ import com.sun.tools.sjavac.comp.SjavacImpl;
|
||||
*/
|
||||
public class SjavacServer implements Terminable {
|
||||
|
||||
// Used in protocol to tell the content of each line
|
||||
// Prefix of line containing return code.
|
||||
public final static String LINE_TYPE_RC = "RC";
|
||||
public final static String LINE_TYPE_STDOUT = "STDOUT";
|
||||
public final static String LINE_TYPE_STDERR = "STDERR";
|
||||
|
||||
final private String portfilename;
|
||||
final private String logfile;
|
||||
final private String stdouterrfile;
|
||||
final private int poolsize;
|
||||
final private int keepalive;
|
||||
final private PrintStream err;
|
||||
|
||||
// The secret cookie shared between server and client through the port file.
|
||||
// Used to prevent clients from believing that they are communicating with
|
||||
@ -75,9 +72,6 @@ public class SjavacServer implements Terminable {
|
||||
// Accumulated build time, not counting idle time, used for logging purposes
|
||||
private long totalBuildTime;
|
||||
|
||||
// The javac server specific log file.
|
||||
PrintWriter theLog;
|
||||
|
||||
// The sjavac implementation to delegate requests to
|
||||
Sjavac sjavac;
|
||||
|
||||
@ -92,40 +86,29 @@ public class SjavacServer implements Terminable {
|
||||
// For the client, all port files fetched, one per started javac server.
|
||||
// Though usually only one javac server is started by a client.
|
||||
private static Map<String, PortFile> allPortFiles;
|
||||
private static Map<String, Long> maxServerMemory;
|
||||
|
||||
public SjavacServer(String settings, PrintStream err) throws FileNotFoundException {
|
||||
public SjavacServer(String settings) throws FileNotFoundException {
|
||||
this(Util.extractStringOption("portfile", settings),
|
||||
Util.extractStringOption("logfile", settings),
|
||||
Util.extractStringOption("stdouterrfile", settings),
|
||||
Util.extractIntOption("poolsize", settings, Runtime.getRuntime().availableProcessors()),
|
||||
Util.extractIntOption("keepalive", settings, 120),
|
||||
err);
|
||||
Util.extractIntOption("keepalive", settings, 120));
|
||||
}
|
||||
|
||||
public SjavacServer(String portfilename,
|
||||
String logfile,
|
||||
String stdouterrfile,
|
||||
int poolsize,
|
||||
int keepalive,
|
||||
PrintStream err)
|
||||
int keepalive)
|
||||
throws FileNotFoundException {
|
||||
this.portfilename = portfilename;
|
||||
this.logfile = logfile;
|
||||
this.stdouterrfile = stdouterrfile;
|
||||
this.poolsize = poolsize;
|
||||
this.keepalive = keepalive;
|
||||
this.err = err;
|
||||
|
||||
myCookie = new Random().nextLong();
|
||||
theLog = new PrintWriter(logfile);
|
||||
this.myCookie = new Random().nextLong();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Acquire the port file. Synchronized since several threads inside an smart javac wrapper client acquires the same port file at the same time.
|
||||
*/
|
||||
public static synchronized PortFile getPortFile(String filename) throws PortFileInaccessibleException {
|
||||
public static synchronized PortFile getPortFile(String filename)
|
||||
throws PortFileInaccessibleException {
|
||||
if (allPortFiles == null) {
|
||||
allPortFiles = new HashMap<>();
|
||||
}
|
||||
@ -169,26 +152,6 @@ public class SjavacServer implements Terminable {
|
||||
totalBuildTime += inc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log this message.
|
||||
*/
|
||||
public void log(String msg) {
|
||||
if (theLog != null) {
|
||||
theLog.println(msg);
|
||||
} else {
|
||||
System.err.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the log is flushed.
|
||||
*/
|
||||
public void flushLog() {
|
||||
if (theLog != null) {
|
||||
theLog.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3"
|
||||
* is sent as the settings parameter. Returns 0 on success, -1 on failure.
|
||||
@ -203,7 +166,7 @@ public class SjavacServer implements Terminable {
|
||||
portFile.lock();
|
||||
portFile.getValues();
|
||||
if (portFile.containsPortInfo()) {
|
||||
err.println("Javac server not started because portfile exists!");
|
||||
Log.info("Javac server not started because portfile exists!");
|
||||
portFile.unlock();
|
||||
return -1;
|
||||
}
|
||||
@ -230,23 +193,23 @@ public class SjavacServer implements Terminable {
|
||||
portFileMonitor = new PortFileMonitor(portFile, this);
|
||||
portFileMonitor.start();
|
||||
|
||||
log("Sjavac server started. Accepting connections...");
|
||||
log(" port: " + getPort());
|
||||
log(" time: " + new java.util.Date());
|
||||
log(" poolsize: " + poolsize);
|
||||
flushLog();
|
||||
Log.info("Sjavac server started. Accepting connections...");
|
||||
Log.info(" port: " + getPort());
|
||||
Log.info(" time: " + new java.util.Date());
|
||||
Log.info(" poolsize: " + poolsize);
|
||||
|
||||
|
||||
keepAcceptingRequests.set(true);
|
||||
do {
|
||||
try {
|
||||
Socket socket = serverSocket.accept();
|
||||
new Thread(new RequestHandler(socket, sjavac)).start();
|
||||
new RequestHandler(socket, sjavac).start();
|
||||
} catch (SocketException se) {
|
||||
// Caused by serverSocket.close() and indicates shutdown
|
||||
}
|
||||
} while (keepAcceptingRequests.get());
|
||||
|
||||
log("Shutting down.");
|
||||
Log.info("Shutting down.");
|
||||
|
||||
// No more connections accepted. If any client managed to connect after
|
||||
// the accept() was interrupted but before the server socket is closed
|
||||
@ -254,8 +217,7 @@ public class SjavacServer implements Terminable {
|
||||
// IOException on the client side.
|
||||
|
||||
long realTime = System.currentTimeMillis() - serverStart;
|
||||
log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
|
||||
flushLog();
|
||||
Log.info("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
|
||||
|
||||
// Shut down
|
||||
sjavac.shutdown();
|
||||
@ -270,8 +232,7 @@ public class SjavacServer implements Terminable {
|
||||
return;
|
||||
}
|
||||
|
||||
log("Quitting: " + quitMsg);
|
||||
flushLog();
|
||||
Log.info("Quitting: " + quitMsg);
|
||||
|
||||
portFileMonitor.shutdown(); // No longer any need to monitor port file
|
||||
|
||||
@ -280,12 +241,12 @@ public class SjavacServer implements Terminable {
|
||||
try {
|
||||
portFile.delete();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
e.printStackTrace(theLog);
|
||||
Log.error(e);
|
||||
}
|
||||
try {
|
||||
serverSocket.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(theLog);
|
||||
Log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.sjavac.server.log;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class LazyInitFileLog extends Log {
|
||||
|
||||
String baseFilename;
|
||||
Path destination = null;
|
||||
|
||||
public LazyInitFileLog(String baseFilename) {
|
||||
super(null, null);
|
||||
this.baseFilename = baseFilename;
|
||||
}
|
||||
|
||||
protected void printLogMsg(Level msgLevel, String msg) {
|
||||
try {
|
||||
// Lazily initialize out/err
|
||||
if (out == null && isLevelLogged(msgLevel)) {
|
||||
destination = getAvailableDestination();
|
||||
out = err = new PrintWriter(new FileWriter(destination.toFile()), true);
|
||||
}
|
||||
// Proceed to log the message
|
||||
super.printLogMsg(msgLevel, msg);
|
||||
} catch (IOException e) {
|
||||
// This could be bad. We might have run into an error and we can't
|
||||
// log it. Resort to printing on stdout.
|
||||
System.out.println("IO error occurred: " + e.getMessage());
|
||||
System.out.println("Original message: [" + msgLevel + "] " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The first available path of baseFilename, baseFilename.1,
|
||||
* basefilename.2, ...
|
||||
*/
|
||||
private Path getAvailableDestination() {
|
||||
Path p = Paths.get(baseFilename);
|
||||
int i = 1;
|
||||
while (Files.exists(p)) {
|
||||
p = Paths.get(baseFilename + "." + i++);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public Path getLogDestination() {
|
||||
return destination;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.sjavac.server.log;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class LoggingOutputStream extends FilterOutputStream {
|
||||
|
||||
private static final byte[] LINE_SEP = System.lineSeparator().getBytes();
|
||||
|
||||
private final Log.Level level;
|
||||
private final String linePrefix;
|
||||
private EolTrackingByteArrayOutputStream buf = new EolTrackingByteArrayOutputStream();
|
||||
|
||||
public LoggingOutputStream(OutputStream out, Log.Level level, String linePrefix) {
|
||||
super(out);
|
||||
this.level = level;
|
||||
this.linePrefix = linePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
super.write(b);
|
||||
buf.write(b);
|
||||
if (buf.isLineComplete()) {
|
||||
String line = new String(buf.toByteArray(), 0, buf.size() - LINE_SEP.length);
|
||||
Log.log(level, linePrefix + line);
|
||||
buf = new EolTrackingByteArrayOutputStream();
|
||||
}
|
||||
}
|
||||
|
||||
private static class EolTrackingByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
private static final byte[] EOL = System.lineSeparator().getBytes();
|
||||
private boolean isLineComplete() {
|
||||
if (count < EOL.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < EOL.length; i++) {
|
||||
if (buf[count - EOL.length + i] != EOL[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -65,11 +65,11 @@ public class IdleShutdown {
|
||||
// Use Sjavac object and wait less than TIMEOUT_MS in between calls
|
||||
Thread.sleep(TIMEOUT_MS - 1000);
|
||||
log("Compiling");
|
||||
service.compile(new String[0], null, null);
|
||||
service.compile(new String[0]);
|
||||
|
||||
Thread.sleep(TIMEOUT_MS - 1000);
|
||||
log("Compiling");
|
||||
service.compile(new String[0], null, null);
|
||||
service.compile(new String[0]);
|
||||
|
||||
if (timeoutTimestamp.get() != -1)
|
||||
throw new AssertionError("Premature timeout detected.");
|
||||
@ -103,7 +103,7 @@ public class IdleShutdown {
|
||||
public void shutdown() {
|
||||
}
|
||||
@Override
|
||||
public int compile(String[] args, Writer out, Writer err) {
|
||||
public int compile(String[] args) {
|
||||
// Attempt to trigger idle timeout during a call by sleeping
|
||||
try {
|
||||
Thread.sleep(TIMEOUT_MS + 1000);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,10 +30,12 @@
|
||||
* @build Wrapper
|
||||
* @run main Wrapper PooledExecution
|
||||
*/
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.sun.tools.sjavac.Log;
|
||||
import com.sun.tools.sjavac.comp.PooledSjavac;
|
||||
import com.sun.tools.sjavac.server.Sjavac;
|
||||
|
||||
@ -67,7 +69,7 @@ public class PooledExecution {
|
||||
for (int i = 0; i < NUM_REQUESTS; i++) {
|
||||
tasks[i] = new Thread() {
|
||||
public void run() {
|
||||
service.compile(new String[0], null, null);
|
||||
service.compile(new String[0]);
|
||||
tasksFinished.incrementAndGet();
|
||||
}
|
||||
};
|
||||
@ -109,7 +111,7 @@ public class PooledExecution {
|
||||
AtomicInteger activeRequests = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public int compile(String[] args, Writer out, Writer err) {
|
||||
public int compile(String[] args) {
|
||||
leftToStart.countDown();
|
||||
int numActiveRequests = activeRequests.incrementAndGet();
|
||||
System.out.printf("Left to start: %2d / Currently active: %2d%n",
|
||||
|
Loading…
Reference in New Issue
Block a user