Cleanup: NotNull/Nullable
[idea/community.git] / java / execution / impl / src / com / intellij / execution / runners / ProcessProxyImpl.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.execution.runners;
3
4 import com.intellij.execution.process.BaseOSProcessHandler;
5 import com.intellij.execution.process.OSProcessUtil;
6 import com.intellij.execution.process.ProcessHandler;
7 import com.intellij.execution.process.UnixProcessManager;
8 import com.intellij.openapi.application.PathManager;
9 import com.intellij.openapi.diagnostic.Logger;
10 import com.intellij.openapi.util.Key;
11 import com.intellij.openapi.util.SystemInfo;
12 import com.intellij.util.ThrowableRunnable;
13 import org.jetbrains.annotations.NotNull;
14
15 import java.io.File;
16 import java.io.IOException;
17 import java.net.InetSocketAddress;
18 import java.net.StandardSocketOptions;
19 import java.nio.ByteBuffer;
20 import java.nio.channels.AsynchronousChannelGroup;
21 import java.nio.channels.AsynchronousServerSocketChannel;
22 import java.nio.channels.AsynchronousSocketChannel;
23 import java.nio.channels.CompletionHandler;
24 import java.nio.charset.StandardCharsets;
25 import java.util.concurrent.TimeUnit;
26
27 /**
28  * @author ven
29  */
30 class ProcessProxyImpl implements ProcessProxy {
31   static final Key<ProcessProxyImpl> KEY = Key.create("ProcessProxyImpl");
32   private static final File ourBreakgenHelper = SystemInfo.isWindows ? PathManager.findBinFile("breakgen.dll") : null;
33
34   private final AsynchronousChannelGroup myGroup;
35   private final int myPort;
36
37   private final Object myLock = new Object();
38   private AsynchronousSocketChannel myConnection;
39   private int myPid;
40
41   ProcessProxyImpl(String mainClass) throws IOException {
42     myGroup = AsynchronousChannelGroup.withFixedThreadPool(1, r -> new Thread(r, "Process Proxy: " + mainClass));
43     AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open(myGroup)
44       .bind(new InetSocketAddress("127.0.0.1", 0))
45       .setOption(StandardSocketOptions.SO_REUSEADDR, true);
46     myPort = ((InetSocketAddress)channel.getLocalAddress()).getPort();
47
48     channel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
49       @Override
50       public void completed(AsynchronousSocketChannel channel, Void attachment) {
51         synchronized (myLock) {
52           myConnection = channel;
53         }
54       }
55
56       @Override
57       public void failed(Throwable t, Void attachment) { }
58     });
59   }
60
61   int getPortNumber() {
62     return myPort;
63   }
64
65   @Override
66   public void attach(@NotNull ProcessHandler processHandler) {
67     processHandler.putUserData(KEY, this);
68     execute(() -> {
69       int pid = -1;
70       if (SystemInfo.isUnix && processHandler instanceof BaseOSProcessHandler) {
71         pid = OSProcessUtil.getProcessID(((BaseOSProcessHandler)processHandler).getProcess());
72       }
73       synchronized (myLock) {
74         myPid = pid;
75       }
76     });
77   }
78
79   private void writeLine(String s) {
80     execute(() -> {
81       ByteBuffer out = ByteBuffer.wrap((s + '\n').getBytes(StandardCharsets.US_ASCII));
82       synchronized (myLock) {
83         myConnection.write(out);
84       }
85     });
86   }
87
88   String getBinPath() {
89     if (SystemInfo.isWindows) {
90       if (ourBreakgenHelper != null) {
91         return ourBreakgenHelper.getParent();
92       }
93     }
94     return PathManager.getBinPath();
95   }
96
97   @Override
98   public boolean canSendBreak() {
99     if (SystemInfo.isWindows) {
100       synchronized (myLock) {
101         if (myConnection == null) return false;
102       }
103       return ourBreakgenHelper != null;
104     }
105
106     if (SystemInfo.isUnix) {
107       synchronized (myLock) {
108         return myPid > 0;
109       }
110     }
111
112     return false;
113   }
114
115   @Override
116   public boolean canSendStop() {
117     synchronized (myLock) {
118       return myConnection != null;
119     }
120   }
121
122   @Override
123   public void sendBreak() {
124     if (SystemInfo.isWindows) {
125       writeLine("BREAK");
126     }
127     else if (SystemInfo.isUnix) {
128       int pid;
129       synchronized (myLock) {
130         pid = myPid;
131       }
132       UnixProcessManager.sendSignal(pid, 3);  // SIGQUIT
133     }
134   }
135
136   @Override
137   public void sendStop() {
138     writeLine("STOP");
139   }
140
141   @Override
142   public void destroy() {
143     execute(() -> {
144       synchronized (myLock) {
145         if (myConnection != null) {
146           myConnection.close();
147         }
148       }
149     });
150     execute(() -> {
151       myGroup.shutdownNow();
152       myGroup.awaitTermination(1, TimeUnit.SECONDS);
153     });
154   }
155
156   private static void execute(ThrowableRunnable<Exception> block) {
157     try {
158       block.run();
159     }
160     catch (Exception e) {
161       Logger.getInstance(ProcessProxy.class).warn(e);
162     }
163   }
164 }