/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. **/ package org.codice.ddf.sdk.cometd; import java.net.MalformedURLException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.cometd.bayeux.Message; import org.cometd.bayeux.client.ClientSessionChannel; import org.cometd.bayeux.client.ClientSessionChannel.MessageListener; import org.cometd.client.BayeuxClient; import org.cometd.client.transport.ClientTransport; import org.cometd.client.transport.LongPollingTransport; import org.eclipse.jetty.client.HttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is used to establish a session with the DDF cometd api. It provides the capability to monitor * notifications, activities as well as the ability to issue queries and download products. */ public class AsyncClient { private static final Logger LOGGER = LoggerFactory.getLogger(AsyncClient.class); private static final String ACTIVITIES_CHANNEL = "/ddf/activities"; private static final String ALL_ACTIVITIES = ACTIVITIES_CHANNEL + "/**"; private static final String NOTIFICATIONS_CHANNEL = "/ddf/notifications"; private static final String ALL_NOTIFICATIONS = NOTIFICATIONS_CHANNEL + "/**"; private static final String DOWNLOADS_CHANNEL = NOTIFICATIONS_CHANNEL + "/downloads"; private static final String COMETD_CONTEXT = "/search/cometd"; private static final String DOWNLOAD_CONTEXT = "/services/catalog/sources/"; private static final String DOWNLOAD_TRANSFORM = "?transform=resource"; private static final long DEFAULT_QUERY_TIMEOUT = 60000; private final String url; private final BayeuxClient client; private final Map<String, Object> emptyMessage = new HashMap<>(); private final Map<String, Object> queryResponse = new HashMap<>(); private String asyncClientId; private Map<String, Object> activities; private Map<String, Object> notifications; /** * Creates a ddf async client with default channel subscriptions * @param url - target ddf url * @param disableValidation - disable ssl hostname validation * @throws Exception */ public AsyncClient(String url, Boolean disableValidation) throws Exception { this.url = url; HttpClient httpClient = new HttpClient(); httpClient.start(); Map<String, Object> options = new HashMap<>(); ClientTransport transport = new LongPollingTransport(options, httpClient); // DisableValidation if (disableValidation) { doTrustAllCertificates(); } LOGGER.debug("Creating client for: {}", url + COMETD_CONTEXT); this.client = new BayeuxClient(url + COMETD_CONTEXT, transport); client.handshake(new MessageListener() { public void onMessage(ClientSessionChannel channel, Message message) { if (message.isSuccessful()) { asyncClientId = channel.getSession() .getId(); client.getChannel(ALL_ACTIVITIES) .addListener((MessageListener) (activitiesChannel, activitiesMessages) -> activities = activitiesMessages.getDataAsMap()); client.getChannel(ALL_NOTIFICATIONS) .addListener((MessageListener) (notificationsChannel, notificationsMessages) -> notifications = notificationsMessages.getDataAsMap()); } } }); boolean handshaken = client.waitFor(1000, BayeuxClient.State.CONNECTED); LOGGER.debug("Client handshaken: {}", handshaken); checkAllActivities(); checkAllNotifications(); } /** * Get all notification messages * @return - notifications map */ public Map<String, Object> getNotifications() { return notifications; } /** * Get all activities messages * @return - activities map */ public Map<String, Object> getActivities() { return activities; } /** * Get all query response messages * @return - query response map */ public Map<String, Object> getQueryResponse() { return queryResponse; } /** * Retrieves all persisted notification. * The notifications channel returns any persisted notifications when it receives an empty message on the channel */ public void checkAllNotifications() { LOGGER.debug("Checking all notifications"); client.getChannel(NOTIFICATIONS_CHANNEL) .publish(emptyMessage); } /** * Retrieves download notifications */ public void checkDownloadNotifications() { LOGGER.debug("Checking all Downloads"); client.getChannel(DOWNLOADS_CHANNEL) .publish(emptyMessage); } /** * Retrieves all persisted activities. * The activities channel returns any persisted activities when it receives an empty message on the channel */ public void checkAllActivities() { LOGGER.debug("Checking all activities"); client.getChannel(ACTIVITIES_CHANNEL) .publish(emptyMessage); } /** * Initiate download of a product. * @param catalogId - Catalog ID of the product to download. * @param sourceName - Name of the source to download the product from. * @throws MalformedURLException */ public void downloadById(String catalogId, String sourceName) throws MalformedURLException { String downloadUrl = url + DOWNLOAD_CONTEXT + sourceName + "/" + catalogId + DOWNLOAD_TRANSFORM + "&session=" + asyncClientId; Thread downloadManager = new Thread(new DownloadManager(downloadUrl, catalogId), catalogId); LOGGER.debug("Starting download for: {}", catalogId); downloadManager.start(); } // Trust All Certifications private void doTrustAllCertificates() throws NoSuchAlgorithmException, KeyManagementException { TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { return; } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { return; } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }}; // Set HttpsURLConnection settings SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); HostnameVerifier hostnameVerifier = (s, sslSession) -> s.equalsIgnoreCase(sslSession.getPeerHost()); HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); } }