8048594: The sjavac client/server protocol should be hidden behind an interface

Reviewed-by: jfranck
This commit is contained in:
Andreas Lundblad 2014-06-17 14:01:27 +02:00
parent ce0935812b
commit 8baafcf7ef
17 changed files with 697 additions and 459 deletions

View file

@ -36,6 +36,7 @@ import java.util.Map;
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 @@ public class CleanProperties implements Transformer
// 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,

View file

@ -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 @@ public class CompileJavaPackages implements Transformer {
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 @@ public class CompileJavaPackages implements Transformer {
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 @@ public class CompileJavaPackages implements Transformer {
}
// 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 @@ public class CompileJavaPackages implements Transformer {
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 @@ public class CompileJavaPackages implements Transformer {
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 @@ public class CompileJavaPackages implements Transformer {
// 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;
}
}

View file

@ -38,6 +38,7 @@ import java.util.HashSet;
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 class CompileProperties implements Transformer
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,

View file

@ -32,6 +32,7 @@ import java.util.HashSet;
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 class CopyFile implements Transformer {
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,

View file

@ -40,6 +40,7 @@ import java.util.*;
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 @@ public class JavacState
sr.put(e.getKey(), e.getValue());
}
}
perform(binDir, sr);
perform(null, binDir, sr);
}
/**
@ -641,20 +642,21 @@ public class JavacState
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 @@ public class JavacState
* 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 @@ public class JavacState
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(),

View file

@ -33,7 +33,9 @@ import java.nio.file.Files;
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 @@ public class Main {
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.

View file

@ -31,6 +31,7 @@ import java.util.Set;
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 @@ public interface Transformer
* 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,

View file

@ -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 class Util {
}
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();

View file

@ -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 java.util.StringTokenizer;
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 @@ public class JavaCompilerWithDeps extends JavaCompiler {
/** 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 @@ public class JavaCompilerWithDeps extends JavaCompiler {
// 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());
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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.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.
@ -147,7 +149,7 @@ public class CompilerPool {
public CompilerThread grabCompilerThread() throws InterruptedException {
available.acquire();
if (compilers.empty()) {
return new CompilerThread(this);
return new CompilerThread(this, new JavacServiceImpl(javacServer));
}
return compilers.pop();
}

View file

@ -55,6 +55,7 @@ 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;
@ -71,6 +72,7 @@ import com.sun.tools.sjavac.comp.SmartFileManager;
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 @@ public class CompilerThread implements Runnable {
// 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 @@ public class CompilerThread implements Runnable {
context = new Context();
ResolveWithDeps.preRegister(context);
AttrWithDeps.preRegister(context);
JavaCompilerWithDeps.preRegister(context, this);
JavaCompilerWithDeps.preRegister(context, javacServiceImpl);
subTasks = new ArrayList<>();
}

View file

@ -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 @@ public class JavacServer {
// 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 @@ public class JavacServer {
/**
* 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 @@ public class JavacServer {
// 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;
@ -219,134 +204,6 @@ public class JavacServer {
}
}
/**
* 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 @@ public class JavacServer {
/**
* 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", ",");
@ -420,243 +277,6 @@ public class JavacServer {
return "";
}
/**
* 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.

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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 @@ class SJavac {
}
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 @@ class SJavac {
"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 @@ class SJavac {
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 @@ class SJavac {
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 @@ class SJavac {
"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 @@ class SJavac {
"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 @@ class SJavac {
"@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 @@ class SJavac {
"@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 @@ class SJavac {
"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 @@ class SJavac {
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 @@ class SJavac {
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 @@ class SJavac {
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 @@ class SJavac {
"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 @@ class SJavac {
"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 @@ class SJavac {
"}");
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 @@ class SJavac {
"}");
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,