[java] AppMain improvements: reversed socket direction to avoid port probing

This commit is contained in:
Roman Shevchenko
2016-10-27 12:30:38 +02:00
parent a558919cc8
commit 0305035b5a
3 changed files with 66 additions and 73 deletions
@@ -21,8 +21,11 @@ import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
import java.io.IOException;
public class ProcessProxyFactoryImpl extends ProcessProxyFactory {
private static final String DONT_USE_LAUNCHER_PROPERTY = "idea.no.launcher";
@@ -34,15 +37,19 @@ public class ProcessProxyFactoryImpl extends ProcessProxyFactory {
if (mainClass != null) {
try {
ProcessProxyImpl proxy = new ProcessProxyImpl();
JavaSdkUtil.addRtJar(javaParameters.getClassPath());
ParametersList vmParametersList = javaParameters.getVMParametersList();
vmParametersList.defineProperty(ProcessProxyImpl.PROPERTY_PORT_NUMBER, String.valueOf(proxy.getPortNumber()));
vmParametersList.defineProperty(ProcessProxyImpl.PROPERTY_BIN_PATH, PathManager.getBinPath());
javaParameters.getProgramParametersList().prepend(mainClass);
javaParameters.setMainClass(ProcessProxyImpl.LAUNCH_MAIN_CLASS);
return proxy;
}
catch (ProcessProxyImpl.NoMoreSocketsException ignored) { }
catch (IOException e) {
Logger.getInstance(ProcessProxy.class).warn(e);
}
}
}
@@ -17,14 +17,17 @@ package com.intellij.execution.runners;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.net.InetAddress;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author ven
@@ -36,66 +39,46 @@ class ProcessProxyImpl implements ProcessProxy {
public static final String PROPERTY_PORT_NUMBER = "idea.launcher.port";
public static final String LAUNCH_MAIN_CLASS = "com.intellij.rt.execution.application.AppMain";
private static final int SOCKET_NUMBER_START = 7532;
private static final int SOCKET_NUMBER = 100;
private static final boolean[] ourUsedSockets = new boolean[SOCKET_NUMBER];
private final ServerSocket mySocket;
private Writer myWriter;
private final int myPortNumber;
private PrintWriter myWriter;
private Socket mySocket;
public static class NoMoreSocketsException extends Exception { }
public ProcessProxyImpl() throws NoMoreSocketsException {
myPortNumber = findFreePort();
if (myPortNumber == -1) throw new NoMoreSocketsException();
}
private static int findFreePort() {
synchronized (ourUsedSockets) {
for (int j = 0; j < SOCKET_NUMBER; j++) {
if (ourUsedSockets[j]) continue;
try {
ServerSocket s = new ServerSocket(j + SOCKET_NUMBER_START);
s.close();
ourUsedSockets[j] = true;
return j + SOCKET_NUMBER_START;
}
catch (IOException ignore) { }
}
}
return -1;
public ProcessProxyImpl() throws IOException {
mySocket = new ServerSocket();
mySocket.bind(new InetSocketAddress("127.0.0.1", 0));
mySocket.setSoTimeout(10000);
}
@Override
public int getPortNumber() {
return myPortNumber;
return mySocket.getLocalPort();
}
@Override
public void attach(@NotNull ProcessHandler processHandler) {
public synchronized void attach(@NotNull ProcessHandler processHandler) {
processHandler.putUserData(KEY, this);
try {
//noinspection SocketOpenedButNotSafelyClosed
myWriter = new OutputStreamWriter(mySocket.accept().getOutputStream(), "US-ASCII");
}
catch (IOException e) {
Logger.getInstance(ProcessProxy.class).warn(e);
}
}
@SuppressWarnings({"SocketOpenedButNotSafelyClosed", "IOResourceOpenedButNotSafelyClosed"})
private synchronized void writeLine(String s) {
if (myWriter == null) {
try {
if (mySocket == null) {
mySocket = new Socket(InetAddress.getLoopbackAddress(), myPortNumber);
}
myWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mySocket.getOutputStream())));
}
catch (IOException e) {
return;
}
try {
myWriter.write(s);
myWriter.write('\n');
myWriter.flush();
}
catch (IOException e) {
Logger.getInstance(ProcessProxy.class).warn(e);
}
myWriter.println(s);
myWriter.flush();
}
@Override
public boolean canSendBreak() {
public synchronized boolean canSendBreak() {
if (myWriter == null) return false;
String libName = null;
if (SystemInfo.isWindows) libName = "breakgen.dll";
else if (SystemInfo.isMac) libName = "libbreakgen.jnilib";
@@ -103,6 +86,11 @@ class ProcessProxyImpl implements ProcessProxy {
return libName != null && new File(PathManager.getBinPath(), libName).exists();
}
@Override
public synchronized boolean canSendStop() {
return myWriter != null;
}
@Override
public void sendBreak() {
writeLine("BREAK");
@@ -115,9 +103,14 @@ class ProcessProxyImpl implements ProcessProxy {
@Override
public synchronized void destroy() {
if (myWriter != null) {
myWriter.close();
try {
if (myWriter != null) {
myWriter.close();
}
mySocket.close();
}
catch (IOException e) {
Logger.getInstance(ProcessProxy.class).warn(e);
}
ourUsedSockets[myPortNumber - SOCKET_NUMBER_START] = false;
}
}
@@ -21,7 +21,6 @@ import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Locale;
@@ -70,37 +69,31 @@ public class AppMain {
Thread t = new Thread("Monitor Ctrl-Break") {
public void run() {
try {
ServerSocket socket = new ServerSocket(portNumber);
Socket client = new Socket("127.0.0.1", portNumber);
try {
Socket client = socket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "US-ASCII"));
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
try {
while (true) {
String msg = reader.readLine();
if ("TERM".equals(msg)) {
return;
}
else if ("BREAK".equals(msg)) {
if (ourHelperLibLoaded) {
triggerControlBreak();
}
}
else if ("STOP".equals(msg)) {
System.exit(1);
while (true) {
String msg = reader.readLine();
if ("TERM".equals(msg)) {
return;
}
else if ("BREAK".equals(msg)) {
if (ourHelperLibLoaded) {
triggerControlBreak();
}
}
}
finally {
reader.close();
else if ("STOP".equals(msg)) {
System.exit(1);
}
}
}
finally {
client.close();
reader.close();
}
}
finally {
socket.close();
client.close();
}
}
catch (Exception ignored) { }