diff --git a/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java b/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java index bb1d2db5b1a9..2f5cf4e494a6 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java @@ -91,7 +91,7 @@ public class CompileServerManager implements ApplicationComponent{ private static final String COMPILE_SERVER_SYSTEM_ROOT = "compile-server"; private static final String LOGGER_CONFIG = "log.xml"; private static final String DEFAULT_LOGGER_CONFIG = "defaultLogConfig.xml"; - private volatile OSProcessHandler myProcessHandler; + private volatile ServerWrapper myProcessHandler; private final File mySystemDirectory; @Nullable private volatile CompileServerClient myClient; @@ -335,7 +335,7 @@ public class CompileServerManager implements ApplicationComponent{ public RequestFuture submitCompilationTask(final Project project, final boolean isRebuild, final boolean isMake, final Collection modules, final Collection artifacts, final Collection paths, - final Map userData, final JpsServerResponseHandler handler) { + final Map _userData, final JpsServerResponseHandler handler) { final String projectId = getProjectPath(project); final Ref futureRef = new Ref(null); final RunnableFuture future = myTaskExecutor.submit(new Runnable() { @@ -344,9 +344,14 @@ public class CompileServerManager implements ApplicationComponent{ try { final CompileServerClient client = ensureServerRunningAndClientConnected(true); if (client != null) { + final Map userData = new LinkedHashMap(); + userData.putAll(_userData); + if (Registry.is("compiler.server.use.external.javac.process")) { + userData.put(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION, "true"); + } final RequestFuture requestFuture = isRebuild ? - client.sendRebuildRequest(projectId, handler) : - client.sendCompileRequest(isMake, projectId, modules, artifacts, paths, userData, handler); + client.sendRebuildRequest(projectId, handler) : + client.sendCompileRequest(isMake, projectId, modules, artifacts, paths, userData, handler); futureRef.set(requestFuture); } else { @@ -390,9 +395,9 @@ public class CompileServerManager implements ApplicationComponent{ // executed in one thread at a time @Nullable private CompileServerClient ensureServerRunningAndClientConnected(boolean forceRestart) throws Throwable { - final OSProcessHandler ph = myProcessHandler; + final ServerWrapper ph = myProcessHandler; final CompileServerClient cl = myClient; - final boolean processNotRunning = ph == null || ph.isProcessTerminated() || ph.isProcessTerminating(); + final boolean processNotRunning = ph == null || ph.isDead(); final boolean clientNotConnected = cl == null || !cl.isConnected(); if (processNotRunning || clientNotConnected) { @@ -405,68 +410,13 @@ public class CompileServerManager implements ApplicationComponent{ return null; } + final File workDirectory = new File(mySystemDirectory, COMPILE_SERVER_SYSTEM_ROOT); + workDirectory.mkdirs(); + ensureLogConfigExists(workDirectory); + final int port = NetUtils.findAvailableSocketPort(); - final long serverPingInterval = Registry.intValue("compiler.server.ping.interval", -1) * 1000L; - final Process process = launchServer(port, serverPingInterval); - - final OSProcessHandler processHandler = new OSProcessHandler(process, null) { - @Override - protected boolean shouldDestroyProcessRecursively() { - return true; - } - }; - final StringBuilder serverStartMessage = new StringBuilder(); - final Semaphore semaphore = new Semaphore(); - semaphore.down(); - processHandler.addProcessListener(new ProcessAdapter() { - @Override - public void onTextAvailable(ProcessEvent event, Key outputType) { - // re-translate server's output to idea.log - final String text = event.getText(); - if (!StringUtil.isEmpty(text)) { - LOG.info("COMPILE_SERVER [" +outputType.toString() +"]: "+ text.trim()); - } - } - }); - processHandler.addProcessListener(new ProcessAdapter() { - @Override - public void processTerminated(ProcessEvent event) { - try { - processHandler.removeProcessListener(this); - } - finally { - semaphore.up(); - } - } - - @Override - public void onTextAvailable(ProcessEvent event, Key outputType) { - if (outputType == ProcessOutputTypes.STDERR) { - try { - final String text = event.getText(); - if (text != null) { - if (text.contains(Server.SERVER_SUCCESS_START_MESSAGE) || text.contains(Server.SERVER_ERROR_START_MESSAGE)) { - processHandler.removeProcessListener(this); - } - if (serverStartMessage.length() > 0) { - serverStartMessage.append("\n"); - } - serverStartMessage.append(text); - } - } - finally { - semaphore.up(); - } - } - } - }); - processHandler.startNotify(); - semaphore.waitFor(); - - final String startupMsg = serverStartMessage.toString(); - if (!startupMsg.contains(Server.SERVER_SUCCESS_START_MESSAGE)) { - throw new Exception("Server startup failed: " + startupMsg); - } + final long serverPingInterval = Registry.intValue("compiler.server.ping.interval", -1) * 1000L; + ServerWrapper wrapper = Registry.is("compiler.server.in.process") ? launchServerThread(workDirectory, port) : launchServerProcess(port, serverPingInterval, workDirectory); CompileServerClient client = new CompileServerClient(serverPingInterval, myAsyncExec); boolean connected = false; @@ -475,19 +425,90 @@ public class CompileServerManager implements ApplicationComponent{ if (connected) { final RequestFuture setupFuture = sendSetupRequest(client); setupFuture.waitFor(); - myProcessHandler = processHandler; + myProcessHandler = wrapper; myClient = client; } } finally { if (!connected) { - shutdownServer(cl, processHandler); + shutdownServer(cl, wrapper); } } } return myClient; } + private static ServerWrapper launchServerThread(final File workDirectory, final int port) { + final Server server = new Server(workDirectory, Registry.is("compiler.server.use.memory.temp.cache")); + //todo hostname + server.start(port); + return new ServerWrapper(null, server); + } + + private ServerWrapper launchServerProcess(int port, long serverPingInterval, File workDirectory) throws Exception { + final Process process = launchServer(port, serverPingInterval, workDirectory); + + final OSProcessHandler processHandler = new OSProcessHandler(process, null) { + @Override + protected boolean shouldDestroyProcessRecursively() { + return true; + } + }; + final StringBuilder serverStartMessage = new StringBuilder(); + final Semaphore semaphore = new Semaphore(); + semaphore.down(); + processHandler.addProcessListener(new ProcessAdapter() { + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + // re-translate server's output to idea.log + final String text = event.getText(); + if (!StringUtil.isEmpty(text)) { + LOG.info("COMPILE_SERVER [" +outputType.toString() +"]: "+ text.trim()); + } + } + }); + processHandler.addProcessListener(new ProcessAdapter() { + @Override + public void processTerminated(ProcessEvent event) { + try { + processHandler.removeProcessListener(this); + } + finally { + semaphore.up(); + } + } + + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + if (outputType == ProcessOutputTypes.STDERR) { + try { + final String text = event.getText(); + if (text != null) { + if (text.contains(Server.SERVER_SUCCESS_START_MESSAGE) || text.contains(Server.SERVER_ERROR_START_MESSAGE)) { + processHandler.removeProcessListener(this); + } + if (serverStartMessage.length() > 0) { + serverStartMessage.append("\n"); + } + serverStartMessage.append(text); + } + } + finally { + semaphore.up(); + } + } + } + }); + processHandler.startNotify(); + semaphore.waitFor(); + + final String startupMsg = serverStartMessage.toString(); + if (!startupMsg.contains(Server.SERVER_SUCCESS_START_MESSAGE)) { + throw new Exception("Server startup failed: " + startupMsg); + } + return new ServerWrapper(processHandler, null); + } + private static RequestFuture sendSetupRequest(final @NotNull CompileServerClient client) throws Exception { final Map data = new HashMap(); @@ -570,7 +591,7 @@ public class CompileServerManager implements ApplicationComponent{ // commandLine.add((launcherUsed? "-J" : "") + "-D" + CharsetToolkit.FILE_ENCODING_PROPERTY + "=" + CharsetToolkit.getDefaultSystemCharset().name()); //} - private Process launchServer(final int port, long pingInterval) throws ExecutionException { + private Process launchServer(final int port, long pingInterval, File workDirectory) throws ExecutionException { // validate tools.jar presence final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler(); if (systemCompiler == null) { @@ -619,9 +640,6 @@ public class CompileServerManager implements ApplicationComponent{ if (Registry.is("compiler.server.use.memory.temp.cache")) { cmdLine.addParameter("-D"+ GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION + "=true"); } - if (Registry.is("compiler.server.use.external.javac.process")) { - cmdLine.addParameter("-D"+ GlobalOptions.USE_EXTERNAL_JAVAC_OPTION + "=true"); - } cmdLine.addParameter("-D"+ GlobalOptions.HOSTNAME_OPTION + "=" + NetUtils.getLocalHostString()); // javac's VM should use the same default locale that IDEA uses in order for javac to print messages in 'correct' language @@ -652,10 +670,6 @@ public class CompileServerManager implements ApplicationComponent{ cmdLine.addParameter(org.jetbrains.jps.server.Server.class.getName()); cmdLine.addParameter(Integer.toString(port)); - final File workDirectory = new File(mySystemDirectory, COMPILE_SERVER_SYSTEM_ROOT); - workDirectory.mkdirs(); - ensureLogConfigExists(workDirectory); - cmdLine.addParameter(FileUtil.toSystemIndependentName(workDirectory.getPath())); cmdLine.setWorkDirectory(workDirectory); @@ -695,7 +709,7 @@ public class CompileServerManager implements ApplicationComponent{ shutdownServer(myClient, myProcessHandler); } - private static void shutdownServer(final CompileServerClient client, final OSProcessHandler processHandler) { + private static void shutdownServer(final CompileServerClient client, final ServerWrapper processHandler) { try { if (client != null && client.isConnected()) { final Future future = client.sendShutdownRequest(); @@ -868,4 +882,28 @@ public class CompileServerManager implements ApplicationComponent{ } } } + + private static class ServerWrapper { + final @Nullable OSProcessHandler myHandler; + final @Nullable Server myServer; + + ServerWrapper(OSProcessHandler handler, Server server) { + myHandler = handler; + myServer = server; + } + + public void destroyProcess() { + if (myHandler != null) { + myHandler.destroyProcess(); + } else if (myServer != null) { + myServer.stop(); + } + } + + public boolean isDead() { + return myHandler != null && (myHandler.isProcessTerminated() || myHandler.isProcessTerminating()) || + myServer != null && myServer.isStopped(); + } + } } + diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java index 4eb3dfa3f5ac..a84911754c50 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -7,13 +7,13 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.io.MappingFailedException; import com.intellij.util.io.PersistentEnumerator; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.*; import org.jetbrains.jps.api.CanceledStatus; import org.jetbrains.jps.api.GlobalOptions; import org.jetbrains.jps.api.RequestFuture; import org.jetbrains.jps.api.SharedThreadPool; import org.jetbrains.jps.incremental.java.ExternalJavacDescriptor; -import org.jetbrains.jps.incremental.java.JavaBuilder; import org.jetbrains.jps.incremental.java.JavaBuilderLogger; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; @@ -164,7 +164,7 @@ public class IncProjectBuilder { return context; } - private static void flushContext(CompileContext context) { + private static void flushContext(@Nullable CompileContext context) { if (context != null) { context.getTimestampStorage().force(); context.getDataManager().flush(false); @@ -181,14 +181,16 @@ public class IncProjectBuilder { } ExternalJavacDescriptor.KEY.set(context, null); } - cleanupJavacNameTable(); + if (context == null || context.getBuilderParameter(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION) == null) { + cleanupJavacNameTable(); + } } private static boolean ourClenupFailed = false; private static void cleanupJavacNameTable() { try { - if (JavaBuilder.USE_EMBEDDED_JAVAC && !ourClenupFailed) { + if (!ourClenupFailed) { final Field freelistField = Class.forName("com.sun.tools.javac.util.Name$Table").getDeclaredField("freelist"); freelistField.setAccessible(true); freelistField.set(null, com.sun.tools.javac.util.List.nil()); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 64d96061c30c..14e158ff66a4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -56,7 +56,6 @@ public class JavaBuilder extends ModuleLevelBuilder { private static final String FORMS_BUILDER_NAME = "forms"; private static final String JAVA_EXTENSION = ".java"; private static final String FORM_EXTENSION = ".form"; - public static final boolean USE_EMBEDDED_JAVAC = System.getProperty(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION) == null; public static final FileFilter JAVA_SOURCES_FILTER = new FileFilter() { public boolean accept(File file) { @@ -386,7 +385,7 @@ public class JavaBuilder extends ModuleLevelBuilder { final ClassProcessingConsumer classesConsumer = new ClassProcessingConsumer(context, outputSink); try { final boolean rc; - if (USE_EMBEDDED_JAVAC) { + if (context.getBuilderParameter(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION) == null) { rc = JavacMain.compile( options, files, classpath, platformCp, sourcePath, outs, diagnosticSink, classesConsumer, context.getCancelStatus() ); diff --git a/jps/jps-builders/src/org/jetbrains/jps/server/Server.java b/jps/jps-builders/src/org/jetbrains/jps/server/Server.java index 1bb5387910b2..baacea57e79d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/server/Server.java +++ b/jps/jps-builders/src/org/jetbrains/jps/server/Server.java @@ -70,7 +70,7 @@ public class Server { private final ScheduledExecutorService myScheduler; private final ServerMessageHandler myMessageHandler; - public Server(File systemDir) { + public Server(File systemDir, boolean cachesInMemory) { Utils.setSystemRoot(systemDir); final ExecutorService threadPool = Executors.newCachedThreadPool(); myScheduler = ConcurrencyUtil.newSingleScheduledThreadExecutor("Client activity checker", Thread.MIN_PRIORITY); @@ -105,6 +105,17 @@ public class Server { ); } }; + ServerState.getInstance().setKeepTempCachesInMemory(cachesInMemory); + Runtime.getRuntime().addShutdownHook(new Thread("Shutdown hook thread") { + public void run() { + try { + myMessageHandler.cancelAllBuildsAndClearState(); + } + finally { + Server.this.stop(); + } + } + }); } public void start(int listenPort) { @@ -181,6 +192,10 @@ public class Server { myLastPingTime = System.currentTimeMillis(); } + public boolean isStopped() { + return myScheduler.isShutdown(); + } + public static void main(String[] args) { try { int port = DEFAULT_SERVER_PORT; @@ -197,21 +212,10 @@ public class Server { systemDir = new File(args[1]); } - final Server server = new Server(systemDir); - Runtime.getRuntime().addShutdownHook(new Thread("Shutdown hook thread") { - public void run() { - try { - server.myMessageHandler.cancelAllBuildsAndClearState(); - } - finally { - server.stop(); - } - } - }); + final Server server = new Server(systemDir, System.getProperty(GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION) != null); initLoggers(); server.start(port); - ServerState.getInstance().setKeepTempCachesInMemory(System.getProperty(GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION) != null); System.out.println("Server classpath: " + System.getProperty("java.class.path")); System.err.println(SERVER_SUCCESS_START_MESSAGE + port); diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 2d64f5371be8..23c285e07e9b 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -151,6 +151,9 @@ compiler.server.use.external.javac.process.description=Run javac compiler in ext compiler.server.ping.interval=5 compiler.server.ping.interval.description=Interval in seconds between ping requests the IDE periodically sends to server. If server does not receive pings for some time, it shuts down. Specify -1 to disable this feature. +compiler.server.in.process=false +compiler.server.in.process.description=Launch compiler server in IDEA process + compiler.server.debug.port=-1 #compiler.server.javac.debug.port=-1