title: How to properly close websocket session on server side when user reloads the web page and websocket session times out?
body: <p><code>java.lang.IllegalStateException</code> when user reloads the page. <code>onClose</code> method is called I get the exception that the socket has already been closed. Because of this, I cannot cleanup few things in a different Thread that stops when the web socket session closes. Everything is fine if user closes the session without reloading page (its basically a server side terminal over websocket). I am on tomcat8.</p>
  
  <p>Questions.</p>
  
  <ol>
  <li>How to properly close the websocket session on the server if user reloads the page?</li>
  <li>Also how do I do a few things when webshocket session times out?</li>
  </ol>
  
  <p>Following is the code that servers the websocket connection.</p>
  
  <pre><code>@ServerEndpoint(value = "/ws/{clientId}", configurator = HttpSessionConfigurator.class)
  public class WSSession {
      public static final long DEFAULT_TIMEOUT = 3600000;
      public static final int DEFAULT_TIMEOUT_IN_SECONDS = 3600;
  
      private Session wsSession;
      private HttpSession httpSession;
  
      private static final ConfigService configService;
  
      static {
          configService = ConfigServiceFactory.getConfigService(System
                  .getenv("ENVRIRONMENT"));
      }
  
      @OnOpen
      public void onOpen(@PathParam("clientId") String clientId, Session session,
              EndpointConfig config) throws IOException {
          Logger logger = Logger.getLogger(clientId);
          try {
              FileHandler fh = new FileHandler(configService.logDirectory()
                      .toString() + "/" + clientId);
              SimpleFormatter formatter = new SimpleFormatter();
              fh.setFormatter(formatter);
              logger.addHandler(fh);
              logger.setLevel(Level.INFO);
          } catch (SecurityException | IOException e1) {
              // TODO Auto-generated catch block
              e1.printStackTrace();
          }
          System.out.println("Got ID: " + clientId);
          // TODO: Get the max timeout from configuration
          session.setMaxIdleTimeout(DEFAULT_TIMEOUT);
          ConnectionDetails cd = configService.getConnectionDetails(clientId);
          this.wsSession = session;
          this.httpSession = (HttpSession) config.getUserProperties().get(
                  HttpSession.class.getName());
          if (this.httpSession.getAttribute(SessionProperties.WS_SESSION.value()) != null
                  &amp;&amp; this.httpSession.getAttribute(SessionProperties.WS_SESSION
                          .value()) != session) {
              Session wSession = (Session) this.httpSession
                      .getAttribute(SessionProperties.WS_SESSION.value());
              if (wSession.isOpen()) {
                  wSession.close();
              }
          }
          // TODO: Code changes for password based login
          // For now there is no password login.
          // TODO: For now the passphrase is NULL and strict checking is disabled
          this.wsSession = session;
          try {
              Connection connection = new Connection(cd.getHost(), cd.getPort());
              connection.connect();
              boolean authenticated = connection.authenticateWithPublicKey(
                      cd.getUserName(), cd.getPrivateKey().toCharArray(), null);
              if (authenticated) {
                  ch.ethz.ssh2.Session sshSession = connection.openSession();
                  sshSession.requestPTY("xterm", 120, 40, 14, 14, null);
                  OutputStream out = sshSession.getStdin();
                  InputStream in = new StreamGobbler(sshSession.getStdout());
                  sshSession.startShell();
                  Basic basicRemote = session.getBasicRemote();
                  Async asyncRemote = session.getAsyncRemote();
                  httpSession.setAttribute(SessionProperties.CLIENT_ID.value(),
                          clientId);
                  httpSession.setAttribute(SessionProperties.SSH_SESSION.value(),
                          sshSession);
                  httpSession.setAttribute(SessionProperties.IN_SSH.value(), out);
                  httpSession.setAttribute(SessionProperties.OUT_SSH.value(), in);
                  httpSession.setAttribute(
                          SessionProperties.BASIC_REMOTE.value(), basicRemote);
                  httpSession.setAttribute(SessionProperties.WS_SESSION.value(),
                          wsSession);
                  httpSession.setAttribute(
                          SessionProperties.ASYNC_REMOTE.value(), asyncRemote);
                  Thread worker = new Thread(new OutputTask(httpSession,
                          wsSession));
                  worker.start();
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  
      @OnMessage
      public void onMessage(String message, Session session) throws IOException {
          // IN_SSH is connection from this machine to remote machine.
          // OUT_SSH is connection from this remote machine to this machine.
          byte[] bytes = message.getBytes();
          OutputStream out = (OutputStream) httpSession
                  .getAttribute(SessionProperties.IN_SSH.value());
          out.write(bytes);
      }
  
      @OnClose
      public void onClose(Session session) throws IOException {
          if (session.isOpen()) {
              try {
                  session.close();
              } catch (IllegalStateException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          }
      }
  }
  </code></pre>
  
  <p>I have a thread that is runs while socket is open, but when user reloads I don't see <code>Exiting OutputTask</code> in the output - which get printed on normal socket close. Code for that thread.</p>
  
  <pre><code>@Override
  public void run() {
      InputStream stream = (StreamGobbler) session
              .getAttribute(SessionProperties.OUT_SSH.value());
      Basic basicRemote = (Basic) session
              .getAttribute(SessionProperties.BASIC_REMOTE.value());
      int read = 0;
      byte[] bt = new byte[1024];
      try {
          while (wsSession.isOpen() &amp;&amp; (read = stream.read(bt)) != -1) {
              logger.info(new String(bt));
              basicRemote.sendText(new String(Arrays.copyOf(bt, read)));
              // TODO: Add logging and finalize code
          }
      } catch (SecurityException | IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      } finally {
          try {
              Handler[] handlers = logger.getHandlers();
              for (Handler h : handlers) {
                  h.close();
              }
              logger = null;
              if (wsSession.isOpen()) {
                  wsSession.close();
              }
              if (stream != null) {
                  stream.close();
              }
              System.out.println("Exiting OutputTask");
              session.invalidate();
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
  }
  </code></pre>
  
  <p>Error when user reload the page.</p>
  
  <pre><code>java.lang.IllegalStateException: Message will not be sent because the WebSocket session has been closed
      at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.writeMessagePart(WsRemoteEndpointImplBase.java:378)
      at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessage(WsRemoteEndpointImplBase.java:335)
      at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:264)
      at org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:536)
      at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:464)
      at org.apache.tomcat.websocket.WsSession.close(WsSession.java:441)
      at org.apache.tomcat.websocket.WsSession.close(WsSession.java:435)
      at com.example.websocket.wss.onClose(WSSession.java:130)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at org.apache.tomcat.websocket.pojo.PojoEndpointBase.onClose(PojoEndpointBase.java:107)
      at org.apache.tomcat.websocket.WsSession.fireEndpointOnClose(WsSession.java:508)
      at org.apache.tomcat.websocket.WsSession.onClose(WsSession.java:491)
      at org.apache.tomcat.websocket.WsFrameBase.processDataControl(WsFrameBase.java:342)
      at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:284)
      at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:130)
      at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:60)
      at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:203)
      at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:194)
      at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:95)
      at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:653)
      at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
      at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1556)
      at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1513)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
      at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
      at java.lang.Thread.run(Thread.java:745)
  </code></pre>