/** {@inheritDoc} */
  public final void execute() throws InternetSCSIException {

    final ProtocolDataUnit protocolDataUnit =
        protocolDataUnitFactory.create(
            false,
            true,
            OperationCode.SCSI_COMMAND,
            connection.getSetting(OperationalTextKey.HEADER_DIGEST),
            connection.getSetting(OperationalTextKey.DATA_DIGEST));
    final SCSICommandParser scsi =
        (SCSICommandParser) protocolDataUnit.getBasicHeaderSegment().getParser();

    scsi.setReadExpectedFlag(false);
    scsi.setWriteExpectedFlag(true);
    scsi.setTaskAttributes(taskAttributes);

    scsi.setExpectedDataTransferLength(expectedDataTransferLength);

    final int maxRecvDataSegmentLength =
        connection.getSettingAsInt(OperationalTextKey.MAX_RECV_DATA_SEGMENT_LENGTH);
    scsi.setCommandDescriptorBlock(
        SCSICommandDescriptorBlockParser.createWriteMessage(logicalBlockAddress, transferLength));

    final IDataSegment dataSegment =
        DataSegmentFactory.create(
            buffer,
            bufferPosition,
            expectedDataTransferLength,
            DataSegmentFormat.BINARY,
            maxRecvDataSegmentLength);
    final IDataSegmentIterator iterator = dataSegment.iterator();
    int bufferOffset = 0;

    if (connection.getSettingAsBoolean(OperationalTextKey.IMMEDIATE_DATA)) {
      final int min =
          Math.min(
              maxRecvDataSegmentLength,
              connection.getSettingAsInt(OperationalTextKey.FIRST_BURST_LENGTH));
      protocolDataUnit.setDataSegment(iterator.next(min));
      bufferOffset += min;
    }

    connection.send(protocolDataUnit);

    if (!connection.getSettingAsBoolean(OperationalTextKey.INITIAL_R2T) && iterator.hasNext()) {
      connection.nextState(
          new WriteFirstBurstState(connection, iterator, 0xFFFFFFFF, 0, bufferOffset));
    } else {
      connection.nextState(new WriteSecondResponseState(connection, iterator, 0, bufferOffset));
    }
    super.stateFollowing = true;
    // return true;
  }
  @Override
  public void execute(ProtocolDataUnit pdu)
      throws IOException, InterruptedException, InternetSCSIException, DigestException,
          IllegalArgumentException, SettingsException {

    LOGGER.debug("Entering LOPN Stage");

    BasicHeaderSegment bhs = pdu.getBasicHeaderSegment();
    initiatorTaskTag = bhs.getInitiatorTaskTag();

    String keyValuePairProposal = receivePduSequence(pdu);

    // negotiate parameters, leave if unsuccessful
    final Vector<String> requestKeyValuePairs =
        TextParameter.tokenizeKeyValuePairs(keyValuePairProposal);
    final Vector<String> responseKeyValuePairs = new Vector<String>();
    if (!negotiator.negotiate(
        stageNumber,
        connection.isLeadingConnection(),
        ((TargetLoginPhase) targetPhase).getFirstPduAndSetToFalse(),
        requestKeyValuePairs,
        responseKeyValuePairs)) {
      // negotiation failure, no exception
      sendRejectPdu(LoginStatus.INITIATOR_ERROR);
      // nextStageNumber = null;//no change
      return;
    }

    // print request and response key value pairs if debugging
    if (LOGGER.isDebugEnabled()) {
      final StringBuilder sb = new StringBuilder();
      sb.append("request: ");
      for (String s : requestKeyValuePairs) {
        sb.append("\n  ");
        sb.append(s);
      }
      sb.append("\nresponse: ");
      for (String s : responseKeyValuePairs) {
        sb.append("\n  ");
        sb.append(s);
      }
      LOGGER.debug(sb.toString());
    }

    // make sure that initiator wants to proceed to FFP, leave if it does
    // not
    if (requestedNextStageNumber != LoginStage.FULL_FEATURE_PHASE) {
      sendRejectPdu(LoginStatus.INITIATOR_ERROR);
      throw new InternetSCSIException();
    }

    // concatenate key-value pairs to null char-separated string
    final String keyValuePairReply = TextParameter.concatenateKeyValuePairs(responseKeyValuePairs);

    // send reply, finish negotiation, and return successfully
    sendPduSequence(keyValuePairReply, LoginStage.FULL_FEATURE_PHASE);
    negotiator.finishNegotiation(true);
    nextStageNumber = LoginStage.FULL_FEATURE_PHASE;
  }
  @Override
  public void execute(ProtocolDataUnit pdu)
      throws IOException, InterruptedException, InternetSCSIException, DigestException,
          SettingsException {

    LOGGER.debug("Initiator has sent FORMAT UNIT command.");

    final BasicHeaderSegment bhs = pdu.getBasicHeaderSegment();
    final SCSICommandParser parser = (SCSICommandParser) bhs.getParser();

    ProtocolDataUnit responsePdu = null; // the response PDU

    // get command details in CDB
    final FormatUnitCDB cdb = new FormatUnitCDB(parser.getCDB());
    final FieldPointerSenseKeySpecificData[] illegalFieldPointers = cdb.getIllegalFieldPointers();

    if (illegalFieldPointers != null) {
      // an illegal request has been made

      FixedFormatSenseData senseData =
          new FixedFormatSenseData(
              false, // valid
              ErrorType.CURRENT, // error type
              false, // file mark
              false, // end of medium
              false, // incorrect length indicator
              SenseKey.ILLEGAL_REQUEST, // sense key
              new FourByteInformation(), // information
              new FourByteInformation(), // command specific information
              AdditionalSenseCodeAndQualifier.INVALID_FIELD_IN_CDB, // additional
              // sense
              // code
              // and
              // qualifier
              (byte) 0, // field replaceable unit code
              illegalFieldPointers[0], // sense key specific data, only
              // report first problem
              new AdditionalSenseBytes()); // additional sense bytes

      responsePdu =
          TargetPduFactory.createSCSIResponsePdu(
              false, // bidirectionalReadResidualOverflow
              false, // bidirectionalReadResidualUnderflow
              false, // residualOverflow
              false, // residualUnderflow,
              SCSIResponseParser.ServiceResponse.COMMAND_COMPLETED_AT_TARGET, // response,
              SCSIStatus.CHECK_CONDITION, // status,
              bhs.getInitiatorTaskTag(), // initiatorTaskTag,
              0, // snackTag
              0, // expectedDataSequenceNumber
              0, // bidirectionalReadResidualCount
              0, // residualCount
              new ScsiResponseDataSegment(
                  senseData, parser.getExpectedDataTransferLength())); // data
      // segment

    } else {
      // PDU is okay

      // carry out command
      /*
       * If we were nice, we would have to get (we would actually have to save it first) the number of blocks and
       * the block length requested by the initiator in the last MODE SENSE command and then change the logical
       * block layout accordingly. However, since the target is not required by the SCSI standard to make those
       * changes ("The degree that the medium is altered by this command is vendor specific."), doing nothing is
       * okay.
       */

      responsePdu =
          createScsiResponsePdu(
              SCSIStatus.GOOD, // status
              bhs.getInitiatorTaskTag(), // initiatorTaskTag,
              parser.getExpectedDataTransferLength(), // expectedDataTransferLength,
              0); // responseDataSize
    }

    // send response
    connection.sendPdu(responsePdu);
  }