8129114: Sjavac should stream back compiler output to the client as soon as it becomes available
authoralundblad
Fri, 04 Sep 2015 13:24:15 +0200
changeset 32542 f4e4f4c4f9f4
parent 32457 2050b3a0aadc
child 32543 f60b1725a83f
8129114: Sjavac should stream back compiler output to the client as soon as it becomes available Summary: Protocol revised, javac output sent back to client slightly earlier. Reviewed-by: jlahoda
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/AutoFlushWriter.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CleanProperties.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CompileProperties.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CopyFile.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/JavacState.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Log.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Transformer.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/ClientMain.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/SjavacClient.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/PooledSjavac.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/SjavacImpl.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/CompilationResult.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/IdleResetSjavac.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/LinePrefixFilterWriter.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/RequestHandler.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/ServerMain.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/Sjavac.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/SjavacServer.java
langtools/test/tools/sjavac/IdleShutdown.java
langtools/test/tools/sjavac/PooledExecution.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/AutoFlushWriter.java	Fri Sep 04 13:24:15 2015 +0200
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+public class AutoFlushWriter extends FilterWriter {
+
+    public AutoFlushWriter(Writer out) {
+        super(out);
+    }
+
+    @Override
+    public void write(int c) throws IOException {
+        super.write(c);
+        if (c == '\n' || c == '\r')
+            flush();
+    }
+
+    @Override
+    public void write(String str, int off, int len) throws IOException {
+        super.write(str, off, len);
+        if (str.contains("\n") || str.contains("\r"))
+            flush();
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        super.write(cbuf, off, len);
+        for (char c : cbuf) {
+            if (c == '\n' || c == '\r') {
+                flush();
+                break;
+            }
+        }
+    }
+}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CleanProperties.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CleanProperties.java	Fri Sep 04 13:24:15 2015 +0200
@@ -31,7 +31,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
-import java.io.PrintStream;
 import java.io.Writer;
 import java.net.URI;
 import java.util.ArrayList;
@@ -78,8 +77,8 @@
                              int debugLevel,
                              boolean incremental,
                              int numCores,
-                             PrintStream out,
-                             PrintStream err) {
+                             Writer out,
+                             Writer err) {
         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	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java	Fri Sep 04 13:24:15 2015 +0200
@@ -26,14 +26,22 @@
 package com.sun.tools.sjavac;
 
 import java.io.File;
-import java.io.PrintStream;
+import java.io.IOException;
+import java.io.Writer;
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 import com.sun.tools.sjavac.comp.CompilationService;
 import com.sun.tools.sjavac.options.Options;
@@ -83,8 +91,8 @@
                              int debugLevel,
                              boolean incremental,
                              int numCores,
-                             final PrintStream out,
-                             final PrintStream err) {
+                             final Writer out,
+                             final Writer err) {
 
         Log.debug("Performing CompileJavaPackages transform...");
 
@@ -200,102 +208,83 @@
             }
         }
 
-        // The return values for each chunked compile.
-        final CompilationSubResult[] rn = new CompilationSubResult[numCompiles];
-        // The requets, might or might not run as a background thread.
-        final Thread[] requests  = new Thread[numCompiles];
-
         long start = System.currentTimeMillis();
 
