public CompletableFuture<ClientSecureChannel> getChannel() {
    State currentState = state.get();

    logger.trace("getChannel(), currentState={}", currentState.getClass().getSimpleName());

    if (currentState instanceof Idle) {
      Connecting nextState = new Connecting();

      if (state.compareAndSet(currentState, nextState)) {
        CompletableFuture<ClientSecureChannel> connected = nextState.connected;

        connect(true, connected);

        return connected.whenCompleteAsync(
            (sc, ex) -> {
              if (sc != null) {
                if (state.compareAndSet(nextState, new Connected(connected))) {
                  sc.getChannel().pipeline().addLast(new InactivityHandler());
                }
              } else {
                state.compareAndSet(nextState, new Idle());
              }
            });
      } else {
        return getChannel();
      }
    } else if (currentState instanceof Connecting) {
      return ((Connecting) currentState).connected;
    } else if (currentState instanceof Connected) {
      return ((Connected) currentState).connected;
    } else if (currentState instanceof Reconnecting) {
      return ((Reconnecting) currentState).reconnected;
    } else if (currentState instanceof Disconnecting) {
      CompletableFuture<ClientSecureChannel> future = new CompletableFuture<>();

      CompletableFuture<Unit> disconnectFuture = ((Disconnecting) currentState).disconnectFuture;

      disconnectFuture.whenCompleteAsync(
          (u, ex) ->
              getChannel()
                  .whenCompleteAsync(
                      (sc, ex2) -> {
                        if (sc != null) future.complete(sc);
                        else future.completeExceptionally(ex2);
                      }),
          client.getExecutorService());

      return future;
    } else {
      throw new IllegalStateException(currentState.getClass().getSimpleName());
    }
  }
 // debuging: Intellij issue to see variable:
 // http://stackoverflow.com/questions/26895708/debugger-cannot-see-local-variable-in-a-lambda
 ///  vs anon class: variable visible. new instances??
 // stack traces: we see "lambda" as method name. maybe meth reference? but closures are not
 // possible ...
 ////////////////// read further:
 // http://baddotrobot.com/blog/2014/02/18/method-references-in-java8/
 @GET
 public void findById(@QueryParam("id") int id, @Suspended AsyncResponse asyncResponse) {
   asyncResponse.setTimeout(10, TimeUnit.SECONDS);
   final CompletableFuture<Product> product = productDao.findNameById(id);
   final CompletableFuture<ProductWithGroups> finalResult =
       product.thenCompose(
           productFound -> {
             if (asyncResponse.isDone()) {
               final CompletableFuture<ProductWithGroups> error = new CompletableFuture<>();
               error.completeExceptionally(new IllegalStateException("Response already done"));
               return error;
             } else {
               final CompletableFuture<List<ProductGroup>> productGroups =
                   productDao.findProductGroupsById(id);
               return productGroups.thenApply(
                   productGroupsFound ->
                       ProductWithGroups.createProductWithGroups(
                           productFound, productGroupsFound));
             }
           });
   finalResult.whenCompleteAsync(
       (productWithGroups, error) -> {
         consumeProductWithGroupOrError(asyncResponse, productWithGroups, error);
       },
       executorToCompleteCalls);
 }
  private void reconnect(Reconnecting reconnectState, long delaySeconds) {
    logger.debug("Scheduling reconnect for +{} seconds...", delaySeconds);

    Stack.sharedScheduledExecutor()
        .schedule(
            () -> {
              logger.debug("{} seconds elapsed; reconnecting...", delaySeconds);

              CompletableFuture<ClientSecureChannel> reconnected = reconnectState.reconnected;

              connect(true, reconnected);

              reconnected.whenCompleteAsync(
                  (sc, ex) -> {
                    if (sc != null) {
                      logger.debug("Reconnect succeeded, channelId={}", sc.getChannelId());

                      if (state.compareAndSet(reconnectState, new Connected(reconnected))) {
                        sc.getChannel().pipeline().addLast(new InactivityHandler());
                      }
                    } else {
                      logger.debug("Reconnect failed: {}", ex.getMessage(), ex);

                      Reconnecting nextState = new Reconnecting();
                      if (state.compareAndSet(reconnectState, nextState)) {
                        reconnect(nextState, nextDelay(delaySeconds));
                      }
                    }
                  });
            },
            delaySeconds,
            TimeUnit.SECONDS);
  }