changeset 3438:5bd36f40e4c5

8048594: The sjavac client/server protocol should be hidden behind an interface Reviewed-by: jfranck
author alundblad
date Tue, 17 Jun 2014 14:01:27 +0200
parents 855f8c7337eb
children f278516ca25e
files src/share/classes/com/sun/tools/sjavac/CleanProperties.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/Main.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/comp/JavaCompilerWithDeps.java src/share/classes/com/sun/tools/sjavac/comp/JavacServiceImpl.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/JavacServer.java src/share/classes/com/sun/tools/sjavac/server/JavacService.java src/share/classes/com/sun/tools/sjavac/server/JavacServiceClient.java test/tools/sjavac/SJavac.java
diffstat 17 files changed, 697 insertions(+), 459 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/sjavac/CleanProperties.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/CleanProperties.java	Tue Jun 17 14:01:27 2014 +0200
@@ -36,6 +36,7 @@
 import java.util.Properties;
 
 import com.sun.tools.sjavac.options.Options;
+import com.sun.tools.sjavac.server.JavacService;
 
 /**
  * The clean properties transform should not be necessary.
@@ -56,7 +57,8 @@
         // Any extra information is ignored for clean properties.
     }
 
-    public boolean transform(Map<String,Set<URI>> pkgSrcs,
+    public boolean transform(JavacService javacService,
+                             Map<String,Set<URI>> pkgSrcs,
                              Set<URI>             visibleSrcs,
                              Map<URI,Set<String>> visibleClasses,
                              Map<String,Set<String>> oldPackageDependencies,
--- a/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/CompileJavaPackages.java	Tue Jun 17 14:01:27 2014 +0200
@@ -25,15 +25,18 @@
 
 package com.sun.tools.sjavac;
 
+import java.io.File;
 import java.io.PrintStream;
 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.JavacServer;
+import com.sun.tools.sjavac.server.CompilationResult;
+import com.sun.tools.sjavac.server.JavacService;
 import com.sun.tools.sjavac.server.SysInfo;
 
 /**
@@ -64,9 +67,10 @@
         args = a;
     }
 
-    public boolean transform(Map<String,Set<URI>> pkgSrcs,
-                             Set<URI>             visibleSources,
-                             Map<URI,Set<String>> visibleClasses,
+    public boolean transform(final JavacService javacService,
+                             Map<String,Set<URI>> pkgSrcs,
+                             final Set<URI>             visibleSources,
+                             final Map<URI,Set<String>> visibleClasses,
                              Map<String,Set<String>> oldPackageDependents,
                              URI destRoot,
                              final Map<String,Set<URI>>    packageArtifacts,
@@ -75,24 +79,25 @@
                              int debugLevel,
                              boolean incremental,
                              int numCores,
-                             PrintStream out,
-                             PrintStream err)
+                             final PrintStream out,
+                             final PrintStream err)
     {
         boolean rc = true;
         boolean concurrentCompiles = true;
 
         // Fetch the id.
-        String id = Util.extractStringOption("id", args.getServerConf());
-        if (id == null || id.equals("")) {
+        String idOpt = Util.extractStringOption("id", args.getServerConf());
+        if (idOpt == null || idOpt.equals("")) {
             // No explicit id set. Create a random id so that the requests can be
             // grouped properly in the server.
-            id = "id"+(((new Random()).nextLong())&Long.MAX_VALUE);
+            idOpt = "id"+(((new Random()).nextLong())&Long.MAX_VALUE);
         }
+        final String id = idOpt;
         // Only keep portfile and sjavac settings..
         String psServerSettings = Util.cleanSubOptions(Util.set("portfile","sjavac","background","keepalive"), args.getServerConf());
 
         // Get maximum heap size from the server!
-        SysInfo sysinfo = JavacServer.connectGetSysInfo(psServerSettings, out, err);
+        SysInfo sysinfo = javacService.getSysInfo();
         if (sysinfo.numCores == -1) {
             Log.error("Could not query server for sysinfo!");
             return false;
@@ -201,13 +206,10 @@
         }
 
         // The return values for each chunked compile.
-        final int[] rn = new int[numCompiles];
+        final CompilationResult[] rn = new CompilationResult[numCompiles];
         // The requets, might or might not run as a background thread.
         final Thread[] requests  = new Thread[numCompiles];
 
-        final Set<URI>             fvisible_sources = visibleSources;
-        final Map<URI,Set<String>> fvisible_classes = visibleClasses;
-
         long start = System.currentTimeMillis();
 
         for (int i=0; i<numCompiles; ++i) {
@@ -215,23 +217,20 @@
             final CompileChunk cc = compileChunks[i];
 
             // Pass the num_cores and the id (appended with the chunk number) to the server.
-            final String cleanedServerSettings = psServerSettings+",poolsize="+numCores+",id="+id+"-"+ii;
-            final PrintStream fout = out;
-            final PrintStream ferr = err;
+            final String cleanedServerSettings = psServerSettings+",poolsize="+numCores+",id="+id+"-"+i;
 
-            requests[ii] = new Thread() {
+            requests[i] = new Thread() {
                 @Override
                 public void run() {
-                                        rn[ii] = JavacServer.useServer(cleanedServerSettings,
-                                                           args.prepJavacArgs(),
-                                                               cc.srcs,
-                                                           fvisible_sources,
-                                                           fvisible_classes,
-                                                           packageArtifacts,
-                                                           packageDependencies,
-                                                           packagePubapis,
-                                                           null,
-                                                           fout, ferr);
+                    rn[ii] = javacService.compile("n/a",
+                                                  id + "-" + ii,
+                                                  args.prepJavacArgs(),
+                                                  Collections.<File>emptyList(),
+                                                  cc.srcs,
+                                                  visibleSources);
+                    packageArtifacts.putAll(rn[ii].packageArtifacts);
+                    packageDependencies.putAll(rn[ii].packageDependencies);
+                    packagePubapis.putAll(rn[ii].packagePubapis);
                 }
             };
 
@@ -253,7 +252,7 @@
                 else {
                     requests[ii].run();
                     // If there was an error, then stop early when running single threaded.
-                    if (rn[i] != 0) {
+                    if (rn[i].returnCode != 0) {
                         return false;
                     }
                 }
@@ -269,7 +268,7 @@
         // Check the return values.
         for (int i=0; i<numCompiles; ++i) {
             if (compileChunks[i].srcs.size() > 0) {
-                if (rn[i] != 0) {
+                if (rn[i].returnCode != 0) {
                     rc = false;
                 }
             }
--- a/src/share/classes/com/sun/tools/sjavac/CompileProperties.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/CompileProperties.java	Tue Jun 17 14:01:27 2014 +0200
@@ -38,6 +38,7 @@
 import java.util.Map;
 
 import com.sun.tools.sjavac.options.Options;
+import com.sun.tools.sjavac.server.JavacService;
 
 /**
  * Compile properties transform a properties file into a Java source file.
@@ -63,7 +64,8 @@
     public void setExtra(Options a) {
     }
 
-    public boolean transform(Map<String,Set<URI>> pkgSrcs,
+    public boolean transform(JavacService javacService,
+                             Map<String,Set<URI>> pkgSrcs,
                              Set<URI>             visibleSrcs,
                              Map<URI,Set<String>> visibleClasses,
                              Map<String,Set<String>> oldPackageDependents,
--- a/src/share/classes/com/sun/tools/sjavac/CopyFile.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/CopyFile.java	Tue Jun 17 14:01:27 2014 +0200
@@ -32,6 +32,7 @@
 import java.util.Map;
 
 import com.sun.tools.sjavac.options.Options;
+import com.sun.tools.sjavac.server.JavacService;
 
 /**
  * The copy file transform simply copies a matching file from -src to -d .
@@ -50,7 +51,8 @@
     public void setExtra(Options a) {
     }
 
-    public boolean transform(Map<String,Set<URI>> pkgSrcs,
+    public boolean transform(JavacService javacService,
+                             Map<String,Set<URI>> pkgSrcs,
                              Set<URI> visibleSrcs,
                              Map<URI,Set<String>> visibleClasses,
                              Map<String,Set<String>> oldPackageDependents,
--- a/src/share/classes/com/sun/tools/sjavac/JavacState.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/JavacState.java	Tue Jun 17 14:01:27 2014 +0200
@@ -40,6 +40,7 @@
 
 import com.sun.tools.sjavac.options.Options;
 import com.sun.tools.sjavac.options.SourceLocation;
+import com.sun.tools.sjavac.server.JavacService;
 
 /**
  * The javac state class maintains the previous (prev) and the current (now)
@@ -625,7 +626,7 @@
                 sr.put(e.getKey(), e.getValue());
             }
         }
-        perform(binDir, sr);
+        perform(null, binDir, sr);
     }
 
     /**
@@ -641,20 +642,21 @@
 
             sr.put(e.getKey(), e.getValue());
         }
-        perform(gensrcDir, sr);
+        perform(null, gensrcDir, sr);
     }
 
     /**
      * Compile all the java sources. Return true, if it needs to be called again!
      */
