private AsyncResult<List<Item>> discoverServices(Predicate<InfoNode> predicate) { // First discover the items of the server. // Then, for each item, discover the features of the item, but ignore any exceptions. return discoverItems(xmppSession.getDomain()).thenCompose(itemDiscovery -> { Collection<CompletionStage<List<Item>>> stages = itemDiscovery.getItems().stream() .map(item -> discoverInformation(item.getJid()).thenApply(infoDiscovery -> { if (predicate.test(infoDiscovery)) { return Collections.singletonList(item); } return Collections.<Item>emptyList(); }).handle((items, throwable) -> { // If one disco#info fails, don't let the whole discoverServices() method fail. // Instead of failing, return an empty list, other services can hopefully be discovered successfully. if (throwable != null) { return Collections.<Item>emptyList(); } else { return items; } })) .collect(Collectors.toList()); return CompletionStages.allOf(stages); }); }
private <S extends Stanza, E extends EventObject> AsyncResult<S> sendAndAwait(S stanza, Function<E, S> stanzaMapper, final Predicate<S> filter, Function<S, SendTask<S>> sendFunction, Consumer<Consumer<E>> addListener, Consumer<Consumer<E>> removeListener, Duration timeout) { CompletableFuture<S> completableFuture = new CompletableFuture<>(); final Consumer<E> listener = e -> { S st = stanzaMapper.apply(e); if (filter.test(st)) { if (st.getError() != null) { completableFuture.completeExceptionally(new StanzaErrorException(st)); } completableFuture.complete(st); } }; addListener.accept(listener); SendTask<S> sendTask = sendFunction.apply(stanza); // When the sending failed, immediately complete the future with the exception. sendTask.onFailed((throwable, s) -> completableFuture.completeExceptionally(throwable)); return new AsyncResult<>(completableFuture // When a response has received, mark the requesting stanza as acknowledged. // This is especially important for Bind and Roster IQs, so that they won't be resend after login. .whenComplete((result, e) -> removeFromQueue(sendTask.getStanza())) .applyToEither(CompletionStages.timeoutAfter(timeout.toMillis(), TimeUnit.MILLISECONDS, () -> new NoResponseException("Timeout reached, while waiting on a response for request: " + stanza)), Function.identity())) // When either a timeout happened or response has received, remove the listener. .whenComplete((result, e) -> removeListener.accept(listener)); }
CompletionStage<ByteStreamSession> withFallbackStage = CompletionStages.withFallback(eitherS5bOrIbb, (f, t) -> completableFutureIbb); return new AsyncResult<>(withFallbackStage.applyToEither(CompletionStages.timeoutAfter(xmppSession.getConfiguration().getDefaultResponseTimeout().toMillis() * 5, TimeUnit.MILLISECONDS), byteStreamSession -> { try { return new FileTransfer(xmppSession, byteStreamSession.getSessionId(), byteStreamSession.getInputStream(), outputStream, fileTransferOffer.getSize());
byteStreamSessionStage = CompletionStages.withFallback(socks5ByteStreamManager.initiateSession(receiver, sessionId), (future, throwable) -> {
/** * Discovers the SOCKS5 proxies. * * @return The async result with the proxies. * @see <a href="https://xmpp.org/extensions/xep-0065.html#disco">4. Discovering Proxies</a> */ public AsyncResult<List<StreamHost>> discoverProxies() { // First discover the items which identify the proxy host. return serviceDiscoveryManager.discoverServices(Identity.proxyByteStreams()).thenCompose(items -> { // For each proxy service, send a disco request to it (to discover stream hosts) Collection<CompletionStage<List<StreamHost>>> stages = items.stream() .map(service -> xmppSession.query(IQ.get(service.getJid(), new Socks5ByteStream())).thenApply(result -> { Socks5ByteStream socks5ByteStream = result.getExtension(Socks5ByteStream.class); if (socks5ByteStream != null) { return socks5ByteStream.getStreamHosts(); } return Collections.<StreamHost>emptyList(); })) .collect(Collectors.toList()); // Combine all discovery of all stream hosts into one future. return CompletionStages.allOf(stages); }); }
@Override protected CompletionStage<Void> closeStream() { final CompletableFuture<Void> future; final ExecutorService executorService; synchronized (this){ executorService = httpBindExecutor; } if (executorService != null && !executorService.isShutdown()) { final String sid = getSessionId(); if (sid != null) { // Terminate the BOSH session. Body.Builder bodyBuilder = Body.builder() .sessionId(sid) .type(Body.Type.TERMINATE); future = sendNewRequest(bodyBuilder, false) .applyToEither(CompletionStages.timeoutAfter(500, TimeUnit.MILLISECONDS), Function.identity()) .exceptionally(exc -> null); } else { future = CompletableFuture.completedFuture(null); } executorService.shutdown(); } else { future = CompletableFuture.completedFuture(null); } return future; }