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:
parent
14e6aa6b9e
commit
ce4c456820
@ -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<>();
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
||||
|
@ -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<>();
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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> {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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 "";
|
@ -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) &&
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
156
langtools/test/tools/sjavac/IdleShutdown.java
Normal file
156
langtools/test/tools/sjavac/IdleShutdown.java
Normal 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 "";
|
||||
}
|
||||
}
|
||||
}
|
160
langtools/test/tools/sjavac/PooledExecution.java
Normal file
160
langtools/test/tools/sjavac/PooledExecution.java
Normal 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 "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user