/** Handles closing the session. */
 private void onClose() {
   if (isOpen()) {
     LOGGER.debug("Closed session: {}", id);
     this.id = 0;
     this.state = State.CLOSED;
     if (connection != null) connection.close();
     client.close();
     context.close();
     closeListeners.forEach(l -> l.accept(this));
   }
 }
Exemple #2
0
  @Override
  public CompletableFuture<Void> close() {
    int i = 0;
    CompletableFuture<?>[] futures = new CompletableFuture[connections.size()];
    for (Connection connection : connections.values()) {
      futures[i++] = connection.close();
    }

    CompletableFuture<Void> future = new CompletableFuture<>();
    CompletableFuture.allOf(futures)
        .whenComplete(
            (result, error) -> {
              channelGroup
                  .close()
                  .addListener(
                      channelFuture -> {
                        future.complete(null);
                      });
            });
    return future;
  }
  /** Sets up the given connection. */
  private CompletableFuture<Connection> setupConnection(Connection connection) {
    this.connection = connection;
    connection.closeListener(
        c -> {
          if (c.equals(this.connection)) {
            this.connection = null;
          }
        });
    connection.exceptionListener(
        c -> {
          if (c.equals(this.connection)) {
            this.connection = null;
          }
        });
    connection.handler(PublishRequest.class, this::handlePublish);

    if (id != 0) {
      ConnectRequest request = ConnectRequest.builder().withSession(id).build();

      CompletableFuture<Connection> future = new CompletableFuture<>();
      connection
          .send(request)
          .whenComplete(
              (response, error) -> {
                if (isOpen()) {
                  if (error == null) {
                    future.complete(connection);
                  } else {
                    future.completeExceptionally(error);
                  }
                }
              });
      return future;
    }
    return CompletableFuture.completedFuture(connection);
  }
  /**
   * Sends a session request to the given connection.
   *
   * @param request The request to send.
   * @param connection The connection to which to send the request.
   * @param future The future to complete once the response is received.
   * @param checkOpen Whether to check if the session is open.
   * @param <T> The request type.
   * @param <U> The response type.
   * @return The provided future to be completed once the response is received.
   */
  private <T extends Request<T>, U extends Response<U>> CompletableFuture<U> request(
      T request,
      Connection connection,
      CompletableFuture<U> future,
      boolean checkOpen,
      boolean recordFailures) {
    LOGGER.debug("Sending: {}", request);
    connection
        .<T, U>send(request)
        .whenComplete(
            (response, error) -> {
              if (!checkOpen || isOpen()) {
                if (error == null) {
                  LOGGER.debug("Received: {}", response);

                  // If the response is an error response, check if the session state has changed.
                  if (response.status() == Response.Status.ERROR) {
                    // If the response error is a no leader error, reset the connection and send
                    // another request.
                    // If this is the first time we've received a response from a server in this
                    // iteration,
                    // set the failure time to keep track of whether the session timed out.
                    if (response.error() == RaftError.Type.NO_LEADER_ERROR) {
                      if (recordFailures) setFailureTime();
                      resetConnection().request(request, future, checkOpen, false);
                    }
                    // If the response error is an unknown session error, immediately expire the
                    // session.
                    else if (response.error() == RaftError.Type.UNKNOWN_SESSION_ERROR) {
                      resetConnection().onExpire();
                      future.completeExceptionally(new IllegalStateException("session expired"));
                    }
                    // If the response error is an application or internal error, immediately
                    // complete the future.
                    else if (response.error() == RaftError.Type.APPLICATION_ERROR
                        || response.error() == RaftError.Type.INTERNAL_ERROR) {
                      resetFailureTime();
                      future.completeExceptionally(response.error().createException());
                    }
                    // If we've made it this far, for all other error types attempt to resend the
                    // request.
                    else {
                      resetFailureTime()
                          .resetConnection()
                          .request(request, future, checkOpen, false);
                    }
                  }
                  // If the response status is OK, reset the failure time and complete the future.
                  else {
                    resetFailureTime();
                    future.complete(response);
                  }
                }
                // If an error occurred, attempt to contact the next server recursively.
                else {
                  LOGGER.debug("Request failed: {}", request);
                  LOGGER.debug("{}", error.getMessage());
                  resetConnection().request(request, future, checkOpen, recordFailures);
                }
              } else {
                future.completeExceptionally(new IllegalStateException("session not open"));
              }
            });
    return future;
  }