-    public boolean performJavaCompilations(Options args,
+    public boolean performJavaCompilations(JavacService javacService,
+                                           Options args,
                                            Set<String> recentlyCompiled,
                                            boolean[] rcValue) {
         Map<String,Transformer> suffixRules = new HashMap<>();
         suffixRules.put(".java", compileJavaPackages);
         compileJavaPackages.setExtra(args);
 
-        rcValue[0] = perform(binDir, suffixRules);
+        rcValue[0] = perform(javacService, binDir, suffixRules);
         recentlyCompiled.addAll(taintedPackages());
         clearTaintedPackages();
         boolean again = !packagesWithChangedPublicApis.isEmpty();
@@ -684,7 +686,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(File outputDir, Map<String,Transformer> suffixRules)
+    private boolean perform(JavacService javacService,
+                            File outputDir,
+                            Map<String,Transformer> suffixRules)
     {
         boolean rc = true;
         // Group sources based on transforms. A source file can only belong to a single transform.
@@ -709,7 +713,8 @@
             Map<String,String> packagePublicApis =
                     Collections.synchronizedMap(new HashMap<String, String>());
 
-            boolean  r = t.transform(srcs,
+            boolean  r = t.transform(javacService,
+                                     srcs,
                                      visibleSrcs,
                                      visibleClasses,
                                      prev.dependents(),
--- a/src/share/classes/com/sun/tools/sjavac/Main.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/Main.java	Tue Jun 17 14:01:27 2014 +0200
@@ -33,7 +33,9 @@
 
 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;
 
 /**
  * The main class of the smart javac wrapper tool.
@@ -339,7 +341,12 @@
             do {
                 // Clean out artifacts in tainted packages.
                 javac_state.deleteClassArtifactsInTaintedPackages();
-                again = javac_state.performJavaCompilations(options, recently_compiled, rc);
+                // 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.getServerConf());
+                again = javac_state.performJavaCompilations(javacService, options, recently_compiled, rc);
                 if (!rc[0]) break;
             } while (again);
             // Only update the state if the compile went well.
--- a/src/share/classes/com/sun/tools/sjavac/Transformer.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/Transformer.java	Tue Jun 17 14:01:27 2014 +0200
@@ -31,6 +31,7 @@
 import java.util.Map;
 
 import com.sun.tools.sjavac.options.Options;
+import com.sun.tools.sjavac.server.JavacService;
 
 /**
  * The transform interface is used to transform content inside a package, from one form to another.
@@ -82,7 +83,8 @@
      * 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(Map<String,Set<URI>> pkgSrcs,
+    boolean transform(JavacService javacService,
+                      Map<String,Set<URI>> pkgSrcs,
                       Set<URI>             visibleSources,
                       Map<URI,Set<String>> visibleClasses,
                       Map<String,Set<String>> oldPackageDependencies,
--- a/src/share/classes/com/sun/tools/sjavac/Util.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/Util.java	Tue Jun 17 14:01:27 2014 +0200
@@ -26,6 +26,8 @@
 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;
@@ -67,17 +69,32 @@
     }
 
     public static String extractStringOption(String opName, String s) {
+        return extractStringOption(opName, s, null);
+    }
+
+    public static String extractStringOption(String opName, String s, String deflt) {
         int p = s.indexOf(opName+"=");
-        if (p == -1) return null;
+        if (p == -1) return deflt;
         p+=opName.length()+1;
         int pe = s.indexOf(',', p);
         if (pe == -1) pe = s.length();
         return s.substring(p, pe);
     }
 
+    public static boolean extractBooleanOption(String opName, String s, boolean deflt) {
+       String str = extractStringOption(opName, s);
+        return "true".equals(str) ? true
+             : "false".equals(str) ? false
+             : deflt;
+    }
+
     public static int extractIntOption(String opName, String s) {
+        return extractIntOption(opName, s, 0);
+    }
+
+    public static int extractIntOption(String opName, String s, int deflt) {
         int p = s.indexOf(opName+"=");
-        if (p == -1) return 0;
+        if (p == -1) return deflt;
         p+=opName.length()+1;
         int pe = s.indexOf(',', p);
         if (pe == -1) pe = s.length();
--- a/src/share/classes/com/sun/tools/sjavac/comp/JavaCompilerWithDeps.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/comp/JavaCompilerWithDeps.java	Tue Jun 17 14:01:27 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
@@ -29,8 +29,6 @@
 import com.sun.tools.javac.main.JavaCompiler;
 import com.sun.tools.javac.util.Context;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
-import com.sun.tools.sjavac.server.CompilerThread;
-import java.io.File;
 
 /** Subclass to Resolve that overrides collect.
  *
@@ -44,16 +42,16 @@
     /** The dependency database
      */
     protected Dependencies deps;
-    protected CompilerThread compilerThread;
+    protected JavacServiceImpl javacService;
 
-    public JavaCompilerWithDeps(Context context, CompilerThread t) {
+    public JavaCompilerWithDeps(Context context, JavacServiceImpl jsi) {
         super(context);
         deps = Dependencies.instance(context);
-        compilerThread = t;
+        javacService = jsi;
         needRootClasses = true;
     }
 
-    public static void preRegister(Context context, final CompilerThread t) {
+    public static void preRegister(Context context, final JavacServiceImpl t) {
         context.put(compilerKey, new Context.Factory<JavaCompiler>() {
             public JavaCompiler make(Context c) {
                 JavaCompiler instance = new JavaCompilerWithDeps(c, t);
@@ -99,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)) {
-                compilerThread.logError("Error: The source file "+sym.sourcefile.getName()+
+                javacService.logError("Error: The source file "+sym.sourcefile.getName()+
                                         " is located in the wrong package directory, because it contains the class "+
                                         sym.getQualifiedName());
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/comp/JavacServiceImpl.java	Tue Jun 17 14:01:27 2014 +0200
@@ -0,0 +1,121 @@
+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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/CompilationResult.java	Tue Jun 17 14:01:27 2014 +0200
@@ -0,0 +1,30 @@
+package com.sun.tools.sjavac.server;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class CompilationResult {
+
+    // 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 CompilationResult(int returnCode) {
+        this.returnCode = returnCode;
+        this.sysinfo = new SysInfo(-1, -1);
+    }
+
+    public void setReturnCode(int returnCode) {
+        this.returnCode = returnCode;
+    }
+}
--- a/src/share/classes/com/sun/tools/sjavac/server/CompilerPool.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/server/CompilerPool.java	Tue Jun 17 14:01:27 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
@@ -31,6 +31,8 @@
 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.
@@ -147,7 +149,7 @@
     public CompilerThread grabCompilerThread() throws InterruptedException {
         available.acquire();
         if (compilers.empty()) {
-            return new CompilerThread(this);
+            return new CompilerThread(this, new JavacServiceImpl(javacServer));
         }
         return compilers.pop();
     }
--- a/src/share/classes/com/sun/tools/sjavac/server/CompilerThread.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/server/CompilerThread.java	Tue Jun 17 14:01:27 2014 +0200
@@ -55,6 +55,7 @@
 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;
 
@@ -71,6 +72,7 @@
 public class CompilerThread implements Runnable {
     private JavacServer javacServer;
     private CompilerPool compilerPool;
+    private JavacServiceImpl javacServiceImpl;
     private List<Future<?>> subTasks;
 
     // Communicating over this socket.
@@ -85,9 +87,10 @@
     // If true, then this thread is serving a request.
     private boolean inUse = false;
 
-    CompilerThread(CompilerPool cp) {
+    CompilerThread(CompilerPool cp, JavacServiceImpl javacServiceImpl) {
         compilerPool = cp;
         javacServer = cp.getJavacServer();
+        this.javacServiceImpl = javacServiceImpl;
     }
 
     /**
@@ -131,7 +134,7 @@
         context = new Context();
         ResolveWithDeps.preRegister(context);
         AttrWithDeps.preRegister(context);
-        JavaCompilerWithDeps.preRegister(context, this);
+        JavaCompilerWithDeps.preRegister(context, javacServiceImpl);
         subTasks = new ArrayList<>();
     }
 
--- a/src/share/classes/com/sun/tools/sjavac/server/JavacServer.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/src/share/classes/com/sun/tools/sjavac/server/JavacServer.java	Tue Jun 17 14:01:27 2014 +0200
@@ -24,30 +24,22 @@
  */
 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.PrintWriter;
 import java.io.FileNotFoundException;
-import java.net.URI;
-import java.util.HashSet;
-import java.util.Set;
 import java.util.HashMap;
 import java.util.Map;
 
 import java.net.InetAddress;
-import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketAddress;
 import java.util.ArrayList;
 import java.util.Random;
 
 import com.sun.tools.sjavac.Util;
 import com.sun.tools.sjavac.ProblemException;
 import java.io.*;
-import java.util.*;
 
 /**
  * The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
@@ -73,8 +65,6 @@
     // 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 int ERROR_FATAL = -1;
-    final static int ERROR_BUT_TRY_AGAIN = -4712;
     final static String PROTOCOL_COOKIE_VERSION = "----THE-COOKIE-V2----";
     final static String PROTOCOL_CWD = "----THE-CWD----";
     final static String PROTOCOL_ID = "----THE-ID----";
@@ -99,7 +89,7 @@
     /**
      * Acquire the port file. Synchronized since several threads inside an smart javac wrapper client acquires the same port file at the same time.
      */
-    private static synchronized PortFile getPortFile(String filename) throws FileNotFoundException {
+    public static synchronized PortFile getPortFile(String filename) throws FileNotFoundException {
         if (allPortFiles == null) {
             allPortFiles = new HashMap<>();
         }
@@ -179,17 +169,12 @@
             // 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 poolsize = Util.extractIntOption("poolsize", settings);
-            if (poolsize <= 0) {
-                // If not set, default to the number of cores.
-                poolsize = Runtime.getRuntime().availableProcessors();
-            }
+            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);
-            if (keepalive <= 0) {
-                keepalive = 120;
-            }
+            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;
@@ -220,134 +205,6 @@
     }
 
     /**
-     * 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 static int useServer(String settings, String[] args,
-            Set<URI> sourcesToCompile,
-            Set<URI> visibleSources,
-            Map<URI, Set<String>> visibleClasses,
-            Map<String, Set<URI>> packageArtifacts,
-            Map<String, Set<String>> packageDependencies,
-            Map<String, String> packagePubapis,
-            SysInfo sysinfo,
-            PrintStream out,
-            PrintStream err) {
-        try {
-            // 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.
-            String id = Util.extractStringOption("id", settings);
-            String portfile = Util.extractStringOption("portfile", settings);
-            String logfile = Util.extractStringOption("logfile", settings);
-            String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
-            String background = Util.extractStringOption("background", settings);
-            if (background == null || !background.equals("false")) {
-                background = "true";
-            }
-            // 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
-            String sjavac = Util.extractStringOption("sjavac", settings);
-            int poolsize = Util.extractIntOption("poolsize", settings);
-            int keepalive = Util.extractIntOption("keepalive", settings);
-
-            if (keepalive <= 0) {
-                // Default keepalive for server is 120 seconds.
-                // I.e. it will accept 120 seconds of inactivity before quitting.
-                keepalive = 120;
-            }
-            if (portfile == null) {
-                err.println("No portfile was specified!");
-                return -1;
-            }
-            if (logfile == null) {
-                logfile = portfile + ".javaclog";
-            }
-            if (stdouterrfile == null) {
-                stdouterrfile = portfile + ".stdouterr";
-            }
-            // Default to sjavac and hope it is in the path.
-            if (sjavac == null) {
-                sjavac = "sjavac";
-            }
-
-            int attempts = 0;
-            int rc = -1;
-            do {
-                PortFile port_file = getPortFile(portfile);
-                synchronized (port_file) {
-                    port_file.lock();
-                    port_file.getValues();
-                    port_file.unlock();
-                }
-                if (!port_file.containsPortInfo()) {
-                    String cmd = fork(sjavac, port_file.getFilename(), logfile, poolsize, keepalive, err, stdouterrfile, background);
-
-                    if (background.equals("true") && !port_file.waitForValidValues()) {
-                        // Ouch the server did not start! Lets print its stdouterrfile and the command used.
-                        printFailedAttempt(cmd, stdouterrfile, err);
-                        // And give up.
-                        return -1;
-                    }
-                }
-                rc = connectAndCompile(port_file, id, args, sourcesToCompile, visibleSources,
-                        packageArtifacts, packageDependencies, packagePubapis, sysinfo,
-                        out, err);
-                // Try again until we manage to connect. Any error after that
-                // will cause the compilation to fail.
-                if (rc == ERROR_BUT_TRY_AGAIN) {
-                    // We could not connect to the server. Try again.
-                    attempts++;
-                    try {
-                        Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS * 1000);
-                    } catch (InterruptedException e) {
-                    }
-                }
-            } while (rc == ERROR_BUT_TRY_AGAIN && attempts < MAX_NUM_CONNECT_ATTEMPTS);
-            return rc;
-        } catch (Exception e) {
-            e.printStackTrace(err);
-            return -1;
-        }
-    }
-
-    private static void printFailedAttempt(String cmd, String f, PrintStream 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.");
-        }
-    }
-
-    /**
      * Spawn the server instance.
      */
 
@@ -367,15 +224,15 @@
     /**
      * Fork a background process. Returns the command line used that can be printed if something failed.
      */
-    private static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive,
-            final PrintStream err, String stdouterrfile, String background)
+    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.equals("true")) {
+        if (background) {
             sjavac += "%20" + startserver;
             sjavac = sjavac.replaceAll("%20", " ");
             sjavac = sjavac.replaceAll("%2C", ",");
@@ -421,243 +278,6 @@
     }
 
     /**
-     * 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;
-    }
-
-    /**
-     * 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.
-     */
-    public static SysInfo connectGetSysInfo(String serverSettings, PrintStream out, PrintStream err) {
-        SysInfo sysinfo = new SysInfo(-1, -1);
-        String id = Util.extractStringOption("id", serverSettings);
-        String portfile = Util.extractStringOption("portfile", serverSettings);
-        try {
-            PortFile pf = getPortFile(portfile);
-            useServer(serverSettings, new String[0],
-                    new HashSet<URI>(),
-                    new HashSet<URI>(),
-                    new HashMap<URI, Set<String>>(),
-                    new HashMap<String, Set<URI>>(),
-                    new HashMap<String, Set<String>>(),
-                    new HashMap<String, String>(),
-                    sysinfo, out, err);
-        } catch (Exception e) {
-            e.printStackTrace(err);
-        }
-        return sysinfo;
-    }
-
-    /**
-     * 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.
-     */
-    private static int connectAndCompile(PortFile portFile, String id, String[] args,
-            Set<URI> sourcesToCompile,
-            Set<URI> visibleSources,
-            Map<String, Set<URI>> packageArtifacts,
-            Map<String, Set<String>> packageDependencies,
-            Map<String, String> packagePublicApis,
-            SysInfo sysinfo,
-            PrintStream out,
-            PrintStream err) {
-        int rc = -3;
-        try {
-            int port = portFile.containsPortInfo() ? portFile.getPort() : 0;
-            if (port == 0) {
-                return 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 = CONNECTION_TIMEOUT * 1000;
-            try {
-                sock.connect(sockaddr, timeoutMs);
-            } catch (java.net.ConnectException e) {
-                err.println("Could not connect to javac server found in portfile: " + portFile.getFilename() + " " + e);
-                return ERROR_BUT_TRY_AGAIN;
-            }
-            if (!sock.isConnected()) {
-                err.println("Could not connect to javac server found in portfile: " + portFile.getFilename());
-                return ERROR_BUT_TRY_AGAIN;
-            }
-            BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
-            PrintWriter sockout = new PrintWriter(sock.getOutputStream());
-
-            sockout.println(PROTOCOL_COOKIE_VERSION);
-            sockout.println("" + cookie);
-            sockout.println(PROTOCOL_CWD);
-            sockout.println(System.getProperty("user.dir"));
-            sockout.println(PROTOCOL_ID);
-            sockout.println(id);
-            sockout.println(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(PROTOCOL_SOURCES_TO_COMPILE);
-            for (URI uri : sourcesToCompile) {
-                sockout.println(uri.toString());
-            }
-            sockout.println(PROTOCOL_VISIBLE_SOURCES);
-            for (URI uri : visibleSources) {
-                sockout.println(uri.toString());
-            }
-            sockout.println(PROTOCOL_END);
-            sockout.flush();
-
-            StringBuffer stdout = new StringBuffer();
-            StringBuffer stderr = new StringBuffer();
-
-            if (!expect(in, PROTOCOL_STDOUT)) {
-                return ERROR_FATAL;
-            }
-            // Load stdout
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return ERROR_FATAL;
-                }
-                if (l.equals(PROTOCOL_STDERR)) {
-                    break;
-                }
-                stdout.append(l);
-                stdout.append('\n');
-            }
-            // Load stderr
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return ERROR_FATAL;
-                }
-                if (l.equals(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 ERROR_FATAL;
-                }
-                if (l.equals(PROTOCOL_PACKAGE_DEPENDENCIES)) {
-                    break;
-                }
-                if (l.length() > 1 && l.charAt(0) == '+') {
-                    String pkg = l.substring(1);
-                    lastUriSet = new HashSet<>();
-                    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 ERROR_FATAL;
-                }
-                if (l.equals(PROTOCOL_PACKAGE_PUBLIC_APIS)) {
-                    break;
-                }
-                if (l.length() > 1 && l.charAt(0) == '+') {
-                    String pkg = l.substring(1);
-                    lastPackageSet = new HashSet<>();
-                    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 ERROR_FATAL;
-                }
-                if (l.equals(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();
-                packagePublicApis.put(p, api);
-            }
-            // Now reading the max memory possible.
-            for (;;) {
-                String l = in.readLine();
-                if (l == null) {
-                    return ERROR_FATAL;
-                }
-                if (l.equals(PROTOCOL_RETURN_CODE)) {
-                    break;
-                }
-                if (l.startsWith("num_cores=") && sysinfo != null) {
-                    sysinfo.numCores = Integer.parseInt(l.substring(10));
-                }
-                if (l.startsWith("max_memory=") && sysinfo != null) {
-                    sysinfo.maxMemory = Long.parseLong(l.substring(11));
-                }
-            }
-            String l = in.readLine();
-            if (l == null) {
-                err.println("No return value from the server!");
-                return ERROR_FATAL;
-            }
-            rc = Integer.parseInt(l);
-            out.print(stdout);
-            err.print(stderr);
-        } catch (Exception e) {
-            e.printStackTrace(err);
-        }
-        return rc;
-    }
-
-    /**
      * 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.
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/JavacService.java	Tue Jun 17 14:01:27 2014 +0200
@@ -0,0 +1,18 @@
+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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/sjavac/server/JavacServiceClient.java	Tue Jun 17 14:01:27 2014 +0200
@@ -0,0 +1,408 @@
+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 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;
+
+    public JavacServiceClient(String settings) {
+        id = Util.extractStringOption("id", settings);
+        portfile = Util.extractStringOption("portfile", settings);
+        logfile = Util.extractStringOption("logfile", settings, portfile + ".javaclog");
+        stdouterrfile = Util.extractStringOption("stdouterrfile", settings, portfile + ".stdouterr");
+        background = Util.extractBooleanOption("background", settings, true);
+        sjavac = Util.extractStringOption("sjavac", settings, "sjavac");
+        int poolsize = Util.extractIntOption("poolsize", settings);
+        keepalive = Util.extractIntOption("keepalive", settings, 120);
+
+        this.poolsize = poolsize > 0 ? poolsize : Runtime.getRuntime().availableProcessors();
+    }
+
+
+    /**
+     * 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/test/tools/sjavac/SJavac.java	Fri Jun 27 20:32:12 2014 +0100
+++ b/test/tools/sjavac/SJavac.java	Tue Jun 17 14:01:27 2014 +0200
@@ -23,7 +23,6 @@
 
 import java.util.*;
 import java.io.*;
-import java.net.*;
 import java.nio.file.*;
 import java.nio.file.attribute.*;
 import java.nio.charset.*;
@@ -43,6 +42,9 @@
     }
 
     FileSystem defaultfs = FileSystems.getDefault();
+    String serverArg = "--server:"
+            + "portfile=testportfile,"
+            + "background=false";
 
     // Where to put generated sources that will
     // test aspects of sjavac, ie JTWork/scratch/gensrc
@@ -136,7 +138,7 @@
             "private int b() { return A.DEFINITION; } native void foo(); }");
 
         compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         previous_bin_state = collectState(bin);
         previous_headers_state = collectState(headers);
     }
@@ -145,7 +147,7 @@
         System.out.println("\nTesting that no change in sources implies no change in binaries.");
         System.out.println("------------------------------------------------------------------");
         compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         Map<String,Long> new_bin_state = collectState(bin);
         verifyEqual(new_bin_state, previous_bin_state);
         Map<String,Long> new_headers_state = collectState(headers);
@@ -158,7 +160,7 @@
         System.out.println("-----------------------------------------");
         removeFrom(gensrc, "alfa/omega/AA.java");
         compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         Map<String,Long> new_bin_state = collectState(bin);
         verifyThatFilesHaveBeenRemoved(previous_bin_state, new_bin_state,
                                        "bin/alfa/omega/AA$1.class",
@@ -185,7 +187,7 @@
                  "public final static int DEFINITION = 18; public void aint() { } private void foo() { } }");
 
         compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         Map<String,Long> new_bin_state = collectState(bin);
 
         verifyNewerFiles(previous_bin_state, new_bin_state,
@@ -211,7 +213,7 @@
                        "private int b() { return A.DEFINITION; } }");
 
         compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         Map<String,Long> new_bin_state = collectState(bin);
         verifyNewerFiles(previous_bin_state, new_bin_state,
                          "bin/beta/B.class",
@@ -236,7 +238,7 @@
                  "@java.lang.annotation.Native final static int alfa = 42; }");
 
         compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         Map<String,Long> new_bin_state = collectState(bin);
         verifyNewerFiles(previous_bin_state, new_bin_state,
                          "bin/beta/B.class",
@@ -262,7 +264,7 @@
                  "@java.lang.annotation.Native final static int alfa = 43; }");
 
         compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         Map<String,Long> new_bin_state = collectState(bin);
         verifyNewerFiles(previous_bin_state, new_bin_state,
                          "bin/beta/B.class",
@@ -299,7 +301,7 @@
                  "package beta; public class B { }");
 
         compile("-x", "beta", "gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false");
+                serverArg);
         Map<String,Long> new_bin_state = collectState(bin);
         verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
                                      "bin/alfa/omega/A.class",
@@ -310,7 +312,7 @@
         System.out.println("----- Compile with exluded beta went well!");
         delete(bin);
         compileExpectFailure("gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1",
-                             "--server:portfile=testserver,background=false");
+                             serverArg);
 
         System.out.println("----- Compile without exluded beta failed, as expected! Good!");
         delete(bin);
@@ -341,7 +343,7 @@
 
         compile("gensrc", "-x", "beta", "-sourcepath", "gensrc2",
                 "-sourcepath", "gensrc3", "-d", "bin", "-h", "headers", "-j", "1",
-                "--server:portfile=testserver,background=false");
+                serverArg);
 
         System.out.println("The first compile went well!");
         Map<String,Long> new_bin_state = collectState(bin);
@@ -353,7 +355,7 @@
         delete(bin);
         compileExpectFailure("gensrc", "-sourcepath", "gensrc2", "-sourcepath", "gensrc3",
                              "-d", "bin", "-h", "headers", "-j", "1",
-                             "--server:portfile=testserver,background=false");
+                             serverArg);
 
         System.out.println("----- Compile without exluded beta failed, as expected! Good!");
         delete(bin);
@@ -378,7 +380,7 @@
                  "package gamma; public class C { alfa.omega.A a; }");
 
         compile("gensrc", "-d", "bin", "-h", "headers", "-j", "3",
-                "--server:portfile=testserver,background=false","--log=debug");
+                serverArg,"--log=debug");
         Map<String,Long> new_bin_state = collectState(bin);
         verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
                                      "bin/alfa/omega/A.class",
@@ -407,7 +409,7 @@
                  "package beta; public class B { }");
 
         compile("-x", "beta", "-src", "gensrc", "-x", "alfa/omega", "-sourcepath", "gensrc",
-                "-d", "bin", "--server:portfile=testserver,background=false");
+                "-d", "bin", serverArg);
 
         Map<String,Long> new_bin_state = collectState(bin);
         verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
@@ -431,7 +433,7 @@
                  "}");
 
         compile("gensrc", "-d", "bin", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         Map<String,Long> previous_bin_state = collectState(bin);
 
         // Change pubapi of A, this should trigger a recompile of B.
@@ -443,7 +445,7 @@
                  "}");
 
         compile("gensrc", "-d", "bin", "-j", "1",
-                "--server:portfile=testserver,background=false", "--log=debug");
+                serverArg, "--log=debug");
         Map<String,Long> new_bin_state = collectState(bin);
 
         verifyNewerFiles(previous_bin_state, new_bin_state,