List of usage examples for java.util.concurrent CompletableFuture thenAccept
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
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; }