summaryrefslogtreecommitdiff
path: root/jps/jps-builders/src/org/jetbrains/jps/javac
diff options
context:
space:
mode:
Diffstat (limited to 'jps/jps-builders/src/org/jetbrains/jps/javac')
-rw-r--r--jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java230
-rw-r--r--jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java (renamed from jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java)318
-rw-r--r--jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java310
-rw-r--r--jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java109
-rw-r--r--jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java78
-rw-r--r--jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java231
6 files changed, 749 insertions, 527 deletions
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java
new file mode 100644
index 000000000000..fd5bc65d1e5b
--- /dev/null
+++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2000-2014 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.javac;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.MessageLite;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.incremental.BinaryContent;
+
+import javax.tools.*;
+import java.io.File;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: 1/22/12
+ */
+public class ExternalJavacMessageHandler {
+ private final DiagnosticOutputConsumer myDiagnosticSink;
+ private final OutputFileConsumer myOutputSink;
+ @Nullable
+ private final String myEncodingName;
+ private volatile boolean myTerminatedSuccessfully;
+
+ public ExternalJavacMessageHandler(DiagnosticOutputConsumer diagnosticSink,
+ OutputFileConsumer outputSink,
+ @Nullable final String encodingName) {
+ myDiagnosticSink = diagnosticSink;
+ myOutputSink = outputSink;
+ myEncodingName = encodingName;
+ }
+
+ public boolean handleMessage(MessageLite message) {
+ try {
+ final JavacRemoteProto.Message msg = (JavacRemoteProto.Message)message;
+ final JavacRemoteProto.Message.Type messageType = msg.getMessageType();
+
+ if (messageType == JavacRemoteProto.Message.Type.RESPONSE) {
+ final JavacRemoteProto.Message.Response response = msg.getResponse();
+ final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType();
+
+ if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_MESSAGE) {
+ final JavacRemoteProto.Message.Response.CompileMessage compileMessage = response.getCompileMessage();
+ final JavacRemoteProto.Message.Response.CompileMessage.Kind messageKind = compileMessage.getKind();
+
+ if (messageKind == JavacRemoteProto.Message.Response.CompileMessage.Kind.STD_OUT) {
+ if (compileMessage.hasText()) {
+ myDiagnosticSink.outputLineAvailable(compileMessage.getText());
+ }
+ }
+ else {
+ final String sourceUri = compileMessage.hasSourceUri()? compileMessage.getSourceUri() : null;
+ final JavaFileObject srcFileObject = sourceUri != null? new DummyJavaFileObject(URI.create(sourceUri)) : null;
+ myDiagnosticSink.report(new DummyDiagnostic(convertKind(messageKind), srcFileObject, compileMessage));
+ }
+
+ return false;
+ }
+
+ if (responseType == JavacRemoteProto.Message.Response.Type.OUTPUT_OBJECT) {
+ final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject();
+ final JavacRemoteProto.Message.Response.OutputObject.Kind kind = outputObject.getKind();
+
+ final String outputRoot = outputObject.hasOutputRoot()? outputObject.getOutputRoot() : null;
+ final File outputRootFile = outputRoot != null? new File(outputRoot) : null;
+
+ final BinaryContent fileObjectContent;
+ final ByteString content = outputObject.hasContent()? outputObject.getContent() : null;
+ if (content != null) {
+ final byte[] bytes = content.toByteArray();
+ fileObjectContent = new BinaryContent(bytes, 0, bytes.length);
+ }
+ else {
+ fileObjectContent = null;
+ }
+
+ final String sourceUri = outputObject.hasSourceUri()? outputObject.getSourceUri() : null;
+ final URI srcUri = sourceUri != null? URI.create(sourceUri) : null;
+ final OutputFileObject fileObject = new OutputFileObject(
+ null,
+ outputRootFile,
+ outputObject.hasRelativePath()? outputObject.getRelativePath() : null,
+ new File(outputObject.getFilePath()),
+ convertKind(kind),
+ outputObject.hasClassName()? outputObject.getClassName() : null,
+ srcUri,
+ myEncodingName, fileObjectContent
+ );
+
+ myOutputSink.save(fileObject);
+ return false;
+ }
+
+ if (responseType == JavacRemoteProto.Message.Response.Type.SRC_FILE_LOADED) {
+ final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject();
+ final File file = new File(outputObject.getFilePath());
+ myDiagnosticSink.javaFileLoaded(file);
+ return false;
+ }
+
+ if (responseType == JavacRemoteProto.Message.Response.Type.CLASS_DATA) {
+ final JavacRemoteProto.Message.Response.ClassData data = response.getClassData();
+ final String className = data.getClassName();
+ final Collection<String> imports = data.getImportStatementList();
+ final Collection<String> staticImports = data.getStaticImportList();
+ myDiagnosticSink.registerImports(className, imports, staticImports);
+ return false;
+ }
+
+ if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_COMPLETED) {
+ if (response.hasCompletionStatus()) {
+ myTerminatedSuccessfully = response.getCompletionStatus();
+ }
+ return true;
+ }
+
+ throw new Exception("Unsupported response type: " + responseType.name());
+ }
+
+ if (messageType == JavacRemoteProto.Message.Type.FAILURE) {
+ final JavacRemoteProto.Message.Failure failure = msg.getFailure();
+ final StringBuilder buf = new StringBuilder();
+ if (failure.hasDescription()) {
+ buf.append(failure.getDescription());
+ }
+ if (failure.hasStacktrace()) {
+ if (buf.length() > 0) {
+ buf.append("\n");
+ }
+ buf.append(failure.getStacktrace());
+ }
+ myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, buf.toString()));
+ return true;
+ }
+
+ throw new Exception("Unsupported message type: " + messageType.name());
+ }
+ catch (Throwable e) {
+ myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage()));
+ return true;
+ }
+ }
+
+ public boolean isTerminatedSuccessfully() {
+ return myTerminatedSuccessfully;
+ }
+
+ private static Diagnostic.Kind convertKind(JavacRemoteProto.Message.Response.CompileMessage.Kind kind) {
+ switch (kind) {
+ case ERROR: return Diagnostic.Kind.ERROR;
+ case WARNING: return Diagnostic.Kind.WARNING;
+ case MANDATORY_WARNING: return Diagnostic.Kind.MANDATORY_WARNING;
+ case NOTE: return Diagnostic.Kind.NOTE;
+ default : return Diagnostic.Kind.OTHER;
+ }
+ }
+
+ private static OutputFileObject.Kind convertKind(JavacRemoteProto.Message.Response.OutputObject.Kind kind) {
+ switch (kind) {
+ case CLASS: return JavaFileObject.Kind.CLASS;
+ case HTML: return JavaFileObject.Kind.HTML;
+ case SOURCE: return JavaFileObject.Kind.SOURCE;
+ default : return JavaFileObject.Kind.OTHER;
+ }
+ }
+
+ private static class DummyDiagnostic implements Diagnostic<JavaFileObject> {
+
+ private final Kind myMessageKind;
+ private final JavaFileObject mySrcFileObject;
+ private final JavacRemoteProto.Message.Response.CompileMessage myCompileMessage;
+
+ public DummyDiagnostic(final Kind messageKind, JavaFileObject srcFileObject, JavacRemoteProto.Message.Response.CompileMessage compileMessage) {
+ myMessageKind = messageKind;
+ mySrcFileObject = srcFileObject;
+ myCompileMessage = compileMessage;
+ }
+
+ public Kind getKind() {
+ return myMessageKind;
+ }
+
+ public JavaFileObject getSource() {
+ return mySrcFileObject;
+ }
+
+ public long getPosition() {
+ return myCompileMessage.hasProblemLocationOffset()? myCompileMessage.getProblemLocationOffset() : -1;
+ }
+
+ public long getStartPosition() {
+ return myCompileMessage.hasProblemBeginOffset()? myCompileMessage.getProblemBeginOffset() : -1;
+ }
+
+ public long getEndPosition() {
+ return myCompileMessage.hasProblemEndOffset()? myCompileMessage.getProblemEndOffset() : -1;
+ }
+
+ public long getLineNumber() {
+ return myCompileMessage.hasLine()? myCompileMessage.getLine() : -1;
+ }
+
+ public long getColumnNumber() {
+ return myCompileMessage.hasColumn()? myCompileMessage.getColumn() : -1;
+ }
+
+ public String getCode() {
+ return null;
+ }
+
+ public String getMessage(Locale locale) {
+ return myCompileMessage.hasText()? myCompileMessage.getText() : null;
+ }
+ }
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java
index 0ecfe7617160..d01ae7af6b4d 100644
--- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java
+++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * Copyright 2000-2014 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.
@@ -15,18 +15,19 @@
*/
package org.jetbrains.jps.javac;
-import io.netty.bootstrap.ServerBootstrap;
+import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
-import io.netty.channel.group.ChannelGroup;
-import io.netty.channel.group.ChannelGroupFuture;
-import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
-import io.netty.util.concurrent.ImmediateEventExecutor;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.netty.util.internal.logging.Log4JLoggerFactory;
+import org.apache.log4j.ConsoleAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.PatternLayout;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.api.CanceledStatus;
import org.jetbrains.jps.builders.impl.java.JavacCompilerTool;
@@ -37,85 +38,109 @@ import org.jetbrains.jps.service.SharedThreadPool;
import javax.tools.*;
import java.io.File;
import java.util.*;
+import java.util.concurrent.TimeUnit;
/**
* @author Eugene Zhuravlev
* Date: 1/22/12
*/
-@SuppressWarnings("UseOfSystemOutOrSystemErr")
-public class JavacServer {
- public static final int DEFAULT_SERVER_PORT = 7878;
- public static final String SERVER_SUCCESS_START_MESSAGE = "Javac server started successfully. Listening on port: ";
- public static final String SERVER_ERROR_START_MESSAGE = "Error starting Javac Server: ";
+public class ExternalJavacProcess {
public static final String JPS_JAVA_COMPILING_TOOL_PROPERTY = "jps.java.compiling.tool";
-
- private ChannelRegistrar myChannelRegistrar;
-
- 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() {
+ private final ChannelInitializer myChannelInitializer;
+ private final EventLoopGroup myEventLoopGroup;
+ private volatile ChannelFuture myConnectFuture;
+ private volatile CancelHandler myCancelHandler;
+
+ static {
+ org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger();
+ if (!root.getAllAppenders().hasMoreElements()) {
+ root.setLevel(Level.INFO);
+ root.addAppender(new ConsoleAppender(new PatternLayout(PatternLayout.DEFAULT_CONVERSION_PATTERN)));
+ }
+ InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory());
+ }
+
+ public ExternalJavacProcess() {
+ final JavacRemoteProto.Message msgDefaultInstance = JavacRemoteProto.Message.getDefaultInstance();
+
+ myEventLoopGroup = new NioEventLoopGroup(1, SharedThreadPool.getInstance());
+ myChannelInitializer = new ChannelInitializer() {
@Override
protected void initChannel(Channel channel) throws Exception {
- channel.pipeline().addLast(myChannelRegistrar,
- new ProtobufVarint32FrameDecoder(),
- new ProtobufDecoder(JavacRemoteProto.Message.getDefaultInstance()),
+ channel.pipeline().addLast(new ProtobufVarint32FrameDecoder(),
+ new ProtobufDecoder(msgDefaultInstance),
new ProtobufVarint32LengthFieldPrepender(),
new ProtobufEncoder(),
- compilationRequestsHandler);
+ new CompilationRequestsHandler()
+ );
}
- });
- myChannelRegistrar.add(bootstrap.bind(listenPort).syncUninterruptibly().channel());
- }
-
- public void stop() {
- myChannelRegistrar.close().awaitUninterruptibly();
+ };
}
-
+
+ //static volatile long myGlobalStart;
+
public static void main(String[] args) {
- JavacServer server = null;
- try {
- int port = DEFAULT_SERVER_PORT;
- if (args.length > 0) {
- try {
- port = Integer.parseInt(args[0]);
- }
- catch (NumberFormatException e) {
- System.err.println("Error parsing port: " + e.getMessage());
- System.exit(-1);
- }
+ //myGlobalStart = System.currentTimeMillis();
+ UUID uuid = null;
+ String host = null;
+ int port = -1;
+ if (args.length > 0) {
+ try {
+ uuid = UUID.fromString(args[0]);
}
-
- server = new JavacServer();
- server.start(port);
- final JavacServer finalServer = server;
- Runtime.getRuntime().addShutdownHook(new Thread("Shutdown hook thread") {
- @Override
- public void run() {
- finalServer.stop();
- }
- });
-
- System.out.println("Server classpath: " + System.getProperty("java.class.path"));
- System.err.println(SERVER_SUCCESS_START_MESSAGE + port);
- }
- catch (Throwable e) {
- System.err.println(SERVER_ERROR_START_MESSAGE + e.getMessage());
- e.printStackTrace(System.err);
+ catch (Exception e) {
+ System.err.println("Error parsing session id: " + e.getMessage());
+ System.exit(-1);
+ }
+
+ host = args[1];
+
try {
- if (server != null) {
- server.stop();
- }
+ port = Integer.parseInt(args[2]);
}
- finally {
+ catch (NumberFormatException e) {
+ System.err.println("Error parsing port: " + e.getMessage());
System.exit(-1);
}
}
+ else {
+ System.err.println("Insufficient parameters");
+ System.exit(-1);
+ }
+
+ final ExternalJavacProcess process = new ExternalJavacProcess();
+ try {
+ //final long connectStart = System.currentTimeMillis();
+ if (process.connect(host, port)) {
+ //final long connectEnd = System.currentTimeMillis();
+ //System.err.println("Connected in " + (connectEnd - connectStart) + " ms; since start: " + (connectEnd - myGlobalStart));
+ process.myConnectFuture.channel().writeAndFlush(
+ JavacProtoUtil.toMessage(uuid, JavacProtoUtil.createRequestAckResponse())
+ );
+ }
+ else {
+ System.err.println("Failed to connect to parent process");
+ System.exit(-1);
+ }
+ }
+ catch (Throwable throwable) {
+ throwable.printStackTrace(System.err);
+ System.exit(-1);
+ }
}
- public static JavacRemoteProto.Message compile(final ChannelHandlerContext context,
+ private boolean connect(final String host, final int port) throws Throwable {
+ final Bootstrap bootstrap = new Bootstrap().group(myEventLoopGroup).channel(NioSocketChannel.class).handler(myChannelInitializer);
+ bootstrap.option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true);
+ final ChannelFuture future = bootstrap.connect(host, port).syncUninterruptibly();
+ if (future.isSuccess()) {
+ myConnectFuture = future;
+ return true;
+ }
+ return false;
+ }
+
+ private static JavacRemoteProto.Message compile(final ChannelHandlerContext context,
final UUID sessionId,
List<String> options,
Collection<File> files,
@@ -124,6 +149,8 @@ public class JavacServer {
Collection<File> sourcePath,
Map<File, Set<File>> outs,
final CanceledStatus canceledStatus) {
+ //final long compileStart = System.currentTimeMillis();
+ //System.err.println("Compile start; since global start: " + (compileStart - myGlobalStart));
final DiagnosticOutputConsumer diagnostic = new DiagnosticOutputConsumer() {
@Override
public void javaFileLoaded(File file) {
@@ -157,7 +184,8 @@ public class JavacServer {
try {
JavaCompilingTool tool = getCompilingTool();
- final boolean rc = JavacMain.compile(options, files, classpath, platformCp, sourcePath, outs, diagnostic, outputSink, canceledStatus, tool);
+ final boolean rc = JavacMain.compile(options, files, classpath, platformCp, sourcePath, outs, diagnostic, outputSink, canceledStatus,
+ tool);
return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createBuildCompletedResponse(rc));
}
catch (Throwable e) {
@@ -165,8 +193,12 @@ public class JavacServer {
e.printStackTrace(System.err);
return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure(e.getMessage(), e));
}
+ //finally {
+ // final long compileEnd = System.currentTimeMillis();
+ // System.err.println("Compiled in " + (compileEnd - compileStart) + " ms; since global start: " + (compileEnd - myGlobalStart));
+ //}
}
-
+
private static JavaCompilingTool getCompilingTool() {
String property = System.getProperty(JPS_JAVA_COMPILING_TOOL_PROPERTY);
if (property != null) {
@@ -178,24 +210,6 @@ public class JavacServer {
return new JavacCompilerTool();
}
- private final Set<CancelHandler> myCancelHandlers = Collections.synchronizedSet(new HashSet<CancelHandler>());
-
- public void cancelBuilds() {
- synchronized (myCancelHandlers) {
- for (CancelHandler handler : myCancelHandlers) {
- handler.cancel();
- }
- }
- }
-
- private static List<File> toFiles(List<String> paths) {
- final List<File> files = new ArrayList<File>(paths.size());
- for (String path : paths) {
- files.add(new File(path));
- }
- return files;
- }
-
@ChannelHandler.Sharable
private class CompilationRequestsHandler extends SimpleChannelInboundHandler<JavacRemoteProto.Message> {
@Override
@@ -210,52 +224,50 @@ public class JavacServer {
final JavacRemoteProto.Message.Request request = message.getRequest();
final JavacRemoteProto.Message.Request.Type requestType = request.getRequestType();
if (requestType == JavacRemoteProto.Message.Request.Type.COMPILE) {
- final List<String> options = request.getOptionList();
- final List<File> files = toFiles(request.getFileList());
- final List<File> cp = toFiles(request.getClasspathList());
- final List<File> platformCp = toFiles(request.getPlatformClasspathList());
- final List<File> srcPath = toFiles(request.getSourcepathList());
-
- final Map<File, Set<File>> outs = new HashMap<File, Set<File>>();
- for (JavacRemoteProto.Message.Request.OutputGroup outputGroup : request.getOutputList()) {
- final Set<File> srcRoots = new HashSet<File>();
- for (String root : outputGroup.getSourceRootList()) {
- srcRoots.add(new File(root));
+ if (myCancelHandler == null) { // if not running yet
+ final List<String> options = request.getOptionList();
+ final List<File> files = toFiles(request.getFileList());
+ final List<File> cp = toFiles(request.getClasspathList());
+ final List<File> platformCp = toFiles(request.getPlatformClasspathList());
+ final List<File> srcPath = toFiles(request.getSourcepathList());
+
+ final Map<File, Set<File>> outs = new HashMap<File, Set<File>>();
+ for (JavacRemoteProto.Message.Request.OutputGroup outputGroup : request.getOutputList()) {
+ final Set<File> srcRoots = new HashSet<File>();
+ for (String root : outputGroup.getSourceRootList()) {
+ srcRoots.add(new File(root));
+ }
+ outs.put(new File(outputGroup.getOutputRoot()), srcRoots);
}
- outs.put(new File(outputGroup.getOutputRoot()), srcRoots);
- }
- final CancelHandler cancelHandler = new CancelHandler();
- myCancelHandlers.add(cancelHandler);
- SharedThreadPool.getInstance().executeOnPooledThread(new Runnable() {
- @Override
- public void run() {
- try {
- context.channel()
- .writeAndFlush(compile(context, sessionId, options, files, cp, platformCp, srcPath, outs, cancelHandler));
+ final CancelHandler cancelHandler = new CancelHandler();
+ myCancelHandler = cancelHandler;
+ SharedThreadPool.getInstance().executeOnPooledThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ context.channel().writeAndFlush(
+ compile(context, sessionId, options, files, cp, platformCp, srcPath, outs, cancelHandler)
+ ).awaitUninterruptibly();
+ }
+ finally {
+ myCancelHandler = null;
+ ExternalJavacProcess.this.stop();
+ }
}
- finally {
- myCancelHandlers.remove(cancelHandler);
- }
- }
- });
+ });
+ }
}
else if (requestType == JavacRemoteProto.Message.Request.Type.CANCEL){
- cancelBuilds();
- reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createRequestAckResponse());
+ cancelBuild();
}
else if (requestType == JavacRemoteProto.Message.Request.Type.SHUTDOWN){
- cancelBuilds();
+ cancelBuild();
new Thread("StopThread") {
@Override
public void run() {
//noinspection finally
- try {
- JavacServer.this.stop();
- }
- finally {
- System.exit(0);
- }
+ ExternalJavacProcess.this.stop();
}
}.start();
}
@@ -274,46 +286,38 @@ public class JavacServer {
}
}
}
-
- @ChannelHandler.Sharable
- private static final class ChannelRegistrar extends ChannelInboundHandlerAdapter {
- private final ChannelGroup openChannels = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);
-
- public boolean isEmpty() {
- return openChannels.isEmpty();
+
+ public void stop() {
+ try {
+ //final long stopStart = System.currentTimeMillis();
+ //System.err.println("Exiting. Since global start " + (stopStart - myGlobalStart));
+ final ChannelFuture future = myConnectFuture;
+ if (future != null) {
+ future.channel().close().await();
+ }
+ myEventLoopGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS).await();
+ //final long stopEnd = System.currentTimeMillis();
+ //System.err.println("Stop completed in " + (stopEnd - stopStart) + "ms; since global start: " + ((stopEnd - myGlobalStart)));
+ System.exit(0);
}
-
- public void add(@NotNull Channel serverChannel) {
- assert serverChannel instanceof ServerChannel;
- openChannels.add(serverChannel);
+ catch (Throwable e) {
+ e.printStackTrace(System.err);
+ System.exit(-1);
}
+ }
- @Override
- public void channelActive(ChannelHandlerContext context) throws Exception {
- // we don't need to remove channel on close - ChannelGroup do it
- openChannels.add(context.channel());
-
- super.channelActive(context);
+ private static List<File> toFiles(List<String> paths) {
+ final List<File> files = new ArrayList<File>(paths.size());
+ for (String path : paths) {
+ files.add(new File(path));
}
-
- public ChannelGroupFuture close() {
- EventLoopGroup eventLoopGroup = null;
- for (Channel channel : openChannels) {
- if (channel instanceof ServerChannel) {
- eventLoopGroup = channel.eventLoop().parent();
- break;
- }
- }
-
- ChannelGroupFuture future;
- try {
- future = openChannels.close();
- }
- finally {
- assert eventLoopGroup != null;
- eventLoopGroup.shutdownGracefully();
- }
- return future;
+ return files;
+ }
+
+ public void cancelBuild() {
+ final CancelHandler cancelHandler = myCancelHandler;
+ if (cancelHandler != null) {
+ cancelHandler.cancel();
}
}
@@ -332,4 +336,4 @@ public class JavacServer {
return myIsCanceled;
}
}
-} \ No newline at end of file
+}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java
new file mode 100644
index 000000000000..f5ff5b175bd8
--- /dev/null
+++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2000-2014 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.javac;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.concurrency.Semaphore;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.*;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.ChannelGroupFuture;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.protobuf.ProtobufDecoder;
+import io.netty.handler.codec.protobuf.ProtobufEncoder;
+import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
+import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
+import io.netty.util.AttributeKey;
+import io.netty.util.concurrent.ImmediateEventExecutor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.builders.java.JavaCompilingTool;
+import org.jetbrains.jps.incremental.CompileContext;
+import org.jetbrains.jps.incremental.GlobalContextKey;
+import org.jetbrains.jps.incremental.Utils;
+import org.jetbrains.jps.model.JpsProject;
+import org.jetbrains.jps.model.java.JpsJavaExtensionService;
+import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration;
+import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions;
+import org.jetbrains.jps.service.SharedThreadPool;
+
+import javax.tools.*;
+import java.io.File;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: 1/22/12
+ */
+@SuppressWarnings("UseOfSystemOutOrSystemErr")
+public class ExternalJavacServer {
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.javac.ExternalJavacServer");
+ public static final GlobalContextKey<ExternalJavacServer> KEY = GlobalContextKey.create("_external_javac_server_");
+
+ public static final int DEFAULT_SERVER_PORT = 7878;
+ private static final AttributeKey<JavacProcessDescriptor> SESSION_DESCRIPTOR = AttributeKey.valueOf("ExternalJavacServer.JavacProcessDescriptor");
+
+ private ChannelRegistrar myChannelRegistrar;
+ private final Map<UUID, JavacProcessDescriptor> myMessageHandlers = new HashMap<UUID, JavacProcessDescriptor>();
+ private int myListenPort = DEFAULT_SERVER_PORT;
+
+ 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
+ protected void initChannel(Channel channel) throws Exception {
+ channel.pipeline().addLast(myChannelRegistrar,
+ new ProtobufVarint32FrameDecoder(),
+ new ProtobufDecoder(JavacRemoteProto.Message.getDefaultInstance()),
+ new ProtobufVarint32LengthFieldPrepender(),
+ new ProtobufEncoder(),
+ compilationRequestsHandler);
+ }
+ });
+ myChannelRegistrar.add(bootstrap.bind(listenPort).syncUninterruptibly().channel());
+ myListenPort = listenPort;
+ }
+
+ private static int getExternalJavacHeapSize(CompileContext context) {
+ final JpsProject project = context.getProjectDescriptor().getProject();
+ final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project);
+ final JpsJavaCompilerOptions options = config.getCurrentCompilerOptions();
+ return options.MAXIMUM_HEAP_SIZE;
+ }
+
+
+ public boolean forkJavac(CompileContext context, List<String> options,
+ List<String> vmOptions, Collection<File> files,
+ Collection<File> classpath,
+ Collection<File> platformCp,
+ Collection<File> sourcePath,
+ Map<File, Set<File>> outs,
+ DiagnosticOutputConsumer diagnosticSink,
+ OutputFileConsumer outputSink,
+ final String javaHome, final JavaCompilingTool compilingTool) {
+ final ExternalJavacMessageHandler rh = new ExternalJavacMessageHandler(diagnosticSink, outputSink, getEncodingName(options));
+ final JavacRemoteProto.Message.Request request = JavacProtoUtil.createCompilationRequest(options, files, classpath, platformCp, sourcePath, outs);
+ final UUID uuid = UUID.randomUUID();
+ final JavacProcessDescriptor processDescriptor = new JavacProcessDescriptor(uuid, rh, request);
+ synchronized (myMessageHandlers) {
+ myMessageHandlers.put(uuid, processDescriptor);
+ }
+ try {
+ final JavacServerBootstrap.ExternalJavacProcessHandler processHandler = JavacServerBootstrap.launchExternalJavacProcess(
+ uuid, javaHome, getExternalJavacHeapSize(context), myListenPort, Utils.getSystemRoot(), vmOptions, compilingTool
+ );
+
+ while (!processDescriptor.waitFor(300L)) {
+ if (processHandler.isProcessTerminated() && processDescriptor.channel == null && processHandler.getExitCode() != 0) {
+ // process terminated abnormally and no communication took place
+ processDescriptor.setDone();
+ break;
+ }
+ if (context.getCancelStatus().isCanceled()) {
+ processDescriptor.cancelBuild();
+ }
+ }
+
+ return rh.isTerminatedSuccessfully();
+ }
+ catch (Throwable e) {
+ LOG.info(e);
+ diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage()));
+ }
+ finally {
+ unregisterMessageHandler(uuid);
+ }
+ return false;
+ }
+
+ private void unregisterMessageHandler(UUID uuid) {
+ final JavacProcessDescriptor descriptor;
+ synchronized (myMessageHandlers) {
+ descriptor = myMessageHandlers.remove(uuid);
+ }
+ if (descriptor != null) {
+ descriptor.setDone();
+ }
+ }
+
+ @Nullable
+ private static String getEncodingName(List<String> options) {
+ boolean found = false;
+ for (String option : options) {
+ if (found) {
+ return option;
+ }
+ if ("-encoding".equalsIgnoreCase(option)) {
+ found = true;
+ }
+ }
+ return null;
+ }
+
+ public void stop() {
+ myChannelRegistrar.close().awaitUninterruptibly();
+ }
+
+ @ChannelHandler.Sharable
+ private class CompilationRequestsHandler extends SimpleChannelInboundHandler<JavacRemoteProto.Message> {
+ @Override
+ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
+ JavacProcessDescriptor descriptor = ctx.attr(SESSION_DESCRIPTOR).get();
+ if (descriptor != null) {
+ descriptor.setDone();
+ }
+ super.channelUnregistered(ctx);
+ }
+
+ @Override
+ public void channelRead0(final ChannelHandlerContext context, JavacRemoteProto.Message message) throws Exception {
+ JavacProcessDescriptor descriptor = context.attr(SESSION_DESCRIPTOR).get();
+
+ UUID sessionId;
+ if (descriptor == null) {
+ // this is the first message for this session, so fill session data with missing info
+ sessionId = JavacProtoUtil.fromProtoUUID(message.getSessionId());
+
+ descriptor = myMessageHandlers.get(sessionId);
+ if (descriptor != null) {
+ descriptor.channel = context.channel();
+ context.attr(SESSION_DESCRIPTOR).set(descriptor);
+ }
+ }
+ else {
+ sessionId = descriptor.sessionId;
+ }
+
+ final ExternalJavacMessageHandler handler = descriptor != null? descriptor.handler : null;
+
+ final JavacRemoteProto.Message.Type messageType = message.getMessageType();
+
+ JavacRemoteProto.Message reply = null;
+ try {
+ if (messageType == JavacRemoteProto.Message.Type.RESPONSE) {
+ final JavacRemoteProto.Message.Response response = message.getResponse();
+ final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType();
+ if (handler != null) {
+ if (responseType == JavacRemoteProto.Message.Response.Type.REQUEST_ACK) {
+ final JavacRemoteProto.Message.Request request = descriptor.request;
+ if (request != null) {
+ reply = JavacProtoUtil.toMessage(sessionId, request);
+ descriptor.request = null;
+ }
+ }
+ else {
+ final boolean terminateOk = handler.handleMessage(message);
+ if (terminateOk) {
+ descriptor.setDone();
+ }
+ }
+ }
+ else {
+ reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createCancelRequest());
+ }
+ }
+ else {
+ reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure("Unsupported message: " + messageType.name(), null));
+ }
+ }
+ finally {
+ if (reply != null) {
+ context.channel().writeAndFlush(reply);
+ }
+ }
+ }
+ }
+
+ @ChannelHandler.Sharable
+ private static final class ChannelRegistrar extends ChannelInboundHandlerAdapter {
+ private final ChannelGroup openChannels = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);
+
+ public boolean isEmpty() {
+ return openChannels.isEmpty();
+ }
+
+ public void add(@NotNull Channel serverChannel) {
+ assert serverChannel instanceof ServerChannel;
+ openChannels.add(serverChannel);
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext context) throws Exception {
+ // we don't need to remove channel on close - ChannelGroup do it
+ openChannels.add(context.channel());
+ super.channelActive(context);
+ }
+
+ public ChannelGroupFuture close() {
+ EventLoopGroup eventLoopGroup = null;
+ for (Channel channel : openChannels) {
+ if (channel instanceof ServerChannel) {
+ eventLoopGroup = channel.eventLoop().parent();
+ break;
+ }
+ }
+
+ ChannelGroupFuture future;
+ try {
+ future = openChannels.close();
+ }
+ finally {
+ assert eventLoopGroup != null;
+ eventLoopGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS);
+ }
+ return future;
+ }
+ }
+
+ private static class JavacProcessDescriptor {
+ @NotNull
+ final UUID sessionId;
+ @NotNull
+ final ExternalJavacMessageHandler handler;
+ volatile JavacRemoteProto.Message.Request request;
+ volatile Channel channel;
+ private final Semaphore myDone = new Semaphore();
+
+ public JavacProcessDescriptor(@NotNull UUID sessionId, @NotNull ExternalJavacMessageHandler handler, @NotNull JavacRemoteProto.Message.Request request) {
+ this.sessionId = sessionId;
+ this.handler = handler;
+ this.request = request;
+ myDone.down();
+ }
+
+ public void cancelBuild() {
+ if (channel != null) {
+ channel.writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createCancelRequest()));
+ }
+ }
+
+ public void setDone() {
+ myDone.up();
+ }
+
+
+ public boolean waitFor(long timeout) {
+ return myDone.waitFor(timeout);
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java
index bb66e9c6b09f..ecf53395eb53 100644
--- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java
+++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java
@@ -20,11 +20,9 @@ import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.concurrency.Semaphore;
import org.jetbrains.jps.builders.java.JavaCompilingTool;
import org.jetbrains.jps.cmdline.ClasspathBootstrap;
import org.jetbrains.jps.service.SharedThreadPool;
@@ -32,6 +30,7 @@ import org.jetbrains.jps.service.SharedThreadPool;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
import java.util.concurrent.Future;
/**
@@ -40,15 +39,15 @@ import java.util.concurrent.Future;
*/
public class JavacServerBootstrap {
- public static BaseOSProcessHandler launchJavacServer(String sdkHomePath,
- int heapSize,
- int port,
- File workingDir,
- List<String> vmOptions,
- JavaCompilingTool compilingTool) throws Exception {
+ public static ExternalJavacProcessHandler launchExternalJavacProcess(UUID uuid, String sdkHomePath,
+ int heapSize,
+ int port,
+ File workingDir,
+ List<String> vmOptions,
+ JavaCompilingTool compilingTool) throws Exception {
final List<String> cmdLine = new ArrayList<String>();
appendParam(cmdLine, getVMExecutablePath(sdkHomePath));
- appendParam(cmdLine, "-XX:MaxPermSize=150m");
+ //appendParam(cmdLine, "-XX:MaxPermSize=150m");
//appendParam(cmdLine, "-XX:ReservedCodeCacheSize=64m");
appendParam(cmdLine, "-Djava.awt.headless=true");
final int xms = heapSize / 2;
@@ -59,7 +58,7 @@ public class JavacServerBootstrap {
// debugging
//appendParam(cmdLine, "-XX:+HeapDumpOnOutOfMemoryError");
- //appendParam(cmdLine, "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5009");
+ //appendParam(cmdLine, "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5009");
// javac's VM should use the same default locale that IDEA uses in order for javac to print messages in 'correct' language
final String encoding = System.getProperty("file.encoding");
@@ -83,18 +82,20 @@ public class JavacServerBootstrap {
appendParam(cmdLine, "-Duser.region=" + region);
}
- appendParam(cmdLine, "-D" + JavacServer.JPS_JAVA_COMPILING_TOOL_PROPERTY + "=" + compilingTool.getId());
+ appendParam(cmdLine, "-D" + ExternalJavacProcess.JPS_JAVA_COMPILING_TOOL_PROPERTY + "=" + compilingTool.getId());
// this will disable standard extensions to ensure javac is loaded from the right tools.jar
appendParam(cmdLine, "-Djava.ext.dirs=");
-
+
+ appendParam(cmdLine, "-Dlog4j.defaultInitOverride=true");
+
for (String option : vmOptions) {
appendParam(cmdLine, option);
}
appendParam(cmdLine, "-classpath");
- final List<File> cp = ClasspathBootstrap.getJavacServerClasspath(sdkHomePath, compilingTool);
+ final List<File> cp = ClasspathBootstrap.getExternalJavacProcessClasspath(sdkHomePath, compilingTool);
final StringBuilder classpath = new StringBuilder();
for (File file : cp) {
if (classpath.length() > 0) {
@@ -104,7 +105,9 @@ public class JavacServerBootstrap {
}
appendParam(cmdLine, classpath.toString());
- appendParam(cmdLine, org.jetbrains.jps.javac.JavacServer.class.getName());
+ appendParam(cmdLine, org.jetbrains.jps.javac.ExternalJavacProcess.class.getName());
+ appendParam(cmdLine, uuid.toString());
+ appendParam(cmdLine, "127.0.0.1");
appendParam(cmdLine, Integer.toString(port));
workingDir.mkdirs();
@@ -115,68 +118,31 @@ public class JavacServerBootstrap {
builder.directory(workingDir);
final Process process = builder.start();
- final BaseOSProcessHandler processHandler = new BaseOSProcessHandler(process, null, null) {
+ final ExternalJavacProcessHandler processHandler = new ExternalJavacProcessHandler(process);
+ processHandler.addProcessListener(new ProcessAdapter() {
@Override
- protected Future<?> executeOnPooledThread(Runnable task) {
- return SharedThreadPool.getInstance().executeOnPooledThread(task);
+ public void processTerminated(ProcessEvent event) {
+ processHandler.setExitCode(event.getExitCode());
}
- };
- configureProcessHandler(processHandler);
-
- return processHandler;
- }
- private static void configureProcessHandler(final BaseOSProcessHandler processHandler) throws Exception {
- processHandler.addProcessListener(new ProcessAdapter() {
public void onTextAvailable(ProcessEvent event, Key outputType) {
final String text = event.getText();
if (!StringUtil.isEmptyOrSpaces(text)) {
if (outputType == ProcessOutputTypes.STDOUT) {
- System.out.print("JAVAC_SERVER: " + text);
- }
- else if (outputType == ProcessOutputTypes.STDERR){
- System.err.print("JAVAC_SERVER: " + text);
+ System.out.print("JAVAC_PROCESS: " + text);
}
- }
- }
- });
- final Semaphore semaphore = new Semaphore();
- semaphore.down();
- final Ref<String> serverStartMessage = new Ref<String>(null);
- processHandler.addProcessListener(new ProcessAdapter() {
- public void processTerminated(ProcessEvent event) {
- try {
- processHandler.removeProcessListener(this);
- }
- finally {
- semaphore.up();
- }
- }
-
- public void onTextAvailable(ProcessEvent event, Key outputType) {
- if (outputType == ProcessOutputTypes.STDERR) {
- final String text = event.getText();
- if (text != null && (text.contains(JavacServer.SERVER_SUCCESS_START_MESSAGE) || text.contains(JavacServer.SERVER_ERROR_START_MESSAGE))) {
- try {
- processHandler.removeProcessListener(this);
- serverStartMessage.set(text);
- }
- finally {
- semaphore.up();
- }
+ else if (outputType == ProcessOutputTypes.STDERR) {
+ System.err.print("JAVAC_PROCESS: " + text);
}
}
}
});
processHandler.startNotify();
- semaphore.waitFor();
-
- final String startupMsg = serverStartMessage.get();
- if (startupMsg == null || !startupMsg.contains(JavacServer.SERVER_SUCCESS_START_MESSAGE)) {
- throw new Exception("Server startup failed: " + startupMsg);
- }
+ return processHandler;
}
+
+
private static void appendParam(List<String> cmdLine, String param) {
if (SystemInfo.isWindows) {
if (param.contains("\"")) {
@@ -192,4 +158,25 @@ public class JavacServerBootstrap {
public static String getVMExecutablePath(String sdkHome) {
return sdkHome + "/bin/java";
}
+
+ public static class ExternalJavacProcessHandler extends BaseOSProcessHandler {
+ private volatile int myExitCode;
+
+ ExternalJavacProcessHandler(Process process) {
+ super(process, null, null);
+ }
+
+ @Override
+ protected Future<?> executeOnPooledThread(Runnable task) {
+ return SharedThreadPool.getInstance().executeOnPooledThread(task);
+ }
+
+ void setExitCode(int exitCode) {
+ myExitCode = exitCode;
+ }
+
+ public int getExitCode() {
+ return myExitCode;
+ }
+ }
}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java
deleted file mode 100644
index 8085068b56e6..000000000000
--- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2000-2012 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.javac;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.jps.api.RequestFuture;
-import org.jetbrains.jps.client.SimpleProtobufClient;
-import org.jetbrains.jps.client.UUIDGetter;
-import org.jetbrains.jps.service.SharedThreadPool;
-
-import java.io.File;
-import java.util.*;
-
-/**
- * @author Eugene Zhuravlev
- * Date: 1/22/12
- */
-public class JavacServerClient extends SimpleProtobufClient<JavacServerResponseHandler>{
-
- public JavacServerClient() {
- super(JavacRemoteProto.Message.getDefaultInstance(), SharedThreadPool.getInstance(), new UUIDGetter() {
- @Override
- @NotNull
- public UUID getSessionUUID(@NotNull JavacRemoteProto.Message message) {
- final JavacRemoteProto.Message.UUID uuid = message.getSessionId();
- return new UUID(uuid.getMostSigBits(), uuid.getLeastSigBits());
- }
- });
- }
-
- public RequestFuture<JavacServerResponseHandler> sendCompileRequest(List<String> options, Collection<File> files, Collection<File> classpath, Collection<File> platformCp, Collection<File> sourcePath, Map<File, Set<File>> outs, DiagnosticOutputConsumer diagnosticSink, OutputFileConsumer outputSink) {
- final JavacServerResponseHandler rh = new JavacServerResponseHandler(diagnosticSink, outputSink, getEncodingName(options));
- final JavacRemoteProto.Message.Request request = JavacProtoUtil.createCompilationRequest(options, files, classpath, platformCp, sourcePath, outs);
- return sendRequest(request, rh, new RequestFuture.CancelAction<JavacServerResponseHandler>() {
- @Override
- public void cancel(RequestFuture<JavacServerResponseHandler> javacServerResponseHandlerRequestFuture) throws Exception {
- sendRequest(JavacProtoUtil.createCancelRequest(), null, null);
- }
- });
- }
-
- public RequestFuture sendShutdownRequest() {
- return sendRequest(JavacProtoUtil.createShutdownRequest(), null, null);
- }
-
- private RequestFuture<JavacServerResponseHandler> sendRequest(final JavacRemoteProto.Message.Request request, final JavacServerResponseHandler responseHandler, final RequestFuture.CancelAction<JavacServerResponseHandler> cancelAction) {
- final UUID requestId = UUID.randomUUID();
- return sendMessage(requestId, JavacProtoUtil.toMessage(requestId, request), responseHandler, cancelAction);
- }
-
- @Nullable
- private static String getEncodingName(List<String> options) {
- boolean found = false;
- for (String option : options) {
- if (found) {
- return option;
- }
- if ("-encoding".equalsIgnoreCase(option)) {
- found = true;
- }
- }
- return null;
- }
-}
diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java
deleted file mode 100644
index 688bf0596ac4..000000000000
--- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright 2000-2012 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.javac;
-
-import com.google.protobuf.ByteString;
-import com.google.protobuf.MessageLite;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.jps.client.ProtobufResponseHandler;
-import org.jetbrains.jps.incremental.BinaryContent;
-
-import javax.tools.Diagnostic;
-import javax.tools.JavaFileObject;
-import java.io.File;
-import java.net.URI;
-import java.util.Collection;
-import java.util.Locale;
-
-/**
- * @author Eugene Zhuravlev
- * Date: 1/22/12
- */
-public class JavacServerResponseHandler implements ProtobufResponseHandler{
- private final DiagnosticOutputConsumer myDiagnosticSink;
- private final OutputFileConsumer myOutputSink;
- @Nullable
- private final String myEncodingName;
- private volatile boolean myTerminatedSuccessfully;
-
- public JavacServerResponseHandler(DiagnosticOutputConsumer diagnosticSink, OutputFileConsumer outputSink, @Nullable final String encodingName) {
- myDiagnosticSink = diagnosticSink;
- myOutputSink = outputSink;
- myEncodingName = encodingName;
- }
-
- public boolean handleMessage(MessageLite message) throws Exception {
- final JavacRemoteProto.Message msg = (JavacRemoteProto.Message)message;
- final JavacRemoteProto.Message.Type messageType = msg.getMessageType();
-
- if (messageType == JavacRemoteProto.Message.Type.RESPONSE) {
- final JavacRemoteProto.Message.Response response = msg.getResponse();
- final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType();
-
- if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_MESSAGE) {
- final JavacRemoteProto.Message.Response.CompileMessage compileMessage = response.getCompileMessage();
- final JavacRemoteProto.Message.Response.CompileMessage.Kind messageKind = compileMessage.getKind();
-
- if (messageKind == JavacRemoteProto.Message.Response.CompileMessage.Kind.STD_OUT) {
- if (compileMessage.hasText()) {
- myDiagnosticSink.outputLineAvailable(compileMessage.getText());
- }
- }
- else {
- final String sourceUri = compileMessage.hasSourceUri()? compileMessage.getSourceUri() : null;
- final JavaFileObject srcFileObject = sourceUri != null? new DummyJavaFileObject(URI.create(sourceUri)) : null;
- myDiagnosticSink.report(new DummyDiagnostic(convertKind(messageKind), srcFileObject, compileMessage));
- }
-
- return false;
- }
-
- if (responseType == JavacRemoteProto.Message.Response.Type.OUTPUT_OBJECT) {
- final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject();
- final JavacRemoteProto.Message.Response.OutputObject.Kind kind = outputObject.getKind();
-
- final String outputRoot = outputObject.hasOutputRoot()? outputObject.getOutputRoot() : null;
- final File outputRootFile = outputRoot != null? new File(outputRoot) : null;
-
- final BinaryContent fileObjectContent;
- final ByteString content = outputObject.hasContent()? outputObject.getContent() : null;
- if (content != null) {
- final byte[] bytes = content.toByteArray();
- fileObjectContent = new BinaryContent(bytes, 0, bytes.length);
- }
- else {
- fileObjectContent = null;
- }
-
- final String sourceUri = outputObject.hasSourceUri()? outputObject.getSourceUri() : null;
- final URI srcUri = sourceUri != null? URI.create(sourceUri) : null;
- final OutputFileObject fileObject = new OutputFileObject(
- null,
- outputRootFile,
- outputObject.hasRelativePath()? outputObject.getRelativePath() : null,
- new File(outputObject.getFilePath()),
- convertKind(kind),
- outputObject.hasClassName()? outputObject.getClassName() : null,
- srcUri,
- myEncodingName, fileObjectContent
- );
-
- myOutputSink.save(fileObject);
- return false;
- }
-
- if (responseType == JavacRemoteProto.Message.Response.Type.SRC_FILE_LOADED) {
- final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject();
- final File file = new File(outputObject.getFilePath());
- myDiagnosticSink.javaFileLoaded(file);
- return false;
- }
-
- if (responseType == JavacRemoteProto.Message.Response.Type.CLASS_DATA) {
- final JavacRemoteProto.Message.Response.ClassData data = response.getClassData();
- final String className = data.getClassName();
- final Collection<String> imports = data.getImportStatementList();
- final Collection<String> staticImports = data.getStaticImportList();
- myDiagnosticSink.registerImports(className, imports, staticImports);
- return false;
- }
-
- if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_COMPLETED) {
- if (response.hasCompletionStatus()) {
- myTerminatedSuccessfully = response.getCompletionStatus();
- }
- return true;
- }
-
- if (responseType == JavacRemoteProto.Message.Response.Type.REQUEST_ACK) {
- return true;
- }
-
- throw new Exception("Unsupported response type: " + responseType.name());
- }
-
- if (messageType == JavacRemoteProto.Message.Type.FAILURE) {
- final JavacRemoteProto.Message.Failure failure = msg.getFailure();
- final StringBuilder buf = new StringBuilder();
- if (failure.hasDescription()) {
- buf.append(failure.getDescription());
- }
- if (failure.hasStacktrace()) {
- if (buf.length() > 0) {
- buf.append("\n");
- }
- buf.append(failure.getStacktrace());
- }
- myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, buf.toString()));
- return true;
- }
-
- throw new Exception("Unsupported message type: " + messageType.name());
- }
-
- public boolean isTerminatedSuccessfully() {
- return myTerminatedSuccessfully;
- }
-
- private static Diagnostic.Kind convertKind(JavacRemoteProto.Message.Response.CompileMessage.Kind kind) {
- switch (kind) {
- case ERROR: return Diagnostic.Kind.ERROR;
- case WARNING: return Diagnostic.Kind.WARNING;
- case MANDATORY_WARNING: return Diagnostic.Kind.MANDATORY_WARNING;
- case NOTE: return Diagnostic.Kind.NOTE;
- default : return Diagnostic.Kind.OTHER;
- }
- }
-
- private static OutputFileObject.Kind convertKind(JavacRemoteProto.Message.Response.OutputObject.Kind kind) {
- switch (kind) {
- case CLASS: return JavaFileObject.Kind.CLASS;
- case HTML: return JavaFileObject.Kind.HTML;
- case SOURCE: return JavaFileObject.Kind.SOURCE;
- default : return JavaFileObject.Kind.OTHER;
- }
- }
-
- public void sessionTerminated() {
- }
-
- private static class DummyDiagnostic implements Diagnostic<JavaFileObject> {
-
- private final Kind myMessageKind;
- private final JavaFileObject mySrcFileObject;
- private final JavacRemoteProto.Message.Response.CompileMessage myCompileMessage;
-
- public DummyDiagnostic(final Kind messageKind, JavaFileObject srcFileObject, JavacRemoteProto.Message.Response.CompileMessage compileMessage) {
- myMessageKind = messageKind;
- mySrcFileObject = srcFileObject;
- myCompileMessage = compileMessage;
- }
-
- public Kind getKind() {
- return myMessageKind;
- }
-
- public JavaFileObject getSource() {
- return mySrcFileObject;
- }
-
- public long getPosition() {
- return myCompileMessage.hasProblemLocationOffset()? myCompileMessage.getProblemLocationOffset() : -1;
- }
-
- public long getStartPosition() {
- return myCompileMessage.hasProblemBeginOffset()? myCompileMessage.getProblemBeginOffset() : -1;
- }
-
- public long getEndPosition() {
- return myCompileMessage.hasProblemEndOffset()? myCompileMessage.getProblemEndOffset() : -1;
- }
-
- public long getLineNumber() {
- return myCompileMessage.hasLine()? myCompileMessage.getLine() : -1;
- }
-
- public long getColumnNumber() {
- return myCompileMessage.hasColumn()? myCompileMessage.getColumn() : -1;
- }
-
- public String getCode() {
- return null;
- }
-
- public String getMessage(Locale locale) {
- return myCompileMessage.hasText()? myCompileMessage.getText() : null;
- }
- }
-}