/**
   * Asynchronously queries a Couchbase view and returns the result. The result can be accessed
   * row-wise via an iterator.
   *
   * @param view the view to run the query against.
   * @param query the type of query to run against the view.
   * @return a Future containing the results of the query.
   */
  private HttpFuture<ViewResponse> asyncQueryAndReduce(final View view, final Query query) {
    if (!view.hasReduce()) {
      throw new RuntimeException("This view doesn't contain a reduce function");
    }
    String uri = view.getURI() + query.toString();
    final CountDownLatch couchLatch = new CountDownLatch(1);
    final HttpFuture<ViewResponse> crv = new HttpFuture<ViewResponse>(couchLatch, 60000);

    final HttpRequest request = new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
    final HttpOperation op =
        new ReducedOperationImpl(
            request,
            new ViewCallback() {
              private ViewResponse vr = null;

              @Override
              public void receivedStatus(OperationStatus status) {
                crv.set(vr, status);
              }

              @Override
              public void complete() {
                couchLatch.countDown();
              }

              @Override
              public void gotData(ViewResponse response) {
                vr = response;
              }
            });
    crv.setOperation(op);
    addOp(op);
    return crv;
  }
  /**
   * Asynchronously queries a Couchbase view and returns the result. The result can be accessed
   * row-wise via an iterator. This type of query will return the view result but will not get the
   * documents associated with each row of the query.
   *
   * @param view the view to run the query against.
   * @param query the type of query to run against the view.
   * @return a Future containing the results of the query.
   */
  private HttpFuture<ViewResponse> asyncQueryAndExcludeDocs(View view, Query query) {
    String uri = view.getURI() + query.toString();
    final CountDownLatch couchLatch = new CountDownLatch(1);
    final HttpFuture<ViewResponse> crv = new HttpFuture<ViewResponse>(couchLatch, 60000);

    final HttpRequest request = new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
    final HttpOperation op =
        new NoDocsOperationImpl(
            request,
            new ViewCallback() {
              private ViewResponse vr = null;

              @Override
              public void receivedStatus(OperationStatus status) {
                crv.set(vr, status);
              }

              @Override
              public void complete() {
                couchLatch.countDown();
              }

              @Override
              public void gotData(ViewResponse response) {
                vr = response;
              }
            });
    crv.setOperation(op);
    addOp(op);
    return crv;
  }
  public HttpFuture<String> asyncHttpGet(String uri) {
    final CountDownLatch couchLatch = new CountDownLatch(1);
    final HttpFuture<String> crv =
        new HttpFuture<String>(couchLatch, operationTimeout, executorService);

    HttpRequest request = new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
    HttpOperationImpl op =
        new TestOperationImpl(
            request,
            new TestCallback() {
              private String json;

              @Override
              public void receivedStatus(OperationStatus status) {
                crv.set(json, status);
              }

              @Override
              public void complete() {
                couchLatch.countDown();
              }

              @Override
              public void getData(String response) {
                json = response;
              }
            });
    crv.setOperation(op);
    addOp(op);
    return crv;
  }
  public HttpFuture<String> asyncHttpPut(String uri, String document)
      throws UnsupportedEncodingException {
    final CountDownLatch couchLatch = new CountDownLatch(1);
    final HttpFuture<String> crv =
        new HttpFuture<String>(couchLatch, operationTimeout, executorService);

    HttpRequest request = new BasicHttpEntityEnclosingRequest("PUT", uri, HttpVersion.HTTP_1_1);
    request.setHeader(new BasicHeader("Content-Type", "application/json"));
    StringEntity entity = new StringEntity(document);
    ((BasicHttpEntityEnclosingRequest) request).setEntity(entity);
    HttpOperationImpl op =
        new TestOperationPutImpl(
            request,
            new TestCallback() {
              private String json;

              @Override
              public void receivedStatus(OperationStatus status) {
                crv.set(json, status);
              }

              @Override
              public void complete() {
                couchLatch.countDown();
              }

              @Override
              public void getData(String response) {
                json = response;
              }
            });
    crv.setOperation(op);
    addOp(op);
    return crv;
  }
  /**
   * Gets access to a view contained in a design document from the cluster.
   *
   * <p>The purpose of a view is take the structured data stored within the Couchbase Server
   * database as JSON documents, extract the fields and information, and to produce an index of the
   * selected information.
   *
   * <p>The result is a view on the stored data. The view that is created during this process allows
   * you to iterate, select and query the information in your database from the raw data objects
   * that have been stored.
   *
   * @param designDocumentName the name of the design document.
   * @param viewName the name of the view to get.
   * @return a View object from the cluster.
   * @throws InterruptedException if the operation is interrupted while in flight
   * @throws ExecutionException if an error occurs during execution
   */
  public HttpFuture<View> asyncGetView(String designDocumentName, final String viewName) {
    designDocumentName = MODE_PREFIX + designDocumentName;
    String bucket = ((CouchbaseConnectionFactory) connFactory).getBucketName();
    String uri = "/" + bucket + "/_design/" + designDocumentName;
    final CountDownLatch couchLatch = new CountDownLatch(1);
    final HttpFuture<View> crv = new HttpFuture<View>(couchLatch, 60000);

    final HttpRequest request = new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
    final HttpOperation op =
        new ViewFetcherOperationImpl(
            request,
            bucket,
            designDocumentName,
            viewName,
            new ViewFetcherOperation.ViewFetcherCallback() {
              private View view = null;

              @Override
              public void receivedStatus(OperationStatus status) {
                crv.set(view, status);
              }

              @Override
              public void complete() {
                couchLatch.countDown();
              }

              @Override
              public void gotData(View v) {
                view = v;
              }
            });
    crv.setOperation(op);
    addOp(op);
    assert crv != null : "Problem retrieving view";
    return crv;
  }