Example usage for java.util.concurrent CompletableFuture thenAccept

List of usage examples for java.util.concurrent CompletableFuture thenAccept

Introduction

In this page you can find the example usage for java.util.concurrent CompletableFuture thenAccept.

Prototype

public CompletableFuture<Void> thenAccept(Consumer<? super T> action) 

Source Link

Usage

From source file:org.apache.pulsar.broker.lookup.TopicLookup.java

/**
 *
 * Lookup broker-service address for a given namespace-bundle which contains given topic.
 *
 * a. Returns broker-address if namespace-bundle is already owned by any broker
 * b. If current-broker receives lookup-request and if it's not a leader
 * then current broker redirects request to leader by returning leader-service address.
 * c. If current-broker is leader then it finds out least-loaded broker to own namespace bundle and
 * redirects request by returning least-loaded broker.
 * d. If current-broker receives request to own the namespace-bundle then it owns a bundle and returns
 * success(connect) response to client.//from   ww w. ja va 2 s .  co m
 *
 * @param pulsarService
 * @param topicName
 * @param authoritative
 * @param clientAppId
 * @param requestId
 * @return
 */
public static CompletableFuture<ByteBuf> lookupTopicAsync(PulsarService pulsarService, TopicName topicName,
        boolean authoritative, String clientAppId, AuthenticationDataSource authenticationData,
        long requestId) {

    final CompletableFuture<ByteBuf> validationFuture = new CompletableFuture<>();
    final CompletableFuture<ByteBuf> lookupfuture = new CompletableFuture<>();
    final String cluster = topicName.getCluster();

    // (1) validate cluster
    getClusterDataIfDifferentCluster(pulsarService, cluster, clientAppId).thenAccept(differentClusterData -> {

        if (differentClusterData != null) {
            if (log.isDebugEnabled()) {
                log.debug("[{}] Redirecting the lookup call to {}/{} cluster={}", clientAppId,
                        differentClusterData.getBrokerServiceUrl(),
                        differentClusterData.getBrokerServiceUrlTls(), cluster);
            }
            validationFuture.complete(newLookupResponse(differentClusterData.getBrokerServiceUrl(),
                    differentClusterData.getBrokerServiceUrlTls(), true, LookupType.Redirect, requestId,
                    false));
        } else {
            // (2) authorize client
            try {
                checkAuthorization(pulsarService, topicName, clientAppId, authenticationData);
            } catch (RestException authException) {
                log.warn("Failed to authorized {} on cluster {}", clientAppId, topicName.toString());
                validationFuture.complete(newLookupErrorResponse(ServerError.AuthorizationError,
                        authException.getMessage(), requestId));
                return;
            } catch (Exception e) {
                log.warn("Unknown error while authorizing {} on cluster {}", clientAppId, topicName.toString());
                validationFuture.completeExceptionally(e);
                return;
            }
            // (3) validate global namespace
            checkLocalOrGetPeerReplicationCluster(pulsarService, topicName.getNamespaceObject())
                    .thenAccept(peerClusterData -> {
                        if (peerClusterData == null) {
                            // (4) all validation passed: initiate lookup
                            validationFuture.complete(null);
                            return;
                        }
                        // if peer-cluster-data is present it means namespace is owned by that peer-cluster and
                        // request should be redirect to the peer-cluster
                        if (StringUtils.isBlank(peerClusterData.getBrokerServiceUrl())
                                && StringUtils.isBlank(peerClusterData.getBrokerServiceUrl())) {
                            validationFuture.complete(newLookupErrorResponse(ServerError.MetadataError,
                                    "Redirected cluster's brokerService url is not configured", requestId));
                            return;
                        }
                        validationFuture.complete(newLookupResponse(peerClusterData.getBrokerServiceUrl(),
                                peerClusterData.getBrokerServiceUrlTls(), true, LookupType.Redirect, requestId,
                                false));

                    }).exceptionally(ex -> {
                        validationFuture.complete(
                                newLookupErrorResponse(ServerError.MetadataError, ex.getMessage(), requestId));
                        return null;
                    });
        }
    }).exceptionally(ex -> {
        validationFuture.completeExceptionally(ex);
        return null;
    });

    // Initiate lookup once validation completes
    validationFuture.thenAccept(validaitonFailureResponse -> {
        if (validaitonFailureResponse != null) {
            lookupfuture.complete(validaitonFailureResponse);
        } else {
            pulsarService.getNamespaceService().getBrokerServiceUrlAsync(topicName, authoritative)
                    .thenAccept(lookupResult -> {

                        if (log.isDebugEnabled()) {
                            log.debug("[{}] Lookup result {}", topicName.toString(), lookupResult);
                        }

                        if (!lookupResult.isPresent()) {
                            lookupfuture.complete(newLookupErrorResponse(ServerError.ServiceNotReady,
                                    "No broker was available to own " + topicName, requestId));
                            return;
                        }

                        LookupData lookupData = lookupResult.get().getLookupData();
                        if (lookupResult.get().isRedirect()) {
                            boolean newAuthoritative = isLeaderBroker(pulsarService);
                            lookupfuture.complete(
                                    newLookupResponse(lookupData.getBrokerUrl(), lookupData.getBrokerUrlTls(),
                                            newAuthoritative, LookupType.Redirect, requestId, false));
                        } else {
                            // When running in standalone mode we want to redirect the client through the service
                            // url, so that the advertised address configuration is not relevant anymore.
                            boolean redirectThroughServiceUrl = pulsarService.getConfiguration()
                                    .isRunningStandalone();

                            lookupfuture.complete(newLookupResponse(lookupData.getBrokerUrl(),
                                    lookupData.getBrokerUrlTls(), true /* authoritative */, LookupType.Connect,
                                    requestId, redirectThroughServiceUrl));
                        }
                    }).exceptionally(ex -> {
                        if (ex instanceof CompletionException
                                && ex.getCause() instanceof IllegalStateException) {
                            log.info("Failed to lookup {} for topic {} with error {}", clientAppId,
                                    topicName.toString(), ex.getCause().getMessage());
                        } else {
                            log.warn("Failed to lookup {} for topic {} with error {}", clientAppId,
                                    topicName.toString(), ex.getMessage(), ex);
                        }
                        lookupfuture.complete(newLookupErrorResponse(ServerError.ServiceNotReady,
                                ex.getMessage(), requestId));
                        return null;
                    });
        }

    }).exceptionally(ex -> {
        if (ex instanceof CompletionException && ex.getCause() instanceof IllegalStateException) {
            log.info("Failed to lookup {} for topic {} with error {}", clientAppId, topicName.toString(),
                    ex.getCause().getMessage());
        } else {
            log.warn("Failed to lookup {} for topic {} with error {}", clientAppId, topicName.toString(),
                    ex.getMessage(), ex);
        }

        lookupfuture.complete(newLookupErrorResponse(ServerError.ServiceNotReady, ex.getMessage(), requestId));
        return null;
    });

    return lookupfuture;
}

