ddf.catalog.test.TestFederation.java Source code

Java tutorial

Introduction

Here is the source code for ddf.catalog.test.TestFederation.java

Source

/**
 * Copyright (c) Codice Foundation
 * 
 * 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.
 * 
 * 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 ddf.catalog.test;

import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.editConfigurationFilePut;
import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.logLevel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.apache.karaf.tooling.exam.options.KarafDistributionKitConfigurationOption.Platform;
import org.apache.karaf.tooling.exam.options.KarafDistributionOption;
import org.apache.karaf.tooling.exam.options.LogLevelOption.LogLevel;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.ExamReactorStrategy;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.ops4j.pax.exam.spi.reactors.EagerSingleStagedReactorFactory;
import org.osgi.service.cm.Configuration;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.LoggerFactory;
import org.slf4j.ext.XLogger;

import ddf.catalog.data.Metacard;
import ddf.catalog.operation.ResourceResponse;
import ddf.catalog.resource.ResourceNotFoundException;
import ddf.catalog.source.FederatedSource;

/**
 * Tests Federation aspects.
 * 
 * @author Ashraf Barakat
 * @author Phillip Klinefelter
 * @author ddf.isgs@lmco.com
 * 
 */
@RunWith(JUnit4TestRunner.class)
@ExamReactorStrategy(EagerSingleStagedReactorFactory.class)
public class TestFederation extends AbstractIntegrationTest {

    private static XLogger LOGGER = new XLogger(LoggerFactory.getLogger(TestFederation.class));

    private static final String SAMPLE_DATA = "sample data";

    private static final int XML_RECORD_INDEX = 1;

    private static final int GEOJSON_RECORD_INDEX = 0;

    /*
     * Temporary fix - Added separate ports to not clash with other distros running.
     */
    protected static final String HTTP_PORT = "9082";

    protected static final String HTTPS_PORT = "9994";

    protected static final String SSH_PORT = "9102";

    protected static final String RMI_SERVER_PORT = "44446";

    protected static final String RMI_REG_PORT = "1101";

    /* ************************ */

    private static final String DEFAULT_KEYWORD = "text";

    private static final String RECORD_TITLE_1 = "myTitle";

    private static final String RECORD_TITLE_2 = "myXmlTitle";

    private static final String SOLR_CONFIG_PID = "ddf.catalog.source.solr.SolrCatalogProvider";

    private static final String CATALOG_SYMBOLIC_NAME_PREFIX = "catalog-";

    private static final String SERVICE_ROOT = "http://localhost:" + HTTP_PORT + "/services";

    private static final String REST_PATH = SERVICE_ROOT + "/catalog/";

    private static final String OPENSEARCH_PATH = REST_PATH + "query/";

    private static final String REMOTE_SITE_NAME = "remoteSite";

    /*
     * The fields must be static if they are purposely used across all test methods.
     */
    private static boolean ranBefore = false;

    private static String[] metacardIds = new String[2];

    private static FederatedSource source;

    /**
     * Configures the pax exam test container
     * 
     * @return list of pax exam options
     */
    @Override
    @org.ops4j.pax.exam.junit.Configuration
    public Option[] config() {
        // @formatter:off
        return options(getPlatformOption(Platform.WINDOWS), getPlatformOption(Platform.NIX),
                logLevel(LogLevel.INFO),
                //                KarafDistributionOption.keepRuntimeFolder(),
                mavenBundle("junit", "junit", "4.10"),
                mavenBundle("org.apache.httpcomponents", "httpcore-osgi", "4.2.1"),
                mavenBundle("org.apache.httpcomponents", "httpclient-osgi", "4.2.1"),
                editConfigurationFilePut("etc/org.apache.karaf.shell.cfg", "sshPort", SSH_PORT),
                editConfigurationFilePut("etc/org.ops4j.pax.web.cfg", "org.osgi.service.http.port", HTTP_PORT),
                editConfigurationFilePut("etc/org.ops4j.pax.web.cfg", "org.osgi.service.http.port.secure",
                        HTTPS_PORT),
                editConfigurationFilePut("etc/org.apache.karaf.management.cfg", "rmiRegistryPort", RMI_REG_PORT),
                editConfigurationFilePut("etc/org.apache.karaf.management.cfg", "rmiServerPort", RMI_SERVER_PORT));
        // @formatter:on
    }

