@Override
  public final void incomingError(Throwable e) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("incoming(WebSocketException)", e);
    }

    if (e instanceof CloseException) {
      CloseException close = (CloseException) e;
      terminateConnection(close.getStatusCode(), close.getMessage());
    }

    onError(e);
  }
  /** Open/Activate the session */
  public void open() {
    if (LOG_OPEN.isDebugEnabled())
      LOG_OPEN.debug("[{}] {}.open()", policy.getBehavior(), this.getClass().getSimpleName());

    if (remote != null) {
      // already opened
      return;
    }

    try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(classLoader)) {
      // Upgrade success
      connection.getIOState().onConnected();

      // Connect remote
      remote = new WebSocketRemoteEndpoint(connection, outgoingHandler, getBatchMode());
      if (LOG_OPEN.isDebugEnabled())
        LOG_OPEN.debug(
            "[{}] {}.open() remote={}",
            policy.getBehavior(),
            this.getClass().getSimpleName(),
            remote);

      // Open WebSocket
      websocket.openSession(this);

      // Open connection
      connection.getIOState().onOpened();

      if (LOG.isDebugEnabled()) {
        LOG.debug("open -> {}", dump());
      }
    } catch (CloseException ce) {
      LOG.warn(ce);
      close(ce.getStatusCode(), ce.getMessage());
    } catch (Throwable t) {
      LOG.warn(t);
      // Exception on end-user WS-Endpoint.
      // Fast-fail & close connection with reason.
      int statusCode = StatusCode.SERVER_ERROR;
      if (policy.getBehavior() == WebSocketBehavior.CLIENT) {
        statusCode = StatusCode.POLICY_VIOLATION;
      }
      close(statusCode, t.getMessage());
    }
  }
  @Override
  public void incomingFrame(Frame frame) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("{}.onFrame({})", websocket.getClass().getSimpleName(), frame);
    }

    try {
      onFrame(frame);

      byte opcode = frame.getOpCode();
      switch (opcode) {
        case OpCode.CLOSE:
          {
            boolean validate = true;
            CloseFrame closeframe = (CloseFrame) frame;
            CloseInfo close = new CloseInfo(closeframe, validate);

            // notify user websocket pojo
            onClose(close);

            // process handshake
            session.getConnection().getIOState().onCloseRemote(close);

            return;
          }
        case OpCode.PING:
          {
            if (LOG.isDebugEnabled()) {
              LOG.debug("PING: {}", BufferUtil.toDetailString(frame.getPayload()));
            }
            ByteBuffer pongBuf;
            if (frame.hasPayload()) {
              pongBuf = ByteBuffer.allocate(frame.getPayload().remaining());
              BufferUtil.put(frame.getPayload().slice(), pongBuf);
              BufferUtil.flipToFlush(pongBuf, 0);
            } else {
              pongBuf = ByteBuffer.allocate(0);
            }
            onPing(frame.getPayload());
            session.getRemote().sendPong(pongBuf);
            break;
          }
        case OpCode.PONG:
          {
            if (LOG.isDebugEnabled()) {
              LOG.debug("PONG: {}", BufferUtil.toDetailString(frame.getPayload()));
            }
            onPong(frame.getPayload());
            break;
          }
        case OpCode.BINARY:
          {
            onBinaryFrame(frame.getPayload(), frame.isFin());
            return;
          }
        case OpCode.TEXT:
          {
            onTextFrame(frame.getPayload(), frame.isFin());
            return;
          }
        case OpCode.CONTINUATION:
          {
            onContinuationFrame(frame.getPayload(), frame.isFin());
            return;
          }
        default:
          {
            LOG.debug("Unhandled OpCode: {}", opcode);
          }
      }
    } catch (NotUtf8Exception e) {
      terminateConnection(StatusCode.BAD_PAYLOAD, e.getMessage());
    } catch (CloseException e) {
      terminateConnection(e.getStatusCode(), e.getMessage());
    } catch (Throwable t) {
      unhandled(t);
    }
  }