From source file:org.apache.pulsar.broker.lookup.TopicLookupBase.java

protected void internalLookupTopicAsync(TopicName topicName, boolean authoritative,
        AsyncResponse asyncResponse) {/*from  w  w  w.  j av a 2  s  .c om*/
    if (!pulsar().getBrokerService().getLookupRequestSemaphore().tryAcquire()) {
        log.warn("No broker was found available for topic {}", topicName);
        asyncResponse.resume(new WebApplicationException(Response.Status.SERVICE_UNAVAILABLE));
        return;
    }

    try {
        validateClusterOwnership(topicName.getCluster());
        checkConnect(topicName);
        validateGlobalNamespaceOwnership(topicName.getNamespaceObject());
    } catch (WebApplicationException we) {
        // Validation checks failed
        log.error("Validation check failed: {}", we.getMessage());
        completeLookupResponseExceptionally(asyncResponse, we);
        return;
    } catch (Throwable t) {
        // Validation checks failed with unknown error
        log.error("Validation check failed: {}", t.getMessage(), t);
        completeLookupResponseExceptionally(asyncResponse, new RestException(t));
        return;
    }

    CompletableFuture<Optional<LookupResult>> lookupFuture = pulsar().getNamespaceService()
            .getBrokerServiceUrlAsync(topicName, authoritative);

    lookupFuture.thenAccept(optionalResult -> {
        if (optionalResult == null || !optionalResult.isPresent()) {
            log.warn("No broker was found available for topic {}", topicName);
            completeLookupResponseExceptionally(asyncResponse,
                    new WebApplicationException(Response.Status.SERVICE_UNAVAILABLE));
            return;
        }

        LookupResult result = optionalResult.get();
        // We have found either a broker that owns the topic, or a broker to which we should redirect the client to
        if (result.isRedirect()) {
            boolean newAuthoritative = this.isLeaderBroker();
            URI redirect;
            try {
                String redirectUrl = isRequestHttps() ? result.getLookupData().getHttpUrlTls()
                        : result.getLookupData().getHttpUrl();
                checkNotNull(redirectUrl, "Redirected cluster's service url is not configured");
                String lookupPath = topicName.isV2() ? LOOKUP_PATH_V2 : LOOKUP_PATH_V1;
                redirect = new URI(String.format("%s%s%s?authoritative=%s", redirectUrl, lookupPath,
                        topicName.getLookupName(), newAuthoritative));
            } catch (URISyntaxException | NullPointerException e) {
                log.error("Error in preparing redirect url for {}: {}", topicName, e.getMessage(), e);
                completeLookupResponseExceptionally(asyncResponse, e);
                return;
            }
            if (log.isDebugEnabled()) {
                log.debug("Redirect lookup for topic {} to {}", topicName, redirect);
            }
            completeLookupResponseExceptionally(asyncResponse,
                    new WebApplicationException(Response.temporaryRedirect(redirect).build()));

        } else {
            // Found broker owning the topic
            if (log.isDebugEnabled()) {
                log.debug("Lookup succeeded for topic {} -- broker: {}", topicName, result.getLookupData());
            }
            completeLookupResponseSuccessfully(asyncResponse, result.getLookupData());
        }
    }).exceptionally(exception -> {
        log.warn("Failed to lookup broker for topic {}: {}", topicName, exception.getMessage(), exception);
        completeLookupResponseExceptionally(asyncResponse, exception);
        return null;
    });
}

