@Override
  public void run() {
    try {
      logger.info("Send OPN message to the PS");
      sendRequest(MessageFactory.getInstance().create(OPCode.OPN, new Properties()));

      // wait for CAT
      final COPSMsg recvMsg = readMessage();

      switch (recvMsg.getHeader().getOpCode()) {
        case CC:
          final COPSClientCloseMsg closeMsg = (COPSClientCloseMsg) recvMsg;
          logger.info("PS requested Client-Close" + closeMsg.getError().getDescription());
          // send a CC message and close the socket
          disconnect();
          break;
        case CAT:
          logger.info("received Client-Accept from PS");
          final COPSClientAcceptMsg acceptMsg = (COPSClientAcceptMsg) recvMsg;
          // Support
          if (acceptMsg.getIntegrity() != null) {
            throw new COPSPepException("Unsupported object (Integrity)");
          }

          // Mandatory KATimer
          final COPSKATimer kt = acceptMsg.getKATimer();
          if (kt == null) throw new COPSPepException("Mandatory COPS object missing (KA Timer)");
          final short kaTimeVal = kt.getTimerVal();

          // ACTimer
          final COPSAcctTimer at = acceptMsg.getAcctTimer();
          short acctTimer = 0;
          if (at != null) acctTimer = at.getTimerVal();

          logger.info("Send a REQ message to the PS");
          final Properties prop = new Properties();
          final COPSMsg reqMsg = MessageFactory.getInstance().create(OPCode.REQ, prop);
          final COPSHandle handle = ((COPSReqMsg) reqMsg).getClientHandle();
          sendRequest(reqMsg);

          // Create the connection manager
          final PcmmCmtsConnection conn = new PcmmCmtsConnection(CLIENT_TYPE, getSocket(), config);
          conn.addRequestState(handle, new CmtsDataProcessor());
          conn.setKaTimer(kaTimeVal);
          conn.setAcctTimer(acctTimer);

          logger.info(getClass().getName() + " Thread(conn).start");
          thread = new Thread(conn);
          thread.start();
          break;
        default:
          throw new COPSPepException(
              "Message not expected. Closing connection for " + getSocket().toString());
      }
    } catch (Exception e) {
      logger.error(e.getMessage());
    }
  }
  /**
   * Parses the data and fills COPSReqMsg with its constituents
   *
   * @param data a byte[]
   * @throws COPSException
   */
  protected void parse(byte[] data) throws COPSException {
    super.parseHeader(data);

    while (_dataStart < _dataLength) {
      byte[] buf = new byte[data.length - _dataStart];
      System.arraycopy(data, _dataStart, buf, 0, data.length - _dataStart);

      COPSObjHeader objHdr = new COPSObjHeader(buf);
      switch (objHdr.getCNum()) {
        case COPSObjHeader.COPS_HANDLE:
          {
            _clientHandle = new COPSHandle(buf);
            _dataStart += _clientHandle.getDataLength();
          }
          break;
        case COPSObjHeader.COPS_CONTEXT:
          {
            if (_context == null) {
              // Message context
              _context = new COPSContext(buf);
              _dataStart += _context.getDataLength();
            } else {
              // lpdp context
              _lpdpContext = new COPSContext(buf);
              _dataStart += _lpdpContext.getDataLength();
            }
          }
          break;
        case COPSObjHeader.COPS_ININTF:
          {
            if (objHdr.getCType() == 1) {
              _inInterface = new COPSIpv4InInterface(buf);
            } else {
              _inInterface = new COPSIpv6InInterface(buf);
            }
            _dataStart += _inInterface.getDataLength();
          }
          break;
        case COPSObjHeader.COPS_OUTINTF:
          {
            if (objHdr.getCType() == 1) {
              _outInterface = new COPSIpv4OutInterface(buf);
            } else {
              _outInterface = new COPSIpv6OutInterface(buf);
            }
            _dataStart += _outInterface.getDataLength();
          }
          break;
        case COPSObjHeader.COPS_LPDP_DEC:
          {
            COPSLPDPDecision lpdp = new COPSLPDPDecision(buf);
            _dataStart += lpdp.getDataLength();
            addLocalDecision(lpdp, _lpdpContext);
          }
          break;
        case COPSObjHeader.COPS_CSI:
          {
            COPSClientSI csi = new COPSClientSI(buf);
            _dataStart += csi.getDataLength();
            _clientSIs.add(csi);
          }
          break;
        case COPSObjHeader.COPS_MSG_INTEGRITY:
          {
            _integrity = new COPSIntegrity(buf);
            _dataStart += _integrity.getDataLength();
          }
          break;
        default:
          {
            throw new COPSException("Bad Message format, unknown object type");
          }
      }
    }
    checkSanity();
  }