shutdown server if IDE crashed

This commit is contained in:
Eugene Zhuravlev
2012-02-27 17:10:00 +04:00
parent 82440c760f
commit 1cdd4a9e10
8 changed files with 108 additions and 0 deletions
@@ -35,6 +35,7 @@ message Message {
RELOAD_PROJECT_COMMAND = 4;
FS_EVENT = 5;
CANCEL_BUILD_COMMAND = 6;
PING = 7;
}
message CompilationRequest {
@@ -10,4 +10,6 @@ public interface GlobalOptions {
String USE_EXTERNAL_JAVAC_OPTION = "use.external.javac.process";
String HOSTNAME_OPTION = "localhost.name";
String VM_EXE_PATH_OPTION = "vm.executable.path";
long SERVER_PING_PERIOD = 2000L; // 2 sec
}
@@ -1019,6 +1019,7 @@ public final class JpsRemoteProto {
RELOAD_PROJECT_COMMAND(3, 4),
FS_EVENT(4, 5),
CANCEL_BUILD_COMMAND(5, 6),
PING(6, 7),
;
@@ -1032,6 +1033,7 @@ public final class JpsRemoteProto {
case 4: return RELOAD_PROJECT_COMMAND;
case 5: return FS_EVENT;
case 6: return CANCEL_BUILD_COMMAND;
case 7: return PING;
default: return null;
}
}
@@ -116,6 +116,10 @@ public class ProtoUtil {
return JpsRemoteProto.Message.Request.newBuilder().setRequestType(JpsRemoteProto.Message.Request.Type.FS_EVENT).setFsEvent(builder.build()).build();
}
public static JpsRemoteProto.Message.Request createPingRequest() {
return JpsRemoteProto.Message.Request.newBuilder().setRequestType(JpsRemoteProto.Message.Request.Type.PING).build();
}
public static JpsRemoteProto.Message.Request createSetupRequest(final Map<String, String> pathVars, List<GlobalLibrary> sdkAndLibs, @Nullable String globalEncoding) {
final JpsRemoteProto.Message.Request.SetupCommand.Builder cmdBuilder = JpsRemoteProto.Message.Request.SetupCommand.newBuilder();
@@ -1,17 +1,23 @@
package org.jetbrains.jps.client;
import com.intellij.util.ConcurrencyUtil;
import org.jboss.netty.channel.MessageEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.api.*;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author Eugene Zhuravlev
* Date: 8/11/11
*/
public class CompileServerClient extends SimpleProtobufClient<JpsServerResponseHandler> {
private static final ScheduledThreadPoolExecutor ourPingService = ConcurrencyUtil.newSingleScheduledThreadExecutor("Compile server ping thread", Thread.MIN_PRIORITY);
private volatile ScheduledFuture<?> myPingFuture;
public CompileServerClient() {
super(JpsRemoteProto.Message.getDefaultInstance(), new UUIDGetter() {
@@ -81,4 +87,22 @@ public class CompileServerClient extends SimpleProtobufClient<JpsServerResponseH
return sendMessage(sessionUUID, ProtoUtil.toMessage(sessionUUID, request), handler, cancelAction);
}
@Override
protected void onConnect() {
myPingFuture = ourPingService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
sendRequest(ProtoUtil.createPingRequest(), null);
}
}, GlobalOptions.SERVER_PING_PERIOD, GlobalOptions.SERVER_PING_PERIOD, TimeUnit.MILLISECONDS);
}
@Override
protected void onDisconnect() {
final ScheduledFuture<?> future = myPingFuture;
if (future != null) {
future.cancel(true);
myPingFuture = null;
}
}
}
@@ -1,6 +1,7 @@
package org.jetbrains.jps.client;
import com.google.protobuf.MessageLite;
import com.intellij.openapi.diagnostic.Logger;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
@@ -21,6 +22,7 @@ import java.util.concurrent.atomic.AtomicReference;
* Date: 1/22/12
*/
public class SimpleProtobufClient<T extends ProtobufResponseHandler> {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.client.SimpleProtobufClient");
private static enum State {
DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING
}
@@ -69,6 +71,12 @@ public class SimpleProtobufClient<T extends ProtobufResponseHandler> {
if (success) {
myConnectFuture = future;
try {
onConnect();
}
catch (Throwable e) {
LOG.error(e);
}
}
else {
final Throwable reason = future.getCause();
@@ -87,6 +95,11 @@ public class SimpleProtobufClient<T extends ProtobufResponseHandler> {
return true;
}
protected void onConnect() {
}
protected void onDisconnect() {
}
public final void disconnect() {
if (myState.compareAndSet(State.CONNECTED, State.DISCONNECTING)) {
try {
@@ -104,6 +117,12 @@ public class SimpleProtobufClient<T extends ProtobufResponseHandler> {
finally {
myConnectFuture = null;
myState.compareAndSet(State.DISCONNECTING, State.DISCONNECTED);
try {
onDisconnect();
}
catch (Throwable e) {
LOG.error(e);
}
}
}
}
@@ -2,6 +2,7 @@ package org.jetbrains.jps.server;
//import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.ConcurrencyUtil;
import org.apache.log4j.Level;
import org.apache.log4j.xml.DOMConfigurator;
import org.jboss.netty.bootstrap.ServerBootstrap;
@@ -24,6 +25,8 @@ import java.io.File;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author Eugene Zhuravlev
@@ -40,10 +43,13 @@ public class Server {
private final ChannelFactory myChannelFactory;
private final ChannelPipelineFactory myPipelineFactory;
private final ExecutorService myBuildsExecutor;
private volatile long myLastPingTime = -1L;
private final ScheduledExecutorService myScheduler;
public Server(File systemDir) {
Paths.getInstance().setSystemRoot(systemDir);
final ExecutorService threadPool = Executors.newCachedThreadPool();
myScheduler = ConcurrencyUtil.newSingleScheduledThreadExecutor("Client activity checker", Thread.MIN_PRIORITY);
myBuildsExecutor = Executors.newFixedThreadPool(MAX_SIMULTANEOUS_BUILD_SESSIONS);
myChannelFactory = new NioServerSocketChannelFactory(threadPool, threadPool, 1);
final ChannelRegistrar channelRegistrar = new ChannelRegistrar();
@@ -69,10 +75,53 @@ public class Server {
bootstrap.setOption("child.keepAlive", true);
final Channel serverChannel = bootstrap.bind(new InetSocketAddress(listenPort));
myAllOpenChannels.add(serverChannel);
startIdleMonitor();
}
private void startIdleMonitor() {
final long allowedIdlePeriod = 2 * GlobalOptions.SERVER_PING_PERIOD;
myScheduler.scheduleAtFixedRate(new Runnable() {
private long myStartTime;
@Override
public void run() {
final long now = System.currentTimeMillis();
final long lastPing = myLastPingTime;
if (lastPing > 0L) {
final long elapsed = now - lastPing;
if (elapsed > allowedIdlePeriod) {
doStop();
}
}
else {
final long start = myStartTime;
if (start > 0) {
final long elapsed = now - start;
if (elapsed > 5 * GlobalOptions.SERVER_PING_PERIOD) {
// no pings received since start
doStop();
}
}
else {
myStartTime = now;
}
}
}
private void doStop() {
try {
stop();
}
finally {
System.exit(0);
}
}
}, allowedIdlePeriod, allowedIdlePeriod, TimeUnit.MILLISECONDS);
}
public void stop() {
try {
myScheduler.shutdownNow();
myBuildsExecutor.shutdownNow();
final ChannelGroupFuture closeFuture = myAllOpenChannels.close();
closeFuture.awaitUninterruptibly();
@@ -82,6 +131,10 @@ public class Server {
}
}
public void pingReceived() {
myLastPingTime = System.currentTimeMillis();
}
public static void main(String[] args) {
try {
int port = DEFAULT_SERVER_PORT;
@@ -138,6 +138,9 @@ class ServerMessageHandler extends SimpleChannelHandler {
}
reply = ProtoUtil.toMessage(sessionId, ProtoUtil.createCommandCompletedEvent(null));
break;
case PING:
myServer.pingReceived();
reply = ProtoUtil.toMessage(sessionId, ProtoUtil.createCommandCompletedEvent(null));
default:
reply = ProtoUtil.toMessage(sessionId, ProtoUtil.createFailure("Unknown request: " + message));
}