    /**
     * Runs each time before each test, items that don't need to be run each time have a conditional
     * flag.
     * 
     * @throws InterruptedException
     * @throws IOException
     */
    @Before
    public void beforeTest() throws InterruptedException, IOException {

        if (!ranBefore) {
            LOGGER.info("Running one-time only setup.");
            setLogLevels();
            waitForRequiredBundles(CATALOG_SYMBOLIC_NAME_PREFIX);
            this.catalogProvider = waitForCatalogProviderToBeAvailable();
            setSolrSoftCommit();

            FederatedSourceProperties sourceProperties = new FederatedSourceProperties();

            createManagedService(FederatedSourceProperties.FACTORY_PID,
                    sourceProperties.createDefaultProperties(String.valueOf(HTTP_PORT), REMOTE_SITE_NAME), 1000);

            this.source = waitForFederatedSource(5000);
            File file = new File("sample.txt");
            file.createNewFile();
            FileUtils.write(file, SAMPLE_DATA);
            String fileLocation = file.toURI().toURL().toString();
            metacardIds[GEOJSON_RECORD_INDEX] = ingest(Library.getSimpleGeoJson(), "application/json");

            LOGGER.debug("File Location: {}", fileLocation);
            metacardIds[XML_RECORD_INDEX] = ingest(Library.getSimpleXml(fileLocation), "text/xml");
            ranBefore = true;
        }

    }

    /**
     * Given what was ingested in beforeTest(), tests that a Federated wildcard search will return
     * all appropriate record(s).
     * 
     * @throws Exception
     */
    @Test
    public void testFederatedQueryByWildCardSearchPhrase() throws Exception {

        // given

        // when
        String queryUrl = OPENSEARCH_PATH + "?q=*&format=xml&src=" + REMOTE_SITE_NAME;

        String result = read(queryUrl);

        // then
        assertNotNull(result);
        assertTrue("Record should include the first record title.", result.contains(RECORD_TITLE_1));
        assertTrue("Record should include the second record title.", result.contains(RECORD_TITLE_2));

    }

    /**
     * Given what was ingested in beforeTest(), tests that a Federated search phrase will return the
     * appropriate record(s).
     * 
     * @throws Exception
     */
    @Test
    public void testFederatedQueryBySearchPhrase() throws Exception {

        // given

        // when
        String queryUrl = OPENSEARCH_PATH + "?q=" + DEFAULT_KEYWORD + "&format=xml&src=" + REMOTE_SITE_NAME;

        String result = read(queryUrl);

        // then
        assertNotNull(result);
        assertTrue("Record should include the first record title.", result.contains(RECORD_TITLE_1));
        assertTrue("Record should include the second record title.", result.contains(RECORD_TITLE_2));

    }

    /**
     * Tests that given a bad test phrase, no records should have been returned.
     * 
     * @throws Exception
     */
    @Test
    public void testFederatedQueryByNegativeSearchPhrase() throws Exception {

        // given

        // when
        String negativeSearchPhrase = "negative";
        String queryUrl = OPENSEARCH_PATH + "?q=" + negativeSearchPhrase + "&format=xml&src=" + REMOTE_SITE_NAME;
        String result = read(queryUrl);

        // then
        assertNotNull(result);
        assertTrue("No records should have been returned.", !result.contains(RECORD_TITLE_1));

    }

    /**
     * Tests that a federated search by ID will return the right record.
     * 
     * @throws Exception
     */
    @Test
    public void testFederatedQueryById() throws Exception {

        // given

        // when
        String restUrl = REST_PATH + "sources/" + REMOTE_SITE_NAME + "/" + metacardIds[GEOJSON_RECORD_INDEX];

        String result = read(restUrl);

        // then
        assertNotNull(result);
        LOGGER.debug("testFederatedQueryById result\n" + result);
        assertTrue("Record should include the right title.", result.contains(RECORD_TITLE_1));

    }