-        for (int i=0; i<numCompiles; ++i) {
-            final int ii = i;
-            final CompileChunk cc = compileChunks[i];
-
-            // Pass the num_cores and the id (appended with the chunk number) to the server.
-            Object lock = new Object();
-            requests[i] = new Thread() {
-                @Override
-                public void run() {
-                    rn[ii] = sjavac.compile("n/a",
-                                            id + "-" + ii,
-                                            args.prepJavacArgs(),
-                                            Collections.<File>emptyList(),
-                                            cc.srcs,
-                                            visibleSources);
-                    // In the code below we have to keep in mind that two
-                    // different compilation results may include results for
-                    // the same package.
-                    synchronized (lock) {
-
-                        for (String pkg : rn[ii].packageArtifacts.keySet()) {
-                            Set<URI> pkgArtifacts = rn[ii].packageArtifacts.get(pkg);
-                            packageArtifacts.merge(pkg, pkgArtifacts, Util::union);
-                        }
-
-                        for (String pkg : rn[ii].packageDependencies.keySet()) {
-                            packageDependencies.putIfAbsent(pkg, new HashMap<>());
-                            packageDependencies.get(pkg).putAll(rn[ii].packageDependencies.get(pkg));
-                        }
-
-                        for (String pkg : rn[ii].packageCpDependencies.keySet()) {
-                            packageCpDependencies.putIfAbsent(pkg, new HashMap<>());
-                            packageCpDependencies.get(pkg).putAll(rn[ii].packageCpDependencies.get(pkg));
-                        }
+        // Prepare compilation calls
+        List<Callable<CompilationSubResult>> compilationCalls = new ArrayList<>();
+        final Object lock = new Object();
+        for (int i = 0; i < numCompiles; i++) {
+            CompileChunk cc = compileChunks[i];
+            if (cc.srcs.isEmpty()) {
+                continue;
+            }
 
-                        for (String pkg : rn[ii].packagePubapis.keySet()) {
-                            packagePubapis.merge(pkg, rn[ii].packagePubapis.get(pkg), PubApi::mergeTypes);
-                        }
-
-                        for (String pkg : rn[ii].dependencyPubapis.keySet()) {
-                            dependencyPubapis.merge(pkg, rn[ii].dependencyPubapis.get(pkg), PubApi::mergeTypes);
-                        }
-                    }
+            String chunkId = id + "-" + String.valueOf(i);
+            compilationCalls.add(() -> {
+                CompilationSubResult result = sjavac.compile("n/a",
+                                                             chunkId,
+                                                             args.prepJavacArgs(),
+                                                             Collections.<File>emptyList(),
+                                                             cc.srcs,
+                                                             visibleSources);
+                synchronized (lock) {
+                    safeWrite(result.stdout, out);
+                    safeWrite(result.stderr, err);
                 }
-            };
+                return result;
+            });
+        }
 
-            if (cc.srcs.size() > 0) {
-                String numdeps = "";
-                if (cc.numDependents > 0) numdeps = "(with "+cc.numDependents+" dependents) ";
-                if (!incremental || cc.numPackages > 16) {
-                    String info = "("+cc.pkgFromTos+")";
-                    if (info.equals("( to )")) {
-                        info = "";
-                    }
-                    Log.info("Compiling "+cc.srcs.size()+" files "+numdeps+"in "+cc.numPackages+" packages "+info);
-                } else {
-                    Log.info("Compiling "+cc.pkgNames+numdeps);
-                }
-                if (concurrentCompiles) {
-                    requests[ii].start();
-                }
-                else {
-                    requests[ii].run();
-                    // If there was an error, then stop early when running single threaded.
-                    if (rn[i].returnCode != 0) {
-                        Log.info(rn[i].stdout);
-                        Log.error(rn[i].stderr);
-                        return false;
-                    }
-                }
+        // Perform compilations and collect results
+        List<CompilationSubResult> subResults = new ArrayList<>();
+        List<Future<CompilationSubResult>> futs = new ArrayList<>();
+        ExecutorService exec = Executors.newFixedThreadPool(concurrentCompiles ? compilationCalls.size() : 1);
+        for (Callable<CompilationSubResult> compilationCall : compilationCalls) {
+            futs.add(exec.submit(compilationCall));
+        }
+        for (Future<CompilationSubResult> fut : futs) {
+            try {
+                subResults.add(fut.get());
+            } catch (ExecutionException ee) {
+                Log.error("Compilation failed: " + ee.getMessage());
+            } catch (InterruptedException ee) {
+                Log.error("Compilation interrupted: " + ee.getMessage());
+                Thread.currentThread().interrupt();
             }
         }
-        if (concurrentCompiles) {
-            // If there are background threads for the concurrent compiles, then join them.
-            for (int i=0; i<numCompiles; ++i) {
-                try { requests[i].join(); } catch (InterruptedException e) { }
+        exec.shutdownNow();
+
+        // Process each sub result
+        for (CompilationSubResult subResult : subResults) {
+            for (String pkg : subResult.packageArtifacts.keySet()) {
+                Set<URI> pkgArtifacts = subResult.packageArtifacts.get(pkg);
+                packageArtifacts.merge(pkg, pkgArtifacts, Util::union);
+            }
+
+            for (String pkg : subResult.packageDependencies.keySet()) {
+                packageDependencies.putIfAbsent(pkg, new HashMap<>());
+                packageDependencies.get(pkg).putAll(subResult.packageDependencies.get(pkg));
+            }
+
+            for (String pkg : subResult.packageCpDependencies.keySet()) {
+                packageCpDependencies.putIfAbsent(pkg, new HashMap<>());
+                packageCpDependencies.get(pkg).putAll(subResult.packageCpDependencies.get(pkg));
+            }
+
+            for (String pkg : subResult.packagePubapis.keySet()) {
+                packagePubapis.merge(pkg, subResult.packagePubapis.get(pkg), PubApi::mergeTypes);
+            }
+
+            for (String pkg : subResult.dependencyPubapis.keySet()) {
+                dependencyPubapis.merge(pkg, subResult.dependencyPubapis.get(pkg), PubApi::mergeTypes);
+            }
+
+            // Check the return values.
+            if (subResult.returnCode != 0) {
+                rc = false;
             }
         }
 
-        // Check the return values.
-        for (int i=0; i<numCompiles; ++i) {
-            if (compileChunks[i].srcs.size() > 0) {
-                if (rn[i].returnCode != 0) {
-                    Log.info(rn[i].stdout);
-                    Log.error(rn[i].stderr);
-                    rc = false;
-                }
-            }
-        }
         long duration = System.currentTimeMillis() - start;
         long minutes = duration/60000;
         long seconds = (duration-minutes*60000)/1000;
@@ -304,6 +293,16 @@
         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	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CompileProperties.java	Fri Sep 04 13:24:15 2015 +0200
@@ -85,8 +85,8 @@
                              int debugLevel,
                              boolean incremental,
                              int numCores,
-                             PrintStream out,
-                             PrintStream err) {
+                             Writer out,
+                             Writer err) {
         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	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/CopyFile.java	Fri Sep 04 13:24:15 2015 +0200
@@ -31,7 +31,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.PrintStream;
+import java.io.Writer;
 import java.net.URI;
 import java.util.HashSet;
 import java.util.Map;
@@ -72,8 +72,8 @@
                              int debugLevel,
                              boolean incremental,
                              int numCores,
-                             PrintStream out,
-                             PrintStream err)
+                             Writer out,
+                             Writer err)
     {
         boolean rc = true;
         String dest_filename;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/JavacState.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/JavacState.java	Fri Sep 04 13:24:15 2015 +0200
@@ -31,7 +31,7 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.io.PrintStream;
+import java.io.Writer;
 import java.net.URI;
 import java.nio.file.NoSuchFileException;
 import java.text.SimpleDateFormat;
@@ -131,12 +131,12 @@
     private CompileJavaPackages compileJavaPackages = new CompileJavaPackages();
 
     // Where to send stdout and stderr.
-    private PrintStream out, err;
+    private Writer out, err;
 
     // Command line options.
     private Options options;
 
-    JavacState(Options op, boolean removeJavacState, PrintStream o, PrintStream e) {
+    JavacState(Options op, boolean removeJavacState, Writer o, Writer e) {
         options = op;
         out = o;
         err = e;
@@ -311,7 +311,7 @@
     /**
      * Load a javac_state file.
      */
-    public static JavacState load(Options options, PrintStream out, PrintStream err) {
+    public static JavacState load(Options options, Writer out, Writer err) {
         JavacState db = new JavacState(options, false, out, err);
         Module  lastModule = null;
         Package lastPackage = null;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Log.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Log.java	Fri Sep 04 13:24:15 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -25,7 +25,8 @@
 
 package com.sun.tools.sjavac;
 
-import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
 
 /**
  * Utility class only for sjavac logging.
@@ -37,7 +38,7 @@
  *  deletion without notice.</b>
  */
 public class Log {
-    private static PrintStream out, err;
+    private static PrintWriter out, err;
 
     public final static int WARN = 1;
     public final static int INFO = 2;
@@ -71,9 +72,9 @@
         err.println(msg);
     }
 
-    static public void initializeLog(PrintStream o, PrintStream e) {
-        out = o;
-        err = e;
+    static public void initializeLog(Writer o, Writer e) {
+        out = new PrintWriter(o);
+        err = new PrintWriter(e);
     }
 
     static public void setLogLevel(String l) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Transformer.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Transformer.java	Fri Sep 04 13:24:15 2015 +0200
@@ -25,7 +25,7 @@
 
 package com.sun.tools.sjavac;
 
-import java.io.PrintStream;
+import java.io.Writer;
 import java.net.URI;
 import java.util.Map;
 import java.util.Set;
@@ -97,8 +97,8 @@
                       int debugLevel,
                       boolean incremental,
                       int numCores,
-                      PrintStream out,
-                      PrintStream err);
+                      Writer out,
+                      Writer err);
 
     void setExtra(String e);
     void setExtra(Options args);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/ClientMain.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/ClientMain.java	Fri Sep 04 13:24:15 2015 +0200
@@ -25,13 +25,14 @@
 
 package com.sun.tools.sjavac.client;
 
-import java.io.PrintStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 
+import com.sun.tools.sjavac.AutoFlushWriter;
 import com.sun.tools.sjavac.Log;
 import com.sun.tools.sjavac.Util;
 import com.sun.tools.sjavac.comp.SjavacImpl;
 import com.sun.tools.sjavac.options.Options;
-import com.sun.tools.sjavac.server.CompilationResult;
 import com.sun.tools.sjavac.server.Sjavac;
 
 /**
@@ -43,10 +44,12 @@
 public class ClientMain {
 
     public static int run(String[] args) {
-        return run(args, System.out, System.err);
+        return run(args,
+                   new AutoFlushWriter(new OutputStreamWriter(System.out)),
+                   new AutoFlushWriter(new OutputStreamWriter(System.err)));
     }
 
-    public static int run(String[] args, PrintStream out, PrintStream err) {
+    public static int run(String[] args, Writer out, Writer err) {
 
         Log.initializeLog(out, err);
 
@@ -78,14 +81,11 @@
             sjavac = new SjavacImpl();
         }
 
-        CompilationResult cr = sjavac.compile(args);
-
-        out.print(cr.stdout);
-        err.print(cr.stderr);
+        int rc = sjavac.compile(args, out, err);
 
         if (!background)
             sjavac.shutdown();
 
-        return cr.returnCode;
+        return rc;
     }
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/SjavacClient.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/client/SjavacClient.java	Fri Sep 04 13:24:15 2015 +0200
@@ -25,11 +25,14 @@
 
 package com.sun.tools.sjavac.client;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
 import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Socket;
@@ -43,7 +46,6 @@
 import com.sun.tools.sjavac.options.OptionHelper;
 import com.sun.tools.sjavac.options.Options;
 import com.sun.tools.sjavac.server.CompilationSubResult;
-import com.sun.tools.sjavac.server.CompilationResult;
 import com.sun.tools.sjavac.server.PortFile;
 import com.sun.tools.sjavac.server.Sjavac;
 import com.sun.tools.sjavac.server.SjavacServer;
@@ -119,29 +121,47 @@
     }
 
     @Override
-    public CompilationResult compile(String[] args) {
-        CompilationResult result;
+    public int compile(String[] args, Writer stdout, Writer stderr) {
+        int result = -1;
         try (Socket socket = tryConnect()) {
-            // The ObjectInputStream constructor will block until the
-            // corresponding ObjectOutputStream has written and flushed the
-            // header, so it is important that the ObjectOutputStreams on server
-            // and client are opened before the ObjectInputStreams.
-            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
-            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
-            oos.writeObject(id);
-            oos.writeObject(SjavacServer.CMD_COMPILE);
-            oos.writeObject(args);
-            oos.flush();
-            result = (CompilationResult) ois.readObject();
-        } catch (IOException | ClassNotFoundException ex) {
-            Log.error("[CLIENT] Exception caught: " + ex);
-            result = new CompilationResult(CompilationSubResult.ERROR_FATAL);
-            result.stderr = Util.getStackTrace(ex);
+            PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
+            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+            // Send args array to server
+            out.println(args.length);
+            for (String arg : args)
+                out.println(arg);
+            out.flush();
+
+            // Read server response line by line
+            String line;
+            while (null != (line = in.readLine())) {
+                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:
+                    result = Integer.parseInt(content);
+                    break;
+                }
+            }
+        } catch (IOException ioe) {
+            Log.error("[CLIENT] Exception caught: " + ioe);
+            result = CompilationSubResult.ERROR_FATAL;
+            ioe.printStackTrace(new PrintWriter(stderr));
         } catch (InterruptedException ie) {
             Thread.currentThread().interrupt(); // Restore interrupt
             Log.error("[CLIENT] compile interrupted.");
-            result = new CompilationResult(CompilationSubResult.ERROR_FATAL);
-            result.stderr = Util.getStackTrace(ie);
+            result = CompilationSubResult.ERROR_FATAL;
+            ie.printStackTrace(new PrintWriter(stderr));
         }
         return result;
     }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/PooledSjavac.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/PooledSjavac.java	Fri Sep 04 13:24:15 2015 +0200
@@ -24,13 +24,13 @@
  */
 package com.sun.tools.sjavac.comp;
 
+import java.io.Writer;
 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.CompilationResult;
 import com.sun.tools.sjavac.server.Sjavac;
 
 /**
@@ -54,10 +54,10 @@
     }
 
     @Override
-    public CompilationResult compile(String[] args) {
+    public int compile(String[] args, Writer out, Writer err) {
         try {
             return pool.submit(() -> {
-                return delegate.compile(args);
+                return delegate.compile(args, out, err);
             }).get();
         } catch (Exception e) {
             e.printStackTrace();
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/SjavacImpl.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/SjavacImpl.java	Fri Sep 04 13:24:15 2015 +0200
@@ -24,12 +24,9 @@
  */
 package com.sun.tools.sjavac.comp;
 
-import static com.sun.tools.sjavac.server.CompilationResult.ERROR_FATAL;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -49,7 +46,6 @@
 import com.sun.tools.sjavac.Util;
 import com.sun.tools.sjavac.options.Options;
 import com.sun.tools.sjavac.options.SourceLocation;
-import com.sun.tools.sjavac.server.CompilationResult;
 import com.sun.tools.sjavac.server.Sjavac;
 
 /**
@@ -64,39 +60,33 @@
 public class SjavacImpl implements Sjavac {
 
     @Override
-    public CompilationResult compile(String[] args) {
-
-        ByteArrayOutputStream outBaos = new ByteArrayOutputStream();
-        ByteArrayOutputStream errBaos = new ByteArrayOutputStream();
-        PrintStream out = new PrintStream(outBaos);
-        PrintStream err = new PrintStream(errBaos);
-
+    public int compile(String[] args, Writer out, Writer err) {
         Options options;
         try {
             options = Options.parseArgs(args);
         } catch (IllegalArgumentException e) {
             Log.error(e.getMessage());
-            return new CompilationResult(ERROR_FATAL);
+            return RC_FATAL;
         }
 
         Log.setLogLevel(options.getLogLevel());
 
         if (!validateOptions(options))
-            return new CompilationResult(ERROR_FATAL);
+            return RC_FATAL;
 
         if (!createIfMissing(options.getDestDir()))
-            return new CompilationResult(ERROR_FATAL);
+            return RC_FATAL;
 
         if (!createIfMissing(options.getStateDir()))
-            return new CompilationResult(ERROR_FATAL);
+            return RC_FATAL;
 
         Path gensrc = options.getGenSrcDir();
         if (gensrc != null && !createIfMissing(gensrc))
-            return new CompilationResult(ERROR_FATAL);
+            return RC_FATAL;
 
         Path hdrdir = options.getHeaderDir();
         if (hdrdir != null && !createIfMissing(hdrdir))
-            return new CompilationResult(ERROR_FATAL);
+            return RC_FATAL;
 
         // Load the prev build state database.
         JavacState javac_state = JavacState.load(options, out, err);
@@ -132,9 +122,7 @@
 
         if (sources.isEmpty()) {
             Log.error("Found nothing to compile!");
-            return new CompilationResult(CompilationResult.ERROR_FATAL,
-                                          new String(outBaos.toByteArray(), UTF_8),
-                                          new String(errBaos.toByteArray(), UTF_8));
+            return RC_FATAL;
         }
 
 
@@ -251,19 +239,13 @@
                 javac_state.removeSuperfluousArtifacts(recently_compiled);
             }
 
-            return new CompilationResult(rc[0] ? 0 : ERROR_FATAL,
-                                          new String(outBaos.toByteArray(), UTF_8),
-                                          new String(errBaos.toByteArray(), UTF_8));
+            return rc[0] ? RC_OK : RC_FATAL;
         } catch (ProblemException e) {
             Log.error(e.getMessage());
-            return new CompilationResult(ERROR_FATAL,
-                                          new String(outBaos.toByteArray(), UTF_8),
-                                          new String(errBaos.toByteArray(), UTF_8));
+            return RC_FATAL;
         } catch (Exception e) {
-            e.printStackTrace(err);
-            return new CompilationResult(ERROR_FATAL,
-                                          new String(outBaos.toByteArray(), UTF_8),
-                                          new String(errBaos.toByteArray(), UTF_8));
+            e.printStackTrace(new PrintWriter(err));
+            return RC_FATAL;
         }
     }
 
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/CompilationResult.java	Wed Jul 05 20:48:33 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2014, 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.Serializable;
-
-/**
- *  <p><b>This is NOT part of any supported API.
- *  If you write code that depends on this, you do so at your own risk.
- *  This code and its internal interfaces are subject to change or
- *  deletion without notice.</b>
- */
-public class CompilationResult implements Serializable {
-
-    static final long serialVersionUID = 46739181113L;
-
-    // Return code constants
-    public final static int ERROR_FATAL = -1;
-
-    public String stdout;
-    public String stderr;
-    public int returnCode;
-
-    public CompilationResult(int returnCode) {
-        this(returnCode, "", "");
-    }
-
-    public CompilationResult(int returnCode, String stdout, String stderr) {
-        this.returnCode = returnCode;
-        this.stdout = stdout;
-        this.stderr = stderr;
-    }
-
-    public void setReturnCode(int returnCode) {
-        this.returnCode = returnCode;
-    }
-}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/IdleResetSjavac.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/IdleResetSjavac.java	Fri Sep 04 13:24:15 2015 +0200
@@ -24,6 +24,7 @@
  */
 package com.sun.tools.sjavac.server;
 
+import java.io.Writer;
 import java.util.Timer;
 import java.util.TimerTask;
 
@@ -60,10 +61,10 @@
     }
 
     @Override
-    public CompilationResult compile(String[] args) {
+    public int compile(String[] args, Writer out, Writer err) {
         startCall();
         try {
-            return delegate.compile(args);
+            return delegate.compile(args, out, err);
         } finally {
             endCall();
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/LinePrefixFilterWriter.java	Fri Sep 04 13:24:15 2015 +0200
@@ -0,0 +1,77 @@
+/*
+ * 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/RequestHandler.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/RequestHandler.java	Fri Sep 04 13:24:15 2015 +0200
@@ -24,15 +24,21 @@
  */
 package com.sun.tools.sjavac.server;
 
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
+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 java.io.BufferedReader;
+import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.io.Writer;
 import java.net.Socket;
 
+import com.sun.tools.sjavac.AutoFlushWriter;
 import com.sun.tools.sjavac.Log;
 
+
 /**
  * A RequestHandler handles requests performed over a socket. Specifically it
  *  - Reads the command string specifying which method is to be invoked
@@ -61,15 +67,26 @@
 
     @Override
     public void run() {
-        try (ObjectOutputStream oout = new ObjectOutputStream(socket.getOutputStream());
-             ObjectInputStream oin = new ObjectInputStream(socket.getInputStream())) {
-            String id = (String) oin.readObject();
-            String cmd = (String) oin.readObject();
-            Log.info("Handling request, id: " + id + " cmd: " + cmd);
-            switch (cmd) {
-            case SjavacServer.CMD_COMPILE: handleCompileRequest(oin, oout); break;
-            default: Log.error("Unknown command: " + cmd);
+        try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+             PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
+
+            // Read argument array
+            int n = Integer.parseInt(in.readLine());
+            String[] args = new String[n];
+            for (int i = 0; i < n; i++) {
+                args[i] = in.readLine();
             }
+
+            // 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();
+
+            // Send return code back to client
+            out.println(LINE_TYPE_RC + ":" + rc);
+
         } catch (Exception ex) {
             // Not much to be done at this point. The client side request
             // code will most likely throw an IOException and the
@@ -79,21 +96,4 @@
             Log.error(sw.toString());
         }
     }
-
-    private void handleCompileRequest(ObjectInputStream oin,
-                                      ObjectOutputStream oout) throws IOException {
-        try {
-            // Read request arguments
-            String[] args = (String[]) oin.readObject();
-
-            // Perform compilation
-            CompilationResult cr = sjavac.compile(args);
-
-            // Write request response
-            oout.writeObject(cr);
-            oout.flush();
-        } catch (ClassNotFoundException cnfe) {
-            throw new IOException(cnfe);
-        }
-    }
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/ServerMain.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/ServerMain.java	Fri Sep 04 13:24:15 2015 +0200
@@ -26,6 +26,7 @@
 package com.sun.tools.sjavac.server;
 
 import java.io.IOException;
+import java.io.OutputStreamWriter;
 
 import com.sun.tools.sjavac.Log;
 
@@ -38,7 +39,8 @@
 public class ServerMain {
     public static int run(String[] args) {
 
-        Log.initializeLog(System.out, System.err);
+        Log.initializeLog(new OutputStreamWriter(System.out),
+                          new OutputStreamWriter(System.err));
 
         // Any options other than --startserver?
         if (args.length > 1) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/Sjavac.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/Sjavac.java	Fri Sep 04 13:24:15 2015 +0200
@@ -24,6 +24,8 @@
  */
 package com.sun.tools.sjavac.server;
 
+import java.io.Writer;
+
 
 /**
  * Interface of the SjavacImpl, the sjavac client and all wrappers such as
@@ -35,6 +37,10 @@
  *  deletion without notice.</b>
  */
 public interface Sjavac {
-    CompilationResult compile(String[] args);
+
+    final static int RC_FATAL = -1;
+    final static int RC_OK = 0;
+
+    int compile(String[] args, Writer stdout, Writer stderr);
     void shutdown();
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/SjavacServer.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/server/SjavacServer.java	Fri Sep 04 13:24:15 2015 +0200
@@ -53,8 +53,10 @@
  */
 public class SjavacServer implements Terminable {
 
-    // Used in protocol to indicate which method to invoke
-    public final static String CMD_COMPILE = "compile";
+    // Used in protocol to tell the content of each line
+    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;
--- a/langtools/test/tools/sjavac/IdleShutdown.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/test/tools/sjavac/IdleShutdown.java	Fri Sep 04 13:24:15 2015 +0200
@@ -29,9 +29,9 @@
  * @build Wrapper
  * @run main Wrapper IdleShutdown
  */
+import java.io.Writer;
 import java.util.concurrent.atomic.AtomicLong;
 
-import com.sun.tools.sjavac.server.CompilationResult;
 import com.sun.tools.sjavac.server.IdleResetSjavac;
 import com.sun.tools.sjavac.server.Sjavac;
 import com.sun.tools.sjavac.server.Terminable;
@@ -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]);
+        service.compile(new String[0], null, null);
 
         Thread.sleep(TIMEOUT_MS - 1000);
         log("Compiling");
-        service.compile(new String[0]);
+        service.compile(new String[0], null, null);
 
         if (timeoutTimestamp.get() != -1)
             throw new AssertionError("Premature timeout detected.");
@@ -103,13 +103,13 @@
         public void shutdown() {
         }
         @Override
-        public CompilationResult compile(String[] args) {
+        public int compile(String[] args, Writer out, Writer err) {
             // Attempt to trigger idle timeout during a call by sleeping
             try {
                 Thread.sleep(TIMEOUT_MS + 1000);
             } catch (InterruptedException e) {
             }
-            return null;
+            return 0;
         }
     }
 }
--- a/langtools/test/tools/sjavac/PooledExecution.java	Wed Jul 05 20:48:33 2017 +0200
+++ b/langtools/test/tools/sjavac/PooledExecution.java	Fri Sep 04 13:24:15 2015 +0200
@@ -30,11 +30,11 @@
  * @build Wrapper
  * @run main Wrapper PooledExecution
  */
+import java.io.Writer;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.sun.tools.sjavac.comp.PooledSjavac;
-import com.sun.tools.sjavac.server.CompilationResult;
 import com.sun.tools.sjavac.server.Sjavac;
 
 
@@ -67,7 +67,7 @@
             for (int i = 0; i < NUM_REQUESTS; i++) {
                 tasks[i] = new Thread() {
                     public void run() {
-                        service.compile(new String[0]);
+                        service.compile(new String[0], null, null);
                         tasksFinished.incrementAndGet();
                     }
                 };
@@ -109,7 +109,7 @@
             AtomicInteger activeRequests = new AtomicInteger(0);
 
             @Override
-            public CompilationResult compile(String[] args) {
+            public int compile(String[] args, Writer out, Writer err) {
                 leftToStart.countDown();
                 int numActiveRequests = activeRequests.incrementAndGet();
                 System.out.printf("Left to start: %2d / Currently active: %2d%n",
@@ -123,7 +123,7 @@
                 }
                 activeRequests.decrementAndGet();
                 System.out.println("Task completed");
-                return null;
+                return 0;
             }
 
             @Override