/**
   * Gives a subclass a chance to handle an {@code opcode}. This implementation handles login,
   * redirection, relocation, and logout but does not handle session or channel messages.
   */
  protected void handleOpCode(byte opcode, MessageBuffer buf) {

    switch (opcode) {
      case SimpleSgsProtocol.LOGIN_SUCCESS:
        reconnectKey = buf.getBytes(buf.limit() - buf.position());
        newReconnectKey(reconnectKey);
        synchronized (lock) {
          loginAck = true;
          loginSuccess = true;
          System.err.println("login succeeded: " + name);
          lock.notifyAll();
        }
        sendMessage(new byte[0], true);
        break;

      case SimpleSgsProtocol.LOGIN_FAILURE:
        String failureReason = buf.getString();
        synchronized (lock) {
          loginAck = true;
          loginSuccess = false;
          System.err.println("login failed: " + name + ", reason:" + failureReason);
          lock.notifyAll();
        }
        break;

      case SimpleSgsProtocol.LOGIN_REDIRECT:
        redirectHost = buf.getString();
        redirectPort = buf.getInt();
        synchronized (lock) {
          loginAck = true;
          loginRedirect = true;
          System.err.println(
              "login redirected: " + name + ", host:" + redirectHost + ", port:" + redirectPort);
          lock.notifyAll();
        }
        break;

      case SimpleSgsProtocol.SUSPEND_MESSAGES:
        checkRelocateProtocolVersion();
        synchronized (lock) {
          if (suspendMessages) {
            break;
          }
          suspendMessages = true;
          if (waitForSuspendMessages) {
            waitForSuspendMessages = false;
            lock.notifyAll();
          } else {
            sendSuspendMessagesComplete();
          }
        }
        break;

      case SimpleSgsProtocol.RELOCATE_NOTIFICATION:
        checkRelocateProtocolVersion();
        relocateHost = buf.getString();
        relocatePort = buf.getInt();
        relocateKey = buf.getBytes(buf.limit() - buf.position());
        synchronized (lock) {
          relocateSession = true;
          System.err.println(
              "session to relocate: "
                  + name
                  + ", host:"
                  + relocateHost
                  + ", port:"
                  + relocatePort
                  + ", key:"
                  + HexDumper.toHexString(relocateKey));
          lock.notifyAll();
        }
        break;

      case SimpleSgsProtocol.RELOCATE_SUCCESS:
        checkRelocateProtocolVersion();
        reconnectKey = buf.getBytes(buf.limit() - buf.position());
        newReconnectKey(reconnectKey);
        synchronized (lock) {
          relocateAck = true;
          relocateSuccess = true;
          System.err.println("relocate succeeded: " + name);
          lock.notifyAll();
        }
        sendMessage(new byte[0], true);
        break;

      case SimpleSgsProtocol.RELOCATE_FAILURE:
        checkRelocateProtocolVersion();
        String relocateFailureReason = buf.getString();
        synchronized (lock) {
          relocateAck = true;
          relocateSuccess = false;
          System.err.println("relocate failed: " + name + ", reason:" + relocateFailureReason);
          lock.notifyAll();
        }
        break;

      case SimpleSgsProtocol.LOGOUT_SUCCESS:
        synchronized (lock) {
          logoutAck = true;
          System.err.println("logout succeeded: " + name);
          lock.notifyAll();
        }
        break;

      default:
        System.err.println(
            "WARNING: [" + name + "] dropping unknown op code: " + String.format("%02x", opcode));
        break;
    }
  }