8048457: Sjavac should not use portfiles, sockets, etc if background=false

8044131: Restructure client / server protocol code

Changes protocol code to use Object input/output streams. Avoids spawning server if background=false. Refactors idleness checks, pooling and port file monitoring.

Reviewed-by: jjg, jfranck
This commit is contained in:
Andreas Lundblad 2014-08-13 14:44:59 +02:00
parent 14e6aa6b9e
commit ce4c456820
47 changed files with 1775 additions and 1631 deletions

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
@ -36,10 +36,10 @@ import java.util.Set;
* from a build. There are usually two build states, the previous one (prev),
* loaded from the javac_state file, and the current one (now).
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class BuildState {
private Map<String,Module> modules = new HashMap<>();

View File

@ -36,19 +36,18 @@ import java.util.Map;
import java.util.Properties;
import com.sun.tools.sjavac.options.Options;
import com.sun.tools.sjavac.server.JavacService;
import com.sun.tools.sjavac.server.Sjavac;
/**
* The clean properties transform should not be necessary.
* Eventually we will cleanup the property file sources in the OpenJDK instead.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class CleanProperties implements Transformer
{
public class CleanProperties implements Transformer {
public void setExtra(String e) {
// Any extra information is ignored for clean properties.
}
@ -57,7 +56,7 @@ public class CleanProperties implements Transformer
// Any extra information is ignored for clean properties.
}
public boolean transform(JavacService javacService,
public boolean transform(Sjavac sjavac,
Map<String,Set<URI>> pkgSrcs,
Set<URI> visibleSrcs,
Map<URI,Set<String>> visibleClasses,
@ -70,8 +69,7 @@ public class CleanProperties implements Transformer
boolean incremental,
int numCores,
PrintStream out,
PrintStream err)
{
PrintStream err) {
boolean rc = true;
for (String pkgName : pkgSrcs.keySet()) {
String pkgNameF = pkgName.replace('.',File.separatorChar);
@ -87,9 +85,12 @@ public class CleanProperties implements Transformer
return rc;
}
boolean clean(String pkgName, String pkgNameF, File src, File destRoot, int debugLevel,
Map<String,Set<URI>> packageArtifacts)
{
boolean clean(String pkgName,
String pkgNameF,
File src,
File destRoot,
int debugLevel,
Map<String,Set<URI>> packageArtifacts) {
// Load the properties file.
Properties p = new Properties();
try {

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
@ -33,10 +33,10 @@ import java.util.Set;
* A compile chunk is a list of sources/packages to be compiled. Possibly a subset of
* the total number of sources/packages to be compiled for this sjavac invocation.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class CompileChunk implements Comparable<CompileChunk> {
public int numPackages;

View File

@ -30,13 +30,12 @@ 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.CompilationResult;
import com.sun.tools.sjavac.server.JavacService;
import com.sun.tools.sjavac.server.Sjavac;
import com.sun.tools.sjavac.server.SysInfo;
/**
@ -67,7 +66,7 @@ public class CompileJavaPackages implements Transformer {
args = a;
}
public boolean transform(final JavacService javacService,
public boolean transform(final Sjavac sjavac,
Map<String,Set<URI>> pkgSrcs,
final Set<URI> visibleSources,
final Map<URI,Set<String>> visibleClasses,
@ -86,12 +85,12 @@ public class CompileJavaPackages implements Transformer {
boolean concurrentCompiles = true;
// Fetch the id.
final String id = Util.extractStringOption("id", javacService.serverSettings());
final String id = Util.extractStringOption("id", sjavac.serverSettings());
// Only keep portfile and sjavac settings..
String psServerSettings = Util.cleanSubOptions(Util.set("portfile","sjavac","background","keepalive"), javacService.serverSettings());
String psServerSettings = Util.cleanSubOptions(Util.set("portfile","sjavac","background","keepalive"), sjavac.serverSettings());
// Get maximum heap size from the server!
SysInfo sysinfo = javacService.getSysInfo();
SysInfo sysinfo = sjavac.getSysInfo();
if (sysinfo.numCores == -1) {
Log.error("Could not query server for sysinfo!");
return false;
@ -216,7 +215,7 @@ public class CompileJavaPackages implements Transformer {
requests[i] = new Thread() {
@Override
public void run() {
rn[ii] = javacService.compile("n/a",
rn[ii] = sjavac.compile("n/a",
id + "-" + ii,
args.prepJavacArgs(),
Collections.<File>emptyList(),

View File

@ -38,20 +38,19 @@ import java.util.HashSet;
import java.util.Map;
import com.sun.tools.sjavac.options.Options;
import com.sun.tools.sjavac.server.JavacService;
import com.sun.tools.sjavac.server.Sjavac;
/**
* Compile properties transform a properties file into a Java source file.
* Java has built in support for reading properties from either a text file
* in the source or a compiled java source file.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class CompileProperties implements Transformer
{
public class CompileProperties implements Transformer {
// Any extra information passed from the command line, for example if:
// -tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle
// then extra will be "sun.util.resources.LocaleNamesBundle"
@ -64,7 +63,7 @@ public class CompileProperties implements Transformer
public void setExtra(Options a) {
}
public boolean transform(JavacService javacService,
public boolean transform(Sjavac sjavac,
Map<String,Set<URI>> pkgSrcs,
Set<URI> visibleSrcs,
Map<URI,Set<String>> visibleClasses,

View File

@ -32,16 +32,16 @@ import java.util.HashSet;
import java.util.Map;
import com.sun.tools.sjavac.options.Options;
import com.sun.tools.sjavac.server.JavacService;
import com.sun.tools.sjavac.server.Sjavac;
/**
* The copy file transform simply copies a matching file from -src to -d .
* Such files are typically images, xml documents and other data files.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class CopyFile implements Transformer {
@ -51,7 +51,7 @@ public class CopyFile implements Transformer {
public void setExtra(Options a) {
}
public boolean transform(JavacService javacService,
public boolean transform(Sjavac sjavac,
Map<String,Set<URI>> pkgSrcs,
Set<URI> visibleSrcs,
Map<URI,Set<String>> visibleClasses,

View File

@ -26,7 +26,6 @@
package com.sun.tools.sjavac;
import java.io.*;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Date;
import java.util.Set;
@ -39,20 +38,18 @@ import java.net.URI;
import java.util.*;
import com.sun.tools.sjavac.options.Options;
import com.sun.tools.sjavac.options.SourceLocation;
import com.sun.tools.sjavac.server.JavacService;
import com.sun.tools.sjavac.server.Sjavac;
/**
* The javac state class maintains the previous (prev) and the current (now)
* build states and everything else that goes into the javac_state file.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class JavacState
{
public class JavacState {
// The arguments to the compile. If not identical, then it cannot
// be an incremental build!
String theArgs;
@ -655,7 +652,7 @@ public class JavacState
/**
* Compile all the java sources. Return true, if it needs to be called again!
*/
public boolean performJavaCompilations(JavacService javacService,
public boolean performJavaCompilations(Sjavac sjavac,
Options args,
Set<String> recentlyCompiled,
boolean[] rcValue) {
@ -663,7 +660,7 @@ public class JavacState
suffixRules.put(".java", compileJavaPackages);
compileJavaPackages.setExtra(args);
rcValue[0] = perform(javacService, binDir, suffixRules);
rcValue[0] = perform(sjavac, binDir, suffixRules);
recentlyCompiled.addAll(taintedPackages());
clearTaintedPackages();
boolean again = !packagesWithChangedPublicApis.isEmpty();
@ -693,10 +690,9 @@ 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(JavacService javacService,
private boolean perform(Sjavac sjavac,
File outputDir,
Map<String,Transformer> suffixRules)
{
Map<String,Transformer> suffixRules) {
boolean rc = true;
// Group sources based on transforms. A source file can only belong to a single transform.
Map<Transformer,Map<String,Set<URI>>> groupedSources = new HashMap<>();
@ -720,7 +716,7 @@ public class JavacState
Map<String,String> packagePublicApis =
Collections.synchronizedMap(new HashMap<String, String>());
boolean r = t.transform(javacService,
boolean r = t.transform(sjavac,
srcs,
visibleSrcs,
visibleClasses,
@ -798,9 +794,7 @@ public class JavacState
* Used to detect bugs where the makefile and sjavac have different opinions on which files
* should be compiled.
*/
public void compareWithMakefileList(File makefileSourceList)
throws ProblemException
{
public void compareWithMakefileList(File makefileSourceList) throws ProblemException {
// If we are building on win32 using for example cygwin the paths in the makefile source list
// might be /cygdrive/c/.... which does not match c:\....
// We need to adjust our calculated sources to be identical, if necessary.

View File

@ -31,10 +31,10 @@ import java.io.PrintStream;
* Utility class only for sjavac logging.
* The log level can be set using for example --log=DEBUG on the sjavac command line.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Log {
private static PrintStream out, err;

View File

@ -31,19 +31,21 @@ import java.util.*;
import java.nio.file.Path;
import java.nio.file.Files;
import com.sun.tools.sjavac.client.SjavacClient;
import com.sun.tools.sjavac.comp.SjavacImpl;
import com.sun.tools.sjavac.comp.PooledSjavac;
import com.sun.tools.sjavac.options.Options;
import com.sun.tools.sjavac.options.SourceLocation;
import com.sun.tools.sjavac.server.JavacService;
import com.sun.tools.sjavac.server.JavacServer;
import com.sun.tools.sjavac.server.JavacServiceClient;
import com.sun.tools.sjavac.server.Sjavac;
import com.sun.tools.sjavac.server.SjavacServer;
/**
* The main class of the smart javac wrapper tool.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Main {
@ -163,13 +165,19 @@ public class Main {
return;
}
// Spawn a background server.
int rc = JavacServer.startServer(args[0], System.err);
System.exit(rc);
try {
SjavacServer server = new SjavacServer(args[0], System.err);
int rc = server.startServer();
System.exit(rc);
} catch (IOException ioex) {
Log.error("IOException caught: " + ioex);
System.exit(-1);
}
}
Main main = new Main();
int rc = main.go(args, System.out, System.err);
// Remove the portfile, but only if this background=false was used.
JavacServer.cleanup(args);
SjavacServer.cleanup(args);
System.exit(rc);
}
@ -341,15 +349,22 @@ public class Main {
// Collect the name of all compiled packages.
Set<String> recently_compiled = new HashSet<>();
boolean[] rc = new boolean[1];
Sjavac sjavac;
boolean background = Util.extractBooleanOption("background", options.getServerConf(), true);
do {
// Clean out artifacts in tainted packages.
javac_state.deleteClassArtifactsInTaintedPackages();
// Create a JavacService to delegate the actual compilation to.
// Currently sjavac always connects to a server through a socket
// regardless if sjavac runs as a background service or not.
// This will most likely change in the future.
JavacService javacService = new JavacServiceClient(options);
again = javac_state.performJavaCompilations(javacService, options, recently_compiled, rc);
// Create an sjavac implementation to be used for compilation
if (background) {
sjavac = new SjavacClient(options);
} else {
int poolsize = Util.extractIntOption("poolsize", options.getServerConf());
if (poolsize <= 0)
poolsize = Runtime.getRuntime().availableProcessors();
sjavac = new PooledSjavac(new SjavacImpl(), poolsize);
}
again = javac_state.performJavaCompilations(sjavac, options, recently_compiled, rc);
if (!rc[0]) break;
} while (again);
// Only update the state if the compile went well.
@ -360,6 +375,8 @@ public class Main {
// Remove artifacts that were generated during the last compile, but not this one.
javac_state.removeSuperfluousArtifacts(recently_compiled);
}
if (!background)
sjavac.shutdown();
return rc[0] ? 0 : -1;
} catch (ProblemException e) {
Log.error(e.getMessage());

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
@ -36,10 +36,10 @@ import java.util.Set;
* The module is the root of a set of packages/sources/artifacts.
* At the moment there is only one module in use, the empty/no-name/default module.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Module implements Comparable<Module> {
private String name;

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
@ -54,10 +54,10 @@ import java.util.Set;
* the visible recompilation of the dependent packages indicates how much circular
* dependencies your code has.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Package implements Comparable<Package> {
// The module this package belongs to. (There is a legacy module with an empty string name,

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
@ -28,10 +28,10 @@ package com.sun.tools.sjavac;
/**
* Used to signal serious problems when running sjavac.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class ProblemException extends Exception {
static final long serialVersionUID = -3387516993124229949L;

View File

@ -37,10 +37,10 @@ import java.util.Map;
* The class also knows how to find source files (scanRoot) given include/exclude
* patterns and a root.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Source implements Comparable<Source> {
// The package the source belongs to.

View File

@ -31,7 +31,7 @@ import java.util.Set;
import java.util.Map;
import com.sun.tools.sjavac.options.Options;
import com.sun.tools.sjavac.server.JavacService;
import com.sun.tools.sjavac.server.Sjavac;
/**
* The transform interface is used to transform content inside a package, from one form to another.
@ -39,13 +39,12 @@ import com.sun.tools.sjavac.server.JavacService;
* but can also be an unpredictable number of generated source files (eg idl2java)
* or a single predictable output file (eg when copying,cleaning or compiling a properties file).
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public interface Transformer
{
public interface Transformer {
/**
* The transform method takes a set of package names, mapped to their source files and to the
* pubapis of the packages.
@ -83,7 +82,7 @@ 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(JavacService javacService,
boolean transform(Sjavac sjavac,
Map<String,Set<URI>> pkgSrcs,
Set<URI> visibleSources,
Map<URI,Set<String>> visibleClasses,

View File

@ -26,8 +26,6 @@
package com.sun.tools.sjavac;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
@ -37,10 +35,10 @@ import java.util.StringTokenizer;
/**
* Utilities.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Util {

View File

@ -0,0 +1,275 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.client;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.util.List;
import java.util.Set;
import com.sun.tools.sjavac.Log;
import com.sun.tools.sjavac.ProblemException;
import com.sun.tools.sjavac.Util;
import com.sun.tools.sjavac.server.CompilationResult;
import com.sun.tools.sjavac.server.PortFile;
import com.sun.tools.sjavac.server.Sjavac;
import com.sun.tools.sjavac.server.SjavacServer;
import com.sun.tools.sjavac.server.SysInfo;
import com.sun.tools.sjavac.options.Options;
/**
* Sjavac implementation that delegates requests to a SjavacServer.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SjavacClient implements Sjavac {
// The id can perhaps be used in the future by the javac server to reuse the
// JavaCompiler instance for several compiles using the same id.
private final String id;
private final String portfileName;
private final String logfile;
private final String stdouterrfile;
private final boolean background;
// Default keepalive for server is 120 seconds.
// I.e. it will accept 120 seconds of inactivity before quitting.
private final int keepalive;
private final int poolsize;
// The sjavac option specifies how the server part of sjavac is spawned.
// If you have the experimental sjavac in your path, you are done. If not, you have
// to point to a com.sun.tools.sjavac.Main that supports --startserver
// for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main
private final String sjavacForkCmd;
// Wait 2 seconds for response, before giving up on javac server.
static int CONNECTION_TIMEOUT = 2000;
static int MAX_CONNECT_ATTEMPTS = 3;
static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 2000;
// Store the server conf settings here.
private final String settings;
public SjavacClient(Options options) {
String tmpServerConf = options.getServerConf();
String serverConf = (tmpServerConf!=null)? tmpServerConf : "";
String tmpId = Util.extractStringOption("id", serverConf);
id = (tmpId!=null) ? tmpId : "id"+(((new java.util.Random()).nextLong())&Long.MAX_VALUE);
String p = Util.extractStringOption("portfile", serverConf);
portfileName = (p!=null) ? p : options.getStateDir().toFile().getAbsolutePath()+File.separatorChar+"javac_server";
logfile = Util.extractStringOption("logfile", serverConf, portfileName + ".javaclog");
stdouterrfile = Util.extractStringOption("stdouterrfile", serverConf, portfileName + ".stdouterr");
background = Util.extractBooleanOption("background", serverConf, true);
sjavacForkCmd = Util.extractStringOption("sjavac", serverConf, "sjavac");
int poolsize = Util.extractIntOption("poolsize", serverConf);
keepalive = Util.extractIntOption("keepalive", serverConf, 120);
this.poolsize = poolsize > 0 ? poolsize : Runtime.getRuntime().availableProcessors();
settings = (serverConf.equals("")) ? "id="+id+",portfile="+portfileName : serverConf;
}
/**
* Hand out the server settings.
* @return The server settings, possibly a default value.
*/
public String serverSettings() {
return settings;
}
/**
* Make a request to the server only to get the maximum possible heap size to use for compilations.
*
* @param port_file The port file used to synchronize creation of this server.
* @param id The identify of the compilation.
* @param out Standard out information.
* @param err Standard err information.
* @return The maximum heap size in bytes.
*/
@Override
public SysInfo getSysInfo() {
try (Socket socket = tryConnect()) {
// The ObjectInputStream constructor will block until the
// corresponding ObjectOutputStream has written and flushed the
// header, so it is important that the ObjectOutputStreams on server
// and client are opened before the ObjectInputStreams.
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
oos.writeObject(id);
oos.writeObject(SjavacServer.CMD_SYS_INFO);
oos.flush();
return (SysInfo) ois.readObject();
} catch (IOException | ClassNotFoundException ex) {
Log.error("[CLIENT] Exception caught: " + ex);
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
}
return null;
}
@Override
public CompilationResult compile(String protocolId,
String invocationId,
String[] args,
List<File> explicitSources,
Set<URI> sourcesToCompile,
Set<URI> visibleSources) {
CompilationResult result;
try (Socket socket = tryConnect()) {
// The ObjectInputStream constructor will block until the
// corresponding ObjectOutputStream has written and flushed the
// header, so it is important that the ObjectOutputStreams on server
// and client are opened before the ObjectInputStreams.
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
oos.writeObject(id);
oos.writeObject(SjavacServer.CMD_COMPILE);
oos.writeObject(protocolId);
oos.writeObject(invocationId);
oos.writeObject(args);
oos.writeObject(explicitSources);
oos.writeObject(sourcesToCompile);
oos.writeObject(visibleSources);
oos.flush();
result = (CompilationResult) ois.readObject();
} catch (IOException | ClassNotFoundException ex) {
Log.error("Exception caught: " + ex);
result = new CompilationResult(CompilationResult.ERROR_FATAL);
result.stderr = ex.getMessage();
}
return result;
}
private Socket tryConnect() throws IOException {
PortFile portFile;
try {
// This should be taken care of at a higher level (JDK-8048451)
portFile = SjavacServer.getPortFile(portfileName);
} catch (FileNotFoundException e) {
// Reached for instance if directory of port file does not exist
Log.error("Port file inaccessable: " + e);
throw new RuntimeException(e);
}
for (int i = 0; i < MAX_CONNECT_ATTEMPTS; i++) {
Log.info(String.format("Trying to connect (attempt %d of %d)",
i+1, MAX_CONNECT_ATTEMPTS));
try {
if (!makeSureServerIsRunning(portFile))
continue;
Socket socket = new Socket();
InetAddress localhost = InetAddress.getByName(null);
socket.connect(new InetSocketAddress(localhost, portFile.getPort()),
CONNECTION_TIMEOUT);
return socket;
} catch (ProblemException | IOException ex) {
Log.error("Caught exception during tryConnect: " + ex);
}
try {
Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
throw new IOException("Could not connect to server");
}
private boolean makeSureServerIsRunning(PortFile portFile)
throws IOException, ProblemException, FileNotFoundException {
synchronized (portFile) {
portFile.lock();
portFile.getValues();
portFile.unlock();
}
if (!portFile.containsPortInfo()) {
String forkCmd = SjavacServer.fork(sjavacForkCmd,
portFile.getFilename(),
logfile,
poolsize,
keepalive,
System.err,
stdouterrfile,
background);
if (!portFile.waitForValidValues()) {
// This can be simplified once JDK-8048457 has been addressed
// since we won't have an SjavacClient if background = false
if (background) {
// There seems be some problem with spawning the external
// process (for instance no fork command provided and no
// sjavac on path)
StringWriter sw = new StringWriter();
SjavacClient.printFailedAttempt(forkCmd,
stdouterrfile,
new PrintWriter(sw));
Log.error(sw.toString());
}
}
}
return portFile.containsPortInfo();
}
public static void printFailedAttempt(String cmd, String f, PrintWriter err) {
err.println("---- Failed to start javac server with this command -----");
err.println(cmd);
try {
BufferedReader in = new BufferedReader(new FileReader(f));
err.println("---- stdout/stderr output from attempt to start javac server -----");
for (;;) {
String l = in.readLine();
if (l == null) {
break;
}
err.println(l);
}
err.println("------------------------------------------------------------------");
} catch (Exception e) {
err.println("The stdout/stderr output in file " + f + " does not exist and the server did not start.");
}
}
@Override
public void shutdown() {
// Nothing to clean up
}
}

View File

@ -30,10 +30,10 @@ import com.sun.tools.javac.code.Symbol;
/** Subclass to Attr that overrides reportDepedence.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class AttrWithDeps extends Attr {

View File

@ -41,10 +41,10 @@ import com.sun.tools.javac.util.Name;
/** Utility class containing dependency information between packages
* and the pubapi for a package.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Dependencies {
protected static final Context.Key<Dependencies> dependenciesKey = new Context.Key<>();

View File

@ -32,29 +32,29 @@ import com.sun.tools.javac.code.Symbol.ClassSymbol;
/** Subclass to Resolve that overrides collect.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class JavaCompilerWithDeps extends JavaCompiler {
/** The dependency database
*/
protected Dependencies deps;
protected JavacServiceImpl javacService;
protected SjavacErrorHandler errorHandler;
public JavaCompilerWithDeps(Context context, JavacServiceImpl jsi) {
public JavaCompilerWithDeps(Context context, SjavacErrorHandler eh) {
super(context);
deps = Dependencies.instance(context);
javacService = jsi;
errorHandler = eh;
needRootClasses = true;
}
public static void preRegister(Context context, final JavacServiceImpl t) {
public static void preRegister(Context context, final SjavacErrorHandler eh) {
context.put(compilerKey, new Context.Factory<JavaCompiler>() {
public JavaCompiler make(Context c) {
JavaCompiler instance = new JavaCompilerWithDeps(c, t);
JavaCompiler instance = new JavaCompilerWithDeps(c, eh);
c.put(JavaCompiler.class, instance);
return instance;
}
@ -97,7 +97,7 @@ 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)) {
javacService.logError("Error: The source file "+sym.sourcefile.getName()+
errorHandler.logError("Error: The source file "+sym.sourcefile.getName()+
" is located in the wrong package directory, because it contains the class "+
sym.getQualifiedName());
}

View File

@ -0,0 +1,141 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.comp;
import java.io.File;
import java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.sun.tools.sjavac.Log;
import com.sun.tools.sjavac.server.CompilationResult;
import com.sun.tools.sjavac.server.Sjavac;
import com.sun.tools.sjavac.server.SysInfo;
/**
* An sjavac implementation that limits the number of concurrent calls by
* wrapping invocations in Callables and delegating them to a FixedThreadPool.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class PooledSjavac implements Sjavac {
final Sjavac delegate;
final ExecutorService pool;
public PooledSjavac(Sjavac delegate, int poolsize) {
Objects.requireNonNull(delegate);
this.delegate = delegate;
pool = Executors.newFixedThreadPool(poolsize, new ThreadFactory() {
AtomicInteger count = new AtomicInteger();
@Override
public Thread newThread(Runnable runnable) {
String cls = PooledSjavac.class.getSimpleName();
int num = count.incrementAndGet();
Thread t = new Thread(runnable, cls + "-" + num);
t.setDaemon(true);
return t;
}
});
}
@Override
public SysInfo getSysInfo() {
try {
return pool.submit(new Callable<SysInfo>() {
@Override
public SysInfo call() throws Exception {
return delegate.getSysInfo();
}
}).get();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Error during getSysInfo", e);
}
}
@Override
public CompilationResult compile(final String protocolId,
final String invocationId,
final String[] args,
final List<File> explicitSources,
final Set<URI> sourcesToCompile,
final Set<URI> visibleSources) {
try {
return pool.submit(new Callable<CompilationResult>() {
@Override
public CompilationResult call() throws Exception {
return delegate.compile(protocolId,
invocationId,
args,
explicitSources,
sourcesToCompile,
visibleSources);
}
}).get();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Error during compile", e);
}
}
@Override
public void shutdown() {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
Log.error("ThreadPool did not terminate");
}
// Grace period for thread termination
Thread.sleep(1000);
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
delegate.shutdown();
}
@Override
public String serverSettings() {
return delegate.serverSettings();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,10 +37,10 @@ import javax.lang.model.util.ElementScanner9;
/** Utility class that constructs a textual representation
* of the public api of a class.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class PubapiVisitor extends ElementScanner9<Void, Void> {

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
@ -30,10 +30,10 @@ import com.sun.tools.javac.code.Symbol;
/** Subclass to Resolve that overrides collect.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class ResolveWithDeps extends Resolve {

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.comp;
/**
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public interface SjavacErrorHandler {
void logError(String msg);
}

View File

@ -22,7 +22,6 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.comp;
import java.io.File;
@ -32,6 +31,7 @@ import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
@ -41,26 +41,21 @@ 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.javac.util.Options;
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.Sjavac;
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);
}
/**
* The sjavac implementation that interacts with javac and performs the actual
* compilation.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SjavacImpl implements Sjavac {
@Override
public SysInfo getSysInfo() {
@ -75,6 +70,7 @@ public class JavacServiceImpl implements JavacService {
List<File> explicitSources,
Set<URI> sourcesToCompile,
Set<URI> visibleSources) {
final AtomicBoolean forcedExit = new AtomicBoolean();
JavacTool compiler = JavacTool.create();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
@ -82,7 +78,12 @@ public class JavacServiceImpl implements JavacService {
Context context = new Context();
ResolveWithDeps.preRegister(context);
AttrWithDeps.preRegister(context);
JavaCompilerWithDeps.preRegister(context, this);
JavaCompilerWithDeps.preRegister(context, new SjavacErrorHandler() {
@Override
public void logError(String msg) {
forcedExit.set(true);
}
});
// Now setup the actual compilation....
CompilationResult compilationResult = new CompilationResult(0);
@ -100,13 +101,6 @@ public class JavacServiceImpl implements JavacService {
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();
@ -120,14 +114,20 @@ public class JavacServiceImpl implements JavacService {
smartFileManager.cleanArtifacts();
smartFileManager.setLog(stdout);
// Do the compilation!
CompilationTask task = compiler.getTask(stderr, smartFileManager, null, Arrays.asList(args), null, compilationUnits, context);
CompilationTask task = compiler.getTask(stderr,
smartFileManager,
null,
Arrays.asList(args),
null,
compilationUnits,
context);
smartFileManager.setSymbolFileEnabled(!Options.instance(context).isSet("ignore.symbol.file"));
rc = ((JavacTaskImpl) task).doCall();
smartFileManager.flush();
}
} catch (Exception e) {
stderr.println(e.getMessage());
stderrLog.append(e.getMessage());
forcedExit.set(true);
}
@ -139,11 +139,19 @@ public class JavacServiceImpl implements JavacService {
compilationResult.stdout = stdoutLog.toString();
compilationResult.stderr = stderrLog.toString();
compilationResult.returnCode = rc.exitCode == 0 && forcedExit.get() ? -1 : rc.exitCode;
return compilationResult;
}
@Override
public void shutdown() {
// Nothing to clean up
// ... maybe we should wait for any current request to finish?
}
@Override
public String serverSettings() {
return "";

View File

@ -37,7 +37,6 @@ import javax.tools.*;
import javax.tools.JavaFileObject.Kind;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.util.BaseFileManager;
import com.sun.tools.javac.util.ListBuffer;
/**
@ -50,10 +49,10 @@ import com.sun.tools.javac.util.ListBuffer;
* Can also blind out the filemanager from seeing certain files in the file system.
* Necessary to prevent javac from seeing some sources where the source path points.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager> {
@ -97,9 +96,7 @@ public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager>
public Iterable<JavaFileObject> list(Location location,
String packageName,
Set<Kind> kinds,
boolean recurse)
throws IOException
{
boolean recurse) throws IOException {
// Acquire the list of files.
Iterable<JavaFileObject> files = super.list(location, packageName, kinds, recurse);
if (visibleSources.isEmpty()) {
@ -112,8 +109,7 @@ public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager>
String t = uri.toString();
if (t.startsWith("jar:")
|| t.endsWith(".class")
|| visibleSources.contains(uri))
{
|| visibleSources.contains(uri)) {
filteredFiles.add(f);
}
}
@ -128,9 +124,7 @@ public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager>
@Override
public JavaFileObject getJavaFileForInput(Location location,
String className,
Kind kind)
throws IOException
{
Kind kind) throws IOException {
JavaFileObject file = super.getJavaFileForInput(location, className, kind);
if (file == null || visibleSources.isEmpty()) {
return file;
@ -146,9 +140,7 @@ public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager>
public JavaFileObject getJavaFileForOutput(Location location,
String className,
Kind kind,
FileObject sibling)
throws IOException
{
FileObject sibling) throws IOException {
JavaFileObject file = super.getJavaFileForOutput(location, className, kind, sibling);
if (file == null) return file;
int dp = className.lastIndexOf('.');
@ -165,9 +157,7 @@ public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager>
@Override
public FileObject getFileForInput(Location location,
String packageName,
String relativeName)
throws IOException
{
String relativeName) throws IOException {
FileObject file = super.getFileForInput(location, packageName, relativeName);
if (file == null || visibleSources.isEmpty()) {
return file;
@ -183,9 +173,7 @@ public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager>
public FileObject getFileForOutput(Location location,
String packageName,
String relativeName,
FileObject sibling)
throws IOException
{
FileObject sibling) throws IOException {
FileObject file = super.getFileForOutput(location, packageName, relativeName, sibling);
if (file == null) return file;
if (location.equals(StandardLocation.NATIVE_HEADER_OUTPUT) &&

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
@ -36,10 +36,10 @@ import javax.tools.JavaFileObject;
* and compare the new content with the old content on disk. Only if they differ,
* will the file be updated.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SmartFileObject implements JavaFileObject {

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
@ -34,9 +34,9 @@ import javax.tools.JavaFileObject;
* If not, the file is not touched.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SmartWriter extends Writer {

View File

@ -27,6 +27,12 @@ package com.sun.tools.sjavac.options;
import java.util.Iterator;
/**
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class ArgumentIterator implements Iterator<String> {
/** The underlying argument iterator */

View File

@ -47,6 +47,11 @@ import com.sun.tools.sjavac.Transformer;
* This enum represents all options from (1) and (2). Note that instances of
* this enum only entail static information about the option. For storage of
* option values, refer to com.sun.tools.sjavac.options.Options.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public enum Option {

View File

@ -26,7 +26,6 @@
package com.sun.tools.sjavac.options;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@ -38,6 +37,11 @@ import com.sun.tools.sjavac.Transformer;
/**
* This class is used to decode sjavac options.
* See com.sun.tools.sjavac.options.Options for example usage.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public abstract class OptionHelper {

View File

@ -39,6 +39,11 @@ import com.sun.tools.sjavac.Transformer;
/**
* Instances of this class represent values for sjavac command line options.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Options {

View File

@ -37,6 +37,11 @@ import com.sun.tools.sjavac.Source;
/**
* Represents a directory to be used for input to sjavac. (For instance a
* sourcepath or classpath.)
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SourceLocation {

View File

@ -25,28 +25,35 @@
package com.sun.tools.sjavac.server;
import java.io.Serializable;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class CompilationResult {
/**
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class CompilationResult implements Serializable {
static final long serialVersionUID = 46739181113L;
// Return code constants
public final static int ERROR_BUT_TRY_AGAIN = -4712;
public final static int ERROR_FATAL = -1;
public int returnCode;
public Map<String, Set<URI>> packageArtifacts = new HashMap<>();
public Map<String, Set<String>> packageDependencies = new HashMap<>();
public Map<String, String> packagePubapis = new HashMap<>();
public SysInfo sysinfo;
public String stdout;
public String stderr;
public String stdout = "";
public String stderr = "";
public CompilationResult(int returnCode) {
this.returnCode = returnCode;
this.sysinfo = new SysInfo(-1, -1);
}
public void setReturnCode(int returnCode) {

View File

@ -1,165 +0,0 @@
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.Stack;
import java.util.concurrent.Future;
import com.sun.tools.sjavac.comp.JavacServiceImpl;
/** The compiler pool maintains compiler threads.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
*/
public class CompilerPool {
// The javac server that created this pool.
private JavacServer javacServer;
// A semaphore protecting the poolsize number of threads.
private Semaphore available;
// The stack of compiler threads.
private Stack<CompilerThread> compilers = new Stack<>();
// And the executor server to spawn threads.
private final ExecutorService executorPool;
// How many requests are active right now?
private int concurrentRequests = 0;
// When was the last request finished?
private long lastRequestFinished = 0;
// The total number of requests to this pool.
private int numRequests = 0;
// Protect access to the three above values.
private static final Object conc = new Object();
/**
* Return the javac server that this pool belongs to.
*/
public JavacServer getJavacServer() {
return javacServer;
}
/**
* Return how many threads are running at this very moment.
*/
public int numActiveRequests()
{
synchronized (conc) {
return concurrentRequests;
}
}
/**
* Return when the last request was finished.
* I.e. the pool has been idle since.
*/
public long lastRequestFinished()
{
synchronized (conc) {
return lastRequestFinished;
}
}
/**
* Up the number of active requests.
*/
public int startRequest() {
int n;
synchronized (conc) {
concurrentRequests++;
numRequests++;
n = numRequests;
}
return n;
}
/**
* Down the number of active requests. Return the current time.
*/
public long stopRequest() {
synchronized (conc) {
concurrentRequests--;
lastRequestFinished = System.currentTimeMillis();
}
return lastRequestFinished;
}
/**
* Create a new compiler pool.
*/
CompilerPool(int poolsize, JavacServer server) {
available = new Semaphore(poolsize, true);
javacServer = server;
executorPool = Executors.newFixedThreadPool(poolsize);
lastRequestFinished = System.currentTimeMillis();
}
/**
* Execute a compiler thread.
*/
public void execute(CompilerThread ct) {
executorPool.execute(ct);
}
/**
* Execute a minor task, for example generating bytecodes and writing them to disk,
* that belong to a major compiler thread task.
*/
public Future<?> executeSubtask(CompilerThread t, Runnable r) {
return executorPool.submit(r);
}
/**
* Shutdown the pool.
*/
public void shutdown() {
executorPool.shutdown();
}
/**
* Acquire a compiler thread from the pool, or block until a thread is available.
* If the pools is empty, create a new thread, but never more than is "available".
*/
public CompilerThread grabCompilerThread() throws InterruptedException {
available.acquire();
if (compilers.empty()) {
return new CompilerThread(this, new JavacServiceImpl(javacServer));
}
return compilers.pop();
}
/**
* Return the specified compiler thread to the pool.
*/
public void returnCompilerThread(CompilerThread h) {
compilers.push(h);
available.release();
}
}

View File

@ -1,412 +0,0 @@
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Options;
import com.sun.tools.javac.util.StringUtils;
import com.sun.tools.sjavac.comp.AttrWithDeps;
import com.sun.tools.sjavac.comp.Dependencies;
import com.sun.tools.sjavac.comp.JavaCompilerWithDeps;
import com.sun.tools.sjavac.comp.JavacServiceImpl;
import com.sun.tools.sjavac.comp.ResolveWithDeps;
import com.sun.tools.sjavac.comp.SmartFileManager;
/**
* The compiler thread maintains a JavaCompiler instance and
* can receive a request from the client, perform the compilation
* requested and report back the results.
*
* * <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
*/
public class CompilerThread implements Runnable {
private JavacServer javacServer;
private CompilerPool compilerPool;
private JavacServiceImpl javacServiceImpl;
private List<Future<?>> subTasks;
// Communicating over this socket.
private Socket socket;
// The necessary classes to do a compilation.
private com.sun.tools.javac.api.JavacTool compiler;
private StandardJavaFileManager fileManager;
private SmartFileManager smartFileManager;
private Context context;
// If true, then this thread is serving a request.
private boolean inUse = false;
CompilerThread(CompilerPool cp, JavacServiceImpl javacServiceImpl) {
compilerPool = cp;
javacServer = cp.getJavacServer();
this.javacServiceImpl = javacServiceImpl;
}
/**
* Execute a minor task, for example generating bytecodes and writing them to disk,
* that belong to a major compiler thread task.
*/
public synchronized void executeSubtask(Runnable r) {
subTasks.add(compilerPool.executeSubtask(this, r));
}
/**
* Count the number of active sub tasks.
*/
public synchronized int numActiveSubTasks() {
int c = 0;
for (Future<?> f : subTasks) {
if (!f.isDone() && !f.isCancelled()) {
c++;
}
}
return c;
}
/**
* Use this socket for the upcoming request.
*/
public void setSocket(Socket s) {
socket = s;
}
/**
* Prepare the compiler thread for use. It is not yet started.
* It will be started by the executor service.
*/
public synchronized void use() {
assert(!inUse);
inUse = true;
compiler = com.sun.tools.javac.api.JavacTool.create();
fileManager = compiler.getStandardFileManager(null, null, null);
smartFileManager = new SmartFileManager(fileManager);
context = new Context();
ResolveWithDeps.preRegister(context);
AttrWithDeps.preRegister(context);
JavaCompilerWithDeps.preRegister(context, javacServiceImpl);
subTasks = new ArrayList<>();
}
/**
* Prepare the compiler thread for idleness.
*/
public synchronized void unuse() {
assert(inUse);
inUse = false;
compiler = null;
fileManager = null;
smartFileManager = null;
context = null;
subTasks = null;
}
/**
* Expect this key on the next line read from the reader.
*/
private static boolean expect(BufferedReader in, String key) throws IOException {
String s = in.readLine();
if (s != null && s.equals(key)) {
return true;
}
return false;
}
// The request identifier, for example GENERATE_NEWBYTECODE
String id = "";
public String currentRequestId() {
return id;
}
PrintWriter stdout;
PrintWriter stderr;
int forcedExitCode = 0;
public void logError(String msg) {
stderr.println(msg);
forcedExitCode = -1;
}
/**
* Invoked by the executor service.
*/
public void run() {
// Unique nr that identifies this request.
int thisRequest = compilerPool.startRequest();
long start = System.currentTimeMillis();
int numClasses = 0;
StringBuilder compiledPkgs = new StringBuilder();
use();
PrintWriter out = null;
try {
javacServer.log("<"+thisRequest+"> Connect from "+socket.getRemoteSocketAddress()+" activethreads="+compilerPool.numActiveRequests());
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(new OutputStreamWriter(
socket.getOutputStream()));
if (!expect(in, JavacServer.PROTOCOL_COOKIE_VERSION)) {
javacServer.log("<"+thisRequest+"> Bad protocol from ip "+socket.getRemoteSocketAddress());
return;
}
String cookie = in.readLine();
if (cookie == null || !cookie.equals(""+javacServer.getCookie())) {
javacServer.log("<"+thisRequest+"> Bad cookie from ip "+socket.getRemoteSocketAddress());
return;
}
if (!expect(in, JavacServer.PROTOCOL_CWD)) {
return;
}
String cwd = in.readLine();
if (cwd == null)
return;
if (!expect(in, JavacServer.PROTOCOL_ID)) {
return;
}
id = in.readLine();
if (id == null)
return;
if (!expect(in, JavacServer.PROTOCOL_ARGS)) {
return;
}
ArrayList<String> the_options = new ArrayList<>();
ArrayList<File> the_classes = new ArrayList<>();
Iterable<File> path = Arrays.<File> asList(new File(cwd));
for (;;) {
String l = in.readLine();
if (l == null)
return;
if (l.equals(JavacServer.PROTOCOL_SOURCES_TO_COMPILE))
break;
if (l.startsWith("--server:"))
continue;
if (!l.startsWith("-") && l.endsWith(".java")) {
the_classes.add(new File(l));
numClasses++;
} else {
the_options.add(l);
}
continue;
}
// Load sources to compile
Set<URI> sourcesToCompile = new HashSet<>();
for (;;) {
String l = in.readLine();
if (l == null)
return;
if (l.equals(JavacServer.PROTOCOL_VISIBLE_SOURCES))
break;
try {
sourcesToCompile.add(new URI(l));
numClasses++;
} catch (URISyntaxException e) {
return;
}
}
// Load visible sources
Set<URI> visibleSources = new HashSet<>();
boolean fix_drive_letter_case =
StringUtils.toLowerCase(System.getProperty("os.name")).startsWith("windows");
for (;;) {
String l = in.readLine();
if (l == null)
return;
if (l.equals(JavacServer.PROTOCOL_END))
break;
try {
URI u = new URI(l);
if (fix_drive_letter_case) {
// Make sure the driver letter is lower case.
String s = u.toString();
if (s.startsWith("file:/") &&
Character.isUpperCase(s.charAt(6))) {
u = new URI("file:/"+Character.toLowerCase(s.charAt(6))+s.substring(7));
}
}
visibleSources.add(u);
} catch (URISyntaxException e) {
return;
}
}
// A completed request has been received.
// Now setup the actual compilation....
// First deal with explicit source files on cmdline and in at file.
ListBuffer<JavaFileObject> compilationUnits = new ListBuffer<>();
for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(the_classes)) {
compilationUnits.append(i);
}
// Now deal with sources supplied as source_to_compile.
ListBuffer<File> sourcesToCompileFiles = new ListBuffer<>();
for (URI u : sourcesToCompile) {
sourcesToCompileFiles.append(new File(u));
}
for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(sourcesToCompileFiles)) {
compilationUnits.append(i);
}
// Log the options to be used.
StringBuilder options = new StringBuilder();
for (String s : the_options) {
options.append(">").append(s).append("< ");
}
javacServer.log(id+" <"+thisRequest+"> options "+options.toString());
forcedExitCode = 0;
// Create a new logger.
StringWriter stdoutLog = new StringWriter();
StringWriter stderrLog = new StringWriter();
stdout = new PrintWriter(stdoutLog);
stderr = new PrintWriter(stderrLog);
com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK;
try {
if (compilationUnits.size() > 0) {
smartFileManager.setVisibleSources(visibleSources);
smartFileManager.cleanArtifacts();
smartFileManager.setLog(stdout);
// Do the compilation!
CompilationTask task = compiler.getTask(stderr, smartFileManager, null, the_options, null, compilationUnits, context);
smartFileManager.setSymbolFileEnabled(!Options.instance(context).isSet("ignore.symbol.file"));
rc = ((JavacTaskImpl) task).doCall();
while (numActiveSubTasks()>0) {
try { Thread.sleep(1000); } catch (InterruptedException e) { }
}
smartFileManager.flush();
}
} catch (Exception e) {
stderr.println(e.getMessage());
forcedExitCode = -1;
}
// Send the response..
out.println(JavacServer.PROTOCOL_STDOUT);
out.print(stdoutLog);
out.println(JavacServer.PROTOCOL_STDERR);
out.print(stderrLog);
// The compilation is complete! And errors will have already been printed on out!
out.println(JavacServer.PROTOCOL_PACKAGE_ARTIFACTS);
Map<String,Set<URI>> pa = smartFileManager.getPackageArtifacts();
for (String aPkgName : pa.keySet()) {
out.println("+"+aPkgName);
Set<URI> as = pa.get(aPkgName);
for (URI a : as) {
out.println(" "+a.toString());
}
}
Dependencies deps = Dependencies.instance(context);
out.println(JavacServer.PROTOCOL_PACKAGE_DEPENDENCIES);
Map<String,Set<String>> pd = deps.getDependencies();
for (String aPkgName : pd.keySet()) {
out.println("+"+aPkgName);
Set<String> ds = pd.get(aPkgName);
// Everything depends on java.lang
if (!ds.contains(":java.lang")) ds.add(":java.lang");
for (String d : ds) {
out.println(" "+d);
}
}
out.println(JavacServer.PROTOCOL_PACKAGE_PUBLIC_APIS);
Map<String,String> pp = deps.getPubapis();
for (String aPkgName : pp.keySet()) {
out.println("+"+aPkgName);
String ps = pp.get(aPkgName);
// getPubapis added a space to each line!
out.println(ps);
compiledPkgs.append(aPkgName+" ");
}
out.println(JavacServer.PROTOCOL_SYSINFO);
out.println("num_cores=" + Runtime.getRuntime().availableProcessors());
out.println("max_memory=" + Runtime.getRuntime().maxMemory());
out.println(JavacServer.PROTOCOL_RETURN_CODE);
// Errors from sjavac that affect compilation status!
int rcv = rc.exitCode;
if (rcv == 0 && forcedExitCode != 0) {
rcv = forcedExitCode;
}
out.println("" + rcv);
out.println(JavacServer.PROTOCOL_END);
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) out.close();
if (!socket.isClosed()) {
socket.close();
}
socket = null;
} catch (Exception e) {
javacServer.log("ERROR "+e);
e.printStackTrace();
}
compilerPool.stopRequest();
long duration = System.currentTimeMillis()-start;
javacServer.addBuildTime(duration);
float classpersec = ((float)numClasses)*(((float)1000.0)/((float)duration));
javacServer.log(id+" <"+thisRequest+"> "+compiledPkgs+" duration " + duration+ " ms num_classes="+numClasses+
" classpersec="+classpersec+" subtasks="+subTasks.size());
javacServer.flushLog();
unuse();
compilerPool.returnCompilerThread(this);
}
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.io.File;
import java.net.URI;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
/**
* An sjavac implementation that keeps track of idleness and shuts down the
* given Terminable upon idleness timeout.
*
* An idleness timeout kicks in {@code idleTimeout} milliseconds after the last
* request is completed.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class IdleResetSjavac implements Sjavac {
private final Sjavac delegate;
private final AtomicInteger outstandingCalls = new AtomicInteger();
private final Terminable toShutdown;
private final Timer idlenessTimer = new Timer();
private final long idleTimeout;
// Class invariant: idlenessTimerTask != null <-> idlenessTimerTask is scheduled
private TimerTask idlenessTimerTask;
public IdleResetSjavac(Sjavac delegate,
Terminable toShutdown,
long idleTimeout) {
this.delegate = delegate;
this.toShutdown = toShutdown;
this.idleTimeout = idleTimeout;
scheduleTimeout();
}
@Override
public SysInfo getSysInfo() {
startCall();
try {
return delegate.getSysInfo();
} finally {
endCall();
}
}
@Override
public CompilationResult compile(String protocolId,
String invocationId,
String[] args,
List<File> explicitSources,
Set<URI> sourcesToCompile,
Set<URI> visibleSources) {
startCall();
try {
return delegate.compile(protocolId,
invocationId,
args,
explicitSources,
sourcesToCompile,
visibleSources);
} finally {
endCall();
}
}
private void startCall() {
// Was there no outstanding calls before this call?
if (outstandingCalls.incrementAndGet() == 1) {
// Then the timer task must have been scheduled
if (idlenessTimerTask == null)
throw new IllegalStateException("Idle timeout already cancelled");
// Cancel timeout task
idlenessTimerTask.cancel();
idlenessTimerTask = null;
}
}
private void endCall() {
if (outstandingCalls.decrementAndGet() == 0) {
// No more outstanding calls. Schedule timeout.
scheduleTimeout();
}
}
private void scheduleTimeout() {
if (idlenessTimerTask != null)
throw new IllegalStateException("Idle timeout already scheduled");
idlenessTimerTask = new TimerTask() {
public void run() {
toShutdown.shutdown("Server has been idle for " + (idleTimeout / 1000) + " seconds.");
}
};
idlenessTimer.schedule(idlenessTimerTask, idleTimeout);
}
@Override
public void shutdown() {
idlenessTimer.cancel();
delegate.shutdown();
}
@Override
public String serverSettings() {
return delegate.serverSettings();
}
}

View File

@ -1,382 +0,0 @@
/*
* Copyright (c) 2011-2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Random;
import com.sun.tools.sjavac.Util;
import com.sun.tools.sjavac.ProblemException;
import java.io.*;
/**
* The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
*
* <p><b>This is NOT part of any supported API. If you write code that depends on this, you do so at your own risk. This code and its internal interfaces are
* subject to change or deletion without notice.</b></p>
*/
public class JavacServer {
// Responding to this tcp/ip port on localhost.
private final ServerSocket serverSocket;
// The secret cookie shared between server and client through the port file.
private final long myCookie;
// When the server was started.
private long serverStart;
// Accumulated build time for all requests, not counting idle time.
private long totalBuildTime;
// The javac server specific log file.
PrintWriter theLog;
// The compiler pool that maintains the compiler threads.
CompilerPool compilerPool;
// For the client, all port files fetched, one per started javac server.
// Though usually only one javac server is started by a client.
private static Map<String, PortFile> allPortFiles;
private static Map<String, Long> maxServerMemory;
final static String PROTOCOL_COOKIE_VERSION = "----THE-COOKIE-V2----";
final static String PROTOCOL_CWD = "----THE-CWD----";
final static String PROTOCOL_ID = "----THE-ID----";
final static String PROTOCOL_ARGS = "----THE-ARGS----";
final static String PROTOCOL_SOURCES_TO_COMPILE = "----THE-SOURCES-TO-COMPILE----";
final static String PROTOCOL_VISIBLE_SOURCES = "----THE-VISIBLE-SOURCES----";
final static String PROTOCOL_END = "----THE-END----";
final static String PROTOCOL_STDOUT = "----THE-STDOUT----";
final static String PROTOCOL_STDERR = "----THE-STDERR----";
final static String PROTOCOL_PACKAGE_ARTIFACTS = "----THE-PACKAGE_ARTIFACTS----";
final static String PROTOCOL_PACKAGE_DEPENDENCIES = "----THE-PACKAGE_DEPENDENCIES----";
final static String PROTOCOL_PACKAGE_PUBLIC_APIS = "----THE-PACKAGE-PUBLIC-APIS----";
final static String PROTOCOL_SYSINFO = "----THE-SYSINFO----";
final static String PROTOCOL_RETURN_CODE = "----THE-RETURN-CODE----";
// Check if the portfile is gone, every 5 seconds.
static int CHECK_PORTFILE_INTERVAL = 5;
// Wait 2 seconds for response, before giving up on javac server.
static int CONNECTION_TIMEOUT = 2;
static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 1;
static int MAX_NUM_CONNECT_ATTEMPTS = 3;
/**
* Acquire the port file. Synchronized since several threads inside an smart javac wrapper client acquires the same port file at the same time.
*/
public static synchronized PortFile getPortFile(String filename) throws FileNotFoundException {
if (allPortFiles == null) {
allPortFiles = new HashMap<>();
}
PortFile pf = allPortFiles.get(filename);
// Port file known. Does it still exist?
if (pf != null) {
try {
if (!pf.exists())
pf = null;
} catch (IOException ioex) {
ioex.printStackTrace();
}
}
if (pf == null) {
pf = new PortFile(filename);
allPortFiles.put(filename, pf);
}
return pf;
}
/**
* Get the cookie used for this server.
*/
long getCookie() {
return myCookie;
}
/**
* Get the port used for this server.
*/
int getPort() {
return serverSocket.getLocalPort();
}
/**
* Sum up the total build time for this javac server.
*/
public void addBuildTime(long inc) {
totalBuildTime += inc;
}
/**
* Log this message.
*/
public void log(String msg) {
if (theLog != null) {
theLog.println(msg);
} else {
System.err.println(msg);
}
}
/**
* Make sure the log is flushed.
*/
public void flushLog() {
if (theLog != null) {
theLog.flush();
}
}
/**
* Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3"
* is sent as the settings parameter. Returns 0 on success, -1 on failure.
*/
public static int startServer(String settings, PrintStream err) {
try {
String portfile = Util.extractStringOption("portfile", settings);
// The log file collects more javac server specific log information.
String logfile = Util.extractStringOption("logfile", settings);
// The stdouterr file collects all the System.out and System.err writes to disk.
String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
// We could perhaps use System.setOut and setErr here.
// But for the moment we rely on the client to spawn a shell where stdout
// and stderr are redirected already.
// The pool size is a limit the number of concurrent compiler threads used.
// The server might use less than these to avoid memory problems.
int defaultPoolSize = Runtime.getRuntime().availableProcessors();
int poolsize = Util.extractIntOption("poolsize", settings, defaultPoolSize);
// How many seconds of inactivity will the server accept before quitting?
int keepalive = Util.extractIntOption("keepalive", settings, 120);
// The port file is locked and the server port and cookie is written into it.
PortFile portFile = getPortFile(portfile);
JavacServer s;
synchronized (portFile) {
portFile.lock();
portFile.getValues();
if (portFile.containsPortInfo()) {
err.println("Javac server not started because portfile exists!");
portFile.unlock();
return -1;
}
s = new JavacServer(poolsize, logfile);
portFile.setValues(s.getPort(), s.getCookie());
portFile.unlock();
}
// Run the server. Will delete the port file when shutting down.
// It will shut down automatically when no new requests have come in
// during the last 125 seconds.
s.run(portFile, err, keepalive);
// The run loop for the server has exited.
return 0;
} catch (Exception e) {
e.printStackTrace(err);
return -1;
}
}
/**
* Spawn the server instance.
*/
private JavacServer(int poolSize, String logfile) throws IOException {
serverStart = System.currentTimeMillis();
// Create a server socket on a random port that is bound to the localhost/127.0.0.1 interface.
// I.e only local processes can connect to this port.
serverSocket = new ServerSocket(0, 128, InetAddress.getByName(null));
compilerPool = new CompilerPool(poolSize, this);
Random rnd = new Random();
myCookie = rnd.nextLong();
theLog = new PrintWriter(logfile);
log("Javac server started. port=" + getPort() + " date=" + (new java.util.Date()) + " with poolsize=" + poolSize);
flushLog();
}
/**
* Fork a background process. Returns the command line used that can be printed if something failed.
*/
public static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive,
final PrintStream err, String stdouterrfile, boolean background)
throws IOException, ProblemException {
if (stdouterrfile != null && stdouterrfile.trim().equals("")) {
stdouterrfile = null;
}
final String startserver = "--startserver:portfile=" + portfile + ",logfile=" + logfile + ",stdouterrfile=" + stdouterrfile + ",poolsize=" + poolsize + ",keepalive="+ keepalive;
if (background) {
sjavac += "%20" + startserver;
sjavac = sjavac.replaceAll("%20", " ");
sjavac = sjavac.replaceAll("%2C", ",");
// If the java/sh/cmd launcher fails the failure will be captured by stdouterr because of the redirection here.
String[] cmd = {"/bin/sh", "-c", sjavac + " >> " + stdouterrfile + " 2>&1"};
if (!(new File("/bin/sh")).canExecute()) {
ArrayList<String> wincmd = new ArrayList<>();
wincmd.add("cmd");
wincmd.add("/c");
wincmd.add("start");
wincmd.add("cmd");
wincmd.add("/c");
wincmd.add(sjavac + " >> " + stdouterrfile + " 2>&1");
cmd = wincmd.toArray(new String[wincmd.size()]);
}
Process pp = null;
try {
pp = Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace(err);
e.printStackTrace(new PrintWriter(stdouterrfile));
}
StringBuilder rs = new StringBuilder();
for (String s : cmd) {
rs.append(s + " ");
}
return rs.toString();
}
// Do not spawn a background server, instead run it within the same JVM.
Thread t = new Thread() {
@Override
public void run() {
try {
JavacServer.startServer(startserver, err);
} catch (Throwable t) {
t.printStackTrace(err);
}
}
};
t.start();
return "";
}
/**
* Run the server thread until it exits. Either because of inactivity or because the port file has been deleted by someone else, or overtaken by some other
* javac server.
*/
private void run(PortFile portFile, PrintStream err, int keepalive) {
boolean fileDeleted = false;
long timeSinceLastCompile;
try {
// Every 5 second (check_portfile_interval) we test if the portfile has disappeared => quit
// Or if the last request was finished more than 125 seconds ago => quit
// 125 = seconds_of_inactivity_before_shutdown+check_portfile_interval
serverSocket.setSoTimeout(CHECK_PORTFILE_INTERVAL*1000);
for (;;) {
try {
Socket s = serverSocket.accept();
CompilerThread ct = compilerPool.grabCompilerThread();
ct.setSocket(s);
compilerPool.execute(ct);
flushLog();
} catch (java.net.SocketTimeoutException e) {
if (compilerPool.numActiveRequests() > 0) {
// Never quit while there are active requests!
continue;
}
// If this is the timeout after the portfile
// has been deleted by us. Then we truly stop.
if (fileDeleted) {
log("Quitting because of "+(keepalive+CHECK_PORTFILE_INTERVAL)+" seconds of inactivity!");
break;
}
// Check if the portfile is still there.
if (!portFile.exists()) {
// Time to quit because the portfile was deleted by another
// process, probably by the makefile that is done building.
log("Quitting because portfile was deleted!");
flushLog();
break;
}
// Check if portfile.stop is still there.
if (portFile.markedForStop()) {
// Time to quit because another process touched the file
// server.port.stop to signal that the server should stop.
// This is necessary on some operating systems that lock
// the port file hard!
log("Quitting because a portfile.stop file was found!");
portFile.delete();
flushLog();
break;
}
// Does the portfile still point to me?
if (!portFile.stillMyValues()) {
// Time to quit because another build has started.
log("Quitting because portfile is now owned by another javac server!");
flushLog();
break;
}
// Check how long since the last request finished.
long diff = System.currentTimeMillis() - compilerPool.lastRequestFinished();
if (diff < keepalive * 1000) {
// Do not quit if we have waited less than 120 seconds.
continue;
}
// Ok, time to quit because of inactivity. Perhaps the build
// was killed and the portfile not cleaned up properly.
portFile.delete();
fileDeleted = true;
log("" + keepalive + " seconds of inactivity quitting in "
+ CHECK_PORTFILE_INTERVAL + " seconds!");
flushLog();
// Now we have a second 5 second grace
// period where javac remote requests
// that have loaded the data from the
// recently deleted portfile can connect
// and complete their requests.
}
}
} catch (Exception e) {
e.printStackTrace(err);
e.printStackTrace(theLog);
flushLog();
} finally {
compilerPool.shutdown();
}
long realTime = System.currentTimeMillis() - serverStart;
log("Shutting down.");
log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
flushLog();
}
public static void cleanup(String... args) {
String settings = Util.findServerSettings(args);
if (settings == null) return;
String portfile = Util.extractStringOption("portfile", settings);
String background = Util.extractStringOption("background", settings);
if (background != null && background.equals("false")) {
// If the server runs within this jvm, then delete the portfile,
// since this jvm is about to exit soon.
File f = new File(portfile);
f.delete();
}
}
}

View File

@ -1,449 +0,0 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sun.tools.sjavac.Util;
import com.sun.tools.sjavac.options.Options;
import static com.sun.tools.sjavac.server.CompilationResult.ERROR_BUT_TRY_AGAIN;
import static com.sun.tools.sjavac.server.CompilationResult.ERROR_FATAL;
public class JavacServiceClient implements JavacService {
// The id can perhaps be used in the future by the javac server to reuse the
// JavaCompiler instance for several compiles using the same id.
private final String id;
private final String portfile;
private final String logfile;
private final String stdouterrfile;
private final boolean background;
// Default keepalive for server is 120 seconds.
// I.e. it will accept 120 seconds of inactivity before quitting.
private final int keepalive;
private final int poolsize;
// The sjavac option specifies how the server part of sjavac is spawned.
// If you have the experimental sjavac in your path, you are done. If not, you have
// to point to a com.sun.tools.sjavac.Main that supports --startserver
// for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main
private final String sjavac;
// Store the server conf settings here.
private final String settings;
public JavacServiceClient(Options options) {
String tmpServerConf = options.getServerConf();
String serverConf = (tmpServerConf!=null)? tmpServerConf : "";
String tmpId = Util.extractStringOption("id", serverConf);
id = (tmpId!=null) ? tmpId : "id"+(((new java.util.Random()).nextLong())&Long.MAX_VALUE);
String p = Util.extractStringOption("portfile", serverConf);
portfile = (p!=null) ? p : options.getStateDir().toFile().getAbsolutePath()+File.separatorChar+"javac_server";
logfile = Util.extractStringOption("logfile", serverConf, portfile + ".javaclog");
stdouterrfile = Util.extractStringOption("stdouterrfile", serverConf, portfile + ".stdouterr");
background = Util.extractBooleanOption("background", serverConf, true);
sjavac = Util.extractStringOption("sjavac", serverConf, "sjavac");
int poolsize = Util.extractIntOption("poolsize", serverConf);
keepalive = Util.extractIntOption("keepalive", serverConf, 120);
this.poolsize = poolsize > 0 ? poolsize : Runtime.getRuntime().availableProcessors();
settings = (serverConf.equals("")) ? "id="+id+",portfile="+portfile : serverConf;
}
/**
* Hand out the server settings.
* @return The server settings, possibly a default value.
*/
public String serverSettings() {
return settings;
}
/**
* Make a request to the server only to get the maximum possible heap size to use for compilations.
*
* @param port_file The port file used to synchronize creation of this server.
* @param id The identify of the compilation.
* @param out Standard out information.
* @param err Standard err information.
* @return The maximum heap size in bytes.
*/
@Override
public SysInfo getSysInfo() {
try {
CompilationResult cr = useServer(new String[0],
Collections.<URI>emptySet(),
Collections.<URI>emptySet(),
Collections.<URI, Set<String>>emptyMap());
return cr.sysinfo;
} catch (Exception e) {
return new SysInfo(-1, -1);
}
}
@Override
public CompilationResult compile(String protocolId,
String invocationId,
String[] args,
List<File> explicitSources,
Set<URI> sourcesToCompile,
Set<URI> visibleSources) {
// Delegate to useServer, which delegates to compileHelper
return useServer(args, sourcesToCompile, visibleSources, null);
}
/**
* Connect and compile using the javac server settings and the args. When using more advanced features, the sources_to_compile and visible_sources are
* supplied to the server and meta data is returned in package_artifacts, package_dependencies and package_pubapis.
*/
public CompilationResult compileHelper(String id,
String[] args,
Set<URI> sourcesToCompile,
Set<URI> visibleSources) {
CompilationResult rc = new CompilationResult(-3);
try {
PortFile portFile = JavacServer.getPortFile(this.portfile);
int port = portFile.containsPortInfo() ? portFile.getPort() : 0;
if (port == 0) {
return new CompilationResult(ERROR_BUT_TRY_AGAIN);
}
long cookie = portFile.getCookie();
// Acquire the localhost/127.0.0.1 address.
InetAddress addr = InetAddress.getByName(null);
SocketAddress sockaddr = new InetSocketAddress(addr, port);
Socket sock = new Socket();
int timeoutMs = JavacServer.CONNECTION_TIMEOUT * 1000;
try {
sock.connect(sockaddr, timeoutMs);
} catch (java.net.ConnectException e) {
rc.setReturnCode(ERROR_BUT_TRY_AGAIN);
rc.stderr = "Could not connect to javac server found in portfile: " + portFile.getFilename() + " " + e;
return rc;
}
if (!sock.isConnected()) {
rc.setReturnCode(ERROR_BUT_TRY_AGAIN);
rc.stderr = "Could not connect to javac server found in portfile: " + portFile.getFilename();
return rc;
}
//
// Send arguments
//
BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
PrintWriter sockout = new PrintWriter(sock.getOutputStream());
sockout.println(JavacServer.PROTOCOL_COOKIE_VERSION);
sockout.println("" + cookie);
sockout.println(JavacServer.PROTOCOL_CWD);
sockout.println(System.getProperty("user.dir"));
sockout.println(JavacServer.PROTOCOL_ID);
sockout.println(id);
sockout.println(JavacServer.PROTOCOL_ARGS);
for (String s : args) {
StringBuffer buf = new StringBuffer();
String[] paths = s.split(File.pathSeparator);
int c = 0;
for (String path : paths) {
File f = new File(path);
if (f.isFile() || f.isDirectory()) {
buf.append(f.getAbsolutePath());
c++;
if (c < paths.length) {
buf.append(File.pathSeparator);
}
} else {
buf = new StringBuffer(s);
break;
}
}
sockout.println(buf.toString());
}
sockout.println(JavacServer.PROTOCOL_SOURCES_TO_COMPILE);
for (URI uri : sourcesToCompile) {
sockout.println(uri.toString());
}
sockout.println(JavacServer.PROTOCOL_VISIBLE_SOURCES);
for (URI uri : visibleSources) {
sockout.println(uri.toString());
}
sockout.println(JavacServer.PROTOCOL_END);
sockout.flush();
//
// Receive result
//
StringBuffer stdout = new StringBuffer();
StringBuffer stderr = new StringBuffer();
if (!JavacServiceClient.expect(in, JavacServer.PROTOCOL_STDOUT)) {
return new CompilationResult(ERROR_FATAL);
}
// Load stdout
for (;;) {
String l = in.readLine();
if (l == null) {
return new CompilationResult(ERROR_FATAL);
}
if (l.equals(JavacServer.PROTOCOL_STDERR)) {
break;
}
stdout.append(l);
stdout.append('\n');
}
// Load stderr
for (;;) {
String l = in.readLine();
if (l == null) {
return new CompilationResult(ERROR_FATAL);
}
if (l.equals(JavacServer.PROTOCOL_PACKAGE_ARTIFACTS)) {
break;
}
stderr.append(l);
stderr.append('\n');
}
// Load the package artifacts
Set<URI> lastUriSet = null;
for (;;) {
String l = in.readLine();
if (l == null) {
return new CompilationResult(ERROR_FATAL);
}
if (l.equals(JavacServer.PROTOCOL_PACKAGE_DEPENDENCIES)) {
break;
}
if (l.length() > 1 && l.charAt(0) == '+') {
String pkg = l.substring(1);
lastUriSet = new HashSet<>();
rc.packageArtifacts.put(pkg, lastUriSet);
} else if (l.length() > 1 && lastUriSet != null) {
lastUriSet.add(new URI(l.substring(1)));
}
}
// Load package dependencies
Set<String> lastPackageSet = null;
for (;;) {
String l = in.readLine();
if (l == null) {
return new CompilationResult(ERROR_FATAL);
}
if (l.equals(JavacServer.PROTOCOL_PACKAGE_PUBLIC_APIS)) {
break;
}
if (l.length() > 1 && l.charAt(0) == '+') {
String pkg = l.substring(1);
lastPackageSet = new HashSet<>();
rc.packageDependencies.put(pkg, lastPackageSet);
} else if (l.length() > 1 && lastPackageSet != null) {
lastPackageSet.add(l.substring(1));
}
}
// Load package pubapis
Map<String, StringBuffer> tmp = new HashMap<>();
StringBuffer lastPublicApi = null;
for (;;) {
String l = in.readLine();
if (l == null) {
return new CompilationResult(ERROR_FATAL);
}
if (l.equals(JavacServer.PROTOCOL_SYSINFO)) {
break;
}
if (l.length() > 1 && l.charAt(0) == '+') {
String pkg = l.substring(1);
lastPublicApi = new StringBuffer();
tmp.put(pkg, lastPublicApi);
} else if (l.length() > 1 && lastPublicApi != null) {
lastPublicApi.append(l.substring(1));
lastPublicApi.append("\n");
}
}
for (String p : tmp.keySet()) {
//assert (packagePublicApis.get(p) == null);
String api = tmp.get(p).toString();
rc.packagePubapis.put(p, api);
}
// Now reading the max memory possible.
for (;;) {
String l = in.readLine();
if (l == null) {
return new CompilationResult(ERROR_FATAL);
}
if (l.equals(JavacServer.PROTOCOL_RETURN_CODE)) {
break;
}
if (l.startsWith("num_cores=")) {
rc.sysinfo.numCores = Integer.parseInt(l.substring(10));
}
if (l.startsWith("max_memory=")) {
rc.sysinfo.maxMemory = Long.parseLong(l.substring(11));
}
}
String l = in.readLine();
if (l == null) {
rc.setReturnCode(ERROR_FATAL);
rc.stderr = "No return value from the server!";
return rc;
}
rc.setReturnCode(Integer.parseInt(l));
rc.stdout = stdout.toString();
rc.stderr = stderr.toString();
} catch (Exception e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
rc.stderr = sw.toString();
}
return rc;
}
/**
* Dispatch a compilation request to a javac server.
*
* @param args are the command line args to javac and is allowed to contain source files, @file and other command line options to javac.
*
* The generated classes, h files and other artifacts from the javac invocation are stored by the javac server to disk.
*
* @param sources_to_compile The sources to compile.
*
* @param visibleSources If visible sources has a non zero size, then visible_sources are the only files in the file system that the javac server can see!
* (Sources to compile are always visible.) The visible sources are those supplied by the (filtered) -sourcepath
*
* @param visibleClasses If visible classes for a specific root/jar has a non zero size, then visible_classes are the only class files that the javac server
* can see, in that root/jar. It maps from a classpath root or a jar file to the set of visible classes for that root/jar.
*
* The server return meta data about the build in the following parameters.
* @param package_artifacts, map from package name to set of created artifacts for that package.
* @param package_dependencies, map from package name to set of packages that it depends upon.
* @param package_pubapis, map from package name to unique string identifying its pub api.
*/
public CompilationResult useServer(String[] args,
Set<URI> sourcesToCompile,
Set<URI> visibleSources,
Map<URI, Set<String>> visibleClasses) {
try {
if (portfile == null) {
CompilationResult cr = new CompilationResult(CompilationResult.ERROR_FATAL);
cr.stderr = "No portfile was specified!";
return cr;
}
int attempts = 0;
CompilationResult rc;
do {
PortFile port_file = JavacServer.getPortFile(portfile);
synchronized (port_file) {
port_file.lock();
port_file.getValues();
port_file.unlock();
}
if (!port_file.containsPortInfo()) {
String cmd = JavacServer.fork(sjavac, port_file.getFilename(), logfile, poolsize, keepalive, System.err, stdouterrfile, background);
if (background && !port_file.waitForValidValues()) {
// Ouch the server did not start! Lets print its stdouterrfile and the command used.
StringWriter sw = new StringWriter();
JavacServiceClient.printFailedAttempt(cmd, stdouterrfile, new PrintWriter(sw));
// And give up.
CompilationResult cr = new CompilationResult(ERROR_FATAL);
cr.stderr = sw.toString();
return cr;
}
}
rc = compileHelper(id, args, sourcesToCompile, visibleSources);
// Try again until we manage to connect. Any error after that
// will cause the compilation to fail.
if (rc.returnCode == CompilationResult.ERROR_BUT_TRY_AGAIN) {
// We could not connect to the server. Try again.
attempts++;
try {
Thread.sleep(JavacServer.WAIT_BETWEEN_CONNECT_ATTEMPTS * 1000);
} catch (InterruptedException e) {
}
}
} while (rc.returnCode == ERROR_BUT_TRY_AGAIN && attempts < JavacServer.MAX_NUM_CONNECT_ATTEMPTS);
return rc;
} catch (Exception e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
CompilationResult cr = new CompilationResult(ERROR_FATAL);
cr.stderr = sw.toString();
return cr;
}
}
public static void printFailedAttempt(String cmd, String f, PrintWriter err) {
err.println("---- Failed to start javac server with this command -----");
err.println(cmd);
try {
BufferedReader in = new BufferedReader(new FileReader(f));
err.println("---- stdout/stderr output from attempt to start javac server -----");
for (;;) {
String l = in.readLine();
if (l == null) {
break;
}
err.println(l);
}
err.println("------------------------------------------------------------------");
} catch (Exception e) {
err.println("The stdout/stderr output in file " + f + " does not exist and the server did not start.");
}
}
/**
* Expect this key on the next line read from the reader.
*/
public static boolean expect(BufferedReader in, String key) throws IOException {
String s = in.readLine();
if (s != null && s.equals(key)) {
return true;
}
return false;
}
}

View File

@ -41,12 +41,12 @@ import com.sun.tools.sjavac.Log;
* primitives to avoid race conditions when several javac clients are started at the same. Note that file
* system locking is not always supported on a all operating systems and/or file systems.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
class PortFile {
public class PortFile {
// Port file format:
// byte ordering: high byte first = big endian
@ -72,8 +72,7 @@ class PortFile {
* Create a new portfile.
* @param filename is the path to the file.
*/
public PortFile(String fn) throws FileNotFoundException
{
public PortFile(String fn) throws FileNotFoundException {
filename = fn;
file = new File(filename);
stopFile = new File(filename+".stop");
@ -88,7 +87,7 @@ class PortFile {
/**
* Lock the port file.
*/
void lock() throws IOException {
public void lock() throws IOException {
lock = channel.lock();
}
@ -115,7 +114,7 @@ class PortFile {
containsPortInfo = false;
}
}
} catch (Exception e) {
} catch (IOException e) {
containsPortInfo = false;
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
/**
* Monitors the presence of a port file and shuts down the given SjavacServer
* whenever the port file is deleted or invalidated.
*
* TODO: JDK-8046882
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class PortFileMonitor {
// Check if the portfile is gone, every 5 seconds.
private final static int CHECK_PORTFILE_INTERVAL = 5000;
final private Timer timer = new Timer();
final private PortFile portFile;
final private SjavacServer server;
public PortFileMonitor(PortFile portFile,
SjavacServer server) {
this.portFile = portFile;
this.server = server;
}
public void start() {
TimerTask shutdownCheck = new TimerTask() {
public void run() {
try {
if (!portFile.exists()) {
// Time to quit because the portfile was deleted by another
// process, probably by the makefile that is done building.
server.shutdown("Quitting because portfile was deleted!");
} else if (portFile.markedForStop()) {
// Time to quit because another process touched the file
// server.port.stop to signal that the server should stop.
// This is necessary on some operating systems that lock
// the port file hard!
server.shutdown("Quitting because a portfile.stop file was found!");
} else if (!portFile.stillMyValues()) {
// Time to quit because another build has started.
server.shutdown("Quitting because portfile is now owned by another javac server!");
}
} catch (IOException e) {
e.printStackTrace(server.theLog);
server.flushLog();
}
}
};
timer.schedule(shutdownCheck, 0, CHECK_PORTFILE_INTERVAL);
}
public void shutdown() {
timer.cancel();
}
}

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.Socket;
import java.net.URI;
import java.util.List;
import java.util.Set;
import com.sun.tools.sjavac.Log;
/**
* A RequestHandler handles requests performed over a socket. Specifically it
* - Reads the command string specifying which method is to be invoked
* - Reads the appropriate arguments
* - Delegates the actual invocation to the given sjavac implementation
* - Writes the result back to the socket output stream
*
* None of the work performed by this class is really bound by the CPU. It
* should be completely fine to have a large number of RequestHandlers active.
* To limit the number of concurrent compilations, use PooledSjavac.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class RequestHandler implements Runnable {
private final Socket socket;
private final Sjavac sjavac;
public RequestHandler(Socket socket, Sjavac sjavac) {
this.socket = socket;
this.sjavac = sjavac;
}
@Override
public void run() {
try (ObjectOutputStream oout = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream oin = new ObjectInputStream(socket.getInputStream())) {
String id = (String) oin.readObject();
String cmd = (String) oin.readObject();
Log.info("Handling request, id: " + id + " cmd: " + cmd);
switch (cmd) {
case SjavacServer.CMD_SYS_INFO: handleSysInfoRequest(oin, oout); break;
case SjavacServer.CMD_COMPILE: handleCompileRequest(oin, oout); break;
default: Log.error("Unknown command: " + cmd);
}
} catch (Exception ex) {
// Not much to be done at this point. The client side request
// code will most likely throw an IOException and the
// compilation will fail.
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
Log.error(sw.toString());
}
}
private void handleSysInfoRequest(ObjectInputStream oin,
ObjectOutputStream oout) throws IOException {
oout.writeObject(sjavac.getSysInfo());
oout.flush();
}
@SuppressWarnings("unchecked")
private void handleCompileRequest(ObjectInputStream oin,
ObjectOutputStream oout) throws IOException {
try {
// Read request arguments
String protocolId = (String) oin.readObject();
String invocationId = (String) oin.readObject();
String[] args = (String[]) oin.readObject();
List<File> explicitSources = (List<File>) oin.readObject();
Set<URI> sourcesToCompile = (Set<URI>) oin.readObject();
Set<URI> visibleSources = (Set<URI>) oin.readObject();
// Perform compilation
CompilationResult cr = sjavac.compile(protocolId,
invocationId,
args,
explicitSources,
sourcesToCompile,
visibleSources);
// Write request response
oout.writeObject(cr);
oout.flush();
} catch (ClassNotFoundException cnfe) {
throw new IOException(cnfe);
}
}
}

View File

@ -22,7 +22,6 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.io.File;
@ -30,7 +29,16 @@ import java.net.URI;
import java.util.List;
import java.util.Set;
public interface JavacService {
/**
* Interface of the SjavacImpl, the sjavac client and all wrappers such as
* PooledSjavac etc.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public interface Sjavac {
SysInfo getSysInfo();
@ -40,5 +48,7 @@ public interface JavacService {
List<File> explicitSources,
Set<URI> sourcesToCompile,
Set<URI> visibleSources);
void shutdown();
String serverSettings();
}

View File

@ -0,0 +1,349 @@
/*
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import com.sun.tools.sjavac.ProblemException;
import com.sun.tools.sjavac.Util;
import com.sun.tools.sjavac.comp.SjavacImpl;
import com.sun.tools.sjavac.comp.PooledSjavac;
/**
* The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SjavacServer implements Terminable {
// Used in protocol to indicate which method to invoke
public final static String CMD_COMPILE = "compile";
public final static String CMD_SYS_INFO = "sys-info";
final private String portfilename;
final private String logfile;
final private String stdouterrfile;
final private int poolsize;
final private int keepalive;
final private PrintStream err;
// The secret cookie shared between server and client through the port file.
// Used to prevent clients from believing that they are communicating with
// an old server when a new server has started and reused the same port as
// an old server.
private final long myCookie;
// Accumulated build time, not counting idle time, used for logging purposes
private long totalBuildTime;
// The javac server specific log file.
PrintWriter theLog;
// The sjavac implementation to delegate requests to
Sjavac sjavac;
private ServerSocket serverSocket;
private PortFile portFile;
private PortFileMonitor portFileMonitor;
// Set to false break accept loop
final AtomicBoolean keepAcceptingRequests = new AtomicBoolean();
// For the client, all port files fetched, one per started javac server.
// Though usually only one javac server is started by a client.
private static Map<String, PortFile> allPortFiles;
private static Map<String, Long> maxServerMemory;
public SjavacServer(String settings, PrintStream err) throws FileNotFoundException {
// Extract options. TODO: Change to proper constructor args
portfilename = Util.extractStringOption("portfile", settings);
logfile = Util.extractStringOption("logfile", settings);
stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
keepalive = Util.extractIntOption("keepalive", settings, 120);
poolsize = Util.extractIntOption("poolsize", settings,
Runtime.getRuntime().availableProcessors());
this.err = err;
myCookie = new Random().nextLong();
theLog = new PrintWriter(logfile);
}
/**
* Acquire the port file. Synchronized since several threads inside an smart javac wrapper client acquires the same port file at the same time.
*/
public static synchronized PortFile getPortFile(String filename) throws FileNotFoundException {
if (allPortFiles == null) {
allPortFiles = new HashMap<>();
}
PortFile pf = allPortFiles.get(filename);
// Port file known. Does it still exist?
if (pf != null) {
try {
if (!pf.exists())
pf = null;
} catch (IOException ioex) {
ioex.printStackTrace();
}
}
if (pf == null) {
pf = new PortFile(filename);
allPortFiles.put(filename, pf);
}
return pf;
}
/**
* Get the cookie used for this server.
*/
long getCookie() {
return myCookie;
}
/**
* Get the port used for this server.
*/
int getPort() {
return serverSocket.getLocalPort();
}
/**
* Sum up the total build time for this javac server.
*/
public void addBuildTime(long inc) {
totalBuildTime += inc;
}
/**
* Log this message.
*/
public void log(String msg) {
if (theLog != null) {
theLog.println(msg);
} else {
System.err.println(msg);
}
}
/**
* Make sure the log is flushed.
*/
public void flushLog() {
if (theLog != null) {
theLog.flush();
}
}
/**
* Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3"
* is sent as the settings parameter. Returns 0 on success, -1 on failure.
*/
public int startServer() throws IOException {
long serverStart = System.currentTimeMillis();
// The port file is locked and the server port and cookie is written into it.
portFile = getPortFile(portfilename);
synchronized (portFile) {
portFile.lock();
portFile.getValues();
if (portFile.containsPortInfo()) {
err.println("Javac server not started because portfile exists!");
portFile.unlock();
return -1;
}
// .-----------. .--------. .------.
// socket -->| IdleReset |-->| Pooled |-->| Impl |--> javac
// '-----------' '--------' '------'
sjavac = new SjavacImpl();
sjavac = new PooledSjavac(sjavac, poolsize);
sjavac = new IdleResetSjavac(sjavac,
this,
keepalive * 1000);
serverSocket = new ServerSocket();
InetAddress localhost = InetAddress.getByName(null);
serverSocket.bind(new InetSocketAddress(localhost, 0));
// At this point the server accepts connections, so it is now safe
// to publish the port / cookie information
portFile.setValues(getPort(), getCookie());
portFile.unlock();
}
portFileMonitor = new PortFileMonitor(portFile, this);
portFileMonitor.start();
log("Sjavac server started. Accepting connections...");
log(" port: " + getPort());
log(" time: " + new java.util.Date());
log(" poolsize: " + poolsize);
flushLog();
keepAcceptingRequests.set(true);
do {
try {
Socket socket = serverSocket.accept();
new Thread(new RequestHandler(socket, sjavac)).start();
} catch (SocketException se) {
// Caused by serverSocket.close() and indicates shutdown
}
} while (keepAcceptingRequests.get());
log("Shutting down.");
// No more connections accepted. If any client managed to connect after
// the accept() was interrupted but before the server socket is closed
// here, any attempt to read or write to the socket will result in an
// IOException on the client side.
long realTime = System.currentTimeMillis() - serverStart;
log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
flushLog();
// Shut down
sjavac.shutdown();
return 0;
}
/**
* Fork a background process. Returns the command line used that can be printed if something failed.
*/
public static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive,
final PrintStream err, String stdouterrfile, boolean background)
throws IOException, ProblemException {
if (stdouterrfile != null && stdouterrfile.trim().equals("")) {
stdouterrfile = null;
}
final String startserver = "--startserver:portfile=" + portfile + ",logfile=" + logfile + ",stdouterrfile=" + stdouterrfile + ",poolsize=" + poolsize + ",keepalive="+ keepalive;
if (background) {
sjavac += "%20" + startserver;
sjavac = sjavac.replaceAll("%20", " ");
sjavac = sjavac.replaceAll("%2C", ",");
// If the java/sh/cmd launcher fails the failure will be captured by stdouterr because of the redirection here.
String[] cmd = {"/bin/sh", "-c", sjavac + " >> " + stdouterrfile + " 2>&1"};
if (!(new File("/bin/sh")).canExecute()) {
ArrayList<String> wincmd = new ArrayList<>();
wincmd.add("cmd");
wincmd.add("/c");
wincmd.add("start");
wincmd.add("cmd");
wincmd.add("/c");
wincmd.add(sjavac + " >> " + stdouterrfile + " 2>&1");
cmd = wincmd.toArray(new String[wincmd.size()]);
}
Process pp = null;
try {
pp = Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace(err);
e.printStackTrace(new PrintWriter(stdouterrfile));
}
StringBuilder rs = new StringBuilder();
for (String s : cmd) {
rs.append(s + " ");
}
return rs.toString();
}
// Do not spawn a background server, instead run it within the same JVM.
Thread t = new Thread() {
@Override
public void run() {
try {
SjavacServer server = new SjavacServer(startserver, err);
server.startServer();
} catch (Throwable t) {
t.printStackTrace(err);
}
}
};
t.setDaemon(true);
t.start();
return "";
}
@Override
public void shutdown(String quitMsg) {
if (!keepAcceptingRequests.compareAndSet(false, true)) {
// Already stopped, no need to shut down again
return;
}
log("Quitting: " + quitMsg);
flushLog();
portFileMonitor.shutdown(); // No longer any need to monitor port file
// Unpublish port before shutting down socket to minimize the number of
// failed connection attempts
try {
portFile.delete();
} catch (IOException e) {
e.printStackTrace(theLog);
}
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace(theLog);
}
}
public static void cleanup(String... args) {
String settings = Util.findServerSettings(args);
if (settings == null) return;
String portfile = Util.extractStringOption("portfile", settings);
String background = Util.extractStringOption("background", settings);
if (background != null && background.equals("false")) {
// If the server runs within this jvm, then delete the portfile,
// since this jvm is about to exit soon.
File f = new File(portfile);
f.delete();
}
}
}

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
@ -34,7 +34,18 @@
*/
package com.sun.tools.sjavac.server;
public class SysInfo {
import java.io.Serializable;
/**
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class SysInfo implements Serializable {
static final long serialVersionUID = -3096346807579L;
public int numCores;
public long maxMemory;

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.server;
/**
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public interface Terminable {
void shutdown(String quitMsg);
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8044131
* @summary Tests the hooks used for detecting idleness of the sjavac server.
* @build Wrapper
* @run main Wrapper IdleShutdown
*/
import java.io.File;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import com.sun.tools.sjavac.server.CompilationResult;
import com.sun.tools.sjavac.server.IdleResetSjavac;
import com.sun.tools.sjavac.server.Sjavac;
import com.sun.tools.sjavac.server.SysInfo;
import com.sun.tools.sjavac.server.Terminable;
public class IdleShutdown {
final static long TEST_START = System.currentTimeMillis();
final static long TIMEOUT_MS = 3000;
public static void main(String[] args) throws InterruptedException {
final AtomicLong timeoutTimestamp = new AtomicLong(-1);
log("Starting IdleCallbackJavacService with timeout: " + TIMEOUT_MS);
Sjavac service = new IdleResetSjavac(
new NoopJavacService(),
new Terminable() {
public void shutdown(String msg) {
// Record the idle timeout time
log("Timeout detected");
timeoutTimestamp.set(System.currentTimeMillis());
}
},
TIMEOUT_MS);
// Make sure it didn't timeout immediately
if (timeoutTimestamp.get() != -1)
throw new AssertionError("Premature timeout detected.");
// Call various methods and wait less than TIMEOUT_MS in between
Thread.sleep(TIMEOUT_MS - 1000);
log("Getting sys info");
service.getSysInfo();
Thread.sleep(TIMEOUT_MS - 1000);
log("Getting sys info");
service.getSysInfo();
if (timeoutTimestamp.get() != -1)
throw new AssertionError("Premature timeout detected.");
Thread.sleep(TIMEOUT_MS - 1000);
log("Compiling");
service.compile("",
"",
new String[0],
Collections.<File>emptyList(),
Collections.<URI>emptySet(),
Collections.<URI>emptySet());
Thread.sleep(TIMEOUT_MS - 1000);
log("Compiling");
service.compile("",
"",
new String[0],
Collections.<File>emptyList(),
Collections.<URI>emptySet(),
Collections.<URI>emptySet());
if (timeoutTimestamp.get() != -1)
throw new AssertionError("Premature timeout detected.");
long expectedTimeout = System.currentTimeMillis() + TIMEOUT_MS;
// Wait for actual timeout
log("Awaiting idle timeout");
Thread.sleep(TIMEOUT_MS + 1000);
// Check result
if (timeoutTimestamp.get() == -1)
throw new AssertionError("Timeout never occurred");
long error = Math.abs(expectedTimeout - timeoutTimestamp.get());
log("Timeout error: " + error + " ms");
if (error > TIMEOUT_MS * .1)
throw new AssertionError("Error too big");
log("Shutting down");
service.shutdown();
}
private static void log(String msg) {
long logTime = System.currentTimeMillis() - TEST_START;
System.out.printf("After %5d ms: %s%n", logTime, msg);
}
private static class NoopJavacService implements Sjavac {
@Override
public SysInfo getSysInfo() {
// Attempt to trigger idle timeout during a call by sleeping
try {
Thread.sleep(TIMEOUT_MS + 1000);
} catch (InterruptedException e) {
}
return null;
}
@Override
public void shutdown() {
}
@Override
public CompilationResult compile(String protocolId,
String invocationId,
String[] args,
List<File> explicitSources,
Set<URI> sourcesToCompile,
Set<URI> visibleSources) {
return null;
}
@Override
public String serverSettings() {
return "";
}
}
}

View File

@ -0,0 +1,160 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8044131
* @summary Makes sure sjavac poolsize option is honored.
* @build Wrapper
* @run main Wrapper PooledExecution
*/
import java.io.File;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import com.sun.tools.sjavac.comp.PooledSjavac;
import com.sun.tools.sjavac.server.CompilationResult;
import com.sun.tools.sjavac.server.Sjavac;
import com.sun.tools.sjavac.server.SysInfo;
public class PooledExecution {
public static void main(String[] args) throws InterruptedException {
new PooledExecutionTest().runTest();
}
static class PooledExecutionTest {
final int POOL_SIZE = 15;
final int NUM_REQUESTS = 100;
// Number of tasks that has not yet started
CountDownLatch leftToStart = new CountDownLatch(NUM_REQUESTS);
// Highest number of concurrently active request seen
int highWaterMark = 0;
public void runTest() throws InterruptedException {
ConcurrencyLoggingService loggingService = new ConcurrencyLoggingService();
final Sjavac service = new PooledSjavac(loggingService, POOL_SIZE);
// Keep track of the number of finished tasks so we can make sure all
// tasks finishes gracefully upon shutdown.
Thread[] tasks = new Thread[NUM_REQUESTS];
final AtomicInteger tasksFinished = new AtomicInteger(0);
for (int i = 0; i < NUM_REQUESTS; i++) {
tasks[i] = new Thread() {
public void run() {
service.compile("",
"",
new String[0],
Collections.<File>emptyList(),
Collections.<URI>emptySet(),
Collections.<URI>emptySet());
tasksFinished.incrementAndGet();
}
};
tasks[i].start();
}
// Wait for all tasks to start (but not necessarily run to completion)
leftToStart.await();
// Shutdown before all tasks are completed
System.out.println("Shutting down!");
service.shutdown();
// Wait for all tasks to complete
for (Thread t : tasks)
t.join();
if (tasksFinished.get() != NUM_REQUESTS) {
throw new AssertionError(tasksFinished.get() + " out of " +
NUM_REQUESTS + " finished. Broken shutdown?");
}
if (highWaterMark > POOL_SIZE) {
throw new AssertionError("Pool size overused: " + highWaterMark +
" used out of " + POOL_SIZE + " allowed.");
}
// Assuming more than POOL_SIZE requests can be processed within 1 sek:
if (highWaterMark < POOL_SIZE) {
throw new AssertionError("Pool size underused: " + highWaterMark +
" used out of " + POOL_SIZE + " allowed.");
}
}
private class ConcurrencyLoggingService implements Sjavac {
// Keeps track of currently active requests
AtomicInteger activeRequests = new AtomicInteger(0);
@Override
public CompilationResult compile(String protocolId,
String invocationId,
String[] args,
List<File> explicitSources,
Set<URI> sourcesToCompile,
Set<URI> visibleSources) {
leftToStart.countDown();
int numActiveRequests = activeRequests.incrementAndGet();
System.out.printf("Left to start: %2d / Currently active: %2d%n",
leftToStart.getCount(),
numActiveRequests);
highWaterMark = Math.max(highWaterMark, numActiveRequests);
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
throw new RuntimeException("Interrupted", ie);
}
activeRequests.decrementAndGet();
System.out.println("Task completed");
return null;
}
@Override
public SysInfo getSysInfo() {
return null;
}
@Override
public void shutdown() {
}
@Override
public String serverSettings() {
return "";
}
}
}
}