From source file:org.apache.pulsar.broker.service.persistent.PersistentTopic.java

@Override
public CompletableFuture<Consumer> subscribe(final ServerCnx cnx, String subscriptionName, long consumerId,
        SubType subType, int priorityLevel, String consumerName, boolean isDurable, MessageId startMessageId,
        Map<String, String> metadata, boolean readCompacted, InitialPosition initialPosition) {

    final CompletableFuture<Consumer> future = new CompletableFuture<>();

    try {//  w  w  w  .j  av a  2s.c om
        brokerService.checkTopicNsOwnership(getName());
    } catch (Exception e) {
        future.completeExceptionally(e);
        return future;
    }

    if (readCompacted && !(subType == SubType.Failover || subType == SubType.Exclusive)) {
        future.completeExceptionally(
                new NotAllowedException("readCompacted only allowed on failover or exclusive subscriptions"));
        return future;
    }
    if (isBlank(subscriptionName)) {
        if (log.isDebugEnabled()) {
            log.debug("[{}] Empty subscription name", topic);
        }
        future.completeExceptionally(new NamingException("Empty subscription name"));
        return future;
    }

    if (hasBatchMessagePublished && !cnx.isBatchMessageCompatibleVersion()) {
        if (log.isDebugEnabled()) {
            log.debug("[{}] Consumer doesn't support batch-message {}", topic, subscriptionName);
        }
        future.completeExceptionally(new UnsupportedVersionException("Consumer doesn't support batch-message"));
        return future;
    }

    if (subscriptionName.startsWith(replicatorPrefix) || subscriptionName.equals(DEDUPLICATION_CURSOR_NAME)) {
        log.warn("[{}] Failed to create subscription for {}", topic, subscriptionName);
        future.completeExceptionally(
                new NamingException("Subscription with reserved subscription name attempted"));
        return future;
    }

    if (cnx.getRemoteAddress() != null && cnx.getRemoteAddress().toString().contains(":")) {
        SubscribeRateLimiter.ConsumerIdentifier consumer = new SubscribeRateLimiter.ConsumerIdentifier(
                cnx.getRemoteAddress().toString().split(":")[0], consumerName, consumerId);
        if (subscribeRateLimiter.isPresent() && !subscribeRateLimiter.get().subscribeAvailable(consumer)
                || !subscribeRateLimiter.get().tryAcquire(consumer)) {
            log.warn("[{}] Failed to create subscription for {} {} limited by {}, available {}", topic,
                    subscriptionName, consumer, subscribeRateLimiter.get().getSubscribeRate(),
                    subscribeRateLimiter.get().getAvailableSubscribeRateLimit(consumer));
            future.completeExceptionally(
                    new NotAllowedException("Subscribe limited by subscribe rate limit per consumer."));
            return future;
        }
    }

    lock.readLock().lock();
    try {
        if (isFenced) {
            log.warn("[{}] Attempting to subscribe to a fenced topic", topic);
            future.completeExceptionally(new TopicFencedException("Topic is temporarily unavailable"));
            return future;
        }
        USAGE_COUNT_UPDATER.incrementAndGet(this);
        if (log.isDebugEnabled()) {
            log.debug("[{}] [{}] [{}] Added consumer -- count: {}", topic, subscriptionName, consumerName,
                    USAGE_COUNT_UPDATER.get(this));
        }
    } finally {
        lock.readLock().unlock();
    }

    CompletableFuture<? extends Subscription> subscriptionFuture = isDurable ? //
            getDurableSubscription(subscriptionName, initialPosition) //
            : getNonDurableSubscription(subscriptionName, startMessageId);

    int maxUnackedMessages = isDurable
            ? brokerService.pulsar().getConfiguration().getMaxUnackedMessagesPerConsumer()
            : 0;

    subscriptionFuture.thenAccept(subscription -> {
        try {
            Consumer consumer = new Consumer(subscription, subType, topic, consumerId, priorityLevel,
                    consumerName, maxUnackedMessages, cnx, cnx.getRole(), metadata, readCompacted,
                    initialPosition);
            subscription.addConsumer(consumer);
            if (!cnx.isActive()) {
                consumer.close();
                if (log.isDebugEnabled()) {
                    log.debug("[{}] [{}] [{}] Subscribe failed -- count: {}", topic, subscriptionName,
                            consumer.consumerName(), USAGE_COUNT_UPDATER.get(PersistentTopic.this));
                }
                future.completeExceptionally(
                        new BrokerServiceException("Connection was closed while the opening the cursor "));
            } else {
                log.info("[{}][{}] Created new subscription for {}", topic, subscriptionName, consumerId);
                future.complete(consumer);
            }
        } catch (BrokerServiceException e) {
            if (e instanceof ConsumerBusyException) {
                log.warn("[{}][{}] Consumer {} {} already connected", topic, subscriptionName, consumerId,
                        consumerName);
            } else if (e instanceof SubscriptionBusyException) {
                log.warn("[{}][{}] {}", topic, subscriptionName, e.getMessage());
            }

            USAGE_COUNT_UPDATER.decrementAndGet(PersistentTopic.this);
            future.completeExceptionally(e);
        }
    }).exceptionally(ex -> {
        log.warn("[{}] Failed to create subscription for {}: ", topic, subscriptionName, ex.getMessage());
        USAGE_COUNT_UPDATER.decrementAndGet(PersistentTopic.this);
        future.completeExceptionally(new PersistenceException(ex));
        return null;
    });

    return future;
}

