package com.intellij.lang.javascript.flex.flexunit;
import com.intellij.execution.ExecutionException;
import com.intellij.lang.javascript.flex.FlexBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
public abstract class ServerConnectionBase {
public enum ConnectionStatus {
NOT_CONNECTED, WAITING_FOR_CONNECTION, CONNECTED, DISCONNECTED, CONNECTION_FAILED
}
private static final Logger LOG = Logger.getInstance(ServerConnectionBase.class.getName());
private final Object myLock = new Object();
private ConnectionStatus myStatus = ConnectionStatus.NOT_CONNECTED;
private boolean myStopped;
private ServerSocket myServerSocket;
private OutputStreamWriter myWriter;
private static final int ACCEPT_TIMEOUT = 250; // ms
public void open(int port) throws ExecutionException {
try {
myServerSocket = new ServerSocket(port);
myServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
}
catch (IOException e) {
throw new ExecutionException(FlexBundle.message("port.is.busy", port), e);
}
ApplicationManager.getApplication().executeOnPooledThread(() -> {
try {
doRun();
setStatus(ConnectionStatus.DISCONNECTED);
}
catch (IOException e) {
LOG.warn(e);
setStatus(ConnectionStatus.CONNECTION_FAILED);
}
});
}
public static int getFreePort(int defaultPort, int attempts) {
for (int i = 0; i < attempts; i++) {
int port = defaultPort + i;
if (tryPort(port)) return port;
}
return -1;
}
public static boolean tryPort(int port) {
ServerSocket socket;
try {
socket = new ServerSocket(port);
socket.close();
return true;
}
catch (IOException e) {
return false;
}
}
protected void setStatus(ConnectionStatus status) {
synchronized (myLock) {
myStatus = status;
}
}
public ConnectionStatus getStatus() {
synchronized (myLock) {
return myStatus;
}
}
public void write(String text) {
if (getStatus() != ConnectionStatus.CONNECTED || myWriter == null) {
return;
}
try {
myWriter.write(text);
myWriter.flush();
}
catch (IOException e) {
LOG.warn("Failed to write", e);
}
}
protected abstract void run(InputStream inputStream) throws IOException;
private void doRun() throws IOException {
try {
setStatus(ConnectionStatus.WAITING_FOR_CONNECTION);
LOG.debug("listening port " + myServerSocket.getLocalPort() + ", timeout: " + myServerSocket.getSoTimeout() + " ms");
Socket socket = null;
while (!isStopped() && socket == null) {
try {
socket = myServerSocket.accept();
}
catch (SocketTimeoutException e) {
//trace("timeout");
}
}
if (socket == null) {
return;
}
try {
setStatus(ConnectionStatus.CONNECTED);
LOG.debug("connected");
myWriter = new OutputStreamWriter(socket.getOutputStream());
run(socket.getInputStream());
}
finally {
LOG.debug("closing client socket");
socket.close();
myWriter.close();
myWriter = null;
}
}
finally {
myServerSocket.close();
}
}
public void close() {
LOG.debug("stopping");
synchronized (myLock) {
if (myStopped) return;
myStopped = true;
}
}
protected boolean isStopped() {
synchronized (myLock) {
return myStopped;
}
}
}