org.codice.ddf.admin.sources.csw.CswSourceUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.admin.sources.csw.CswSourceUtils.java

Source

/**
 * 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.admin.sources.csw;

import static java.net.HttpURLConnection.HTTP_OK;
import static org.codice.ddf.admin.api.handler.commons.SourceHandlerCommons.SOURCES_NAMESPACE_CONTEXT;
import static org.codice.ddf.admin.api.services.CswServiceProperties.CSW_GMD_FACTORY_PID;
import static org.codice.ddf.admin.api.services.CswServiceProperties.CSW_PROFILE_FACTORY_PID;
import static org.codice.ddf.admin.api.services.CswServiceProperties.CSW_SPEC_FACTORY_PID;
import static org.codice.ddf.admin.sources.SourcesCommons.closeClientAndResponse;
import static org.codice.ddf.admin.sources.SourcesCommons.getCloseableHttpClient;

import java.util.List;
import java.util.Optional;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.codice.ddf.admin.api.config.sources.CswSourceConfiguration;
import org.codice.ddf.admin.api.handler.commons.UrlAvailability;
import org.w3c.dom.Document;

import com.google.common.collect.ImmutableList;
import com.google.common.net.HttpHeaders;

public class CswSourceUtils {

    public static final String GET_CAPABILITIES_PARAMS = "?service=CSW&request=GetCapabilities";

    private static final List<String> URL_FORMATS = ImmutableList.of("https://%s:%d/services/csw",
            "https://%s:%d/csw", "http://%s:%d/services/csw", "http://%s:%d/csw");

    private static final List<String> CSW_MIME_TYPES = ImmutableList.of("text/xml", "application/xml");

    private static final String GMD_OUTPUT_SCHEMA = "http://www.isotc211.org/2005/gmd";

    private static final String HAS_CATALOG_METACARD_EXP = "//ows:OperationsMetadata//ows:Operation[@name='GetRecords']/ows:Parameter[@name='OutputSchema' or @name='outputSchema']/ows:Value/text()='urn:catalog:metacard'";

    private static final String HAS_GMD_ISO_EXP = "//ows:OperationsMetadata/ows:Operation[@name='GetRecords']/ows:Parameter[@name='OutputSchema' or @name='outputSchema']/ows:Value/text()='http://www.isotc211.org/2005/gmd'";

    private static final String GET_FIRST_OUTPUT_SCHEMA = "//ows:OperationsMetadata/ows:Operation[@name='GetRecords']/ows:Parameter[@name='OutputSchema' or @name='outputSchema']/ows:Value[1]/text()";

    // Given a config with an endpoint URL, determines if that URL is a functional CSW endpoint.
    public UrlAvailability getUrlAvailability(String url, String un, String pw) {
        UrlAvailability result = new UrlAvailability(url);
        String contentType;
        int status;
        url += GET_CAPABILITIES_PARAMS;
        HttpGet request = new HttpGet(url);
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        if (url.startsWith("https") && un != null && pw != null) {
            byte[] auth = Base64.encodeBase64((un + ":" + pw).getBytes());
            request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + new String(auth));
        }
        try {
            client = getCloseableHttpClient(false);
            response = client.execute(request);
            status = response.getStatusLine().getStatusCode();
            contentType = response.getEntity().getContentType().getValue();
            if (status == HTTP_OK && CSW_MIME_TYPES.contains(contentType)) {
                return result.trustedCertAuthority(true).certError(false).available(true);
            } else {
                return result.trustedCertAuthority(true).certError(false).available(false);
            }
        } catch (SSLPeerUnverifiedException e) {
            // This is the hostname != cert name case - if this occurs, the URL's SSL cert configuration
            // is incorrect, or a serious network security issue has occurred.
            return result.trustedCertAuthority(false).certError(true).available(false);
        } catch (Exception e) {
            try {
                // We want to trust any root CA, but maintain all other standard SSL checks
                client = getCloseableHttpClient(true);
                response = client.execute(request);
                status = response.getStatusLine().getStatusCode();
                contentType = response.getEntity().getContentType().getValue();
                if (status == HTTP_OK && CSW_MIME_TYPES.contains(contentType)) {
                    return result.trustedCertAuthority(false).certError(false).available(true);
                }
            } catch (Exception e1) {
                return result.trustedCertAuthority(false).certError(false).available(false);
            }
        } finally {
            closeClientAndResponse(client, response);
        }
        return result;
    }

    // Given a configuration, determines the preferred CSW source type and output schema and returns
    // a config with the appropriate factoryPid and Output Schema.
    public Optional<CswSourceConfiguration> getPreferredConfig(CswSourceConfiguration config) {
        CswSourceConfiguration preferred = new CswSourceConfiguration(config);
        HttpGet getCapabilitiesRequest = new HttpGet(preferred.endpointUrl() + GET_CAPABILITIES_PARAMS);
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        if (config.endpointUrl().startsWith("https") && config.sourceUserName() != null
                && config.sourceUserPassword() != null) {
            byte[] auth = Base64
                    .encodeBase64((config.sourceUserName() + ":" + config.sourceUserPassword()).getBytes());
            getCapabilitiesRequest.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + new String(auth));
        }
        XPath xpath = XPathFactory.newInstance().newXPath();
        xpath.setNamespaceContext(SOURCES_NAMESPACE_CONTEXT);
        try {
            client = getCloseableHttpClient(true);
            response = client.execute(getCapabilitiesRequest);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document capabilitiesXml = builder.parse(response.getEntity().getContent());
            if ((Boolean) xpath.compile(HAS_CATALOG_METACARD_EXP).evaluate(capabilitiesXml,
                    XPathConstants.BOOLEAN)) {
                return Optional.of((CswSourceConfiguration) preferred.factoryPid(CSW_PROFILE_FACTORY_PID));
            } else if ((Boolean) xpath.compile(HAS_GMD_ISO_EXP).evaluate(capabilitiesXml, XPathConstants.BOOLEAN)) {
                return Optional.of(((CswSourceConfiguration) preferred.factoryPid(CSW_GMD_FACTORY_PID))
                        .outputSchema(GMD_OUTPUT_SCHEMA));
            } else {
                return Optional.of(((CswSourceConfiguration) (preferred.factoryPid(CSW_SPEC_FACTORY_PID)))
                        .outputSchema(xpath.compile(GET_FIRST_OUTPUT_SCHEMA).evaluate(capabilitiesXml)));
            }
        } catch (Exception e) {
            return Optional.empty();
        } finally {
            closeClientAndResponse(client, response);
        }
    }

    // Determines the correct CSW endpoint URL format given a config with a Hostname and Port
    public UrlAvailability confirmEndpointUrl(CswSourceConfiguration config) {
        Optional<UrlAvailability> result = URL_FORMATS.stream()
                .map(formatUrl -> String.format(formatUrl, config.sourceHostName(), config.sourcePort()))
                .map(url -> getUrlAvailability(url, config.sourceUserName(), config.sourceUserPassword()))
                .filter(avail -> avail.isAvailable() || avail.isCertError()).findFirst();
        return result.isPresent() ? result.get() : null;
    }
}