@InternalUseOnly()
  AsyncHelper(
      final LDAPConnection connection,
      final OperationType operationType,
      final int messageID,
      final AsyncResultListener resultListener,
      final IntermediateResponseListener intermediateResponseListener) {
    this.connection = connection;
    this.operationType = operationType;
    this.resultListener = resultListener;
    this.intermediateResponseListener = intermediateResponseListener;

    asyncRequestID = new AsyncRequestID(messageID, connection);
    responseReturned = new AtomicBoolean(false);
    createTime = System.nanoTime();
  }
  @InternalUseOnly()
  public void responseReceived(final LDAPResponse response) throws LDAPException {
    if (!responseReturned.compareAndSet(false, true)) {
      return;
    }

    final long responseTime = System.nanoTime() - createTime;
    if (response instanceof ConnectionClosedResponse) {
      final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
      final String message = ccr.getMessage();
      if (message == null) {
        throw new LDAPException(
            ccr.getResultCode(), ERR_CONN_CLOSED_WAITING_FOR_ASYNC_RESPONSE.get());
      } else {
        throw new LDAPException(
            ccr.getResultCode(),
            ERR_CONN_CLOSED_WAITING_FOR_ASYNC_RESPONSE_WITH_MESSAGE.get(message));
      }
    }

    switch (operationType) {
      case ADD:
        connection.getConnectionStatistics().incrementNumAddResponses(responseTime);
        break;
      case DELETE:
        connection.getConnectionStatistics().incrementNumDeleteResponses(responseTime);
        break;
      case MODIFY:
        connection.getConnectionStatistics().incrementNumModifyResponses(responseTime);
        break;
      case MODIFY_DN:
        connection.getConnectionStatistics().incrementNumModifyDNResponses(responseTime);
        break;
    }

    final LDAPResult result = (LDAPResult) response;
    resultListener.ldapResultReceived(asyncRequestID, result);
    asyncRequestID.setResult(result);
  }