@Override
 public void close() throws IOException {
   synchronized (clients) {
     clients.remove(this);
   }
   super.close();
   if (handle != null) {
     handle.disconnect();
   }
 }
 @Override
 protected void handle(Message msg) throws IOException {
   try {
     if (msg instanceof Hello) {
       timeout.cancel();
       timeout = null;
       Hello hello = (Hello) msg;
       ChildProcAppHandle handle = pending.remove(hello.secret);
       if (handle != null) {
         handle.setState(SparkAppHandle.State.CONNECTED);
         handle.setConnection(this);
         this.handle = handle;
       } else {
         throw new IllegalArgumentException("Received Hello for unknown client.");
       }
     } else {
       if (handle == null) {
         throw new IllegalArgumentException(
             "Expected hello, got: " + msg != null ? msg.getClass().getName() : null);
       }
       if (msg instanceof SetAppId) {
         SetAppId set = (SetAppId) msg;
         handle.setAppId(set.appId);
       } else if (msg instanceof SetState) {
         handle.setState(((SetState) msg).state);
       } else {
         throw new IllegalArgumentException(
             "Invalid message: " + msg != null ? msg.getClass().getName() : null);
       }
     }
   } catch (Exception e) {
     LOG.log(Level.INFO, "Error handling message from client.", e);
     if (timeout != null) {
       timeout.cancel();
     }
     close();
   } finally {
     timeoutTimer.purge();
   }
 }
 /**
  * Removes the client handle from the pending list (in case it's still there), and unrefs the
  * server.
  */
 void unregister(ChildProcAppHandle handle) {
   pending.remove(handle.getSecret());
   unref();
 }