debugger to perform compilations in external process using target jdk

This commit is contained in:
Eugene Zhuravlev
2015-06-18 23:22:01 +02:00
parent beea1f0c92
commit 520f7556b0
10 changed files with 319 additions and 204 deletions
@@ -111,7 +111,7 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements
private Map<Module, ProcessorConfigProfile> myProcessorsProfilesMap = null;
@Nullable
private String myBytecodeTargetLevel = null; // null means compiler default
private String myBytecodeTargetLevel = null; // null means same as effective language level
private final Map<String, String> myModuleBytecodeTarget = new HashMap<String, String>();
public CompilerConfigurationImpl(Project project) {
@@ -109,7 +109,8 @@ import org.jetbrains.jps.incremental.Utils;
import org.jetbrains.jps.model.serialization.JpsGlobalLoader;
import javax.swing.*;
import javax.tools.*;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.awt.*;
import java.io.File;
import java.io.FileFilter;
@@ -918,6 +919,58 @@ public class BuildManager implements ApplicationComponent{
return "com.intellij.compiler.server.BuildManager";
}
@NotNull
public static Pair<Sdk, JavaSdkVersion> getBuildProcessRuntimeSdk(Project project) {
Sdk projectJdk = null;
int sdkMinorVersion = 0;
JavaSdkVersion sdkVersion = null;
final Set<Sdk> candidates = new HashSet<Sdk>();
final Sdk defaultSdk = ProjectRootManager.getInstance(project).getProjectSdk();
if (defaultSdk != null && defaultSdk.getSdkType() instanceof JavaSdkType) {
candidates.add(defaultSdk);
}
for (Module module : ModuleManager.getInstance(project).getModules()) {
final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
if (sdk != null && sdk.getSdkType() instanceof JavaSdkType) {
candidates.add(sdk);
}
}
// now select the latest version from the sdks that are used in the project, but not older than the internal sdk version
final JavaSdk javaSdkType = JavaSdk.getInstance();
for (Sdk candidate : candidates) {
final String vs = candidate.getVersionString();
if (vs != null) {
final JavaSdkVersion candidateVersion = javaSdkType.getVersion(vs);
if (candidateVersion != null) {
final int candidateMinorVersion = getMinorVersion(vs);
if (projectJdk == null) {
sdkVersion = candidateVersion;
sdkMinorVersion = candidateMinorVersion;
projectJdk = candidate;
}
else {
final int result = candidateVersion.compareTo(sdkVersion);
if (result > 0 || (result == 0 && candidateMinorVersion > sdkMinorVersion)) {
sdkVersion = candidateVersion;
sdkMinorVersion = candidateMinorVersion;
projectJdk = candidate;
}
}
}
}
}
final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
if (projectJdk == null || sdkVersion == null || !sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_6)) {
projectJdk = internalJdk;
sdkVersion = javaSdkType.getVersion(internalJdk);
}
return Pair.create(projectJdk, sdkVersion);
}
private Future<Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler>> launchPreloadedBuildProcess(final Project project, SequentialTaskExecutor projectTaskQueue) throws Exception {
ensureListening();
@@ -955,52 +1008,11 @@ public class BuildManager implements ApplicationComponent{
if (StringUtil.isEmptyOrSpaces(forcedCompiledJdkHome)) {
// choosing sdk with which the build process should be run
Sdk projectJdk = null;
int sdkMinorVersion = 0;
final Set<Sdk> candidates = new HashSet<Sdk>();
final Sdk defaultSdk = ProjectRootManager.getInstance(project).getProjectSdk();
if (defaultSdk != null && defaultSdk.getSdkType() instanceof JavaSdkType) {
candidates.add(defaultSdk);
}
for (Module module : ModuleManager.getInstance(project).getModules()) {
final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
if (sdk != null && sdk.getSdkType() instanceof JavaSdkType) {
candidates.add(sdk);
}
}
// now select the latest version from the sdks that are used in the project, but not older than the internal sdk version
final JavaSdk javaSdkType = JavaSdk.getInstance();
for (Sdk candidate : candidates) {
final String vs = candidate.getVersionString();
if (vs != null) {
final JavaSdkVersion candidateVersion = javaSdkType.getVersion(vs);
if (candidateVersion != null) {
final int candidateMinorVersion = getMinorVersion(vs);
if (projectJdk == null) {
sdkVersion = candidateVersion;
sdkMinorVersion = candidateMinorVersion;
projectJdk = candidate;
}
else {
final int result = candidateVersion.compareTo(sdkVersion);
if (result > 0 || (result == 0 && candidateMinorVersion > sdkMinorVersion)) {
sdkVersion = candidateVersion;
sdkMinorVersion = candidateMinorVersion;
projectJdk = candidate;
}
}
}
}
}
final Pair<Sdk, JavaSdkVersion> pair = getBuildProcessRuntimeSdk(project);
final Sdk projectJdk = pair.first;
sdkVersion = pair.second;
final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
if (projectJdk == null || sdkVersion == null || !sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_6)) {
projectJdk = internalJdk;
}
// validate tools.jar presence
final JavaSdkType projectJdkType = (JavaSdkType)projectJdk.getSdkType();
if (FileUtil.pathsEqual(projectJdk.getHomePath(), internalJdk.getHomePath())) {
+2
View File
@@ -15,12 +15,14 @@
<orderEntry type="module" module-name="xdebugger-impl" />
<orderEntry type="module" module-name="lang-api" />
<orderEntry type="module" module-name="compiler-openapi" />
<orderEntry type="module" module-name="compiler-impl" />
<orderEntry type="module" module-name="java-runtime" />
<orderEntry type="module" module-name="jsp-openapi" />
<orderEntry type="module" module-name="java-impl" />
<orderEntry type="module" module-name="platform-impl" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="diff-api" />
<orderEntry type="module" module-name="jps-builders" />
</component>
<component name="copyright">
<Base>
@@ -37,15 +37,14 @@ import com.sun.jdi.ClassLoaderReference;
import com.sun.jdi.ClassType;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.BinaryContent;
import org.jetbrains.jps.javac.OutputFileObject;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.ClassWriter;
import org.jetbrains.org.objectweb.asm.Opcodes;
import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
/**
@@ -81,8 +80,7 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
ClassLoaderReference classLoader = ClassLoadingUtils.getClassLoader(evaluationContext, process);
String version = ((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).version();
JavaSdkVersion sdkVersion = JdkVersionUtil.getVersion(version);
Collection<OutputFileObject> classes = compile(sdkVersion != null ? sdkVersion.getDescription() : null);
Collection<OutputFileObject> classes = compile(JdkVersionUtil.getVersion(version));
defineClasses(classes, evaluationContext, process, classLoader);
@@ -120,8 +118,11 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
ClassLoaderReference classLoader) throws EvaluateException {
for (OutputFileObject cls : classes) {
if (cls.getName().contains(GEN_CLASS_NAME)) {
byte[] bytes = changeSuperToMagicAccessor(cls.toByteArray());
ClassLoadingUtils.defineClass(cls.myOrigName, bytes, context, process, classLoader);
final BinaryContent content = cls.getContent();
if (content != null) {
byte[] bytes = changeSuperToMagicAccessor(content.toByteArray());
ClassLoadingUtils.defineClass(cls.getClassName(), bytes, context, process, classLoader);
}
}
}
return (ClassType)process.findClass(context, getGenClassQName(), classLoader);
@@ -164,58 +165,6 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
///////////////// Compiler stuff
@NotNull
protected abstract Collection<OutputFileObject> compile(String target) throws EvaluateException;
private static URI getUri(String name, JavaFileObject.Kind kind) {
return URI.create("memo:///" + name.replace('.', '/') + kind.extension);
}
protected static class SourceFileObject extends SimpleJavaFileObject {
private final String myContent;
SourceFileObject(String name, Kind kind, String content) {
super(getUri(name, kind), kind);
myContent = content;
}
@Override
public CharSequence getCharContent(boolean ignore) {
return myContent;
}
}
protected static class OutputFileObject extends SimpleJavaFileObject {
private final ByteArrayOutputStream myStream = new ByteArrayOutputStream();
private final String myOrigName;
OutputFileObject(String name, Kind kind) {
super(getUri(name, kind), kind);
myOrigName = name;
}
byte[] toByteArray() {
return myStream.toByteArray();
}
@Override
public ByteArrayOutputStream openOutputStream() {
return myStream;
}
}
protected static class MemoryFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
protected final Collection<OutputFileObject> classes = new ArrayList<OutputFileObject>();
MemoryFileManager(JavaCompiler compiler) {
super(compiler.getStandardFileManager(null, null, null));
}
@Override
public OutputFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) {
OutputFileObject mc = new OutputFileObject(name, kind);
classes.add(mc);
return mc;
}
}
protected abstract Collection<OutputFileObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException;
}
@@ -15,70 +15,144 @@
*/
package com.intellij.debugger.ui.impl.watch;
import com.intellij.compiler.CompilerConfiguration;
import com.intellij.compiler.server.BuildManager;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.DebugProcessAdapter;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.EffectiveLanguageLevelUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkTypeId;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler;
import com.intellij.util.PathsList;
import com.intellij.util.net.NetUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.api.CanceledStatus;
import org.jetbrains.jps.builders.impl.java.JavacCompilerTool;
import org.jetbrains.jps.javac.DiagnosticOutputConsumer;
import org.jetbrains.jps.javac.ExternalJavacManager;
import org.jetbrains.jps.javac.OutputFileConsumer;
import org.jetbrains.jps.javac.OutputFileObject;
import javax.tools.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.util.*;
// todo: consider batching compilations in order not to start a separate process for every class that needs to be compiled
public class CompilingEvaluatorImpl extends CompilingEvaluator {
public CompilingEvaluatorImpl(@NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) {
private final EvaluationContextImpl myEvaluationContext;
public CompilingEvaluatorImpl(EvaluationContextImpl evaluationContext, @NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) {
super(context, data);
myEvaluationContext = evaluationContext;
}
@Override
@NotNull
protected Collection<OutputFileObject> compile(String target) throws EvaluateException {
if (!SystemInfo.isJavaVersionAtLeast(target)) {
throw new EvaluateException("Unable to compile for target level " + target + ". Need to run IDEA on java version at least " + target + ", currently running on " + SystemInfo.JAVA_RUNTIME_VERSION);
}
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
MemoryFileManager manager = new MemoryFileManager(compiler);
DiagnosticCollector<JavaFileObject> diagnostic = new DiagnosticCollector<JavaFileObject>();
Module module = ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
protected Collection<OutputFileObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException {
final Pair<Sdk, JavaSdkVersion> runtime = BuildManager.getBuildProcessRuntimeSdk(myEvaluationContext.getProject());
final Module module = ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
@Override
public Module compute() {
return ModuleUtilCore.findModuleForPsiElement(myPsiContext);
}
});
List<String> options = new ArrayList<String>();
if (module != null) {
options.add("-cp");
PathsList cp = ModuleRootManager.getInstance(module).orderEntries().compileOnly().recursively().exportedOnly().withoutSdk().getPathsList();
options.add(cp.getPathsString());
String javaHome = null;
final Sdk sdk = runtime.getFirst();
final SdkTypeId type = sdk.getSdkType();
if (type instanceof JavaSdkType) {
javaHome = sdk.getHomePath();
}
if (!StringUtil.isEmpty(target)) {
if (javaHome == null) {
throw new EvaluateException("Was not able to determine JDK for current evaluation context");
}
final List<String> options = new ArrayList<String>();
options.add("-proc:none"); // for our purposes annotation processing is not needed
options.add("-encoding");
options.add("UTF-8");
final List<File> platformClasspath = new ArrayList<File>();
final List<File> classpath = new ArrayList<File>();
if (module != null) {
final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
rootManager.orderEntries().compileOnly().recursively().exportedOnly().withoutSdk().getPathsList().addAllFiles(classpath);
rootManager.orderEntries().compileOnly().sdkOnly().getPathsList().addAllFiles(platformClasspath);
final String sourceOption = getSourceOption(ApplicationManager.getApplication().runReadAction(new Computable<LanguageLevel>() {
public LanguageLevel compute() {
return EffectiveLanguageLevelUtil.getEffectiveLanguageLevel(module);
}
}));
options.add("-source");
options.add(target);
options.add(sourceOption);
String target = CompilerConfiguration.getInstance(module.getProject()).getBytecodeTargetLevel(module);
if (target == null) {
target = sourceOption;
}
options.add("-target");
options.add(target);
}
else {
if (debuggeeVersion != null) {
// if both module context and debuggee version are unknown, let source and target be the compiler's defaults
String sourceOption;
final JavaSdkVersion buildRuntimeVersion = runtime.getSecond();
if (buildRuntimeVersion == null) {
sourceOption = getSourceOption(debuggeeVersion.getMaxLanguageLevel());
}
else {
final JavaSdkVersion minVersion = buildRuntimeVersion.ordinal() > debuggeeVersion.ordinal() ? debuggeeVersion : buildRuntimeVersion;
sourceOption = getSourceOption(minVersion.getMaxLanguageLevel());
}
options.add("-source");
options.add(sourceOption);
options.add("-target");
options.add(sourceOption);
}
}
File sourceFile = null;
final OutputCollector outputSink = new OutputCollector();
try {
if (!compiler.getTask(null,
manager,
diagnostic,
options,
null,
Collections.singletonList(new SourceFileObject(getMainClassName(), JavaFileObject.Kind.SOURCE, getClassCode()))
).call()) {
StringBuilder res = new StringBuilder("Compilation failed:\n");
final ExternalJavacManager javacManager = getJavacManager();
if (javacManager == null) {
throw new EvaluateException("Cannot compile java code");
}
sourceFile = generateTempSourceFile(javacManager.getWorkingDir());
final File srcDir = sourceFile.getParentFile();
final Map<File, Set<File>> output = Collections.singletonMap(srcDir, Collections.singleton(srcDir));
DiagnosticCollector diagnostic = new DiagnosticCollector();
final List<String> vmOptions = Collections.emptyList();
final List<File> sourcePath = Collections.emptyList();
final Set<File> sources = Collections.singleton(sourceFile);
boolean compiledOK = javacManager.forkJavac(
javaHome, -1, vmOptions, options, platformClasspath, classpath, sourcePath, sources, output, diagnostic, outputSink, new JavacCompilerTool(), CanceledStatus.NULL
);
if (!compiledOK) {
final StringBuilder res = new StringBuilder("Compilation failed:\n");
for (Diagnostic<? extends JavaFileObject> d : diagnostic.getDiagnostics()) {
res.append(d);
if (d.getKind() == Diagnostic.Kind.ERROR) {
res.append(d.getMessage(Locale.US));
}
}
throw new EvaluateException(res.toString());
}
@@ -86,24 +160,111 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
catch (Exception e) {
throw new EvaluateException(e.getMessage());
}
return manager.classes;
finally {
if (sourceFile != null) {
FileUtil.delete(sourceFile);
}
}
return outputSink.getCompiledClasses();
}
protected String getClassCode() {
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override
public String compute() {
return myData.getGeneratedInnerClass().getContainingFile().getText();
}
});
@NotNull
private static String getSourceOption(@NotNull LanguageLevel languageLevel) {
return "1." + Integer.valueOf(3 + languageLevel.ordinal());
}
protected String getMainClassName() {
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
private File generateTempSourceFile(File workingDir) throws IOException {
final Pair<String, String> fileData = ApplicationManager.getApplication().runReadAction(new Computable<Pair<String, String>>() {
@Override
public String compute() {
return FileUtil.getNameWithoutExtension(myData.getGeneratedInnerClass().getContainingFile().getName());
public Pair<String, String> compute() {
final PsiFile file = myData.getGeneratedInnerClass().getContainingFile();
return Pair.create(file.getName(), file.getText());
}
});
if (fileData.first == null) {
throw new IOException("Class file name not specified");
}
if (fileData.second == null) {
throw new IOException("Class source code not specified");
}
final File file = new File(workingDir, "src/"+fileData.first);
FileUtil.writeToFile(file, fileData.second);
return file;
}
private static final Key<ExternalJavacManager> JAVAC_MANAGER_KEY = Key.create("_external_java_compiler_manager_");
@Nullable
private ExternalJavacManager getJavacManager() throws IOException {
// need dedicated thread access to be able to cache the manager in the user data
DebuggerManagerThreadImpl.assertIsManagerThread();
final DebugProcessImpl debugProcess = myEvaluationContext.getDebugProcess();
ExternalJavacManager manager = JAVAC_MANAGER_KEY.get(debugProcess);
if (manager == null && debugProcess.isAttached()) {
final File compilerWorkingDir = getCompilerWorkingDir();
if (compilerWorkingDir == null) {
return null; // should not happen for real projects
}
final int listenPort = NetUtils.findAvailableSocketPort();
manager = new ExternalJavacManager(compilerWorkingDir);
manager.start(listenPort);
final ExternalJavacManager _manager = manager;
debugProcess.addDebugProcessListener(new DebugProcessAdapter() {
public void processDetached(DebugProcess process, boolean closedByUser) {
if (process == debugProcess) {
_manager.stop();
}
}
});
JAVAC_MANAGER_KEY.set(debugProcess, manager);
}
return manager;
}
@Nullable
private File getCompilerWorkingDir() {
final File projectBuildDir = BuildManager.getInstance().getProjectSystemDirectory(myEvaluationContext.getProject());
if (projectBuildDir == null) {
return null;
}
final File root = new File(projectBuildDir, "debugger");
root.mkdirs();
return root;
}
private static class DiagnosticCollector implements DiagnosticOutputConsumer {
private final List<Diagnostic<? extends JavaFileObject>> myDiagnostics = new ArrayList<Diagnostic<? extends JavaFileObject>>();
public void outputLineAvailable(String line) {
// todo: do we need these messages?
}
public void registerImports(String className, Collection<String> imports, Collection<String> staticImports) {
// ignore
}
public void javaFileLoaded(File file) {
// ignore
}
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
myDiagnostics.add(diagnostic);
}
public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() {
return myDiagnostics;
}
}
private static class OutputCollector implements OutputFileConsumer {
private List<OutputFileObject> myClasses = new ArrayList<OutputFileObject>();
public void save(@NotNull OutputFileObject fileObject) {
myClasses.add(fileObject);
}
public List<OutputFileObject> getCompiledClasses() {
return myClasses;
}
}
}
@@ -99,7 +99,7 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject(myProject,
psiFile, fragment, CompilingEvaluator.getGeneratedClassName());
if (data != null) {
return new CompilingEvaluatorImpl(psiContext, data);
return new CompilingEvaluatorImpl(evaluationContext, psiContext, data);
}
}
catch (PrepareFailedException e) {
@@ -67,6 +67,7 @@ import java.io.*;
import java.net.ServerSocket;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -531,7 +532,15 @@ public class JavaBuilder extends ModuleLevelBuilder {
return server;
}
final int listenPort = findFreePort();
server = new ExternalJavacManager(Utils.getSystemRoot(), SharedThreadPool.getInstance());
server = new ExternalJavacManager(Utils.getSystemRoot()) {
protected ExternalJavacProcessHandler createProcessHandler(Process process) {
return new ExternalJavacProcessHandler(process) {
protected Future<?> executeOnPooledThread(Runnable task) {
return SharedThreadPool.getInstance().executeOnPooledThread(task);
}
};
}
};
server.start(listenPort);
ExternalJavacManager.KEY.set(context, server);
return server;
@@ -45,12 +45,10 @@ import org.jetbrains.jps.builders.java.JavaCompilingTool;
import org.jetbrains.jps.cmdline.ClasspathBootstrap;
import org.jetbrains.jps.incremental.GlobalContextKey;
import org.jetbrains.jps.service.SharedThreadPool;
import org.jetbrains.jps.service.ThreadExecutor;
import javax.tools.Diagnostic;
import java.io.File;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
@@ -66,22 +64,26 @@ public class ExternalJavacManager {
public static final String STDOUT_LINE_PREFIX = "JAVAC_PROCESS[STDOUT]";
public static final String STDERR_LINE_PREFIX = "JAVAC_PROCESS[STDERR]";
private static final AttributeKey<JavacProcessDescriptor> SESSION_DESCRIPTOR = AttributeKey.valueOf("ExternalJavacServer.JavacProcessDescriptor");
private final File mySystemRoot;
private final ThreadExecutor myThreadExecutor;
private ChannelRegistrar myChannelRegistrar;
@NotNull
private final File myWorkingDir;
@NotNull
private final ChannelRegistrar myChannelRegistrar;
private final Map<UUID, JavacProcessDescriptor> myMessageHandlers = new HashMap<UUID, JavacProcessDescriptor>();
private int myListenPort = DEFAULT_SERVER_PORT;
public ExternalJavacManager(final File systemRoot, @NotNull ThreadExecutor threadExecutor) {
mySystemRoot = systemRoot;
myThreadExecutor = threadExecutor;
public ExternalJavacManager(@NotNull final File workingDir) {
myWorkingDir = workingDir;
myChannelRegistrar = new ChannelRegistrar();
}
@NotNull
public File getWorkingDir() {
return myWorkingDir;
}
public void start(int listenPort) {
final ServerBootstrap bootstrap = new ServerBootstrap().group(new NioEventLoopGroup(1, SharedThreadPool.getInstance())).channel(NioServerSocketChannel.class);
bootstrap.childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true);
myChannelRegistrar = new ChannelRegistrar();
final ChannelHandler compilationRequestsHandler = new CompilationRequestsHandler();
bootstrap.childHandler(new ChannelInitializer() {
@Override
@@ -117,7 +119,7 @@ public class ExternalJavacManager {
}
try {
final ExternalJavacProcessHandler processHandler = launchExternalJavacProcess(
uuid, javaHome, heapSize, myListenPort, mySystemRoot, vmOptions, compilingTool
uuid, javaHome, heapSize, myListenPort, myWorkingDir, vmOptions, compilingTool
);
processHandler.addProcessListener(new ProcessAdapter() {
public void onTextAvailable(ProcessEvent event, Key outputType) {
@@ -200,11 +202,14 @@ public class ExternalJavacManager {
//appendParam(cmdLine, "-XX:MaxPermSize=150m");
//appendParam(cmdLine, "-XX:ReservedCodeCacheSize=64m");
appendParam(cmdLine, "-Djava.awt.headless=true");
final int xms = heapSize / 2;
if (xms > 32) {
appendParam(cmdLine, "-Xms" + xms + "m");
if (heapSize > 0) {
// if the value is zero or negative, use JVM default memory settings
final int xms = heapSize / 2;
if (xms > 32) {
appendParam(cmdLine, "-Xms" + xms + "m");
}
appendParam(cmdLine, "-Xmx" + heapSize + "m");
}
appendParam(cmdLine, "-Xmx" + heapSize + "m");
// debugging
//appendParam(cmdLine, "-XX:+HeapDumpOnOutOfMemoryError");
@@ -268,7 +273,11 @@ public class ExternalJavacManager {
builder.directory(workingDir);
final Process process = builder.start();
return new ExternalJavacProcessHandler(process, myThreadExecutor);
return createProcessHandler(process);
}
protected ExternalJavacProcessHandler createProcessHandler(Process process) {
return new ExternalJavacProcessHandler(process);
}
private static void appendParam(List<String> cmdLine, String param) {
@@ -287,14 +296,11 @@ public class ExternalJavacManager {
return sdkHome + "/bin/java";
}
private static class ExternalJavacProcessHandler extends BaseOSProcessHandler {
@NotNull
private final ThreadExecutor myExecutorService;
protected static class ExternalJavacProcessHandler extends BaseOSProcessHandler {
private volatile int myExitCode;
ExternalJavacProcessHandler(Process process, @NotNull ThreadExecutor executorService) {
public ExternalJavacProcessHandler(Process process) {
super(process, null, null);
myExecutorService = executorService;
addProcessListener(new ProcessAdapter() {
@Override
public void processTerminated(ProcessEvent event) {
@@ -303,11 +309,6 @@ public class ExternalJavacManager {
});
}
@Override
protected Future<?> executeOnPooledThread(Runnable task) {
return myExecutorService.executeOnPooledThread(task);
}
public int getExitCode() {
return myExitCode;
}
@@ -15,12 +15,16 @@
*/
package org.jetbrains.jps.service;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
/**
* @author nik
*/
public abstract class SharedThreadPool implements ThreadExecutor {
public abstract class SharedThreadPool implements Executor {
public static SharedThreadPool getInstance() {
return JpsServiceManager.getInstance().getService(SharedThreadPool.class);
}
public abstract Future<?> executeOnPooledThread(Runnable action);
}
@@ -1,23 +0,0 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.service;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
public interface ThreadExecutor extends Executor {
Future<?> executeOnPooledThread(Runnable action);
}