    /**
     * Tests Source can retrieve product existing product.
     * 
     * @throws Exception
     */
    @Test
    public void testFederatedRetrieveExistingProduct() throws Exception {

        // given
        Map<String, Serializable> requestProperties = new Hashtable<String, Serializable>();
        requestProperties.put(Metacard.ID, metacardIds[XML_RECORD_INDEX]);

        // when
        ResourceResponse response = source.retrieveResource(null, requestProperties);

        // then
        String mimeTypeValue = response.getResource().getMimeTypeValue();
        LOGGER.info("MimeType returned [{}]", mimeTypeValue);
        assertEquals("text/plain", mimeTypeValue);
        assertEquals(SAMPLE_DATA, IOUtils.toString(response.getResource().getInputStream()));
    }

    /**
     * Tests Source can retrieve nonexistent product.
     * 
     * @throws Exception
     */
    @Test(expected = ResourceNotFoundException.class)
    public void testFederatedRetrieveNoProduct() throws Exception {

        // given

        Map<String, Serializable> requestProperties = new Hashtable<String, Serializable>();
        requestProperties.put(Metacard.ID, metacardIds[GEOJSON_RECORD_INDEX]);

        // when
        source.retrieveResource(null, requestProperties);

        // then
        // exception thrown, see expected
    }

    private FederatedSource waitForFederatedSource(long timeout) throws InterruptedException {
        ServiceTracker st = new ServiceTracker(bundleCtx, FederatedSource.class.getName(), null);

        st.open();

        FederatedSource source = (FederatedSource) st.waitForService(timeout);

        long millisWaited = 0;

        boolean available = source.isAvailable();
        while (!available && millisWaited < timeout) {
            Thread.sleep(100);
            available = source.isAvailable();
        }

        if (!available) {
            fail("Federated Source was not created in a timely manner.");
        }

        return source;
    }

    private String read(String getUrl) throws IOException, ClientProtocolException {
        DefaultHttpClient httpclient = new DefaultHttpClient();

        HttpGet getCall = new HttpGet(getUrl);

        HttpResponse response = httpclient.execute(getCall);

        String result = IOUtils.toString(response.getEntity().getContent());
        return result;
    }

    private String ingest(String data, String mimeType)
            throws UnsupportedEncodingException, IOException, ClientProtocolException {

        DefaultHttpClient httpclient = new DefaultHttpClient();
        HttpPost postCall = new HttpPost(REST_PATH);
        postCall.setEntity(new StringEntity(data));
        postCall.setHeader(HTTP.CONTENT_TYPE, mimeType);
        HttpResponse postResponse = httpclient.execute(postCall);
        EntityUtils.consume(postResponse.getEntity());
        String id = postResponse.getFirstHeader("id").getValue();
        assertTrue("Ingest failed, Header not returned.", id != null);
        return id;

    }

    private void setSolrSoftCommit() throws IOException {
        Configuration solrConfig = configAdmin.getConfiguration(SOLR_CONFIG_PID, null);
        Dictionary<String, Object> properties = new Hashtable<String, Object>();
        properties.put("forceAutoCommit", "true");
        solrConfig.update(properties);
    }

    public class FederatedSourceProperties extends Hashtable<String, Object> {

        public static final String FACTORY_PID = "OpenSearchSource";

        public Dictionary<String, Object> createDefaultProperties(String port, String remoteSiteName) {

            this.put("endpointUrl", "http://localhost:" + port
                    + "/services/catalog/query?q={searchTerms}&src={fs:routeTo?}&mr={fs:maxResults?}&count={count?}&mt={fs:maxTimeout?}&dn={idn:userDN?}&lat={geo:lat?}&lon={geo:lon?}&radius={geo:radius?}&bbox={geo:box?}&polygon={geo:polygon?}&dtstart={time:start?}&dtend={time:end?}&dateName={cat:dateName?}&filter={fsa:filter?}&sort={fsa:sort?}");
            this.put("localQueryOnly", "true");
            this.put("shouldConvertToBBox", "true");
            this.put("trustStoreLocation", "trustStore.jks");
            this.put("trustStorePassword", "password");
            this.put("keyStoreLocation", "keyStore.jks");
            this.put("keyStorePassword", "password");
            this.put("shortname", remoteSiteName);

            return this;
        }

    }

}