changeset 2584:98a99928a76b

8048457: Sjavac should not use portfiles, sockets, etc if background=false 8044131: Restructure client / server protocol code Summary: Changes protocol code to use Object input/output streams. Avoids spawning server if background=false. Refactors idleness checks, pooling and port file monitoring. Reviewed-by: jjg, jfranck
author alundblad
date Wed, 13 Aug 2014 14:44:59 +0200
parents 01d5bf0c20eb
children 09bb56ec5fec
files src/share/classes/com/sun/tools/sjavac/BuildState.java src/share/classes/com/sun/tools/sjavac/CleanProperties.java src/share/classes/com/sun/tools/sjavac/CompileChunk.java src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java src/share/classes/com/sun/tools/sjavac/CompileProperties.java src/share/classes/com/sun/tools/sjavac/CopyFile.java src/share/classes/com/sun/tools/sjavac/JavacState.java src/share/classes/com/sun/tools/sjavac/Log.java src/share/classes/com/sun/tools/sjavac/Main.java src/share/classes/com/sun/tools/sjavac/Module.java src/share/classes/com/sun/tools/sjavac/Package.java src/share/classes/com/sun/tools/sjavac/ProblemException.java src/share/classes/com/sun/tools/sjavac/Source.java src/share/classes/com/sun/tools/sjavac/Transformer.java src/share/classes/com/sun/tools/sjavac/Util.java src/share/classes/com/sun/tools/sjavac/client/SjavacClient.java src/share/classes/com/sun/tools/sjavac/comp/AttrWithDeps.java src/share/classes/com/sun/tools/sjavac/comp/Dependencies.java src/share/classes/com/sun/tools/sjavac/comp/JavaCompilerWithDeps.java src/share/classes/com/sun/tools/sjavac/comp/JavacServiceImpl.java src/share/classes/com/sun/tools/sjavac/comp/PooledSjavac.java src/share/classes/com/sun/tools/sjavac/comp/PubapiVisitor.java src/share/classes/com/sun/tools/sjavac/comp/ResolveWithDeps.java src/share/classes/com/sun/tools/sjavac/comp/SjavacErrorHandler.java src/share/classes/com/sun/tools/sjavac/comp/SjavacImpl.java src/share/classes/com/sun/tools/sjavac/comp/SmartFileManager.java src/share/classes/com/sun/tools/sjavac/comp/SmartFileObject.java src/share/classes/com/sun/tools/sjavac/comp/SmartWriter.java src/share/classes/com/sun/tools/sjavac/options/ArgumentIterator.java src/share/classes/com/sun/tools/sjavac/options/Option.java src/share/classes/com/sun/tools/sjavac/options/OptionHelper.java src/share/classes/com/sun/tools/sjavac/options/Options.java src/share/classes/com/sun/tools/sjavac/options/SourceLocation.java src/share/classes/com/sun/tools/sjavac/server/CompilationResult.java src/share/classes/com/sun/tools/sjavac/server/CompilerPool.java src/share/classes/com/sun/tools/sjavac/server/CompilerThread.java src/share/classes/com/sun/tools/sjavac/server/IdleResetSjavac.java src/share/classes/com/sun/tools/sjavac/server/JavacServer.java src/share/classes/com/sun/tools/sjavac/server/JavacService.java src/share/classes/com/sun/tools/sjavac/server/JavacServiceClient.java src/share/classes/com/sun/tools/sjavac/server/PortFile.java src/share/classes/com/sun/tools/sjavac/server/PortFileMonitor.java src/share/classes/com/sun/tools/sjavac/server/RequestHandler.java src/share/classes/com/sun/tools/sjavac/server/Sjavac.java src/share/classes/com/sun/tools/sjavac/server/SjavacServer.java src/share/classes/com/sun/tools/sjavac/server/SysInfo.java src/share/classes/com/sun/tools/sjavac/server/Terminable.java test/tools/sjavac/IdleShutdown.java test/tools/sjavac/PooledExecution.java
diffstat 49 files changed, 1940 insertions(+), 1796 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/sjavac/BuildState.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/BuildState.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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
@@ -36,10 +36,10 @@
  * from a build. There are usually two build states, the previous one (prev),
  * loaded from the javac_state file, and the current one (now).
  *