From source file:org.apache.pulsar.broker.service.persistent.PersistentTopic.java

/**
 * Delete the managed ledger associated with this topic
 *
 * @param failIfHasSubscriptions//from w ww.j a v a  2s  .  c  om
 *            Flag indicating whether delete should succeed if topic still has unconnected subscriptions. Set to
 *            false when called from admin API (it will delete the subs too), and set to true when called from GC
 *            thread
 * @param closeIfClientsConnected
 *            Flag indicate whether explicitly close connected producers/consumers/replicators before trying to delete topic. If
 *            any client is connected to a topic and if this flag is disable then this operation fails.
 *
 * @return Completable future indicating completion of delete operation Completed exceptionally with:
 *         IllegalStateException if topic is still active ManagedLedgerException if ledger delete operation fails
 */
private CompletableFuture<Void> delete(boolean failIfHasSubscriptions, boolean closeIfClientsConnected) {
    CompletableFuture<Void> deleteFuture = new CompletableFuture<>();

    lock.writeLock().lock();
    try {
        if (isFenced) {
            log.warn("[{}] Topic is already being closed or deleted", topic);
            deleteFuture.completeExceptionally(new TopicFencedException("Topic is already fenced"));
            return deleteFuture;
        }

        CompletableFuture<Void> closeClientFuture = new CompletableFuture<>();
        if (closeIfClientsConnected) {
            List<CompletableFuture<Void>> futures = Lists.newArrayList();
            replicators.forEach((cluster, replicator) -> futures.add(replicator.disconnect()));
            producers.forEach(producer -> futures.add(producer.disconnect()));
            subscriptions.forEach((s, sub) -> futures.add(sub.disconnect()));
            FutureUtil.waitForAll(futures).thenRun(() -> {
                closeClientFuture.complete(null);
            }).exceptionally(ex -> {
                log.error("[{}] Error closing clients", topic, ex);
                isFenced = false;
                closeClientFuture.completeExceptionally(ex);
                return null;
            });
        } else {
            closeClientFuture.complete(null);
        }

        closeClientFuture.thenAccept(delete -> {
            if (USAGE_COUNT_UPDATER.get(this) == 0) {
                isFenced = true;

                List<CompletableFuture<Void>> futures = Lists.newArrayList();

                if (failIfHasSubscriptions) {
                    if (!subscriptions.isEmpty()) {
                        isFenced = false;
                        deleteFuture.completeExceptionally(new TopicBusyException("Topic has subscriptions"));
                        return;
                    }
                } else {
                    subscriptions.forEach((s, sub) -> futures.add(sub.delete()));
                }

                FutureUtil.waitForAll(futures).whenComplete((v, ex) -> {
                    if (ex != null) {
                        log.error("[{}] Error deleting topic", topic, ex);
                        isFenced = false;
                        deleteFuture.completeExceptionally(ex);
                    } else {
                        ledger.asyncDelete(new AsyncCallbacks.DeleteLedgerCallback() {
                            @Override
                            public void deleteLedgerComplete(Object ctx) {
                                brokerService.removeTopicFromCache(topic);
                                log.info("[{}] Topic deleted", topic);
                                deleteFuture.complete(null);
                            }

                            @Override
                            public void deleteLedgerFailed(ManagedLedgerException exception, Object ctx) {
                                isFenced = false;
                                log.error("[{}] Error deleting topic", topic, exception);
                                deleteFuture.completeExceptionally(new PersistenceException(exception));
                            }
                        }, null);
                    }
                });
            } else {
                deleteFuture.completeExceptionally(new TopicBusyException(
                        "Topic has " + USAGE_COUNT_UPDATER.get(this) + " connected producers/consumers"));
            }
        }).exceptionally(ex -> {
            deleteFuture.completeExceptionally(
                    new TopicBusyException("Failed to close clients before deleting topic."));
            return null;
        });
    } finally {
        lock.writeLock().unlock();
    }

    return deleteFuture;
}