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