- * <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></p>
+ *  <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 BuildState {
     private Map<String,Module> modules = new HashMap<>();
--- a/src/share/classes/com/sun/tools/sjavac/CleanProperties.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/CleanProperties.java	Wed Aug 13 14:44:59 2014 +0200
@@ -36,19 +36,18 @@
 import java.util.Properties;
 
 import com.sun.tools.sjavac.options.Options;
-import com.sun.tools.sjavac.server.JavacService;
+import com.sun.tools.sjavac.server.Sjavac;
 
 /**
  * The clean properties transform should not be necessary.
  * Eventually we will cleanup the property file sources in the OpenJDK instead.
  *
- * <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></p>
+ *  <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 CleanProperties implements Transformer
-{
+public class CleanProperties implements Transformer {
     public void setExtra(String e) {
         // Any extra information is ignored for clean properties.
     }
@@ -57,7 +56,7 @@
         // Any extra information is ignored for clean properties.
     }
 
-    public boolean transform(JavacService javacService,
+    public boolean transform(Sjavac sjavac,
                              Map<String,Set<URI>> pkgSrcs,
                              Set<URI>             visibleSrcs,
                              Map<URI,Set<String>> visibleClasses,
@@ -70,8 +69,7 @@
                              boolean incremental,
                              int numCores,
                              PrintStream out,
-                             PrintStream err)
-    {
+                             PrintStream err) {
         boolean rc = true;
         for (String pkgName : pkgSrcs.keySet()) {
             String pkgNameF = pkgName.replace('.',File.separatorChar);
@@ -87,9 +85,12 @@
         return rc;
     }
 
-    boolean clean(String pkgName, String pkgNameF, File src, File destRoot, int debugLevel,
-                  Map<String,Set<URI>> packageArtifacts)
-    {
+    boolean clean(String pkgName,
+                  String pkgNameF,
+                  File src,
+                  File destRoot,
+                  int debugLevel,
+                  Map<String,Set<URI>> packageArtifacts) {
         // Load the properties file.
         Properties p = new Properties();
         try {
--- a/src/share/classes/com/sun/tools/sjavac/CompileChunk.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/CompileChunk.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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
@@ -33,10 +33,10 @@
  * A compile chunk is a list of sources/packages to be compiled. Possibly a subset of
  * the total number of sources/packages to be compiled for this sjavac invocation.
  *
- * <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></p>
+ *  <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 CompileChunk implements Comparable<CompileChunk> {
     public int numPackages;
--- a/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java	Wed Aug 13 14:44:59 2014 +0200
@@ -30,13 +30,12 @@
 import java.net.URI;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Random;
 import java.util.Set;
 import java.util.Map;
 
 import com.sun.tools.sjavac.options.Options;
 import com.sun.tools.sjavac.server.CompilationResult;
-import com.sun.tools.sjavac.server.JavacService;
+import com.sun.tools.sjavac.server.Sjavac;
 import com.sun.tools.sjavac.server.SysInfo;
 
 /**
@@ -67,7 +66,7 @@
         args = a;
     }
 
-    public boolean transform(final JavacService javacService,
+    public boolean transform(final Sjavac sjavac,
                              Map<String,Set<URI>> pkgSrcs,
                              final Set<URI>             visibleSources,
                              final Map<URI,Set<String>> visibleClasses,
@@ -86,12 +85,12 @@
         boolean concurrentCompiles = true;
 
         // Fetch the id.
-        final String id = Util.extractStringOption("id", javacService.serverSettings());
+        final String id = Util.extractStringOption("id", sjavac.serverSettings());
         // Only keep portfile and sjavac settings..
-        String psServerSettings = Util.cleanSubOptions(Util.set("portfile","sjavac","background","keepalive"), javacService.serverSettings());
+        String psServerSettings = Util.cleanSubOptions(Util.set("portfile","sjavac","background","keepalive"), sjavac.serverSettings());
 
         // Get maximum heap size from the server!
-        SysInfo sysinfo = javacService.getSysInfo();
+        SysInfo sysinfo = sjavac.getSysInfo();
         if (sysinfo.numCores == -1) {
             Log.error("Could not query server for sysinfo!");
             return false;
@@ -216,7 +215,7 @@
             requests[i] = new Thread() {
                 @Override
                 public void run() {
-                    rn[ii] = javacService.compile("n/a",
+                    rn[ii] = sjavac.compile("n/a",
                                                   id + "-" + ii,
                                                   args.prepJavacArgs(),
                                                   Collections.<File>emptyList(),
--- a/src/share/classes/com/sun/tools/sjavac/CompileProperties.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/CompileProperties.java	Wed Aug 13 14:44:59 2014 +0200
@@ -38,20 +38,19 @@
 import java.util.Map;
 
 import com.sun.tools.sjavac.options.Options;
-import com.sun.tools.sjavac.server.JavacService;
+import com.sun.tools.sjavac.server.Sjavac;
 
 /**
  * Compile properties transform a properties file into a Java source file.
  * Java has built in support for reading properties from either a text file
  * in the source or a compiled java source file.
  *
- * <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></p>
+ *  <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 CompileProperties implements Transformer
-{
+public class CompileProperties implements Transformer {
     // Any extra information passed from the command line, for example if:
     // -tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle
     // then extra will be "sun.util.resources.LocaleNamesBundle"
@@ -64,7 +63,7 @@
     public void setExtra(Options a) {
     }
 
-    public boolean transform(JavacService javacService,
+    public boolean transform(Sjavac sjavac,
                              Map<String,Set<URI>> pkgSrcs,
                              Set<URI>             visibleSrcs,
                              Map<URI,Set<String>> visibleClasses,
--- a/src/share/classes/com/sun/tools/sjavac/CopyFile.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/CopyFile.java	Wed Aug 13 14:44:59 2014 +0200
@@ -32,16 +32,16 @@
 import java.util.Map;
 
 import com.sun.tools.sjavac.options.Options;
-import com.sun.tools.sjavac.server.JavacService;
+import com.sun.tools.sjavac.server.Sjavac;
 
 /**
  * The copy file transform simply copies a matching file from -src to -d .
  * Such files are typically images, xml documents and other data files.
  *
- * <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></p>
+ *  <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 CopyFile implements Transformer {
 
@@ -51,7 +51,7 @@
     public void setExtra(Options a) {
     }
 
-    public boolean transform(JavacService javacService,
+    public boolean transform(Sjavac sjavac,
                              Map<String,Set<URI>> pkgSrcs,
                              Set<URI> visibleSrcs,
                              Map<URI,Set<String>> visibleClasses,
--- a/src/share/classes/com/sun/tools/sjavac/JavacState.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/JavacState.java	Wed Aug 13 14:44:59 2014 +0200
@@ -26,7 +26,6 @@
 package com.sun.tools.sjavac;
 
 import java.io.*;
-import java.nio.file.Path;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Set;
@@ -39,20 +38,18 @@
 import java.util.*;
 
 import com.sun.tools.sjavac.options.Options;
-import com.sun.tools.sjavac.options.SourceLocation;
-import com.sun.tools.sjavac.server.JavacService;
+import com.sun.tools.sjavac.server.Sjavac;
 
 /**
  * The javac state class maintains the previous (prev) and the current (now)
  * build states and everything else that goes into the javac_state file.
  *
- * <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></p>
+ *  <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 JavacState
-{
+public class JavacState {
     // The arguments to the compile. If not identical, then it cannot
     // be an incremental build!
     String theArgs;
@@ -655,7 +652,7 @@
     /**
      * Compile all the java sources. Return true, if it needs to be called again!
      */
-    public boolean performJavaCompilations(JavacService javacService,
+    public boolean performJavaCompilations(Sjavac sjavac,
                                            Options args,
                                            Set<String> recentlyCompiled,
                                            boolean[] rcValue) {
@@ -663,7 +660,7 @@
         suffixRules.put(".java", compileJavaPackages);
         compileJavaPackages.setExtra(args);
 
-        rcValue[0] = perform(javacService, binDir, suffixRules);
+        rcValue[0] = perform(sjavac, binDir, suffixRules);
         recentlyCompiled.addAll(taintedPackages());
         clearTaintedPackages();
         boolean again = !packagesWithChangedPublicApis.isEmpty();
@@ -693,10 +690,9 @@
      * For all packages, find all sources belonging to the package, group the sources
      * based on their transformers and apply the transformers on each source code group.
      */
-    private boolean perform(JavacService javacService,
+    private boolean perform(Sjavac sjavac,
                             File outputDir,
-                            Map<String,Transformer> suffixRules)
-    {
+                            Map<String,Transformer> suffixRules) {
         boolean rc = true;
         // Group sources based on transforms. A source file can only belong to a single transform.
         Map<Transformer,Map<String,Set<URI>>> groupedSources = new HashMap<>();
@@ -720,7 +716,7 @@
             Map<String,String> packagePublicApis =
                     Collections.synchronizedMap(new HashMap<String, String>());
 
-            boolean  r = t.transform(javacService,
+            boolean  r = t.transform(sjavac,
                                      srcs,
                                      visibleSrcs,
                                      visibleClasses,
@@ -798,9 +794,7 @@
      * Used to detect bugs where the makefile and sjavac have different opinions on which files
      * should be compiled.
      */
-    public void compareWithMakefileList(File makefileSourceList)
-            throws ProblemException
-    {
+    public void compareWithMakefileList(File makefileSourceList) throws ProblemException {
         // If we are building on win32 using for example cygwin the paths in the makefile source list
         // might be /cygdrive/c/.... which does not match c:\....
         // We need to adjust our calculated sources to be identical, if necessary.
--- a/src/share/classes/com/sun/tools/sjavac/Log.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/Log.java	Wed Aug 13 14:44:59 2014 +0200
@@ -31,10 +31,10 @@
  * Utility class only for sjavac logging.
  * The log level can be set using for example --log=DEBUG on the sjavac command line.
  *
- * <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></p>
+ *  <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 Log {
     private static PrintStream out, err;
--- a/src/share/classes/com/sun/tools/sjavac/Main.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/Main.java	Wed Aug 13 14:44:59 2014 +0200
@@ -31,19 +31,21 @@
 import java.nio.file.Path;
 import java.nio.file.Files;
 
+import com.sun.tools.sjavac.client.SjavacClient;
+import com.sun.tools.sjavac.comp.SjavacImpl;
+import com.sun.tools.sjavac.comp.PooledSjavac;
 import com.sun.tools.sjavac.options.Options;
 import com.sun.tools.sjavac.options.SourceLocation;
-import com.sun.tools.sjavac.server.JavacService;
-import com.sun.tools.sjavac.server.JavacServer;
-import com.sun.tools.sjavac.server.JavacServiceClient;
+import com.sun.tools.sjavac.server.Sjavac;
+import com.sun.tools.sjavac.server.SjavacServer;
 
 /**
  * The main class of the smart javac wrapper tool.
  *
- * <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></p>
+ *  <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 Main {
 
@@ -163,13 +165,19 @@
                 return;
             }
             // Spawn a background server.
-            int rc = JavacServer.startServer(args[0], System.err);
-            System.exit(rc);
+            try {
+                SjavacServer server = new SjavacServer(args[0], System.err);
+                int rc = server.startServer();
+                System.exit(rc);
+            } catch (IOException ioex) {
+                Log.error("IOException caught: " + ioex);
+                System.exit(-1);
+            }
         }
         Main main = new Main();
         int rc = main.go(args, System.out, System.err);
         // Remove the portfile, but only if this background=false was used.
-        JavacServer.cleanup(args);
+        SjavacServer.cleanup(args);
         System.exit(rc);
     }
 
@@ -341,15 +349,22 @@
             // Collect the name of all compiled packages.
             Set<String> recently_compiled = new HashSet<>();
             boolean[] rc = new boolean[1];
+            Sjavac sjavac;
+            boolean background = Util.extractBooleanOption("background", options.getServerConf(), true);
             do {
                 // Clean out artifacts in tainted packages.
                 javac_state.deleteClassArtifactsInTaintedPackages();
-                // Create a JavacService to delegate the actual compilation to.
-                // Currently sjavac always connects to a server through a socket
-                // regardless if sjavac runs as a background service or not.
-                // This will most likely change in the future.
-                JavacService javacService = new JavacServiceClient(options);
-                again = javac_state.performJavaCompilations(javacService, options, recently_compiled, rc);
+                // Create an sjavac implementation to be used for compilation
+                if (background) {
+                    sjavac = new SjavacClient(options);
+                } else {
+                    int poolsize = Util.extractIntOption("poolsize", options.getServerConf());
+                    if (poolsize <= 0)
+                        poolsize = Runtime.getRuntime().availableProcessors();
+                    sjavac = new PooledSjavac(new SjavacImpl(), poolsize);
+                }
+
+                again = javac_state.performJavaCompilations(sjavac, options, recently_compiled, rc);
                 if (!rc[0]) break;
             } while (again);
             // Only update the state if the compile went well.
@@ -360,6 +375,8 @@
                 // Remove artifacts that were generated during the last compile, but not this one.
                 javac_state.removeSuperfluousArtifacts(recently_compiled);
             }
+            if (!background)
+                sjavac.shutdown();
             return rc[0] ? 0 : -1;
         } catch (ProblemException e) {
             Log.error(e.getMessage());
--- a/src/share/classes/com/sun/tools/sjavac/Module.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/Module.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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
@@ -36,10 +36,10 @@
  * The module is the root of a set of packages/sources/artifacts.
  * At the moment there is only one module in use, the empty/no-name/default module.
  *
- * <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></p>
+ *  <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 Module implements Comparable<Module> {
     private String name;
--- a/src/share/classes/com/sun/tools/sjavac/Package.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/Package.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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
@@ -54,10 +54,10 @@
  * the visible recompilation of the dependent packages indicates how much circular
  * dependencies your code has.
  *
- * <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></p>
+ *  <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 Package implements Comparable<Package> {
     // The module this package belongs to. (There is a legacy module with an empty string name,
--- a/src/share/classes/com/sun/tools/sjavac/ProblemException.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/ProblemException.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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
@@ -28,10 +28,10 @@
 /**
  * Used to signal serious problems when running sjavac.
  *
- * <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></p>
+ *  <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 ProblemException extends Exception {
     static final long serialVersionUID = -3387516993124229949L;
--- a/src/share/classes/com/sun/tools/sjavac/Source.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/Source.java	Wed Aug 13 14:44:59 2014 +0200
@@ -37,10 +37,10 @@
  * The class also knows how to find source files (scanRoot) given include/exclude
  * patterns and a root.
  *
- * <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></p>
+ *  <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 Source implements Comparable<Source> {
     // The package the source belongs to.
--- a/src/share/classes/com/sun/tools/sjavac/Transformer.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/Transformer.java	Wed Aug 13 14:44:59 2014 +0200
@@ -31,7 +31,7 @@
 import java.util.Map;
 
 import com.sun.tools.sjavac.options.Options;
-import com.sun.tools.sjavac.server.JavacService;
+import com.sun.tools.sjavac.server.Sjavac;
 
 /**
  * The transform interface is used to transform content inside a package, from one form to another.
@@ -39,13 +39,12 @@
  * but can also be an unpredictable number of generated source files (eg idl2java)
  * or a single predictable output file (eg when copying,cleaning or compiling a properties file).
  *
- * <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></p>
+ *  <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 interface Transformer
-{
+public interface Transformer {
     /**
      * The transform method takes a set of package names, mapped to their source files and to the
      * pubapis of the packages.
@@ -83,7 +82,7 @@
      * If num_cores is set to a non-zero value. The transform should attempt to use no more than these
      * number of threads for heavy work.
      */
-    boolean transform(JavacService javacService,
+    boolean transform(Sjavac sjavac,
                       Map<String,Set<URI>> pkgSrcs,
                       Set<URI>             visibleSources,
                       Map<URI,Set<String>> visibleClasses,
--- a/src/share/classes/com/sun/tools/sjavac/Util.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/Util.java	Wed Aug 13 14:44:59 2014 +0200
@@ -26,8 +26,6 @@
 package com.sun.tools.sjavac;
 
 import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -37,10 +35,10 @@
 /**
  * Utilities.
  *
- * <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></p>
+ *  <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 Util {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/client/SjavacClient.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2014, 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.client;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import com.sun.tools.sjavac.Log;
+import com.sun.tools.sjavac.ProblemException;
+import com.sun.tools.sjavac.Util;
+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;
+import com.sun.tools.sjavac.server.SysInfo;
+import com.sun.tools.sjavac.options.Options;
+
+/**
+ * Sjavac implementation that delegates requests to a SjavacServer.
+ *
+ *  <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 SjavacClient implements Sjavac {
+
+    // The id can perhaps be used in the future by the javac server to reuse the
+    // JavaCompiler instance for several compiles using the same id.
+    private final String id;
+    private final String portfileName;
+    private final String logfile;
+    private final String stdouterrfile;
+    private final boolean background;
+
+    // Default keepalive for server is 120 seconds.
+    // I.e. it will accept 120 seconds of inactivity before quitting.
+    private final int keepalive;
+    private final int poolsize;
+
+    // The sjavac option specifies how the server part of sjavac is spawned.
+    // If you have the experimental sjavac in your path, you are done. If not, you have
+    // to point to a com.sun.tools.sjavac.Main that supports --startserver
+    // for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main
+    private final String sjavacForkCmd;
+
+    // Wait 2 seconds for response, before giving up on javac server.
+    static int CONNECTION_TIMEOUT = 2000;
+    static int MAX_CONNECT_ATTEMPTS = 3;
+    static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 2000;
+
+    // Store the server conf settings here.
+    private final String settings;
+
+    public SjavacClient(Options options) {
+        String tmpServerConf = options.getServerConf();
+        String serverConf = (tmpServerConf!=null)? tmpServerConf : "";
+        String tmpId = Util.extractStringOption("id", serverConf);
+        id = (tmpId!=null) ? tmpId : "id"+(((new java.util.Random()).nextLong())&Long.MAX_VALUE);
+        String p = Util.extractStringOption("portfile", serverConf);
+        portfileName = (p!=null) ? p : options.getStateDir().toFile().getAbsolutePath()+File.separatorChar+"javac_server";
+        logfile = Util.extractStringOption("logfile", serverConf, portfileName + ".javaclog");
+        stdouterrfile = Util.extractStringOption("stdouterrfile", serverConf, portfileName + ".stdouterr");
+        background = Util.extractBooleanOption("background", serverConf, true);
+        sjavacForkCmd = Util.extractStringOption("sjavac", serverConf, "sjavac");
+        int poolsize = Util.extractIntOption("poolsize", serverConf);
+        keepalive = Util.extractIntOption("keepalive", serverConf, 120);
+
+        this.poolsize = poolsize > 0 ? poolsize : Runtime.getRuntime().availableProcessors();
+        settings = (serverConf.equals("")) ? "id="+id+",portfile="+portfileName : serverConf;
+    }
+
+    /**
+     * Hand out the server settings.
+     * @return The server settings, possibly a default value.
+     */
+    public String serverSettings() {
+        return settings;
+    }
+
+    /**
+     * Make a request to the server only to get the maximum possible heap size to use for compilations.
+     *
+     * @param port_file The port file used to synchronize creation of this server.
+     * @param id The identify of the compilation.
+     * @param out Standard out information.
+     * @param err Standard err information.
+     * @return The maximum heap size in bytes.
+     */
+    @Override
+    public SysInfo getSysInfo() {
+        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_SYS_INFO);
+            oos.flush();
+            return (SysInfo) ois.readObject();
+        } catch (IOException | ClassNotFoundException ex) {
+            Log.error("[CLIENT] Exception caught: " + ex);
+            StringWriter sw = new StringWriter();
+            ex.printStackTrace(new PrintWriter(sw));
+        }
+        return null;
+    }
+
+    @Override
+    public CompilationResult compile(String protocolId,
+                                     String invocationId,
+                                     String[] args,
+                                     List<File> explicitSources,
+                                     Set<URI> sourcesToCompile,
+                                     Set<URI> visibleSources) {
+        CompilationResult result;
+        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(protocolId);
+            oos.writeObject(invocationId);
+            oos.writeObject(args);
+            oos.writeObject(explicitSources);
+            oos.writeObject(sourcesToCompile);
+            oos.writeObject(visibleSources);
+            oos.flush();
+            result = (CompilationResult) ois.readObject();
+        } catch (IOException | ClassNotFoundException ex) {
+            Log.error("Exception caught: " + ex);
+            result = new CompilationResult(CompilationResult.ERROR_FATAL);
+            result.stderr = ex.getMessage();
+        }
+        return result;
+    }
+
+    private Socket tryConnect() throws IOException {
+
+        PortFile portFile;
+        try {
+            // This should be taken care of at a higher level (JDK-8048451)
+            portFile = SjavacServer.getPortFile(portfileName);
+        } catch (FileNotFoundException e) {
+            // Reached for instance if directory of port file does not exist
+            Log.error("Port file inaccessable: " + e);
+            throw new RuntimeException(e);
+        }
+        for (int i = 0; i < MAX_CONNECT_ATTEMPTS; i++) {
+            Log.info(String.format("Trying to connect (attempt %d of %d)",
+                                   i+1, MAX_CONNECT_ATTEMPTS));
+            try {
+                if (!makeSureServerIsRunning(portFile))
+                    continue;
+                Socket socket = new Socket();
+                InetAddress localhost = InetAddress.getByName(null);
+                socket.connect(new InetSocketAddress(localhost, portFile.getPort()),
+                               CONNECTION_TIMEOUT);
+                return socket;
+            } catch (ProblemException | IOException ex) {
+                Log.error("Caught exception during tryConnect: " + ex);
+            }
+
+            try {
+                Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        throw new IOException("Could not connect to server");
+    }
+
+    private boolean makeSureServerIsRunning(PortFile portFile)
+            throws IOException, ProblemException, FileNotFoundException {
+
+        synchronized (portFile) {
+            portFile.lock();
+            portFile.getValues();
+            portFile.unlock();
+        }
+
+        if (!portFile.containsPortInfo()) {
+            String forkCmd = SjavacServer.fork(sjavacForkCmd,
+                                               portFile.getFilename(),
+                                               logfile,
+                                               poolsize,
+                                               keepalive,
+                                               System.err,
+                                               stdouterrfile,
+                                               background);
+            if (!portFile.waitForValidValues()) {
+                // This can be simplified once JDK-8048457 has been addressed
+                // since we won't have an SjavacClient if background = false
+                if (background) {
+                    // There seems be some problem with spawning the external
+                    // process (for instance no fork command provided and no
+                    // sjavac on path)
+                    StringWriter sw = new StringWriter();
+                    SjavacClient.printFailedAttempt(forkCmd,
+                                                    stdouterrfile,
+                                                    new PrintWriter(sw));
+                    Log.error(sw.toString());
+                }
+            }
+        }
+        return portFile.containsPortInfo();
+    }
+
+
+    public static void printFailedAttempt(String cmd, String f, PrintWriter err) {
+        err.println("---- Failed to start javac server with this command -----");
+        err.println(cmd);
+        try {
+            BufferedReader in = new BufferedReader(new FileReader(f));
+            err.println("---- stdout/stderr output from attempt to start javac server -----");
+            for (;;) {
+                String l = in.readLine();
+                if (l == null) {
+                    break;
+                }
+                err.println(l);
+            }
+            err.println("------------------------------------------------------------------");
+        } catch (Exception e) {
+            err.println("The stdout/stderr output in file " + f + " does not exist and the server did not start.");
+        }
+    }
+
+    @Override
+    public void shutdown() {
+        // Nothing to clean up
+    }
+}
--- a/src/share/classes/com/sun/tools/sjavac/comp/AttrWithDeps.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/comp/AttrWithDeps.java	Wed Aug 13 14:44:59 2014 +0200
@@ -30,10 +30,10 @@
 
 /** Subclass to Attr that overrides reportDepedence.
  *
- * <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></p>
+ *  <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 AttrWithDeps extends Attr {
 
--- a/src/share/classes/com/sun/tools/sjavac/comp/Dependencies.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/comp/Dependencies.java	Wed Aug 13 14:44:59 2014 +0200
@@ -41,10 +41,10 @@
 /** Utility class containing dependency information between packages
  *  and the pubapi for a package.
  *
- * <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></p>
+ *  <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 Dependencies {
     protected static final Context.Key<Dependencies> dependenciesKey = new Context.Key<>();
--- a/src/share/classes/com/sun/tools/sjavac/comp/JavaCompilerWithDeps.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/comp/JavaCompilerWithDeps.java	Wed Aug 13 14:44:59 2014 +0200
@@ -32,29 +32,29 @@
 
 /** Subclass to Resolve that overrides collect.
  *
- * <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></p>
+ *  <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 JavaCompilerWithDeps extends JavaCompiler {
 
     /** The dependency database
      */
     protected Dependencies deps;
-    protected JavacServiceImpl javacService;
+    protected SjavacErrorHandler errorHandler;
 
-    public JavaCompilerWithDeps(Context context, JavacServiceImpl jsi) {
+    public JavaCompilerWithDeps(Context context, SjavacErrorHandler eh) {
         super(context);
         deps = Dependencies.instance(context);
-        javacService = jsi;
+        errorHandler = eh;
         needRootClasses = true;
     }
 
-    public static void preRegister(Context context, final JavacServiceImpl t) {
+    public static void preRegister(Context context, final SjavacErrorHandler eh) {
         context.put(compilerKey, new Context.Factory<JavaCompiler>() {
             public JavaCompiler make(Context c) {
-                JavaCompiler instance = new JavaCompilerWithDeps(c, t);
+                JavaCompiler instance = new JavaCompilerWithDeps(c, eh);
                 c.put(JavaCompiler.class, instance);
                 return instance;
             }
@@ -97,7 +97,7 @@
 
             // Now check if the truncated uri ends with the path. (It does not == failure!)
             if (path.length() > 0 && !path.equals("/unnamed package/") && !pp.endsWith(path)) {
-                javacService.logError("Error: The source file "+sym.sourcefile.getName()+
+                errorHandler.logError("Error: The source file "+sym.sourcefile.getName()+
                                         " is located in the wrong package directory, because it contains the class "+
                                         sym.getQualifiedName());
             }
--- a/src/share/classes/com/sun/tools/sjavac/comp/JavacServiceImpl.java	Tue Aug 12 17:48:30 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-/*
- * Copyright (c) 2014, 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.comp;
-
-import java.io.File;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.URI;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-import javax.tools.JavaCompiler.CompilationTask;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-
-import com.sun.tools.javac.api.JavacTaskImpl;
-import com.sun.tools.javac.api.JavacTool;
-import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.util.ListBuffer;
-import com.sun.tools.sjavac.Util;
-import com.sun.tools.sjavac.server.CompilationResult;
-import com.sun.tools.sjavac.server.JavacServer;
-import com.sun.tools.sjavac.server.JavacService;
-import com.sun.tools.sjavac.server.SysInfo;
-
-public class JavacServiceImpl implements JavacService {
-
-    JavacServer javacServer;
-    private ThreadLocal<Boolean> forcedExit;
-
-    public JavacServiceImpl(JavacServer javacServer) {
-        this.javacServer = javacServer;
-
-    }
-
-    public void logError(String msg) {
-//        stderr.println(msg);
-        forcedExit.set(true);
-    }
-
-    @Override
-    public SysInfo getSysInfo() {
-        return new SysInfo(Runtime.getRuntime().availableProcessors(),
-                           Runtime.getRuntime().maxMemory());
-    }
-
-    @Override
-    public CompilationResult compile(String protocolId,
-                                     String invocationId,
-                                     String[] args,
-                                     List<File> explicitSources,
-                                     Set<URI> sourcesToCompile,
-                                     Set<URI> visibleSources) {
-
-        JavacTool compiler = JavacTool.create();
-        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
-        SmartFileManager smartFileManager = new SmartFileManager(fileManager);
-        Context context = new Context();
-        ResolveWithDeps.preRegister(context);
-        AttrWithDeps.preRegister(context);
-        JavaCompilerWithDeps.preRegister(context, this);
-
-        // Now setup the actual compilation....
-        CompilationResult compilationResult = new CompilationResult(0);
-
-        // First deal with explicit source files on cmdline and in at file.
-        ListBuffer<JavaFileObject> compilationUnits = new ListBuffer<>();
-        for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(explicitSources)) {
-            compilationUnits.append(i);
-        }
-        // Now deal with sources supplied as source_to_compile.
-        ListBuffer<File> sourcesToCompileFiles = new ListBuffer<>();
-        for (URI u : sourcesToCompile) {
-            sourcesToCompileFiles.append(new File(u));
-        }
-        for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(sourcesToCompileFiles)) {
-            compilationUnits.append(i);
-        }
-        // Log the options to be used.
-        StringBuilder options = new StringBuilder();
-        for (String s : args) {
-            options.append(">").append(s).append("< ");
-        }
-        javacServer.log(protocolId+" <"+invocationId+"> options "+options.toString());
-
-        forcedExit.set(false);
-        // Create a new logger.
-        StringWriter stdoutLog = new StringWriter();
-        StringWriter stderrLog = new StringWriter();
-        PrintWriter stdout = new PrintWriter(stdoutLog);
-        PrintWriter stderr = new PrintWriter(stderrLog);
-        com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK;
-        try {
-            if (compilationUnits.size() > 0) {
-                smartFileManager.setVisibleSources(visibleSources);
-                smartFileManager.cleanArtifacts();
-                smartFileManager.setLog(stdout);
-
-
-                // Do the compilation!
-                CompilationTask task = compiler.getTask(stderr, smartFileManager, null, Arrays.asList(args), null, compilationUnits, context);
-                rc = ((JavacTaskImpl) task).doCall();
-                smartFileManager.flush();
-            }
-        } catch (Exception e) {
-            stderr.println(e.getMessage());
-            forcedExit.set(true);
-        }
-
-        compilationResult.packageArtifacts = smartFileManager.getPackageArtifacts();
-
-        Dependencies deps = Dependencies.instance(context);
-        compilationResult.packageDependencies = deps.getDependencies();
-        compilationResult.packagePubapis = deps.getPubapis();
-
-        compilationResult.stdout = stdoutLog.toString();
-        compilationResult.stderr = stderrLog.toString();
-        compilationResult.returnCode = rc.exitCode == 0 && forcedExit.get() ? -1 : rc.exitCode;
-
-        return compilationResult;
-    }
-
-    @Override
-    public String serverSettings() {
-        return "";
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/PooledSjavac.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2014, 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.comp;
+
+import java.io.File;
+import java.net.URI;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.sun.tools.sjavac.Log;
+import com.sun.tools.sjavac.server.CompilationResult;
+import com.sun.tools.sjavac.server.Sjavac;
+import com.sun.tools.sjavac.server.SysInfo;
+
+/**
+ * An sjavac implementation that limits the number of concurrent calls by
+ * wrapping invocations in Callables and delegating them to a FixedThreadPool.
+ *
+ *  <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 PooledSjavac implements Sjavac {
+
+    final Sjavac delegate;
+    final ExecutorService pool;
+
+    public PooledSjavac(Sjavac delegate, int poolsize) {
+        Objects.requireNonNull(delegate);
+        this.delegate = delegate;
+        pool = Executors.newFixedThreadPool(poolsize, new ThreadFactory() {
+            AtomicInteger count = new AtomicInteger();
+            @Override
+            public Thread newThread(Runnable runnable) {
+                String cls = PooledSjavac.class.getSimpleName();
+                int num = count.incrementAndGet();
+                Thread t = new Thread(runnable, cls + "-" + num);
+                t.setDaemon(true);
+                return t;
+            }
+        });
+    }
+
+    @Override
+    public SysInfo getSysInfo() {
+        try {
+            return pool.submit(new Callable<SysInfo>() {
+                @Override
+                public SysInfo call() throws Exception {
+                    return delegate.getSysInfo();
+                }
+            }).get();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("Error during getSysInfo", e);
+        }
+    }
+
+    @Override
+    public CompilationResult compile(final String protocolId,
+                                     final String invocationId,
+                                     final String[] args,
+                                     final List<File> explicitSources,
+                                     final Set<URI> sourcesToCompile,
+                                     final Set<URI> visibleSources) {
+        try {
+            return pool.submit(new Callable<CompilationResult>() {
+                @Override
+                public CompilationResult call() throws Exception {
+                    return delegate.compile(protocolId,
+                                            invocationId,
+                                            args,
+                                            explicitSources,
+                                            sourcesToCompile,
+                                            visibleSources);
+                }
+            }).get();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("Error during compile", e);
+        }
+    }
+
+    @Override
+    public void shutdown() {
+        pool.shutdown(); // Disable new tasks from being submitted
+        try {
+            // Wait a while for existing tasks to terminate
+            if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
+                pool.shutdownNow(); // Cancel currently executing tasks
+                // Wait a while for tasks to respond to being cancelled
+                if (!pool.awaitTermination(60, TimeUnit.SECONDS))
+                    Log.error("ThreadPool did not terminate");
+            }
+            // Grace period for thread termination
+            Thread.sleep(1000);
+        } catch (InterruptedException ie) {
+          // (Re-)Cancel if current thread also interrupted
+          pool.shutdownNow();
+          // Preserve interrupt status
+          Thread.currentThread().interrupt();
+        }
+
+        delegate.shutdown();
+    }
+
+    @Override
+    public String serverSettings() {
+        return delegate.serverSettings();
+    }
+}
--- a/src/share/classes/com/sun/tools/sjavac/comp/PubapiVisitor.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/comp/PubapiVisitor.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, 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
@@ -37,10 +37,10 @@
 /** Utility class that constructs a textual representation
  * of the public api of a class.
  *
- * <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></p>
+ *  <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 PubapiVisitor extends ElementScanner9<Void, Void> {
 
--- a/src/share/classes/com/sun/tools/sjavac/comp/ResolveWithDeps.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/comp/ResolveWithDeps.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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,10 @@
 
 /** Subclass to Resolve that overrides collect.
  *
- * <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></p>
+ *  <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 ResolveWithDeps extends Resolve {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/SjavacErrorHandler.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 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.comp;
+
+/**
+ *  <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 interface SjavacErrorHandler {
+    void logError(String msg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/SjavacImpl.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2014, 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.comp;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.tools.JavaCompiler.CompilationTask;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Options;
+import com.sun.tools.sjavac.server.CompilationResult;
+import com.sun.tools.sjavac.server.Sjavac;
+import com.sun.tools.sjavac.server.SysInfo;
+
+/**
+ * The sjavac implementation that interacts with javac and performs the actual
+ * compilation.
+ *
+ *  <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 SjavacImpl implements Sjavac {
+
+    @Override
+    public SysInfo getSysInfo() {
+        return new SysInfo(Runtime.getRuntime().availableProcessors(),
+                           Runtime.getRuntime().maxMemory());
+    }
+
+    @Override
+    public CompilationResult compile(String protocolId,
+                                     String invocationId,
+                                     String[] args,
+                                     List<File> explicitSources,
+                                     Set<URI> sourcesToCompile,
+                                     Set<URI> visibleSources) {
+        final AtomicBoolean forcedExit = new AtomicBoolean();
+
+        JavacTool compiler = JavacTool.create();
+        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
+        SmartFileManager smartFileManager = new SmartFileManager(fileManager);
+        Context context = new Context();
+        ResolveWithDeps.preRegister(context);
+        AttrWithDeps.preRegister(context);
+        JavaCompilerWithDeps.preRegister(context, new SjavacErrorHandler() {
+            @Override
+            public void logError(String msg) {
+                forcedExit.set(true);
+            }
+        });
+
+        // Now setup the actual compilation....
+        CompilationResult compilationResult = new CompilationResult(0);
+
+        // First deal with explicit source files on cmdline and in at file.
+        ListBuffer<JavaFileObject> compilationUnits = new ListBuffer<>();
+        for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(explicitSources)) {
+            compilationUnits.append(i);
+        }
+        // Now deal with sources supplied as source_to_compile.
+        ListBuffer<File> sourcesToCompileFiles = new ListBuffer<>();
+        for (URI u : sourcesToCompile) {
+            sourcesToCompileFiles.append(new File(u));
+        }
+        for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(sourcesToCompileFiles)) {
+            compilationUnits.append(i);
+        }
+        forcedExit.set(false);
+        // Create a new logger.
+        StringWriter stdoutLog = new StringWriter();
+        StringWriter stderrLog = new StringWriter();
+        PrintWriter stdout = new PrintWriter(stdoutLog);
+        PrintWriter stderr = new PrintWriter(stderrLog);
+        com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK;
+        try {
+            if (compilationUnits.size() > 0) {
+                smartFileManager.setVisibleSources(visibleSources);
+                smartFileManager.cleanArtifacts();
+                smartFileManager.setLog(stdout);
+
+                // Do the compilation!
+                CompilationTask task = compiler.getTask(stderr,
+                                                        smartFileManager,
+                                                        null,
+                                                        Arrays.asList(args),
+                                                        null,
+                                                        compilationUnits,
+                                                        context);
+                smartFileManager.setSymbolFileEnabled(!Options.instance(context).isSet("ignore.symbol.file"));
+                rc = ((JavacTaskImpl) task).doCall();
+                smartFileManager.flush();
+            }
+        } catch (Exception e) {
+            stderrLog.append(e.getMessage());
+            forcedExit.set(true);
+        }
+
+        compilationResult.packageArtifacts = smartFileManager.getPackageArtifacts();
+
+        Dependencies deps = Dependencies.instance(context);
+        compilationResult.packageDependencies = deps.getDependencies();
+        compilationResult.packagePubapis = deps.getPubapis();
+
+        compilationResult.stdout = stdoutLog.toString();
+        compilationResult.stderr = stderrLog.toString();
+
+        compilationResult.returnCode = rc.exitCode == 0 && forcedExit.get() ? -1 : rc.exitCode;
+
+        return compilationResult;
+    }
+
+    @Override
+    public void shutdown() {
+        // Nothing to clean up
+        // ... maybe we should wait for any current request to finish?
+    }
+
+
+    @Override
+    public String serverSettings() {
+        return "";
+    }
+
+}
--- a/src/share/classes/com/sun/tools/sjavac/comp/SmartFileManager.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/comp/SmartFileManager.java	Wed Aug 13 14:44:59 2014 +0200
@@ -37,7 +37,6 @@
 import javax.tools.JavaFileObject.Kind;
 
 import com.sun.tools.javac.file.JavacFileManager;
-import com.sun.tools.javac.util.BaseFileManager;
 import com.sun.tools.javac.util.ListBuffer;
 
 /**
@@ -50,10 +49,10 @@
  * Can also blind out the filemanager from seeing certain files in the file system.
  * Necessary to prevent javac from seeing some sources where the source path points.
  *
- * <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></p>
+ *  <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 SmartFileManager extends ForwardingJavaFileManager<JavaFileManager> {
 
@@ -97,9 +96,7 @@
     public Iterable<JavaFileObject> list(Location location,
                                          String packageName,
                                          Set<Kind> kinds,
-                                         boolean recurse)
-        throws IOException
-    {
+                                         boolean recurse) throws IOException {
         // Acquire the list of files.
         Iterable<JavaFileObject> files = super.list(location, packageName, kinds, recurse);
         if (visibleSources.isEmpty()) {
@@ -112,8 +109,7 @@
             String t = uri.toString();
             if (t.startsWith("jar:")
                 || t.endsWith(".class")
-                || visibleSources.contains(uri))
-            {
+                || visibleSources.contains(uri)) {
                 filteredFiles.add(f);
             }
         }
@@ -128,9 +124,7 @@
     @Override
     public JavaFileObject getJavaFileForInput(Location location,
                                               String className,
-                                              Kind kind)
-        throws IOException
-    {
+                                              Kind kind) throws IOException {
         JavaFileObject file = super.getJavaFileForInput(location, className, kind);
         if (file == null || visibleSources.isEmpty()) {
             return file;
@@ -146,9 +140,7 @@
     public JavaFileObject getJavaFileForOutput(Location location,
                                                String className,
                                                Kind kind,
-                                               FileObject sibling)
-        throws IOException
-    {
+                                               FileObject sibling) throws IOException {
         JavaFileObject file = super.getJavaFileForOutput(location, className, kind, sibling);
         if (file == null) return file;
         int dp = className.lastIndexOf('.');
@@ -165,9 +157,7 @@
     @Override
     public FileObject getFileForInput(Location location,
                                       String packageName,
-                                      String relativeName)
-        throws IOException
-    {
+                                      String relativeName) throws IOException {
         FileObject file =  super.getFileForInput(location, packageName, relativeName);
         if (file == null || visibleSources.isEmpty()) {
             return file;
@@ -183,9 +173,7 @@
     public FileObject getFileForOutput(Location location,
                                        String packageName,
                                        String relativeName,
-                                       FileObject sibling)
-        throws IOException
-    {
+                                       FileObject sibling) throws IOException {
         FileObject file = super.getFileForOutput(location, packageName, relativeName, sibling);
         if (file == null) return file;
         if (location.equals(StandardLocation.NATIVE_HEADER_OUTPUT) &&
--- a/src/share/classes/com/sun/tools/sjavac/comp/SmartFileObject.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/comp/SmartFileObject.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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
@@ -36,10 +36,10 @@
  * and compare the new content with the old content on disk. Only if they differ,
  * will the file be updated.
  *
- * <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></p>
+ *  <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 SmartFileObject implements JavaFileObject {
 
--- a/src/share/classes/com/sun/tools/sjavac/comp/SmartWriter.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/comp/SmartWriter.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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
@@ -34,9 +34,9 @@
  * If not, the file is not touched.
  *
  *  <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></p>
+ *  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 SmartWriter extends Writer {
 
--- a/src/share/classes/com/sun/tools/sjavac/options/ArgumentIterator.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/options/ArgumentIterator.java	Wed Aug 13 14:44:59 2014 +0200
@@ -27,6 +27,12 @@
 
 import java.util.Iterator;
 
+/**
+ *  <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 ArgumentIterator implements Iterator<String> {
 
     /** The underlying argument iterator */
--- a/src/share/classes/com/sun/tools/sjavac/options/Option.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/options/Option.java	Wed Aug 13 14:44:59 2014 +0200
@@ -47,6 +47,11 @@
  * This enum represents all options from (1) and (2). Note that instances of
  * this enum only entail static information about the option. For storage of
  * option values, refer to com.sun.tools.sjavac.options.Options.
+ *
+ *  <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 enum Option {
 
--- a/src/share/classes/com/sun/tools/sjavac/options/OptionHelper.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/options/OptionHelper.java	Wed Aug 13 14:44:59 2014 +0200
@@ -26,7 +26,6 @@
 package com.sun.tools.sjavac.options;
 
 import java.io.IOException;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
@@ -38,6 +37,11 @@
 /**
  * This class is used to decode sjavac options.
  * See com.sun.tools.sjavac.options.Options for example usage.
+ *
+ *  <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 abstract class OptionHelper {
 
--- a/src/share/classes/com/sun/tools/sjavac/options/Options.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/options/Options.java	Wed Aug 13 14:44:59 2014 +0200
@@ -39,6 +39,11 @@
 
 /**
  * Instances of this class represent values for sjavac command line options.
+ *
+ *  <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 Options {
 
--- a/src/share/classes/com/sun/tools/sjavac/options/SourceLocation.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/options/SourceLocation.java	Wed Aug 13 14:44:59 2014 +0200
@@ -37,6 +37,11 @@
 /**
  * Represents a directory to be used for input to sjavac. (For instance a
  * sourcepath or classpath.)
+ *
+ *  <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 SourceLocation {
 
--- a/src/share/classes/com/sun/tools/sjavac/server/CompilationResult.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/server/CompilationResult.java	Wed Aug 13 14:44:59 2014 +0200
@@ -25,28 +25,35 @@
 
 package com.sun.tools.sjavac.server;
 
+import java.io.Serializable;
 import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
-public class CompilationResult {
+/**
+ *
+ *  <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_BUT_TRY_AGAIN = -4712;
     public final static int ERROR_FATAL = -1;
 
     public int returnCode;
     public Map<String, Set<URI>> packageArtifacts = new HashMap<>();
     public Map<String, Set<String>> packageDependencies = new HashMap<>();
     public Map<String, String> packagePubapis = new HashMap<>();
-    public SysInfo sysinfo;
-    public String stdout;
-    public String stderr;
+    public String stdout = "";
+    public String stderr = "";
 
     public CompilationResult(int returnCode) {
         this.returnCode = returnCode;
-        this.sysinfo = new SysInfo(-1, -1);
     }
 
     public void setReturnCode(int returnCode) {
--- a/src/share/classes/com/sun/tools/sjavac/server/CompilerPool.java	Tue Aug 12 17:48:30 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * Copyright (c) 2012, 2014, 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.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Semaphore;
-import java.util.Stack;
-import java.util.concurrent.Future;
-
-import com.sun.tools.sjavac.comp.JavacServiceImpl;
-
-/** The compiler pool maintains compiler threads.
- *
- * <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></p>
- */
-public class CompilerPool {
-    // The javac server that created this pool.
-    private JavacServer javacServer;
-    // A semaphore protecting the poolsize number of threads.
-    private Semaphore available;
-    // The stack of compiler threads.
-    private Stack<CompilerThread> compilers = new Stack<>();
-    // And the executor server to spawn threads.
-    private final ExecutorService executorPool;
-    // How many requests are active right now?
-    private int concurrentRequests = 0;
-    // When was the last request finished?
-    private long lastRequestFinished = 0;
-    // The total number of requests to this pool.
-    private int numRequests = 0;
-    // Protect access to the three above values.
-    private static final Object conc = new Object();
-
-    /**
-     * Return the javac server that this pool belongs to.
-     */
-    public JavacServer getJavacServer() {
-        return javacServer;
-    }
-
-    /**
-     * Return how many threads are running at this very moment.
-     */
-    public int numActiveRequests()
-    {
-        synchronized (conc) {
-            return concurrentRequests;
-        }
-    }
-
-    /**
-     * Return when the last request was finished.
-     * I.e. the pool has been idle since.
-     */
-    public long lastRequestFinished()
-    {
-        synchronized (conc) {
-            return lastRequestFinished;
-        }
-    }
-
-    /**
-     * Up the number of active requests.
-     */
-    public int startRequest() {
-        int n;
-        synchronized (conc) {
-            concurrentRequests++;
-            numRequests++;
-            n = numRequests;
-        }
-        return n;
-    }
-
-    /**
-     * Down the number of active requests. Return the current time.
-     */
-    public long stopRequest() {
-        synchronized (conc) {
-            concurrentRequests--;
-            lastRequestFinished = System.currentTimeMillis();
-        }
-        return lastRequestFinished;
-    }
-
-    /**
-     * Create a new compiler pool.
-     */
-    CompilerPool(int poolsize, JavacServer server) {
-        available = new Semaphore(poolsize, true);
-        javacServer = server;
-        executorPool = Executors.newFixedThreadPool(poolsize);
-        lastRequestFinished = System.currentTimeMillis();
-    }
-
-    /**
-     * Execute a compiler thread.
-     */
-    public void execute(CompilerThread ct) {
-        executorPool.execute(ct);
-    }
-
-    /**
-     * Execute a minor task, for example generating bytecodes and writing them to disk,
-     * that belong to a major compiler thread task.
-     */
-    public Future<?> executeSubtask(CompilerThread t, Runnable r) {
-        return executorPool.submit(r);
-    }
-
-    /**
-     * Shutdown the pool.
-     */
-    public void shutdown() {
-        executorPool.shutdown();
-    }
-
-    /**
-     * Acquire a compiler thread from the pool, or block until a thread is available.
-     * If the pools is empty, create a new thread, but never more than is "available".
-     */
-    public CompilerThread grabCompilerThread() throws InterruptedException {
-        available.acquire();
-        if (compilers.empty()) {
-            return new CompilerThread(this, new JavacServiceImpl(javacServer));
-        }
-        return compilers.pop();
-    }
-
-    /**
-     * Return the specified compiler thread to the pool.
-     */
-    public void returnCompilerThread(CompilerThread h) {
-        compilers.push(h);
-        available.release();
-    }
-}
-
--- a/src/share/classes/com/sun/tools/sjavac/server/CompilerThread.java	Tue Aug 12 17:48:30 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,412 +0,0 @@
-/*
- * Copyright (c) 2012, 2014, 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.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.Socket;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Future;
-
-import javax.tools.JavaCompiler.CompilationTask;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-
-import com.sun.tools.javac.api.JavacTaskImpl;
-import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.util.ListBuffer;
-import com.sun.tools.javac.util.Options;
-import com.sun.tools.javac.util.StringUtils;
-import com.sun.tools.sjavac.comp.AttrWithDeps;
-import com.sun.tools.sjavac.comp.Dependencies;
-import com.sun.tools.sjavac.comp.JavaCompilerWithDeps;
-import com.sun.tools.sjavac.comp.JavacServiceImpl;
-import com.sun.tools.sjavac.comp.ResolveWithDeps;
-import com.sun.tools.sjavac.comp.SmartFileManager;
-
-/**
- * The compiler thread maintains a JavaCompiler instance and
- * can receive a request from the client, perform the compilation
- * requested and report back the results.
- *
- *  * <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></p>
- */
-public class CompilerThread implements Runnable {
-    private JavacServer javacServer;
-    private CompilerPool compilerPool;
-    private JavacServiceImpl javacServiceImpl;
-    private List<Future<?>> subTasks;
-
-    // Communicating over this socket.
-    private Socket socket;
-
-    // The necessary classes to do a compilation.
-    private com.sun.tools.javac.api.JavacTool compiler;
-    private StandardJavaFileManager fileManager;
-    private SmartFileManager smartFileManager;
-    private Context context;
-
-    // If true, then this thread is serving a request.
-    private boolean inUse = false;
-
-    CompilerThread(CompilerPool cp, JavacServiceImpl javacServiceImpl) {
-        compilerPool = cp;
-        javacServer = cp.getJavacServer();
-        this.javacServiceImpl = javacServiceImpl;
-    }
-
-    /**
-     * Execute a minor task, for example generating bytecodes and writing them to disk,
-     * that belong to a major compiler thread task.
-     */
-    public synchronized void executeSubtask(Runnable r) {
-        subTasks.add(compilerPool.executeSubtask(this, r));
-    }
-
-    /**
-     * Count the number of active sub tasks.
-     */
-    public synchronized int numActiveSubTasks() {
-        int c = 0;
-        for (Future<?> f : subTasks) {
-            if (!f.isDone() && !f.isCancelled()) {
-                c++;
-            }
-        }
-        return c;
-    }
-
-    /**
-     * Use this socket for the upcoming request.
-     */
-    public void setSocket(Socket s) {
-        socket = s;
-    }
-
-    /**
-     * Prepare the compiler thread for use. It is not yet started.
-     * It will be started by the executor service.
-     */
-    public synchronized void use() {
-        assert(!inUse);
-        inUse = true;
-        compiler = com.sun.tools.javac.api.JavacTool.create();
-        fileManager = compiler.getStandardFileManager(null, null, null);
-        smartFileManager = new SmartFileManager(fileManager);
-        context = new Context();
-        ResolveWithDeps.preRegister(context);
-        AttrWithDeps.preRegister(context);
-        JavaCompilerWithDeps.preRegister(context, javacServiceImpl);
-        subTasks = new ArrayList<>();
-    }
-
-    /**
-     * Prepare the compiler thread for idleness.
-     */
-    public synchronized void unuse() {
-        assert(inUse);
-        inUse = false;
-        compiler = null;
-        fileManager = null;
-        smartFileManager = null;
-        context = null;
-        subTasks = null;
-    }
-
-    /**
-     * Expect this key on the next line read from the reader.
-     */
-    private static boolean expect(BufferedReader in, String key) throws IOException {
-        String s = in.readLine();
-        if (s != null && s.equals(key)) {
-            return true;
-        }
-        return false;
-    }
-
-    // The request identifier, for example GENERATE_NEWBYTECODE
-    String id = "";
-
-    public String currentRequestId() {
-        return id;
-    }
-
-    PrintWriter stdout;
-    PrintWriter stderr;
-    int forcedExitCode = 0;
-
-    public void logError(String msg) {
-        stderr.println(msg);
-        forcedExitCode = -1;
-    }
-
-    /**
-     * Invoked by the executor service.
-     */
-    public void run() {
-        // Unique nr that identifies this request.
-        int thisRequest = compilerPool.startRequest();
-        long start = System.currentTimeMillis();
-        int numClasses = 0;
-        StringBuilder compiledPkgs = new StringBuilder();
-        use();
-
-        PrintWriter out = null;
-        try {
-            javacServer.log("<"+thisRequest+"> Connect from "+socket.getRemoteSocketAddress()+" activethreads="+compilerPool.numActiveRequests());
-            BufferedReader in = new BufferedReader(new InputStreamReader(
-                                                       socket.getInputStream()));
-            out = new PrintWriter(new OutputStreamWriter(
-                                                  socket.getOutputStream()));
-            if (!expect(in, JavacServer.PROTOCOL_COOKIE_VERSION)) {
-                javacServer.log("<"+thisRequest+"> Bad protocol from ip "+socket.getRemoteSocketAddress());
-                return;
-            }
-
-            String cookie = in.readLine();
-            if (cookie == null || !cookie.equals(""+javacServer.getCookie())) {
-                javacServer.log("<"+thisRequest+"> Bad cookie from ip "+socket.getRemoteSocketAddress());
-                return;
-            }
-            if (!expect(in, JavacServer.PROTOCOL_CWD)) {
-                return;
-            }
-            String cwd = in.readLine();
-            if (cwd == null)
-                return;
-            if (!expect(in, JavacServer.PROTOCOL_ID)) {
-                return;
-            }
-            id = in.readLine();
-            if (id == null)
-                return;
-            if (!expect(in, JavacServer.PROTOCOL_ARGS)) {
-                return;
-            }
-            ArrayList<String> the_options = new ArrayList<>();
-            ArrayList<File> the_classes = new ArrayList<>();
-            Iterable<File> path = Arrays.<File> asList(new File(cwd));
-
-            for (;;) {
-                String l = in.readLine();
-                if (l == null)
-                    return;
-                if (l.equals(JavacServer.PROTOCOL_SOURCES_TO_COMPILE))
-                    break;
-                if (l.startsWith("--server:"))
-                    continue;
-                if (!l.startsWith("-") && l.endsWith(".java")) {
-                    the_classes.add(new File(l));
-                    numClasses++;
-                } else {
-                    the_options.add(l);
-                }
-                continue;
-            }
-
-            // Load sources to compile
-            Set<URI> sourcesToCompile = new HashSet<>();
-            for (;;) {
-                String l = in.readLine();
-                if (l == null)
-                    return;
-                if (l.equals(JavacServer.PROTOCOL_VISIBLE_SOURCES))
-                    break;
-                try {
-                    sourcesToCompile.add(new URI(l));
-                    numClasses++;
-                } catch (URISyntaxException e) {
-                    return;
-                }
-            }
-            // Load visible sources
-            Set<URI> visibleSources = new HashSet<>();
-            boolean fix_drive_letter_case =
-                StringUtils.toLowerCase(System.getProperty("os.name")).startsWith("windows");
-            for (;;) {
-                String l = in.readLine();
-                if (l == null)
-                    return;
-                if (l.equals(JavacServer.PROTOCOL_END))
-                    break;
-                try {
-                    URI u = new URI(l);
-                    if (fix_drive_letter_case) {
-                        // Make sure the driver letter is lower case.
-                        String s = u.toString();
-                        if (s.startsWith("file:/") &&
-                            Character.isUpperCase(s.charAt(6))) {
-                            u = new URI("file:/"+Character.toLowerCase(s.charAt(6))+s.substring(7));
-                        }
-                    }
-                    visibleSources.add(u);
-                } catch (URISyntaxException e) {
-                    return;
-                }
-            }
-
-            // A completed request has been received.
-
-            // Now setup the actual compilation....
-            // First deal with explicit source files on cmdline and in at file.
-            ListBuffer<JavaFileObject> compilationUnits = new ListBuffer<>();
-            for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(the_classes)) {
-                compilationUnits.append(i);
-            }
-            // Now deal with sources supplied as source_to_compile.
-            ListBuffer<File> sourcesToCompileFiles = new ListBuffer<>();
-            for (URI u : sourcesToCompile) {
-                sourcesToCompileFiles.append(new File(u));
-            }
-            for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(sourcesToCompileFiles)) {
-                compilationUnits.append(i);
-            }
-            // Log the options to be used.
-            StringBuilder options = new StringBuilder();
-            for (String s : the_options) {
-                options.append(">").append(s).append("< ");
-            }
-            javacServer.log(id+" <"+thisRequest+"> options "+options.toString());
-
-            forcedExitCode = 0;
-            // Create a new logger.
-            StringWriter stdoutLog = new StringWriter();
-            StringWriter stderrLog = new StringWriter();
-            stdout = new PrintWriter(stdoutLog);
-            stderr = new PrintWriter(stderrLog);
-            com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK;
-            try {
-                if (compilationUnits.size() > 0) {
-                    smartFileManager.setVisibleSources(visibleSources);
-                    smartFileManager.cleanArtifacts();
-                    smartFileManager.setLog(stdout);
-
-                    // Do the compilation!
-                    CompilationTask task = compiler.getTask(stderr, smartFileManager, null, the_options, null, compilationUnits, context);
-                    smartFileManager.setSymbolFileEnabled(!Options.instance(context).isSet("ignore.symbol.file"));
-                    rc = ((JavacTaskImpl) task).doCall();
-
-                    while (numActiveSubTasks()>0) {
-                        try { Thread.sleep(1000); } catch (InterruptedException e) { }
-                    }
-
-                    smartFileManager.flush();
-                }
-            } catch (Exception e) {
-                stderr.println(e.getMessage());
-                forcedExitCode = -1;
-            }
-
-            // Send the response..
-            out.println(JavacServer.PROTOCOL_STDOUT);
-            out.print(stdoutLog);
-            out.println(JavacServer.PROTOCOL_STDERR);
-            out.print(stderrLog);
-            // The compilation is complete! And errors will have already been printed on out!
-            out.println(JavacServer.PROTOCOL_PACKAGE_ARTIFACTS);
-            Map<String,Set<URI>> pa = smartFileManager.getPackageArtifacts();
-            for (String aPkgName : pa.keySet()) {
-                out.println("+"+aPkgName);
-                Set<URI> as = pa.get(aPkgName);
-                for (URI a : as) {
-                    out.println(" "+a.toString());
-                }
-            }
-            Dependencies deps = Dependencies.instance(context);
-            out.println(JavacServer.PROTOCOL_PACKAGE_DEPENDENCIES);
-            Map<String,Set<String>> pd = deps.getDependencies();
-            for (String aPkgName : pd.keySet()) {
-                out.println("+"+aPkgName);
-                Set<String> ds = pd.get(aPkgName);
-                    // Everything depends on java.lang
-                    if (!ds.contains(":java.lang")) ds.add(":java.lang");
-                for (String d : ds) {
-                    out.println(" "+d);
-                }
-            }
-            out.println(JavacServer.PROTOCOL_PACKAGE_PUBLIC_APIS);
-            Map<String,String> pp = deps.getPubapis();
-            for (String aPkgName : pp.keySet()) {
-                out.println("+"+aPkgName);
-                String ps = pp.get(aPkgName);
-                // getPubapis added a space to each line!
-                out.println(ps);
-                compiledPkgs.append(aPkgName+" ");
-            }
-            out.println(JavacServer.PROTOCOL_SYSINFO);
-            out.println("num_cores=" + Runtime.getRuntime().availableProcessors());
-            out.println("max_memory=" + Runtime.getRuntime().maxMemory());
-            out.println(JavacServer.PROTOCOL_RETURN_CODE);
-
-            // Errors from sjavac that affect compilation status!
-            int rcv = rc.exitCode;
-            if (rcv == 0 && forcedExitCode != 0) {
-                rcv = forcedExitCode;
-            }
-            out.println("" + rcv);
-            out.println(JavacServer.PROTOCOL_END);
-            out.flush();
-        } catch (IOException e) {
-            e.printStackTrace();
-        } finally {
-            try {
-                if (out != null) out.close();
-                if (!socket.isClosed()) {
-                    socket.close();
-                }
-                socket = null;
-            } catch (Exception e) {
-                javacServer.log("ERROR "+e);
-                e.printStackTrace();
-            }
-            compilerPool.stopRequest();
-            long duration = System.currentTimeMillis()-start;
-            javacServer.addBuildTime(duration);
-            float classpersec = ((float)numClasses)*(((float)1000.0)/((float)duration));
-            javacServer.log(id+" <"+thisRequest+"> "+compiledPkgs+" duration " + duration+ " ms    num_classes="+numClasses+
-                             "     classpersec="+classpersec+" subtasks="+subTasks.size());
-            javacServer.flushLog();
-            unuse();
-            compilerPool.returnCompilerThread(this);
-        }
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/IdleResetSjavac.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2014, 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.File;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * An sjavac implementation that keeps track of idleness and shuts down the
+ * given Terminable upon idleness timeout.
+ *
+ * An idleness timeout kicks in {@code idleTimeout} milliseconds after the last
+ * request is completed.
+ *
+ *  <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 IdleResetSjavac implements Sjavac {
+
+    private final Sjavac delegate;
+    private final AtomicInteger outstandingCalls = new AtomicInteger();
+    private final Terminable toShutdown;
+    private final Timer idlenessTimer = new Timer();
+    private final long idleTimeout;
+
+    // Class invariant: idlenessTimerTask != null <-> idlenessTimerTask is scheduled
+    private TimerTask idlenessTimerTask;
+
+    public IdleResetSjavac(Sjavac delegate,
+                            Terminable toShutdown,
+                            long idleTimeout) {
+        this.delegate = delegate;
+        this.toShutdown = toShutdown;
+        this.idleTimeout = idleTimeout;
+        scheduleTimeout();
+    }
+
+    @Override
+    public SysInfo getSysInfo() {
+        startCall();
+        try {
+            return delegate.getSysInfo();
+        } finally {
+            endCall();
+        }
+    }
+
+    @Override
+    public CompilationResult compile(String protocolId,
+                                     String invocationId,
+                                     String[] args,
+                                     List<File> explicitSources,
+                                     Set<URI> sourcesToCompile,
+                                     Set<URI> visibleSources) {
+        startCall();
+        try {
+            return delegate.compile(protocolId,
+                                    invocationId,
+                                    args,
+                                    explicitSources,
+                                    sourcesToCompile,
+                                    visibleSources);
+        } finally {
+            endCall();
+        }
+    }
+
+    private void startCall() {
+        // Was there no outstanding calls before this call?
+        if (outstandingCalls.incrementAndGet() == 1) {
+            // Then the timer task must have been scheduled
+            if (idlenessTimerTask == null)
+                throw new IllegalStateException("Idle timeout already cancelled");
+            // Cancel timeout task
+            idlenessTimerTask.cancel();
+            idlenessTimerTask = null;
+        }
+    }
+
+    private void endCall() {
+        if (outstandingCalls.decrementAndGet() == 0) {
+            // No more outstanding calls. Schedule timeout.
+            scheduleTimeout();
+        }
+    }
+
+    private void scheduleTimeout() {
+        if (idlenessTimerTask != null)
+            throw new IllegalStateException("Idle timeout already scheduled");
+        idlenessTimerTask = new TimerTask() {
+            public void run() {
+                toShutdown.shutdown("Server has been idle for " + (idleTimeout / 1000) + " seconds.");
+            }
+        };
+        idlenessTimer.schedule(idlenessTimerTask, idleTimeout);
+    }
+
+    @Override
+    public void shutdown() {
+        idlenessTimer.cancel();
+        delegate.shutdown();
+    }
+
+    @Override
+    public String serverSettings() {
+        return delegate.serverSettings();
+    }
+}
--- a/src/share/classes/com/sun/tools/sjavac/server/JavacServer.java	Tue Aug 12 17:48:30 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,382 +0,0 @@
-/*
- * Copyright (c) 2011-2012, 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.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.FileNotFoundException;
-import java.util.HashMap;
-import java.util.Map;
-
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.Random;
-
-import com.sun.tools.sjavac.Util;
-import com.sun.tools.sjavac.ProblemException;
-import java.io.*;
-
-/**
- * The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
- *
- * <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></p>
- */
-public class JavacServer {
-    // Responding to this tcp/ip port on localhost.
-
-    private final ServerSocket serverSocket;
-    // The secret cookie shared between server and client through the port file.
-    private final long myCookie;
-    // When the server was started.
-    private long serverStart;
-    // Accumulated build time for all requests, not counting idle time.
-    private long totalBuildTime;
-    // The javac server specific log file.
-    PrintWriter theLog;
-    // The compiler pool that maintains the compiler threads.
-    CompilerPool compilerPool;
-    // 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;
-    final static String PROTOCOL_COOKIE_VERSION = "----THE-COOKIE-V2----";
-    final static String PROTOCOL_CWD = "----THE-CWD----";
-    final static String PROTOCOL_ID = "----THE-ID----";
-    final static String PROTOCOL_ARGS = "----THE-ARGS----";
-    final static String PROTOCOL_SOURCES_TO_COMPILE = "----THE-SOURCES-TO-COMPILE----";
-    final static String PROTOCOL_VISIBLE_SOURCES = "----THE-VISIBLE-SOURCES----";
-    final static String PROTOCOL_END = "----THE-END----";
-    final static String PROTOCOL_STDOUT = "----THE-STDOUT----";
-    final static String PROTOCOL_STDERR = "----THE-STDERR----";
-    final static String PROTOCOL_PACKAGE_ARTIFACTS = "----THE-PACKAGE_ARTIFACTS----";
-    final static String PROTOCOL_PACKAGE_DEPENDENCIES = "----THE-PACKAGE_DEPENDENCIES----";
-    final static String PROTOCOL_PACKAGE_PUBLIC_APIS = "----THE-PACKAGE-PUBLIC-APIS----";
-    final static String PROTOCOL_SYSINFO = "----THE-SYSINFO----";
-    final static String PROTOCOL_RETURN_CODE = "----THE-RETURN-CODE----";
-    // Check if the portfile is gone, every 5 seconds.
-    static int CHECK_PORTFILE_INTERVAL = 5;
-    // Wait 2 seconds for response, before giving up on javac server.
-    static int CONNECTION_TIMEOUT = 2;
-    static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 1;
-    static int MAX_NUM_CONNECT_ATTEMPTS = 3;
-
-    /**
-     * 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 FileNotFoundException {
-        if (allPortFiles == null) {
-            allPortFiles = new HashMap<>();
-        }
-        PortFile pf = allPortFiles.get(filename);
-
-        // Port file known. Does it still exist?
-        if (pf != null) {
-            try {
-                if (!pf.exists())
-                    pf = null;
-            } catch (IOException ioex) {
-                ioex.printStackTrace();
-            }
-        }
-
-        if (pf == null) {
-            pf = new PortFile(filename);
-            allPortFiles.put(filename, pf);
-        }
-        return pf;
-    }
-
-    /**
-     * Get the cookie used for this server.
-     */
-    long getCookie() {
-        return myCookie;
-    }
-
-    /**
-     * Get the port used for this server.
-     */
-    int getPort() {
-        return serverSocket.getLocalPort();
-    }
-
-    /**
-     * Sum up the total build time for this javac server.
-     */
-    public void addBuildTime(long inc) {
-        totalBuildTime += inc;
-    }
-
-    /**
-     * Log this message.
-     */
-    public void log(String msg) {
-        if (theLog != null) {
-            theLog.println(msg);
-        } else {
-            System.err.println(msg);
-        }
-    }
-
-    /**
-     * Make sure the log is flushed.
-     */
-    public void flushLog() {
-        if (theLog != null) {
-            theLog.flush();
-        }
-    }
-
-    /**
-     * Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3"
-     * is sent as the settings parameter. Returns 0 on success, -1 on failure.
-     */
-    public static int startServer(String settings, PrintStream err) {
-        try {
-            String portfile = Util.extractStringOption("portfile", settings);
-            // The log file collects more javac server specific log information.
-            String logfile = Util.extractStringOption("logfile", settings);
-            // The stdouterr file collects all the System.out and System.err writes to disk.
-            String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
-            // We could perhaps use System.setOut and setErr here.
-            // But for the moment we rely on the client to spawn a shell where stdout
-            // and stderr are redirected already.
-            // The pool size is a limit the number of concurrent compiler threads used.
-            // The server might use less than these to avoid memory problems.
-            int defaultPoolSize = Runtime.getRuntime().availableProcessors();
-            int poolsize = Util.extractIntOption("poolsize", settings, defaultPoolSize);
-
-            // How many seconds of inactivity will the server accept before quitting?
-            int keepalive = Util.extractIntOption("keepalive", settings, 120);
-
-            // The port file is locked and the server port and cookie is written into it.
-            PortFile portFile = getPortFile(portfile);
-            JavacServer s;
-
-            synchronized (portFile) {
-                portFile.lock();
-                portFile.getValues();
-                if (portFile.containsPortInfo()) {
-                    err.println("Javac server not started because portfile exists!");
-                    portFile.unlock();
-                    return -1;
-                }
-                s = new JavacServer(poolsize, logfile);
-                portFile.setValues(s.getPort(), s.getCookie());
-                portFile.unlock();
-            }
-
-            // Run the server. Will delete the port file when shutting down.
-            // It will shut down automatically when no new requests have come in
-            // during the last 125 seconds.
-            s.run(portFile, err, keepalive);
-            // The run loop for the server has exited.
-            return 0;
-        } catch (Exception e) {
-            e.printStackTrace(err);
-            return -1;
-        }
-    }
-
-    /**
-     * Spawn the server instance.
-     */
-
-    private JavacServer(int poolSize, String logfile) throws IOException {
-        serverStart = System.currentTimeMillis();
-        // Create a server socket on a random port that is bound to the localhost/127.0.0.1 interface.
-        // I.e only local processes can connect to this port.
-        serverSocket = new ServerSocket(0, 128, InetAddress.getByName(null));
-        compilerPool = new CompilerPool(poolSize, this);
-        Random rnd = new Random();
-        myCookie = rnd.nextLong();
-        theLog = new PrintWriter(logfile);
-        log("Javac server started. port=" + getPort() + " date=" + (new java.util.Date()) + " with poolsize=" + poolSize);
-        flushLog();
-    }
-
-    /**
-     * Fork a background process. Returns the command line used that can be printed if something failed.
-     */
-    public static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive,
-            final PrintStream err, String stdouterrfile, boolean background)
-            throws IOException, ProblemException {
-        if (stdouterrfile != null && stdouterrfile.trim().equals("")) {
-            stdouterrfile = null;
-        }
-        final String startserver = "--startserver:portfile=" + portfile + ",logfile=" + logfile + ",stdouterrfile=" + stdouterrfile + ",poolsize=" + poolsize + ",keepalive="+ keepalive;
-
-        if (background) {
-            sjavac += "%20" + startserver;
-            sjavac = sjavac.replaceAll("%20", " ");
-            sjavac = sjavac.replaceAll("%2C", ",");
-            // If the java/sh/cmd launcher fails the failure will be captured by stdouterr because of the redirection here.
-            String[] cmd = {"/bin/sh", "-c", sjavac + " >> " + stdouterrfile + " 2>&1"};
-            if (!(new File("/bin/sh")).canExecute()) {
-                ArrayList<String> wincmd = new ArrayList<>();
-                wincmd.add("cmd");
-                wincmd.add("/c");
-                wincmd.add("start");
-                wincmd.add("cmd");
-                wincmd.add("/c");
-                wincmd.add(sjavac + " >> " + stdouterrfile + " 2>&1");
-                cmd = wincmd.toArray(new String[wincmd.size()]);
-            }
-            Process pp = null;
-            try {
-                pp = Runtime.getRuntime().exec(cmd);
-            } catch (Exception e) {
-                e.printStackTrace(err);
-                e.printStackTrace(new PrintWriter(stdouterrfile));
-            }
-            StringBuilder rs = new StringBuilder();
-            for (String s : cmd) {
-                rs.append(s + " ");
-            }
-            return rs.toString();
-        }
-
-        // Do not spawn a background server, instead run it within the same JVM.
-        Thread t = new Thread() {
-            @Override
-            public void run() {
-                try {
-                    JavacServer.startServer(startserver, err);
-                } catch (Throwable t) {
-                    t.printStackTrace(err);
-                }
-            }
-        };
-        t.start();
-        return "";
-    }
-
-    /**
-     * Run the server thread until it exits. Either because of inactivity or because the port file has been deleted by someone else, or overtaken by some other
-     * javac server.
-     */
-    private void run(PortFile portFile, PrintStream err, int keepalive) {
-        boolean fileDeleted = false;
-        long timeSinceLastCompile;
-        try {
-            // Every 5 second (check_portfile_interval) we test if the portfile has disappeared => quit
-            // Or if the last request was finished more than 125 seconds ago => quit
-            // 125 = seconds_of_inactivity_before_shutdown+check_portfile_interval
-            serverSocket.setSoTimeout(CHECK_PORTFILE_INTERVAL*1000);
-            for (;;) {
-                try {
-                    Socket s = serverSocket.accept();
-                    CompilerThread ct = compilerPool.grabCompilerThread();
-                    ct.setSocket(s);
-                    compilerPool.execute(ct);
-                    flushLog();
-                } catch (java.net.SocketTimeoutException e) {
-                    if (compilerPool.numActiveRequests() > 0) {
-                        // Never quit while there are active requests!
-                        continue;
-                    }
-                    // If this is the timeout after the portfile
-                    // has been deleted by us. Then we truly stop.
-                    if (fileDeleted) {
-                        log("Quitting because of "+(keepalive+CHECK_PORTFILE_INTERVAL)+" seconds of inactivity!");
-                        break;
-                    }
-                    // Check if the portfile is still there.
-                    if (!portFile.exists()) {
-                        // Time to quit because the portfile was deleted by another
-                        // process, probably by the makefile that is done building.
-                        log("Quitting because portfile was deleted!");
-                        flushLog();
-                        break;
-                    }
-                    // Check if portfile.stop is still there.
-                    if (portFile.markedForStop()) {
-                        // Time to quit because another process touched the file
-                        // server.port.stop to signal that the server should stop.
-                        // This is necessary on some operating systems that lock
-                        // the port file hard!
-                        log("Quitting because a portfile.stop file was found!");
-                        portFile.delete();
-                        flushLog();
-                        break;
-                    }
-                    // Does the portfile still point to me?
-                    if (!portFile.stillMyValues()) {
-                        // Time to quit because another build has started.
-                        log("Quitting because portfile is now owned by another javac server!");
-                        flushLog();
-                        break;
-                    }
-
-                    // Check how long since the last request finished.
-                    long diff = System.currentTimeMillis() - compilerPool.lastRequestFinished();
-                    if (diff < keepalive * 1000) {
-                        // Do not quit if we have waited less than 120 seconds.
-                        continue;
-                    }
-                    // Ok, time to quit because of inactivity. Perhaps the build
-                    // was killed and the portfile not cleaned up properly.
-                    portFile.delete();
-                    fileDeleted = true;
-                    log("" + keepalive + " seconds of inactivity quitting in "
-                        + CHECK_PORTFILE_INTERVAL + " seconds!");
-                    flushLog();
-                    // Now we have a second 5 second grace
-                    // period where javac remote requests
-                    // that have loaded the data from the
-                    // recently deleted portfile can connect
-                    // and complete their requests.
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace(err);
-            e.printStackTrace(theLog);
-            flushLog();
-        } finally {
-            compilerPool.shutdown();
-        }
-        long realTime = System.currentTimeMillis() - serverStart;
-        log("Shutting down.");
-        log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
-        flushLog();
-    }
-
-    public static void cleanup(String... args) {
-        String settings = Util.findServerSettings(args);
-        if (settings == null) return;
-        String portfile = Util.extractStringOption("portfile", settings);
-        String background = Util.extractStringOption("background", settings);
-        if (background != null && background.equals("false")) {
-            // If the server runs within this jvm, then delete the portfile,
-            // since this jvm is about to exit soon.
-            File f = new File(portfile);
-            f.delete();
-        }
-    }
-}
--- a/src/share/classes/com/sun/tools/sjavac/server/JavacService.java	Tue Aug 12 17:48:30 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2014, 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.File;
-import java.net.URI;
-import java.util.List;
-import java.util.Set;
-
-public interface JavacService {
-
-    SysInfo getSysInfo();
-
-    CompilationResult compile(String protocolId,
-                              String invocationId,
-                              String[] args,
-                              List<File> explicitSources,
-                              Set<URI> sourcesToCompile,
-                              Set<URI> visibleSources);
-    String serverSettings();
-}
--- a/src/share/classes/com/sun/tools/sjavac/server/JavacServiceClient.java	Tue Aug 12 17:48:30 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,449 +0,0 @@
-/*
- * Copyright (c) 2014, 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.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.URI;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import com.sun.tools.sjavac.Util;
-import com.sun.tools.sjavac.options.Options;
-
-import static com.sun.tools.sjavac.server.CompilationResult.ERROR_BUT_TRY_AGAIN;
-import static com.sun.tools.sjavac.server.CompilationResult.ERROR_FATAL;
-
-public class JavacServiceClient implements JavacService {
-
-
-    // The id can perhaps be used in the future by the javac server to reuse the
-    // JavaCompiler instance for several compiles using the same id.
-    private final String id;
-    private final String portfile;
-    private final String logfile;
-    private final String stdouterrfile;
-    private final boolean background;
-
-    // Default keepalive for server is 120 seconds.
-    // I.e. it will accept 120 seconds of inactivity before quitting.
-    private final int keepalive;
-    private final int poolsize;
-
-    // The sjavac option specifies how the server part of sjavac is spawned.
-    // If you have the experimental sjavac in your path, you are done. If not, you have
-    // to point to a com.sun.tools.sjavac.Main that supports --startserver
-    // for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main
-    private final String sjavac;
-
-    // Store the server conf settings here.
-    private final String settings;
-
-    public JavacServiceClient(Options options) {
-        String tmpServerConf = options.getServerConf();
-        String serverConf = (tmpServerConf!=null)? tmpServerConf : "";
-        String tmpId = Util.extractStringOption("id", serverConf);
-        id = (tmpId!=null) ? tmpId : "id"+(((new java.util.Random()).nextLong())&Long.MAX_VALUE);
-        String p = Util.extractStringOption("portfile", serverConf);
-        portfile = (p!=null) ? p : options.getStateDir().toFile().getAbsolutePath()+File.separatorChar+"javac_server";
-        logfile = Util.extractStringOption("logfile", serverConf, portfile + ".javaclog");
-        stdouterrfile = Util.extractStringOption("stdouterrfile", serverConf, portfile + ".stdouterr");
-        background = Util.extractBooleanOption("background", serverConf, true);
-        sjavac = Util.extractStringOption("sjavac", serverConf, "sjavac");
-        int poolsize = Util.extractIntOption("poolsize", serverConf);
-        keepalive = Util.extractIntOption("keepalive", serverConf, 120);
-
-        this.poolsize = poolsize > 0 ? poolsize : Runtime.getRuntime().availableProcessors();
-        settings = (serverConf.equals("")) ? "id="+id+",portfile="+portfile : serverConf;
-    }
-
-    /**
-     * Hand out the server settings.
-     * @return The server settings, possibly a default value.
-     */
-    public String serverSettings() {
-        return settings;
-    }
-
-    /**
-     * Make a request to the server only to get the maximum possible heap size to use for compilations.
-     *
-     * @param port_file The port file used to synchronize creation of this server.
-     * @param id The identify of the compilation.
-     * @param out Standard out information.
-     * @param err Standard err information.
-     * @return The maximum heap size in bytes.
-     */
-    @Override
-    public SysInfo getSysInfo() {
-        try {
-            CompilationResult cr = useServer(new String[0],
-                                             Collections.<URI>emptySet(),
-                                             Collections.<URI>emptySet(),
-                                             Collections.<URI, Set<String>>emptyMap());
-            return cr.sysinfo;
-        } catch (Exception e) {
-            return new SysInfo(-1, -1);
-        }
-    }
-
-    @Override
-    public CompilationResult compile(String protocolId,
-                                     String invocationId,
-                                     String[] args,
-                                     List<File> explicitSources,
-                                     Set<URI> sourcesToCompile,
-                                     Set<URI> visibleSources) {
-        // Delegate to useServer, which delegates to compileHelper
-        return useServer(args, sourcesToCompile, visibleSources, null);
-    }
-
-    /**
-     * Connect and compile using the javac server settings and the args. When using more advanced features, the sources_to_compile and visible_sources are
-     * supplied to the server and meta data is returned in package_artifacts, package_dependencies and package_pubapis.
-     */
-    public CompilationResult compileHelper(String id,
-                                           String[] args,
-                                           Set<URI> sourcesToCompile,
-                                           Set<URI> visibleSources) {
-
-        CompilationResult rc = new CompilationResult(-3);
-
-        try {
-            PortFile portFile = JavacServer.getPortFile(this.portfile);
-
-            int port = portFile.containsPortInfo() ? portFile.getPort() : 0;
-            if (port == 0) {
-                return new CompilationResult(ERROR_BUT_TRY_AGAIN);
-            }
-            long cookie = portFile.getCookie();
-            // Acquire the localhost/127.0.0.1 address.
-            InetAddress addr = InetAddress.getByName(null);
-            SocketAddress sockaddr = new InetSocketAddress(addr, port);
-            Socket sock = new Socket();
-            int timeoutMs = JavacServer.CONNECTION_TIMEOUT * 1000;
-            try {
-                sock.connect(sockaddr, timeoutMs);
-            } catch (java.net.ConnectException e) {
-                rc.setReturnCode(ERROR_BUT_TRY_AGAIN);
-                rc.stderr = "Could not connect to javac server found in portfile: " + portFile.getFilename() + " " + e;
-                return rc;
-            }
-            if (!sock.isConnected()) {
-                rc.setReturnCode(ERROR_BUT_TRY_AGAIN);
-                rc.stderr = "Could not connect to javac server found in portfile: " + portFile.getFilename();
-                return rc;
-            }
-
-            //
-            // Send arguments
-            //
-            BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
-            PrintWriter sockout = new PrintWriter(sock.getOutputStream());
-
-            sockout.println(JavacServer.PROTOCOL_COOKIE_VERSION);
-            sockout.println("" + cookie);
-            sockout.println(JavacServer.PROTOCOL_CWD);
-            sockout.println(System.getProperty("user.dir"));
-            sockout.println(JavacServer.PROTOCOL_ID);
-            sockout.println(id);
-            sockout.println(JavacServer.PROTOCOL_ARGS);
-            for (String s : args) {
-                StringBuffer buf = new StringBuffer();
-                String[] paths = s.split(File.pathSeparator);
-                int c = 0;
-                for (String path : paths) {
-                    File f = new File(path);
-                    if (f.isFile() || f.isDirectory()) {
-                        buf.append(f.getAbsolutePath());
-                        c++;
-                        if (c < paths.length) {
-                            buf.append(File.pathSeparator);
-                        }
-                    } else {
-                        buf = new StringBuffer(s);
-                        break;
-                    }
-                }
-                sockout.println(buf.toString());
-            }
-            sockout.println(JavacServer.PROTOCOL_SOURCES_TO_COMPILE);
-            for (URI uri : sourcesToCompile) {
-                sockout.println(uri.toString());
-            }
-            sockout.println(JavacServer.PROTOCOL_VISIBLE_SOURCES);
-            for (URI uri : visibleSources) {
-                sockout.println(uri.toString());
-            }
-            sockout.println(JavacServer.PROTOCOL_END);
-            sockout.flush();
-
-            //
-            // Receive result
-            //
-            StringBuffer stdout = new StringBuffer();
-            StringBuffer stderr = new StringBuffer();
-
-            if (!JavacServiceClient.expect(in, JavacServer.PROTOCOL_STDOUT)) {
-                return new CompilationResult(ERROR_FATAL);
-            }
-            // Load stdout
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return new CompilationResult(ERROR_FATAL);
-                }
-                if (l.equals(JavacServer.PROTOCOL_STDERR)) {
-                    break;
-                }
-                stdout.append(l);
-                stdout.append('\n');
-            }
-            // Load stderr
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return new CompilationResult(ERROR_FATAL);
-                }
-                if (l.equals(JavacServer.PROTOCOL_PACKAGE_ARTIFACTS)) {
-                    break;
-                }
-                stderr.append(l);
-                stderr.append('\n');
-            }
-            // Load the package artifacts
-            Set<URI> lastUriSet = null;
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return new CompilationResult(ERROR_FATAL);
-                }
-                if (l.equals(JavacServer.PROTOCOL_PACKAGE_DEPENDENCIES)) {
-                    break;
-                }
-                if (l.length() > 1 && l.charAt(0) == '+') {
-                    String pkg = l.substring(1);
-                    lastUriSet = new HashSet<>();
-                    rc.packageArtifacts.put(pkg, lastUriSet);
-                } else if (l.length() > 1 && lastUriSet != null) {
-                    lastUriSet.add(new URI(l.substring(1)));
-                }
-            }
-            // Load package dependencies
-            Set<String> lastPackageSet = null;
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return new CompilationResult(ERROR_FATAL);
-                }
-                if (l.equals(JavacServer.PROTOCOL_PACKAGE_PUBLIC_APIS)) {
-                    break;
-                }
-                if (l.length() > 1 && l.charAt(0) == '+') {
-                    String pkg = l.substring(1);
-                    lastPackageSet = new HashSet<>();
-                    rc.packageDependencies.put(pkg, lastPackageSet);
-                } else if (l.length() > 1 && lastPackageSet != null) {
-                    lastPackageSet.add(l.substring(1));
-                }
-            }
-            // Load package pubapis
-            Map<String, StringBuffer> tmp = new HashMap<>();
-            StringBuffer lastPublicApi = null;
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return new CompilationResult(ERROR_FATAL);
-                }
-                if (l.equals(JavacServer.PROTOCOL_SYSINFO)) {
-                    break;
-                }
-                if (l.length() > 1 && l.charAt(0) == '+') {
-                    String pkg = l.substring(1);
-                    lastPublicApi = new StringBuffer();
-                    tmp.put(pkg, lastPublicApi);
-                } else if (l.length() > 1 && lastPublicApi != null) {
-                    lastPublicApi.append(l.substring(1));
-                    lastPublicApi.append("\n");
-                }
-            }
-            for (String p : tmp.keySet()) {
-                //assert (packagePublicApis.get(p) == null);
-                String api = tmp.get(p).toString();
-                rc.packagePubapis.put(p, api);
-            }
-            // Now reading the max memory possible.
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return new CompilationResult(ERROR_FATAL);
-                }
-                if (l.equals(JavacServer.PROTOCOL_RETURN_CODE)) {
-                    break;
-                }
-                if (l.startsWith("num_cores=")) {
-                    rc.sysinfo.numCores = Integer.parseInt(l.substring(10));
-                }
-                if (l.startsWith("max_memory=")) {
-                    rc.sysinfo.maxMemory = Long.parseLong(l.substring(11));
-                }
-            }
-            String l = in.readLine();
-            if (l == null) {
-                rc.setReturnCode(ERROR_FATAL);
-                rc.stderr = "No return value from the server!";
-                return rc;
-            }
-            rc.setReturnCode(Integer.parseInt(l));
-            rc.stdout = stdout.toString();
-            rc.stderr = stderr.toString();
-        } catch (Exception e) {
-            StringWriter sw = new StringWriter();
-            e.printStackTrace(new PrintWriter(sw));
-            rc.stderr = sw.toString();
-        }
-        return rc;
-    }
-
-    /**
-     * Dispatch a compilation request to a javac server.
-     *
-     * @param args are the command line args to javac and is allowed to contain source files, @file and other command line options to javac.
-     *
-     * The generated classes, h files and other artifacts from the javac invocation are stored by the javac server to disk.
-     *
-     * @param sources_to_compile The sources to compile.
-     *
-     * @param visibleSources If visible sources has a non zero size, then visible_sources are the only files in the file system that the javac server can see!
-     * (Sources to compile are always visible.) The visible sources are those supplied by the (filtered) -sourcepath
-     *
-     * @param visibleClasses If visible classes for a specific root/jar has a non zero size, then visible_classes are the only class files that the javac server
-     * can see, in that root/jar. It maps from a classpath root or a jar file to the set of visible classes for that root/jar.
-     *
-     * The server return meta data about the build in the following parameters.
-     * @param package_artifacts, map from package name to set of created artifacts for that package.
-     * @param package_dependencies, map from package name to set of packages that it depends upon.
-     * @param package_pubapis, map from package name to unique string identifying its pub api.
-     */
-    public CompilationResult useServer(String[] args,
-                                       Set<URI> sourcesToCompile,
-                                       Set<URI> visibleSources,
-                                       Map<URI, Set<String>> visibleClasses) {
-        try {
-            if (portfile == null) {
-                CompilationResult cr = new CompilationResult(CompilationResult.ERROR_FATAL);
-                cr.stderr = "No portfile was specified!";
-                return cr;
-            }
-
-            int attempts = 0;
-            CompilationResult rc;
-            do {
-                PortFile port_file = JavacServer.getPortFile(portfile);
-                synchronized (port_file) {
-                    port_file.lock();
-                    port_file.getValues();
-                    port_file.unlock();
-                }
-                if (!port_file.containsPortInfo()) {
-                    String cmd = JavacServer.fork(sjavac, port_file.getFilename(), logfile, poolsize, keepalive, System.err, stdouterrfile, background);
-
-                    if (background && !port_file.waitForValidValues()) {
-                        // Ouch the server did not start! Lets print its stdouterrfile and the command used.
-                        StringWriter sw = new StringWriter();
-                        JavacServiceClient.printFailedAttempt(cmd, stdouterrfile, new PrintWriter(sw));
-                        // And give up.
-                        CompilationResult cr = new CompilationResult(ERROR_FATAL);
-                        cr.stderr = sw.toString();
-                        return cr;
-                    }
-                }
-                rc = compileHelper(id, args, sourcesToCompile, visibleSources);
-                // Try again until we manage to connect. Any error after that
-                // will cause the compilation to fail.
-                if (rc.returnCode == CompilationResult.ERROR_BUT_TRY_AGAIN) {
-                    // We could not connect to the server. Try again.
-                    attempts++;
-                    try {
-                        Thread.sleep(JavacServer.WAIT_BETWEEN_CONNECT_ATTEMPTS * 1000);
-                    } catch (InterruptedException e) {
-                    }
-                }
-            } while (rc.returnCode == ERROR_BUT_TRY_AGAIN && attempts < JavacServer.MAX_NUM_CONNECT_ATTEMPTS);
-            return rc;
-        } catch (Exception e) {
-            StringWriter sw = new StringWriter();
-            e.printStackTrace(new PrintWriter(sw));
-            CompilationResult cr = new CompilationResult(ERROR_FATAL);
-            cr.stderr = sw.toString();
-            return cr;
-        }
-    }
-
-    public static void printFailedAttempt(String cmd, String f, PrintWriter err) {
-        err.println("---- Failed to start javac server with this command -----");
-        err.println(cmd);
-        try {
-            BufferedReader in = new BufferedReader(new FileReader(f));
-            err.println("---- stdout/stderr output from attempt to start javac server -----");
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    break;
-                }
-                err.println(l);
-            }
-            err.println("------------------------------------------------------------------");
-        } catch (Exception e) {
-            err.println("The stdout/stderr output in file " + f + " does not exist and the server did not start.");
-        }
-    }
-
-    /**
-     * Expect this key on the next line read from the reader.
-     */
-    public static boolean expect(BufferedReader in, String key) throws IOException {
-        String s = in.readLine();
-        if (s != null && s.equals(key)) {
-            return true;
-        }
-        return false;
-    }
-}
--- a/src/share/classes/com/sun/tools/sjavac/server/PortFile.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/server/PortFile.java	Wed Aug 13 14:44:59 2014 +0200
@@ -41,12 +41,12 @@
  * primitives to avoid race conditions when several javac clients are started at the same. Note that file
  * system locking is not always supported on a all operating systems and/or file systems.
  *
- * <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></p>
+ *  <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>
  */
-class PortFile {
+public class PortFile {
 
     // Port file format:
     // byte ordering: high byte first = big endian
@@ -72,8 +72,7 @@
      * Create a new portfile.
      * @param filename is the path to the file.
      */
-    public PortFile(String fn) throws FileNotFoundException
-    {
+    public PortFile(String fn) throws FileNotFoundException {
         filename = fn;
         file = new File(filename);
         stopFile = new File(filename+".stop");
@@ -88,7 +87,7 @@
     /**
      * Lock the port file.
      */
-    void lock() throws IOException {
+    public void lock() throws IOException {
         lock = channel.lock();
     }
 
@@ -115,7 +114,7 @@
                     containsPortInfo = false;
                 }
             }
-        } catch (Exception e) {
+        } catch (IOException e) {
             containsPortInfo = false;
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/PortFileMonitor.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014, 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.IOException;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Monitors the presence of a port file and shuts down the given SjavacServer
+ * whenever the port file is deleted or invalidated.
+ *
+ * TODO: JDK-8046882
+ *
+ *  <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 PortFileMonitor {
+
+    // Check if the portfile is gone, every 5 seconds.
+    private final static int CHECK_PORTFILE_INTERVAL = 5000;
+
+    final private Timer timer = new Timer();
+    final private PortFile portFile;
+    final private SjavacServer server;
+
+    public PortFileMonitor(PortFile portFile,
+                           SjavacServer server) {
+        this.portFile = portFile;
+        this.server = server;
+    }
+
+    public void start() {
+        TimerTask shutdownCheck = new TimerTask() {
+            public void run() {
+                try {
+                    if (!portFile.exists()) {
+                        // Time to quit because the portfile was deleted by another
+                        // process, probably by the makefile that is done building.
+                        server.shutdown("Quitting because portfile was deleted!");
+                    } else if (portFile.markedForStop()) {
+                        // Time to quit because another process touched the file
+                        // server.port.stop to signal that the server should stop.
+                        // This is necessary on some operating systems that lock
+                        // the port file hard!
+                        server.shutdown("Quitting because a portfile.stop file was found!");
+                    } else if (!portFile.stillMyValues()) {
+                        // Time to quit because another build has started.
+                        server.shutdown("Quitting because portfile is now owned by another javac server!");
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace(server.theLog);
+                    server.flushLog();
+                }
+            }
+        };
+
+        timer.schedule(shutdownCheck, 0, CHECK_PORTFILE_INTERVAL);
+    }
+
+    public void shutdown() {
+        timer.cancel();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/RequestHandler.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014, 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.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.Socket;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+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
+ *  - Reads the appropriate arguments
+ *  - Delegates the actual invocation to the given sjavac implementation
+ *  - Writes the result back to the socket output stream
+ *
+ * None of the work performed by this class is really bound by the CPU. It
+ * should be completely fine to have a large number of RequestHandlers active.
+ * To limit the number of concurrent compilations, use PooledSjavac.
+ *
+ *  <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 RequestHandler implements Runnable {
+
+    private final Socket socket;
+    private final Sjavac sjavac;
+
+    public RequestHandler(Socket socket, Sjavac sjavac) {
+        this.socket = socket;
+        this.sjavac = sjavac;
+    }
+
+    @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_SYS_INFO: handleSysInfoRequest(oin, oout); break;
+            case SjavacServer.CMD_COMPILE:  handleCompileRequest(oin, oout); break;
+            default: Log.error("Unknown command: " + cmd);
+            }
+        } 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());
+        }
+    }
+
+    private void handleSysInfoRequest(ObjectInputStream oin,
+                                      ObjectOutputStream oout) throws IOException {
+        oout.writeObject(sjavac.getSysInfo());
+        oout.flush();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void handleCompileRequest(ObjectInputStream oin,
+                                      ObjectOutputStream oout) throws IOException {
+        try {
+            // Read request arguments
+            String protocolId = (String) oin.readObject();
+            String invocationId = (String) oin.readObject();
+            String[] args = (String[]) oin.readObject();
+            List<File> explicitSources = (List<File>) oin.readObject();
+            Set<URI> sourcesToCompile = (Set<URI>) oin.readObject();
+            Set<URI> visibleSources = (Set<URI>) oin.readObject();
+
+            // Perform compilation
+            CompilationResult cr = sjavac.compile(protocolId,
+                                                  invocationId,
+                                                  args,
+                                                  explicitSources,
+                                                  sourcesToCompile,
+                                                  visibleSources);
+            // Write request response
+            oout.writeObject(cr);
+            oout.flush();
+        } catch (ClassNotFoundException cnfe) {
+            throw new IOException(cnfe);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/Sjavac.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, 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.File;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Interface of the SjavacImpl, the sjavac client and all wrappers such as
+ * PooledSjavac etc.
+ *
+ *  <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 interface Sjavac {
+
+    SysInfo getSysInfo();
+
+    CompilationResult compile(String protocolId,
+                              String invocationId,
+                              String[] args,
+                              List<File> explicitSources,
+                              Set<URI> sourcesToCompile,
+                              Set<URI> visibleSources);
+
+    void shutdown();
+    String serverSettings();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/SjavacServer.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2011, 2014, 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.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.sun.tools.sjavac.ProblemException;
+import com.sun.tools.sjavac.Util;
+import com.sun.tools.sjavac.comp.SjavacImpl;
+import com.sun.tools.sjavac.comp.PooledSjavac;
+
+/**
+ * The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
+ *
+ *  <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 SjavacServer implements Terminable {
+
+    // Used in protocol to indicate which method to invoke
+    public final static String CMD_COMPILE = "compile";
+    public final static String CMD_SYS_INFO = "sys-info";
+
+    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
+    // an old server when a new server has started and reused the same port as
+    // an old server.
+    private final long myCookie;
+
+    // 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;
+
+    private ServerSocket serverSocket;
+
+    private PortFile portFile;
+    private PortFileMonitor portFileMonitor;
+
+    // Set to false break accept loop
+    final AtomicBoolean keepAcceptingRequests = new AtomicBoolean();
+
+    // 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 {
+        // Extract options. TODO: Change to proper constructor args
+        portfilename = Util.extractStringOption("portfile", settings);
+        logfile = Util.extractStringOption("logfile", settings);
+        stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
+        keepalive = Util.extractIntOption("keepalive", settings, 120);
+        poolsize = Util.extractIntOption("poolsize", settings,
+                                         Runtime.getRuntime().availableProcessors());
+        this.err = err;
+
+        myCookie = new Random().nextLong();
+        theLog = new PrintWriter(logfile);
+    }
+
+
+    /**
+     * 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 FileNotFoundException {
+        if (allPortFiles == null) {
+            allPortFiles = new HashMap<>();
+        }
+        PortFile pf = allPortFiles.get(filename);
+
+        // Port file known. Does it still exist?
+        if (pf != null) {
+            try {
+                if (!pf.exists())
+                    pf = null;
+            } catch (IOException ioex) {
+                ioex.printStackTrace();
+            }
+        }
+
+        if (pf == null) {
+            pf = new PortFile(filename);
+            allPortFiles.put(filename, pf);
+        }
+        return pf;
+    }
+
+    /**
+     * Get the cookie used for this server.
+     */
+    long getCookie() {
+        return myCookie;
+    }
+
+    /**
+     * Get the port used for this server.
+     */
+    int getPort() {
+        return serverSocket.getLocalPort();
+    }
+
+    /**
+     * Sum up the total build time for this javac server.
+     */
+    public void addBuildTime(long inc) {
+        totalBuildTime += inc;
+    }
+
+    /**
+     * Log this message.
+     */
+    public void log(String msg) {
+        if (theLog != null) {
+            theLog.println(msg);
+        } else {
+            System.err.println(msg);
+        }
+    }
+
+    /**
+     * Make sure the log is flushed.
+     */
+    public void flushLog() {
+        if (theLog != null) {
+            theLog.flush();
+        }
+    }
+
+    /**
+     * Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3"
+     * is sent as the settings parameter. Returns 0 on success, -1 on failure.
+     */
+    public int startServer() throws IOException {
+        long serverStart = System.currentTimeMillis();
+
+        // The port file is locked and the server port and cookie is written into it.
+        portFile = getPortFile(portfilename);
+
+        synchronized (portFile) {
+            portFile.lock();
+            portFile.getValues();
+            if (portFile.containsPortInfo()) {
+                err.println("Javac server not started because portfile exists!");
+                portFile.unlock();
+                return -1;
+            }
+
+            //           .-----------.   .--------.   .------.
+            // socket -->| IdleReset |-->| Pooled |-->| Impl |--> javac
+            //           '-----------'   '--------'   '------'
+            sjavac = new SjavacImpl();
+            sjavac = new PooledSjavac(sjavac, poolsize);
+            sjavac = new IdleResetSjavac(sjavac,
+                                         this,
+                                         keepalive * 1000);
+
+            serverSocket = new ServerSocket();
+            InetAddress localhost = InetAddress.getByName(null);
+            serverSocket.bind(new InetSocketAddress(localhost, 0));
+
+            // At this point the server accepts connections, so it is  now safe
+            // to publish the port / cookie information
+            portFile.setValues(getPort(), getCookie());
+            portFile.unlock();
+        }
+
+        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();
+
+        keepAcceptingRequests.set(true);
+        do {
+            try {
+                Socket socket = serverSocket.accept();
+                new Thread(new RequestHandler(socket, sjavac)).start();
+            } catch (SocketException se) {
+                // Caused by serverSocket.close() and indicates shutdown
+            }
+        } while (keepAcceptingRequests.get());
+
+        log("Shutting down.");
+
+        // No more connections accepted. If any client managed to connect after
+        // the accept() was interrupted but before the server socket is closed
+        // here, any attempt to read or write to the socket will result in an
+        // IOException on the client side.
+
+        long realTime = System.currentTimeMillis() - serverStart;
+        log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
+        flushLog();
+
+        // Shut down
+        sjavac.shutdown();
+
+        return 0;
+    }
+
+    /**
+     * Fork a background process. Returns the command line used that can be printed if something failed.
+     */
+    public static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive,
+            final PrintStream err, String stdouterrfile, boolean background)
+            throws IOException, ProblemException {
+        if (stdouterrfile != null && stdouterrfile.trim().equals("")) {
+            stdouterrfile = null;
+        }
+        final String startserver = "--startserver:portfile=" + portfile + ",logfile=" + logfile + ",stdouterrfile=" + stdouterrfile + ",poolsize=" + poolsize + ",keepalive="+ keepalive;
+
+        if (background) {
+            sjavac += "%20" + startserver;
+            sjavac = sjavac.replaceAll("%20", " ");
+            sjavac = sjavac.replaceAll("%2C", ",");
+            // If the java/sh/cmd launcher fails the failure will be captured by stdouterr because of the redirection here.
+            String[] cmd = {"/bin/sh", "-c", sjavac + " >> " + stdouterrfile + " 2>&1"};
+            if (!(new File("/bin/sh")).canExecute()) {
+                ArrayList<String> wincmd = new ArrayList<>();
+                wincmd.add("cmd");
+                wincmd.add("/c");
+                wincmd.add("start");
+                wincmd.add("cmd");
+                wincmd.add("/c");
+                wincmd.add(sjavac + " >> " + stdouterrfile + " 2>&1");
+                cmd = wincmd.toArray(new String[wincmd.size()]);
+            }
+            Process pp = null;
+            try {
+                pp = Runtime.getRuntime().exec(cmd);
+            } catch (Exception e) {
+                e.printStackTrace(err);
+                e.printStackTrace(new PrintWriter(stdouterrfile));
+            }
+            StringBuilder rs = new StringBuilder();
+            for (String s : cmd) {
+                rs.append(s + " ");
+            }
+            return rs.toString();
+        }
+
+        // Do not spawn a background server, instead run it within the same JVM.
+        Thread t = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    SjavacServer server = new SjavacServer(startserver, err);
+                    server.startServer();
+                } catch (Throwable t) {
+                    t.printStackTrace(err);
+                }
+            }
+        };
+        t.setDaemon(true);
+        t.start();
+        return "";
+    }
+
+    @Override
+    public void shutdown(String quitMsg) {
+        if (!keepAcceptingRequests.compareAndSet(false, true)) {
+            // Already stopped, no need to shut down again
+            return;
+        }
+
+        log("Quitting: " + quitMsg);
+        flushLog();
+
+        portFileMonitor.shutdown(); // No longer any need to monitor port file
+
+        // Unpublish port before shutting down socket to minimize the number of
+        // failed connection attempts
+        try {
+            portFile.delete();
+        } catch (IOException e) {
+            e.printStackTrace(theLog);
+        }
+        try {
+            serverSocket.close();
+        } catch (IOException e) {
+            e.printStackTrace(theLog);
+        }
+    }
+
+    public static void cleanup(String... args) {
+        String settings = Util.findServerSettings(args);
+        if (settings == null) return;
+        String portfile = Util.extractStringOption("portfile", settings);
+        String background = Util.extractStringOption("background", settings);
+        if (background != null && background.equals("false")) {
+            // If the server runs within this jvm, then delete the portfile,
+            // since this jvm is about to exit soon.
+            File f = new File(portfile);
+            f.delete();
+        }
+    }
+}
--- a/src/share/classes/com/sun/tools/sjavac/server/SysInfo.java	Tue Aug 12 17:48:30 2014 -0700
+++ b/src/share/classes/com/sun/tools/sjavac/server/SysInfo.java	Wed Aug 13 14:44:59 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, 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
@@ -34,7 +34,18 @@
  */
 package com.sun.tools.sjavac.server;
 
-public class SysInfo {
+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 SysInfo implements Serializable {
+
+    static final long serialVersionUID = -3096346807579L;
+
     public int numCores;
     public long maxMemory;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/Terminable.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 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;
+
+/**
+ *  <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 interface Terminable {
+    void shutdown(String quitMsg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/sjavac/IdleShutdown.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+/*
+ * @test
+ * @bug 8044131
+ * @summary Tests the hooks used for detecting idleness of the sjavac server.
+ * @build Wrapper
+ * @run main Wrapper IdleShutdown
+ */
+import java.io.File;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+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.SysInfo;
+import com.sun.tools.sjavac.server.Terminable;
+
+
+public class IdleShutdown {
+
+    final static long TEST_START = System.currentTimeMillis();
+    final static long TIMEOUT_MS = 3000;
+
+    public static void main(String[] args) throws InterruptedException {
+
+        final AtomicLong timeoutTimestamp = new AtomicLong(-1);
+
+        log("Starting IdleCallbackJavacService with timeout: " + TIMEOUT_MS);
+        Sjavac service = new IdleResetSjavac(
+                new NoopJavacService(),
+                new Terminable() {
+                    public void shutdown(String msg) {
+                        // Record the idle timeout time
+                        log("Timeout detected");
+                        timeoutTimestamp.set(System.currentTimeMillis());
+                    }
+                },
+                TIMEOUT_MS);
+
+        // Make sure it didn't timeout immediately
+        if (timeoutTimestamp.get() != -1)
+            throw new AssertionError("Premature timeout detected.");
+
+        // Call various methods and wait less than TIMEOUT_MS in between
+        Thread.sleep(TIMEOUT_MS - 1000);
+        log("Getting sys info");
+        service.getSysInfo();
+
+        Thread.sleep(TIMEOUT_MS - 1000);
+        log("Getting sys info");
+        service.getSysInfo();
+
+        if (timeoutTimestamp.get() != -1)
+            throw new AssertionError("Premature timeout detected.");
+
+        Thread.sleep(TIMEOUT_MS - 1000);
+        log("Compiling");
+        service.compile("",
+                        "",
+                        new String[0],
+                        Collections.<File>emptyList(),
+                        Collections.<URI>emptySet(),
+                        Collections.<URI>emptySet());
+
+        Thread.sleep(TIMEOUT_MS - 1000);
+        log("Compiling");
+        service.compile("",
+                        "",
+                        new String[0],
+                        Collections.<File>emptyList(),
+                        Collections.<URI>emptySet(),
+                        Collections.<URI>emptySet());
+
+        if (timeoutTimestamp.get() != -1)
+            throw new AssertionError("Premature timeout detected.");
+
+        long expectedTimeout = System.currentTimeMillis() + TIMEOUT_MS;
+
+        // Wait for actual timeout
+        log("Awaiting idle timeout");
+        Thread.sleep(TIMEOUT_MS + 1000);
+
+        // Check result
+        if (timeoutTimestamp.get() == -1)
+            throw new AssertionError("Timeout never occurred");
+
+        long error = Math.abs(expectedTimeout - timeoutTimestamp.get());
+        log("Timeout error: " + error + " ms");
+        if (error > TIMEOUT_MS * .1)
+            throw new AssertionError("Error too big");
+
+        log("Shutting down");
+        service.shutdown();
+    }
+
+    private static void log(String msg) {
+        long logTime = System.currentTimeMillis() - TEST_START;
+        System.out.printf("After %5d ms: %s%n", logTime, msg);
+    }
+
+    private static class NoopJavacService implements Sjavac {
+        @Override
+        public SysInfo getSysInfo() {
+            // Attempt to trigger idle timeout during a call by sleeping
+            try {
+                Thread.sleep(TIMEOUT_MS + 1000);
+            } catch (InterruptedException e) {
+            }
+            return null;
+        }
+        @Override
+        public void shutdown() {
+        }
+        @Override
+        public CompilationResult compile(String protocolId,
+                                         String invocationId,
+                                         String[] args,
+                                         List<File> explicitSources,
+                                         Set<URI> sourcesToCompile,
+                                         Set<URI> visibleSources) {
+            return null;
+        }
+        @Override
+        public String serverSettings() {
+            return "";
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/sjavac/PooledExecution.java	Wed Aug 13 14:44:59 2014 +0200
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+/*
+ * @test
+ * @bug 8044131
+ * @summary Makes sure sjavac poolsize option is honored.
+ * @build Wrapper
+ * @run main Wrapper PooledExecution
+ */
+import java.io.File;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+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;
+import com.sun.tools.sjavac.server.SysInfo;
+
+
+public class PooledExecution {
+
+    public static void main(String[] args) throws InterruptedException {
+        new PooledExecutionTest().runTest();
+    }
+
+    static class PooledExecutionTest {
+
+        final int POOL_SIZE = 15;
+        final int NUM_REQUESTS = 100;
+
+        // Number of tasks that has not yet started
+        CountDownLatch leftToStart = new CountDownLatch(NUM_REQUESTS);
+
+        // Highest number of concurrently active request seen
+        int highWaterMark = 0;
+
+        public void runTest() throws InterruptedException {
+            ConcurrencyLoggingService loggingService = new ConcurrencyLoggingService();
+            final Sjavac service = new PooledSjavac(loggingService, POOL_SIZE);
+
+            // Keep track of the number of finished tasks so we can make sure all
+            // tasks finishes gracefully upon shutdown.
+            Thread[] tasks = new Thread[NUM_REQUESTS];
+            final AtomicInteger tasksFinished = new AtomicInteger(0);
+
+            for (int i = 0; i < NUM_REQUESTS; i++) {
+                tasks[i] = new Thread() {
+                    public void run() {
+                        service.compile("",
+                                        "",
+                                        new String[0],
+                                        Collections.<File>emptyList(),
+                                        Collections.<URI>emptySet(),
+                                        Collections.<URI>emptySet());
+                        tasksFinished.incrementAndGet();
+                    }
+                };
+                tasks[i].start();
+            }
+
+            // Wait for all tasks to start (but not necessarily run to completion)
+            leftToStart.await();
+
+            // Shutdown before all tasks are completed
+            System.out.println("Shutting down!");
+            service.shutdown();
+
+            // Wait for all tasks to complete
+            for (Thread t : tasks)
+                t.join();
+
+            if (tasksFinished.get() != NUM_REQUESTS) {
+                throw new AssertionError(tasksFinished.get() + " out of " +
+                        NUM_REQUESTS + " finished. Broken shutdown?");
+            }
+
+            if (highWaterMark > POOL_SIZE) {
+                throw new AssertionError("Pool size overused: " + highWaterMark +
+                                         " used out of " + POOL_SIZE + " allowed.");
+            }
+
+            // Assuming more than POOL_SIZE requests can be processed within 1 sek:
+            if (highWaterMark < POOL_SIZE) {
+                throw new AssertionError("Pool size underused: " + highWaterMark +
+                                         " used out of " + POOL_SIZE + " allowed.");
+            }
+        }
+
+
+        private class ConcurrencyLoggingService implements Sjavac {
+
+            // Keeps track of currently active requests
+            AtomicInteger activeRequests = new AtomicInteger(0);
+
+            @Override
+            public CompilationResult compile(String protocolId,
+                                             String invocationId,
+                                             String[] args,
+                                             List<File> explicitSources,
+                                             Set<URI> sourcesToCompile,
+                                             Set<URI> visibleSources) {
+                leftToStart.countDown();
+                int numActiveRequests = activeRequests.incrementAndGet();
+                System.out.printf("Left to start: %2d / Currently active: %2d%n",
+                                  leftToStart.getCount(),
+                                  numActiveRequests);
+                highWaterMark = Math.max(highWaterMark, numActiveRequests);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException ie) {
+                    throw new RuntimeException("Interrupted", ie);
+                }
+                activeRequests.decrementAndGet();
+                System.out.println("Task completed");
+                return null;
+            }
+
+            @Override
+            public SysInfo getSysInfo() {
+                return null;
+            }
+
+            @Override
+            public void shutdown() {
+            }
+
+            @Override
+            public String serverSettings() {
+                return "";
+            }
+        }
+    }
+}