public <T> Request makeManagedRequest(
      final Command cmd, final Manager<T> manager, final Request.Builder<T> builder) {
    final Request request = newRequest(cmd);
    final boolean[] responded = new boolean[] {false};
    request.once(
        Request.OnTimeout.class,
        new Request.OnTimeout() {
          @Override
          public void called(Response args) {
            if (!responded[0] && manager.retryOnUnsuccessful(null)) {
              logRetry(request, "Request timed out");
              request.clearAllListeners();
              queueRetry(50, cmd, manager, builder);
            }
          }
        });
    final OnDisconnected cb =
        new OnDisconnected() {
          @Override
          public void called(Client c) {
            if (!responded[0] && manager.retryOnUnsuccessful(null)) {
              logRetry(request, "Client disconnected");
              request.clearAllListeners();
              queueRetry(50, cmd, manager, builder);
            }
          }
        };
    once(OnDisconnected.class, cb);
    request.once(
        Request.OnResponse.class,
        new Request.OnResponse() {
          @Override
          public void called(final Response response) {
            responded[0] = true;
            Client.this.removeListener(OnDisconnected.class, cb);

            if (response.succeeded) {
              final T t = builder.buildTypedResponse(response);
              manager.cb(response, t);
            } else {
              if (manager.retryOnUnsuccessful(response)) {
                queueRetry(50, cmd, manager, builder);
              } else {
                manager.cb(response, null);
              }
            }
          }
        });
    builder.beforeRequest(request);
    manager.beforeRequest(request);
    request.request();
    return request;
  }
  private void subscribe(JSONObject subscription) {
    Request request = newRequest(Command.subscribe);

    request.json(subscription);
    request.on(
        Request.OnSuccess.class,
        new Request.OnSuccess() {
          @Override
          public void called(Response response) {
            // TODO ... make sure this isn't just an account subscription
            serverInfo.update(response.result);
            emit(OnSubscribed.class, serverInfo);
          }
        });
    request.request();
  }
  private static void walkAccountTx(final Client c, final Object marker, final int pages) {
    Request request = c.newRequest(Command.account_tx);
    request.json("binary", true);
    request.json("account", theAccount);

    if (marker != null) {
      request.json("marker", marker);
    }
    request.json("ledger_index_max", -1);

    request.once(
        Request.OnSuccess.class,
        new Request.OnSuccess() {
          @Override
          public void called(Response response) {
            JSONObject result = response.result;
            try {
              JSONArray transactions = result.getJSONArray("transactions");
              System.out.printf("Found %d (more) transactions %n", transactions.length());
              appendTo(outputFile, result.toString());

              Object newMarker = result.opt("marker");
              System.out.printf("Marker %s%n", newMarker);
              if (marker != null
                  && newMarker != null
                  && marker.toString().equals(newMarker.toString())) {
                // This shouldn't happen since Stef's patch but who knows how
                // pervasively it's been deployed ?
                //                        return;
                newMarker = null;
              }
              if ((newMarker != null) && (pages - 1 > 0) && transactions.length() > 0) {
                System.out.printf("Found new marker %s%n", newMarker);
                walkAccountTx(c, newMarker, pages - 1);
              } else {
                System.out.printf("Found all transactions");
              }

            } catch (Exception e) {
              throw new RuntimeException(e);
            }
          }
        });

    request.request();
  }