8147569: Error messages from sjavac server does not always get relayed back to client
Summary: Refactored how logging works in sjavac.
Reviewed-by: jlahoda
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CleanProperties.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CleanProperties.java Mon Feb 29 13:24:01 2016 +0100
@@ -75,9 +75,7 @@
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);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java Mon Feb 29 13:24:01 2016 +0100
@@ -42,6 +42,8 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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!
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CompileProperties.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CompileProperties.java Mon Feb 29 13:24:01 2016 +0100
@@ -83,9 +83,7 @@
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);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CopyFile.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CopyFile.java Mon Feb 29 13:24:01 2016 +0100
@@ -70,9 +70,7 @@
Map<String, PubApi> dependencyPubapis,
int debugLevel,
boolean incremental,
- int numCores,
- Writer out,
- Writer err)
+ int numCores)
{
boolean rc = true;
String dest_filename;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/JavacState.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/JavacState.java Mon Feb 29 13:24:01 2016 +0100
@@ -123,16 +123,11 @@
// 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 @@
/**
* 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 @@
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 @@
dependencyPublicApis,
0,
isIncremental(),
- numCores,
- out,
- err);
+ numCores);
if (!r)
rc = false;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Log.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Log.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 @@
* deletion without notice.</b>
*/
public class Log {
- private static PrintWriter out, err;
+
+ 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 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 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);
+ }
}
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Transformer.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Transformer.java Mon Feb 29 13:24:01 2016 +0100
@@ -95,9 +95,7 @@
Map<String, PubApi> dependencyApis,
int debugLevel,
boolean incremental,
- int numCores,
- Writer out,
- Writer err);
+ int numCores);
void setExtra(String e);
void setExtra(Options args);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Util.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Util.java Mon Feb 29 13:24:01 2016 +0100
@@ -36,7 +36,9 @@
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 @@
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())));
+ }
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/ClientMain.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/ClientMain.java Mon Feb 29 13:24:01 2016 +0100
@@ -51,7 +51,7 @@
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 @@
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 @@
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)
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/SjavacClient.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/SjavacClient.java Mon Feb 29 13:24:01 2016 +0100
@@ -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.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.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.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 @@
// 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 @@
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 @@
}
@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 @@
// 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 @@
// 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 @@
/*
* 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);
}
}
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/PooledSjavac.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/PooledSjavac.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 @@
}
@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();
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/SjavacImpl.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/SjavacImpl.java Mon Feb 29 13:24:01 2016 +0100
@@ -27,6 +27,7 @@
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 @@
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 @@
return RC_FATAL;
}
- Log.setLogLevel(options.getLogLevel());
-
if (!validateOptions(options))
return RC_FATAL;
@@ -100,18 +99,21 @@
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
+ Main.Result result = new Main("javac", printWriter).compile(passThroughArgs, context);
- // Compile
- com.sun.tools.javac.main.Main compiler = new com.sun.tools.javac.main.Main("javac", writer);
- Main.Result result = compiler.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 @@
} 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 @@
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;
}
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/IdleResetSjavac.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/IdleResetSjavac.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 @@
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 @@
}
@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 @@
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.");
}
};
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/LinePrefixFilterWriter.java Mon Feb 29 11:54:06 2016 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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;
- }
-}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/PortFileMonitor.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/PortFileMonitor.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 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 @@
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);
}
}
};
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/RequestHandler.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/RequestHandler.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 @@
* 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 @@
@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 @@
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.");
}
}
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/ServerMain.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/ServerMain.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 @@
* 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 @@
return exitCode;
}
+
+ public static LazyInitFileLog getErrorLog() {
+ return errorLog;
+ }
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/Sjavac.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/Sjavac.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 @@
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();
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/SjavacServer.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/SjavacServer.java Mon Feb 29 13:24:01 2016 +0100
@@ -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.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 @@
*/
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 @@
// 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 @@
// 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<>();
}
@@ -170,26 +153,6 @@
}
/**
- * 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 @@
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 @@
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 @@
// 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 @@
return;
}
- log("Quitting: " + quitMsg);
- flushLog();
+ Log.info("Quitting: " + quitMsg);
portFileMonitor.shutdown(); // No longer any need to monitor port file
@@ -280,12 +241,12 @@
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);
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/log/LazyInitFileLog.java Mon Feb 29 13:24:01 2016 +0100
@@ -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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/log/LoggingOutputStream.java Mon Feb 29 13:24:01 2016 +0100
@@ -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;
+ }
+ }
+}
--- a/langtools/test/tools/sjavac/IdleShutdown.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/test/tools/sjavac/IdleShutdown.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 @@
// 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 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);
--- a/langtools/test/tools/sjavac/PooledExecution.java Mon Feb 29 11:54:06 2016 +0100
+++ b/langtools/test/tools/sjavac/PooledExecution.java Mon Feb 29 13:24:01 2016 +0100
@@ -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 @@
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 @@
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",