/** 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; }